From 5f64321c2abb23c08368e44ff69944196de1d4db Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 4 Aug 2020 17:52:16 +0300 Subject: [PATCH 001/203] fix compilation on older qt versions --- core/networkaccess.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 8c120d8..2d66a70 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -334,7 +334,11 @@ void Core::NetworkAccess::startDownload(const QString& messageId, const QString& QNetworkRequest req(url); dwn->reply = manager->get(req); connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(dwn->reply, qOverload(&QNetworkReply::errorOccurred), this, &NetworkAccess::onDownloadError); +#else + connect(dwn->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onDownloadError); +#endif connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished); downloads.insert(std::make_pair(url, dwn)); emit downloadFileProgress(messageId, 0); @@ -414,7 +418,12 @@ void Core::NetworkAccess::startUpload(const QString& messageId, const QString& u upl->reply = manager->put(req, file); connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(upl->reply, qOverload(&QNetworkReply::errorOccurred), this, &NetworkAccess::onUploadError); +#else + connect(upl->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onUploadError); +#endif + connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished); uploads.insert(std::make_pair(url, upl)); emit downloadFileProgress(messageId, 0); @@ -490,7 +499,11 @@ void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& pa upl->reply = manager->put(req, file); connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(upl->reply, qOverload(&QNetworkReply::errorOccurred), this, &NetworkAccess::onUploadError); +#else + connect(upl->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onUploadError); +#endif connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished); uploads.insert(std::make_pair(put.toString(), upl)); emit downloadFileProgress(messageId, 0); From 3a120c773a9c1db16c544303b24331cd27e8326a Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 8 Aug 2020 02:33:03 +0300 Subject: [PATCH 002/203] reconnection issues --- CHANGELOG.md | 8 ++++++ core/account.cpp | 11 +++++--- core/account.h | 2 +- core/squawk.cpp | 54 ++++++++++++++++++++++--------------- ui/models/item.cpp | 5 ++-- ui/widgets/conversation.cpp | 16 +++++++++++ ui/widgets/conversation.h | 1 + 7 files changed, 67 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1d31c..3e1ebd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Squawk 0.2.0 (Unreleased) +### Bug fixes +- carbon copies switches on again after reconnection +- requesting the history of the current chat after reconnection +- global availability (in drop down list) gets restored after reconnection +- status icon in active chat changes when presence of the pen pal changes + + ## Squawk 0.1.5 (Jul 29, 2020) ### Bug fixes - error with sending attached files to the conference diff --git a/core/account.cpp b/core/account.cpp index 0c8c272..094fd3c 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -183,7 +183,6 @@ void Core::Account::connect() reconnectTimer->stop(); } if (state == Shared::ConnectionState::disconnected) { - qDebug() << presence.availableStatusType(); client.connectToServer(config, presence); } else { qDebug("An attempt to connect an account which is already connected, skipping"); @@ -219,6 +218,7 @@ void Core::Account::onClientStateChange(QXmppClient::State st) Shared::ConnectionState os = state; state = Shared::ConnectionState::connected; if (os == Shared::ConnectionState::connecting) { + qDebug() << "running service discovery for account" << name; dm->requestItems(getServer()); dm->requestInfo(getServer()); } @@ -238,9 +238,8 @@ void Core::Account::onClientStateChange(QXmppClient::State st) } break; case QXmppClient::DisconnectedState: { - cancelHistoryRequests(); - pendingVCardRequests.clear(); if (state != Shared::ConnectionState::disconnected) { + handleDisconnection(); state = Shared::ConnectionState::disconnected; emit connectionStateChanged(state); } else { @@ -887,15 +886,18 @@ void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) { + qDebug() << "Discovery info received for account" << name; if (info.from() == getServer()) { if (info.features().contains("urn:xmpp:carbons:2")) { + qDebug() << "Enabling carbon copies for account" << name; cm->setCarbonsEnabled(true); } } } -void Core::Account::cancelHistoryRequests() +void Core::Account::handleDisconnection() { + cm->setCarbonsEnabled(false); rh->handleOffline(); archiveQueries.clear(); pendingVCardRequests.clear(); @@ -903,6 +905,7 @@ void Core::Account::cancelHistoryRequests() for (const QString& jid : pendingVCardRequests) { emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error } + pendingVCardRequests.clear(); ownVCardRequestInProgress = false; } diff --git a/core/account.h b/core/account.h index d7ca113..49c7ca9 100644 --- a/core/account.h +++ b/core/account.h @@ -185,7 +185,7 @@ private slots: void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); private: - void cancelHistoryRequests(); + void handleDisconnection(); void onReconnectTimer(); }; diff --git a/core/squawk.cpp b/core/squawk.cpp index 9bb2f14..1689d71 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -223,30 +223,40 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}}); - switch (p_state) { - case Shared::ConnectionState::disconnected: { - bool equals = true; - for (Accounts::const_iterator itr = accounts.begin(), end = accounts.end(); itr != end; itr++) { - if ((*itr)->getState() != Shared::ConnectionState::disconnected) { - equals = false; - } - } - if (equals && state != Shared::Availability::offline) { - state = Shared::Availability::offline; - emit stateChanged(state); - } - } - break; - case Shared::ConnectionState::connected: #ifdef WITH_KWALLET - if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) { - kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true); - } -#endif - break; - default: - break; + if (p_state == Shared::ConnectionState::connected) { + if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) { + kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true); + } } +#endif + + Accounts::const_iterator itr = accounts.begin(); + bool es = true; + bool ea = true; + Shared::ConnectionState cs = (*itr)->getState(); + Shared::Availability av = (*itr)->getAvailability(); + itr++; + for (Accounts::const_iterator end = accounts.end(); itr != end; itr++) { + Account* item = *itr; + if (item->getState() != cs) { + es = false; + } + if (item->getAvailability() != av) { + ea = false; + } + } + + if (es) { + if (cs == Shared::ConnectionState::disconnected) { + state = Shared::Availability::offline; + emit stateChanged(state); + } else if (ea) { + state = av; + emit stateChanged(state); + } + } + } void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap& data) diff --git a/ui/models/item.cpp b/ui/models/item.cpp index c5d6e2a..e006ad0 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -215,9 +215,8 @@ void Models::Item::_removeChild(int index) void Models::Item::changed(int col) { - if (parent != nullptr) { - emit childChanged(this, row(), col); - } + + emit childChanged(this, row(), col); } void Models::Item::toOfflineState() diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 60b0210..fd87d9f 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -54,6 +54,8 @@ Conversation::Conversation(bool muc, Models::Account* acc, const QString pJid, c { m_ui->setupUi(this); + connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged); + filesLayout = new FlowLayout(m_ui->filesPanel, 0); m_ui->filesPanel->setLayout(filesLayout); @@ -121,6 +123,20 @@ Conversation::~Conversation() { } +void Conversation::onAccountChanged(Models::Item* item, int row, int col) +{ + if (item == account) { + if (col == 2 && account->getState() == Shared::ConnectionState::connected) { + if (!requestingHistory) { + requestingHistory = true; + line->showBusyIndicator(); + emit requestArchive(""); + scroll = down; + } + } + } +} + void Conversation::applyVisualEffects() { DropShadowEffect *e1 = new DropShadowEffect; diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index e5ac53a..ea87607 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -121,6 +121,7 @@ protected slots: void onBadgeClose(); void onClearButton(); void onTextEditDocSizeChanged(const QSizeF& size); + void onAccountChanged(Models::Item* item, int row, int col); public: const bool isMuc; From ef1a9846bf7a0b8b160b52e58c4e80078f782d9b Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 9 Aug 2020 19:28:03 +0300 Subject: [PATCH 003/203] found a way to avoid requesting all the MUCs history alltogether on startup --- CHANGELOG.md | 3 +++ core/archive.cpp | 20 ++++++++++++++++++++ core/archive.h | 1 + core/rosteritem.cpp | 16 ++++++++++------ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e1ebd4..06c4ce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - global availability (in drop down list) gets restored after reconnection - status icon in active chat changes when presence of the pen pal changes +### Improvements +- slightly reduced the traffic on the startup by not requesting history of all MUCs + ## Squawk 0.1.5 (Jul 29, 2020) ### Bug fixes diff --git a/core/archive.cpp b/core/archive.cpp index 628723d..a1f8b76 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -214,6 +214,26 @@ Shared::Message Core::Archive::getElement(const QString& id) const } } +bool Core::Archive::hasElement(const QString& id) const +{ + if (!opened) { + throw Closed("hasElement", jid.toStdString()); + } + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + + bool has; + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.toStdString().c_str(); + int rc = mdb_get(txn, main, &lmdbKey, &lmdbData); + has = rc == 0; + mdb_txn_abort(txn); + + return has; +} + Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn) const { MDB_val lmdbKey, lmdbData; diff --git a/core/archive.h b/core/archive.h index b71a8be..dd7a167 100644 --- a/core/archive.h +++ b/core/archive.h @@ -46,6 +46,7 @@ public: bool addElement(const Shared::Message& message); unsigned int addElements(const std::list& messages); Shared::Message getElement(const QString& id) const; + bool hasElement(const QString& id) const; void changeMessage(const QString& id, const QMap& data); Shared::Message oldest(); QString oldestId(); diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 5275993..32b70f4 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -248,18 +248,22 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) } break; case beginning: - appendCache.push_back(msg); - if (!syncronizing) { - requestHistory(-1, getId(msg)); + if (!archive->hasElement(msg.getId())) { + appendCache.push_back(msg); + if (!syncronizing) { + requestHistory(-1, getId(msg)); + } } break; case end: archive->addElement(msg); break; case chunk: - appendCache.push_back(msg); - if (!syncronizing) { - requestHistory(-1, getId(msg)); + if (!archive->hasElement(msg.getId())) { + appendCache.push_back(msg); + if (!syncronizing) { + requestHistory(-1, getId(msg)); + } } break; case complete: From 38159eafeb67d3991879d7822d66e1f594449806 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 12 Aug 2020 01:49:51 +0300 Subject: [PATCH 004/203] first compiling prototype, doesnt work yet --- ui/CMakeLists.txt | 6 ++ ui/models/contact.cpp | 195 +---------------------------------- ui/models/contact.h | 33 +----- ui/models/element.cpp | 151 +++++++++++++++++++++++++++ ui/models/element.h | 70 +++++++++++++ ui/models/messagefeed.cpp | 145 ++++++++++++++++++++++++++ ui/models/messagefeed.h | 100 ++++++++++++++++++ ui/models/presence.cpp | 71 +------------ ui/models/presence.h | 15 --- ui/models/room.cpp | 123 +--------------------- ui/models/room.h | 30 +----- ui/models/roster.cpp | 48 +++++---- ui/models/roster.h | 6 +- ui/squawk.cpp | 62 ++--------- ui/squawk.h | 3 +- ui/widgets/chat.cpp | 42 +++----- ui/widgets/chat.h | 4 +- ui/widgets/conversation.cpp | 198 +++++++++++++++++------------------- ui/widgets/conversation.h | 4 +- ui/widgets/conversation.ui | 61 ++++------- ui/widgets/room.cpp | 29 +----- 21 files changed, 662 insertions(+), 734 deletions(-) create mode 100644 ui/models/element.cpp create mode 100644 ui/models/element.h create mode 100644 ui/models/messagefeed.cpp create mode 100644 ui/models/messagefeed.h diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 52913a8..0ed806f 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -9,6 +9,10 @@ set(CMAKE_AUTOUIC ON) # Find the QtWidgets library find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5DBus CONFIG REQUIRED) +find_package(Boost 1.36.0 CONFIG REQUIRED) +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) +endif() add_subdirectory(widgets) @@ -25,6 +29,8 @@ set(squawkUI_SRC models/abstractparticipant.cpp models/participant.cpp models/reference.cpp + models/messagefeed.cpp + models/element.cpp utils/messageline.cpp utils//message.cpp utils/resizer.cpp diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 57744d8..d54fccf 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -17,55 +17,26 @@ */ #include "contact.h" -#include "account.h" #include Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap &data, Item *parentItem): - Item(Item::contact, data, parentItem), - jid(p_jid), + Element(Item::contact, acc, p_jid, data, parentItem), availability(Shared::Availability::offline), state(Shared::SubscriptionState::none), - avatarState(Shared::Avatar::empty), presences(), - messages(), - childMessages(0), - status(), - avatarPath(), - account(acc) + status() { QMap::const_iterator itr = data.find("state"); if (itr != data.end()) { setState(itr.value().toUInt()); } - - itr = data.find("avatarState"); - if (itr != data.end()) { - setAvatarState(itr.value().toUInt()); - } - itr = data.find("avatarPath"); - if (itr != data.end()) { - setAvatarPath(itr.value().toString()); - } } Models::Contact::~Contact() { } -QString Models::Contact::getJid() const -{ - return jid; -} - -void Models::Contact::setJid(const QString p_jid) -{ - if (jid != p_jid) { - jid = p_jid; - changed(1); - } -} - void Models::Contact::setAvailability(unsigned int p_state) { setAvailability(Shared::Global::fromInt(p_state)); @@ -144,16 +115,12 @@ void Models::Contact::update(const QString& field, const QVariant& value) { if (field == "name") { setName(value.toString()); - } else if (field == "jid") { - setJid(value.toString()); } else if (field == "availability") { setAvailability(value.toUInt()); } else if (field == "state") { setState(value.toUInt()); - } else if (field == "avatarState") { - setAvatarState(value.toUInt()); - } else if (field == "avatarPath") { - setAvatarPath(value.toString()); + } else { + Element::update(field, value); } } @@ -192,11 +159,9 @@ void Models::Contact::refresh() { QDateTime lastActivity; Presence* presence = 0; - unsigned int count = 0; for (QMap::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { Presence* pr = itr.value(); QDateTime la = pr->getLastActivity(); - count += pr->getMessagesCount(); if (la > lastActivity) { lastActivity = la; @@ -211,11 +176,6 @@ void Models::Contact::refresh() setAvailability(Shared::Availability::offline); setStatus(""); } - - if (childMessages != count) { - childMessages = count; - changed(4); - } } void Models::Contact::_removeChild(int index) @@ -257,81 +217,6 @@ QIcon Models::Contact::getStatusIcon(bool big) const } } -void Models::Contact::addMessage(const Shared::Message& data) -{ - const QString& res = data.getPenPalResource(); - if (res.size() > 0) { - QMap::iterator itr = presences.find(res); - if (itr == presences.end()) { - // this is actually the place when I can spot someone's invisible presence, and there is nothing criminal in it, cuz the sender sent us a message - // therefore he have revealed himself - // the only issue is to find out when the sender is gone offline - Presence* pr = new Presence({}); - pr->setName(res); - pr->setAvailability(Shared::Availability::invisible); - pr->setLastActivity(QDateTime::currentDateTimeUtc()); - presences.insert(res, pr); - appendChild(pr); - pr->addMessage(data); - return; - } - itr.value()->addMessage(data); - } else { - messages.emplace_back(data); - changed(4); - } -} - -void Models::Contact::changeMessage(const QString& id, const QMap& data) -{ - - bool found = false; - for (Shared::Message& msg : messages) { - if (msg.getId() == id) { - msg.change(data); - found = true; - break; - } - } - if (!found) { - for (Presence* pr : presences) { - found = pr->changeMessage(id, data); - if (found) { - break; - } - } - } -} - -unsigned int Models::Contact::getMessagesCount() const -{ - return messages.size() + childMessages; -} - -void Models::Contact::dropMessages() -{ - if (messages.size() > 0) { - messages.clear(); - changed(4); - } - - for (QMap::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { - itr.value()->dropMessages(); - } -} - -void Models::Contact::getMessages(Models::Contact::Messages& container) const -{ - for (Messages::const_iterator itr = messages.begin(), end = messages.end(); itr != end; ++itr) { - const Shared::Message& msg = *itr; - container.push_back(msg); - } - - for (QMap::const_iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { - itr.value()->getMessages(container); - } -} - void Models::Contact::toOfflineState() { std::deque::size_type size = childItems.size(); @@ -355,75 +240,3 @@ QString Models::Contact::getDisplayedName() const return getContactName(); } -bool Models::Contact::columnInvolvedInDisplay(int col) -{ - return Item::columnInvolvedInDisplay(col) && col == 1; -} - -Models::Contact * Models::Contact::copy() const -{ - Contact* cnt = new Contact(*this); - return cnt; -} - -Models::Contact::Contact(const Models::Contact& other): - Item(other), - jid(other.jid), - availability(other.availability), - state(other.state), - presences(), - messages(other.messages), - childMessages(0), - account(other.account) -{ - for (const Presence* pres : other.presences) { - Presence* pCopy = new Presence(*pres); - presences.insert(pCopy->getName(), pCopy); - Item::appendChild(pCopy); - connect(pCopy, &Item::childChanged, this, &Contact::refresh); - } - - refresh(); -} - -QString Models::Contact::getAvatarPath() const -{ - return avatarPath; -} - -Shared::Avatar Models::Contact::getAvatarState() const -{ - return avatarState; -} - -void Models::Contact::setAvatarPath(const QString& path) -{ - if (path != avatarPath) { - avatarPath = path; - changed(7); - } -} - -void Models::Contact::setAvatarState(Shared::Avatar p_state) -{ - if (avatarState != p_state) { - avatarState = p_state; - changed(6); - } -} - -void Models::Contact::setAvatarState(unsigned int p_state) -{ - if (p_state <= static_cast(Shared::Avatar::valid)) { - Shared::Avatar state = static_cast(p_state); - setAvatarState(state); - } else { - qDebug() << "An attempt to set invalid avatar state" << p_state << "to the contact" << jid << ", skipping"; - } -} - -const Models::Account * Models::Contact::getParentAccount() const -{ - return account; -} - diff --git a/ui/models/contact.h b/ui/models/contact.h index c8c99b5..7e76f5b 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -19,7 +19,7 @@ #ifndef MODELS_CONTACT_H #define MODELS_CONTACT_H -#include "item.h" +#include "element.h" #include "presence.h" #include "shared/enums.h" #include "shared/message.h" @@ -31,49 +31,34 @@ #include namespace Models { -class Account; -class Contact : public Item +class Contact : public Element { Q_OBJECT public: - typedef std::deque Messages; Contact(const Account* acc, const QString& p_jid, const QMap &data, Item *parentItem = 0); - Contact(const Contact& other); ~Contact(); - QString getJid() const; Shared::Availability getAvailability() const; Shared::SubscriptionState getState() const; - Shared::Avatar getAvatarState() const; - QString getAvatarPath() const; + QIcon getStatusIcon(bool big = false) const; int columnCount() const override; QVariant data(int column) const override; - void update(const QString& field, const QVariant& value); + void update(const QString& field, const QVariant& value) override; void addPresence(const QString& name, const QMap& data); void removePresence(const QString& name); QString getContactName() const; QString getStatus() const; - - void addMessage(const Shared::Message& data); - void changeMessage(const QString& id, const QMap& data); - unsigned int getMessagesCount() const; - void dropMessages(); - void getMessages(Messages& container) const; QString getDisplayedName() const override; - Contact* copy() const; - protected: void _removeChild(int index) override; void _appendChild(Models::Item * child) override; - bool columnInvolvedInDisplay(int col) override; - const Account* getParentAccount() const override; protected slots: void refresh(); @@ -84,23 +69,13 @@ protected: void setAvailability(unsigned int p_state); void setState(Shared::SubscriptionState p_state); void setState(unsigned int p_state); - void setAvatarState(Shared::Avatar p_state); - void setAvatarState(unsigned int p_state); - void setAvatarPath(const QString& path); - void setJid(const QString p_jid); void setStatus(const QString& p_state); private: - QString jid; Shared::Availability availability; Shared::SubscriptionState state; - Shared::Avatar avatarState; QMap presences; - Messages messages; - unsigned int childMessages; QString status; - QString avatarPath; - const Account* account; }; } diff --git a/ui/models/element.cpp b/ui/models/element.cpp new file mode 100644 index 0000000..98a54a5 --- /dev/null +++ b/ui/models/element.cpp @@ -0,0 +1,151 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "element.h" +#include "account.h" + +#include + +Models::Element::Element(Type p_type, const Models::Account* acc, const QString& p_jid, const QMap& data, Models::Item* parentItem): + Item(p_type, data, parentItem), + jid(p_jid), + avatarPath(), + avatarState(Shared::Avatar::empty), + account(acc), + feed(new MessageFeed()) +{ + connect(feed, &MessageFeed::requestArchive, this, &Element::requestArchive); + + QMap::const_iterator itr = data.find("avatarState"); + if (itr != data.end()) { + setAvatarState(itr.value().toUInt()); + } + itr = data.find("avatarPath"); + if (itr != data.end()) { + setAvatarPath(itr.value().toString()); + } +} + +Models::Element::~Element() +{ + delete feed; +} + + +QString Models::Element::getJid() const +{ + return jid; +} + +void Models::Element::setJid(const QString& p_jid) +{ + if (jid != p_jid) { + jid = p_jid; + changed(1); + } +} + +void Models::Element::update(const QString& field, const QVariant& value) +{ + if (field == "jid") { + setJid(value.toString()); + } else if (field == "avatarState") { + setAvatarState(value.toUInt()); + } else if (field == "avatarPath") { + setAvatarPath(value.toString()); + } +} + +QString Models::Element::getAvatarPath() const +{ + return avatarPath; +} + +Shared::Avatar Models::Element::getAvatarState() const +{ + return avatarState; +} + +void Models::Element::setAvatarPath(const QString& path) +{ + if (path != avatarPath) { + avatarPath = path; + if (type == contact) { + changed(7); + } else if (type == room) { + changed(8); + } + } +} + +void Models::Element::setAvatarState(Shared::Avatar p_state) +{ + if (avatarState != p_state) { + avatarState = p_state; + if (type == contact) { + changed(6); + } else if (type == room) { + changed(7); + } + } +} + +void Models::Element::setAvatarState(unsigned int p_state) +{ + if (p_state <= static_cast(Shared::Avatar::valid)) { + Shared::Avatar state = static_cast(p_state); + setAvatarState(state); + } else { + qDebug() << "An attempt to set invalid avatar state" << p_state << "to the element" << jid << ", skipping"; + } +} + +bool Models::Element::columnInvolvedInDisplay(int col) +{ + return Item::columnInvolvedInDisplay(col) && col == 1; +} + +const Models::Account * Models::Element::getParentAccount() const +{ + return account; +} + +unsigned int Models::Element::getMessagesCount() const +{ + return feed->unreadMessagesCount(); +} + +void Models::Element::addMessage(const Shared::Message& data) +{ + feed->addMessage(data); + if (type == contact) { + changed(4); + } else if (type == room) { + changed(5); + } +} + +void Models::Element::changeMessage(const QString& id, const QMap& data) +{ + +} + +void Models::Element::responseArchive(const std::list list) +{ + feed->responseArchive(list); +} diff --git a/ui/models/element.h b/ui/models/element.h new file mode 100644 index 0000000..29c4e76 --- /dev/null +++ b/ui/models/element.h @@ -0,0 +1,70 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ELEMENT_H +#define ELEMENT_H + +#include "item.h" +#include "messagefeed.h" + +namespace Models { + +class Element : public Item +{ + Q_OBJECT +protected: + Element(Type p_type, const Account* acc, const QString& p_jid, const QMap &data, Item *parentItem = 0); + ~Element(); + +public: + QString getJid() const; + Shared::Avatar getAvatarState() const; + QString getAvatarPath() const; + + virtual void update(const QString& field, const QVariant& value); + + void addMessage(const Shared::Message& data); + void changeMessage(const QString& id, const QMap& data); + unsigned int getMessagesCount() const; + void responseArchive(const std::list list); + +signals: + void requestArchive(const QString& before); + +protected: + void setJid(const QString& p_jid); + void setAvatarState(Shared::Avatar p_state); + void setAvatarState(unsigned int p_state); + void setAvatarPath(const QString& path); + bool columnInvolvedInDisplay(int col) override; + const Account* getParentAccount() const override; + +protected: + QString jid; + QString avatarPath; + Shared::Avatar avatarState; + + const Account* account; + +public: + MessageFeed* feed; +}; + +} + +#endif // ELEMENT_H diff --git a/ui/models/messagefeed.cpp b/ui/models/messagefeed.cpp new file mode 100644 index 0000000..bd86b67 --- /dev/null +++ b/ui/models/messagefeed.cpp @@ -0,0 +1,145 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "messagefeed.h" + +#include + +MessageFeed::MessageFeed(QObject* parent): + QAbstractListModel(parent), + storage(), + indexById(storage.get()), + indexByTime(storage.get