From aa1f728da949549a458f6e5f157e968fcd4f8b24 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 21 Apr 2019 22:17:04 +0300 Subject: [PATCH 1/6] some progress on history --- core/account.cpp | 24 ++++++- core/account.h | 2 +- core/archive.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++++- core/archive.h | 10 ++- core/contact.cpp | 54 +++++++++++++++- core/contact.h | 14 +++++ 6 files changed, 258 insertions(+), 7 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 9517c39..d6d36e4 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -427,8 +427,30 @@ void Core::Account::onMamMessageReceived(const QString& bareJid, const QXmppMess handleChatMessage(msg, false, true, true); } -void Core::Account::requestAchive(const QString& jid) +void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { + std::map::const_iterator itr = contacts.find(jid); + if (itr == contacts.end()) { + qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping"; + return; + } + Contact* contact = itr->second; + + contact->requestHistory(count, before); + Contact::ArchiveState as = contact->getArchiveState(); + switch (as) { + case Contact::empty: + break; + case Contact::beginning: + break; + case Contact::chunk: + break; + case Contact::complete: + break; + case Contact::end: + break; + } + QXmppResultSetQuery query; query.setMax(100); QDateTime from = QDateTime::currentDateTime().addDays(-7); diff --git a/core/account.h b/core/account.h index 72abb55..9b41cbf 100644 --- a/core/account.h +++ b/core/account.h @@ -41,7 +41,7 @@ public: void setAvailability(Shared::Availability avail); QString getFullJid() const; void sendMessage(const Shared::Message& data); - void requestAchive(const QString& jid); + void requestArchive(const QString& jid, int count, const QString& before); signals: void connectionStateChanged(int); diff --git a/core/archive.cpp b/core/archive.cpp index 70327a7..49df1c0 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -28,6 +28,7 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent): QObject(parent), jid(p_jid), opened(false), + fromTheBeginning(false), environment(), main(), order() @@ -54,14 +55,16 @@ void Core::Archive::open(const QString& account) } } - mdb_env_set_maxdbs(environment, 2); + mdb_env_set_maxdbs(environment, 3); mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); mdb_dbi_open(txn, "main", MDB_CREATE, &main); mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order); + mdb_dbi_open(txn, "order", MDB_CREATE, &stats); mdb_txn_commit(txn); + fromTheBeginning = _isFromTheBeginning(); opened = true; } } @@ -69,6 +72,7 @@ void Core::Archive::open(const QString& account) void Core::Archive::close() { if (opened) { + mdb_dbi_close(environment, stats); mdb_dbi_close(environment, order); mdb_dbi_close(environment, main); mdb_env_close(environment); @@ -76,7 +80,7 @@ void Core::Archive::close() } } -void Core::Archive::addElement(const Shared::Message& message) +bool Core::Archive::addElement(const Shared::Message& message) { if (!opened) { throw Closed("addElement", jid.toStdString()); @@ -95,7 +99,7 @@ void Core::Archive::addElement(const Shared::Message& message) MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); int rc; - rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0); + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); if (rc == 0) { MDB_val orderKey; orderKey.mv_size = 8; @@ -105,15 +109,19 @@ void Core::Archive::addElement(const Shared::Message& message) if (rc) { qDebug() << "An element couldn't be inserted into the index" << mdb_strerror(rc); mdb_txn_abort(txn); + return false; } else { rc = mdb_txn_commit(txn); if (rc) { qDebug() << "A transaction error: " << mdb_strerror(rc); + return false; } + return true; } } else { qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc); mdb_txn_abort(txn); + return false; } } @@ -237,3 +245,150 @@ long unsigned int Core::Archive::size() const mdb_txn_abort(txn); return stat.ms_entries; } + +std::list Core::Archive::getBefore(int count, const QString& id) +{ + if (!opened) { + throw Closed("getBefore", jid.toStdString()); + } + std::list res; + MDB_cursor* cursor; + MDB_txn *txn; + MDB_val lmdbKey, lmdbData; + int rc; + rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + if (id == "") { + rc = mdb_cursor_open(txn, order, &cursor); + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST); + if (rc) { + qDebug() << "Error geting before newest newest " << mdb_strerror(rc); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + throw new Empty(jid.toStdString()); + } else { + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + } + } else { + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.toStdString().c_str(); + rc = mdb_get(txn, main, &lmdbKey, &lmdbData); + if (rc) { + qDebug() <<"Error getting before: " << mdb_strerror(rc); + mdb_txn_abort(txn); + throw NotFound(id.toStdString(), jid.toStdString()); + } else { + QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + + Shared::Message msg; + msg.deserialize(ds); + quint64 stamp = msg.getTime().toMSecsSinceEpoch(); + lmdbKey.mv_data = (quint8*)&stamp; + lmdbKey.mv_size = 8; + + rc = mdb_cursor_open(txn, order, &cursor); + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET); + + if (rc) { + qDebug() << "Error getting before " << mdb_strerror(rc); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + throw new NotFound(id.toStdString(), jid.toStdString()); + } else { + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV); + if (rc) { + qDebug() << "Error getting before " << mdb_strerror(rc); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + throw new NotFound(id.toStdString(), jid.toStdString()); + } + } + } + } + + do { + QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + + res.emplace_back(); + Shared::Message& msg = res.back(); + msg.deserialize(ds); + + --count; + + } while (count > 0 && mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV) == 0); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + return res; +} + +bool Core::Archive::_isFromTheBeginning() +{ + std::string strKey = "beginning"; + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = strKey.size(); + lmdbKey.mv_data = (char*)strKey.c_str(); + + MDB_txn *txn; + int rc; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); + if (rc == MDB_NOTFOUND) { + mdb_txn_abort(txn); + return false; + } else if (rc) { + qDebug() <<"isFromTheBeginning error: " << mdb_strerror(rc); + mdb_txn_abort(txn); + throw NotFound(strKey, jid.toStdString()); + } else { + uint8_t value = *(uint8_t*)(lmdbData.mv_data); + bool is; + if (value == 144) { + is = false; + } else if (value == 72) { + is = true; + } else { + qDebug() <<"isFromTheBeginning error: stored value doesn't match any magic number, the answer is most probably wrong"; + } + return is; + } +} + +bool Core::Archive::isFromTheBeginning() +{ + if (!opened) { + throw Closed("isFromTheBeginning", jid.toStdString()); + } + return fromTheBeginning; +} + +bool Core::Archive::setFromTheBeginning(bool is) +{ + if (!opened) { + throw Closed("setFromTheBeginning", jid.toStdString()); + } + if (fromTheBeginning != is) { + fromTheBeginning = is; + const std::string& id = "beginning"; + uint8_t value = 144; + if (is) { + value = 72; + } + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = sizeof value; + lmdbData.mv_data = &value; + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + int rc; + rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + qDebug() << "Couldn't store beginning key into stat database:" << mdb_strerror(rc); + mdb_txn_abort(txn); + } + } +} diff --git a/core/archive.h b/core/archive.h index 614ce40..025d927 100644 --- a/core/archive.h +++ b/core/archive.h @@ -23,6 +23,7 @@ #include "../global.h" #include #include "../exception.h" +#include namespace Core { @@ -36,7 +37,7 @@ public: void open(const QString& account); void close(); - void addElement(const Shared::Message& message); + bool addElement(const Shared::Message& message); Shared::Message getElement(const QString& id); Shared::Message oldest(); QString oldestId(); @@ -44,6 +45,9 @@ public: QString newestId(); void clear(); long unsigned int size() const; + std::list getBefore(int count, const QString& id); + bool isFromTheBeginning(); + bool setFromTheBeginning(bool is); public: const QString jid; @@ -97,9 +101,13 @@ public: private: bool opened; + bool fromTheBeginning; MDB_env* environment; MDB_dbi main; MDB_dbi order; + MDB_dbi stats; + + bool _isFromTheBeginning(); }; } diff --git a/core/contact.cpp b/core/contact.cpp index 1cc7e8a..8525f96 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -25,9 +25,22 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* par groups(), archiveState(empty), archive(new Archive(jid)), - subscriptionState(Shared::unknown) + subscriptionState(Shared::unknown), + syncronizing(false), + requestedCount(0), + receivedCount(0), + hisoryCache(), + appendCache(), + requestCache() { archive->open(account); + if (archive->isFromTheBeginning()) { + archiveState = beginning; + } else { + if (archive->size() != 0) { + archiveState = chunk; + } + } } Core::Contact::~Contact() @@ -91,3 +104,42 @@ unsigned int Core::Contact::groupsCount() const { return groups.size(); } + +void Core::Contact::addMessageToArchive(const Shared::Message& msg) +{ + +} + +void Core::Contact::requestHistory(int count, const QString& before) +{ + if (syncronizing) { + requestCache.emplace_back(count, before); + } else { + switch (archiveState) { + case empty: + if (appendCache.size() != 0) { + //from last + } else { + //search + } + break; + case beginning: + //from last + break; + case end: + //try + break; + case chunk: + //from last + break; + case complete: + //just give + break; + } + syncronizing = true; + requestedCount = count; + receivedCount = 0; + hisoryCache.clear(); + } +} + diff --git a/core/contact.h b/core/contact.h index b245d45..cc3f413 100644 --- a/core/contact.h +++ b/core/contact.h @@ -23,6 +23,7 @@ #include #include "archive.h" #include "../global.h" +#include namespace Core { @@ -48,12 +49,18 @@ public: void setSubscriptionState(Shared::SubscriptionState state); Shared::SubscriptionState getSubscriptionState() const; unsigned int groupsCount() const; + void addMessageToArchive(const Shared::Message& msg); + void appendMessageToArchive(const Shared::Message& msg); + void flushMessagesToArchive(); + void requestHistory(int count, const QString& before); signals: void groupAdded(const QString& name); void groupRemoved(const QString& name); void nameChanged(const QString& name); void subscriptionStateChanged(Shared::SubscriptionState state); + void historyResponse(const std::list& messages); + void needEarlierHistory(const QString& before, const QString& after, int count, const QDateTime& from, const QDateTime& to); public: const QString jid; @@ -64,6 +71,13 @@ private: ArchiveState archiveState; Archive* archive; Shared::SubscriptionState subscriptionState; + + bool syncronizing; + int requestedCount; + int receivedCount; + std::list hisoryCache; + std::list appendCache; + std::list> requestCache; }; } From 484d6c34f095557ae88e35336d74e5c6607fa8d8 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 24 Apr 2019 17:50:54 +0300 Subject: [PATCH 2/6] nothing special --- core/archive.cpp | 2 +- core/contact.cpp | 179 ++++++++++++++++++++++++++++++++++++++++------- core/contact.h | 7 +- 3 files changed, 160 insertions(+), 28 deletions(-) diff --git a/core/archive.cpp b/core/archive.cpp index 49df1c0..ac8a852 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -261,7 +261,7 @@ std::list Core::Archive::getBefore(int count, const QString& id rc = mdb_cursor_open(txn, order, &cursor); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST); if (rc) { - qDebug() << "Error geting before newest newest " << mdb_strerror(rc); + qDebug() << "Error geting before " << mdb_strerror(rc); mdb_cursor_close(cursor); mdb_txn_abort(txn); throw new Empty(jid.toStdString()); diff --git a/core/contact.cpp b/core/contact.cpp index 8525f96..d1a490d 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -31,7 +31,8 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* par receivedCount(0), hisoryCache(), appendCache(), - requestCache() + requestCache(), + responseCache() { archive->open(account); if (archive->isFromTheBeginning()) { @@ -115,31 +116,157 @@ void Core::Contact::requestHistory(int count, const QString& before) if (syncronizing) { requestCache.emplace_back(count, before); } else { - switch (archiveState) { - case empty: - if (appendCache.size() != 0) { - //from last - } else { - //search - } - break; - case beginning: - //from last - break; - case end: - //try - break; - case chunk: - //from last - break; - case complete: - //just give - break; - } - syncronizing = true; - requestedCount = count; - receivedCount = 0; - hisoryCache.clear(); + performRequest(count, before); } } +void Core::Contact::nextRequest() +{ + if (requestCache.size() > 0) { + std::pair request = requestCache.front(); + requestCache.pop_front(); + performRequest(request.first, request.second); + } else { + syncronizing = false; + requestedCount = 0; + receivedCount = 0; + hisoryCache.clear(); + responseCache.clear(); + } +} + +void Core::Contact::performRequest(int count, const QString& before) +{ + syncronizing = true; + requestedCount = count; + receivedCount = 0; + hisoryCache.clear(); + responseCache.clear(); + + switch (archiveState) { + case empty: + emit needEarlierHistory(before, "", QDateTime(), QDateTime()); + break; + case beginning: { + QString lBefore; + bool found = false; + if (appendCache.size() != 0) { + if (before.size() == 0) { + lBefore = appendCache.back().getId(); + } + for (std::list::const_reverse_iterator itr = appendCache.rbegin(), end = appendCache.rend(); itr != end; ++itr) { + const Shared::Message& msg = *itr; + if (found) { + responseCache.emplace_front(msg); + ++receivedCount; + } else { + if (msg.getId() == before) { + found = true; + responseCache.emplace_front(*itr); + ++receivedCount; + } + } + if (responseCache.size() == count) { + break; + } + } + if (responseCache.size() == count) { + emit historyResponse(responseCache); + nextRequest(); + break; + } + } + if (found) { + lBefore = responseCache.front().getId(); + emit needEarlierHistory(lBefore, "", QDateTime(), QDateTime()); + } else { + std::list arc; + if (responseCache.size() > 0) { + lBefore = responseCache.front().getId(); + } else { + lBefore = before; + } + if (count != -1) { + try { + arc = archive->getBefore(requestedCount - receivedCount, lBefore); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + emit historyResponse(responseCache); + nextRequest(); + } catch (Archive::NotFound e) { + requestCache.emplace_back(count, before); + requestedCount = -1; + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + } + } else { + try { + arc = archive->getBefore(1, lBefore); + //just do nothing since response is not required + nextRequest(); //may be even it's a signal that the history is now complete? + } catch (Archive::NotFound e) { + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + } + } + } + } + break; + case end: + std::list arc; + if (count != -1) { + try { + arc = archive->getBefore(requestedCount - receivedCount, before); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + emit historyResponse(responseCache); + nextRequest(); + } catch (Archive::NotFound e) { + requestCache.emplace_back(count, before); + requestedCount = -1; + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + } + } else { + try { + arc = archive->getBefore(1, before); + //just do nothing since response is not required + nextRequest(); //may be even it's a signal that the history is now complete? + } catch (Archive::NotFound e) { + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + } + } + break; + case chunk: + //from last + break; + case complete: + //just give + break; + } +} + + +void Core::Contact::appendMessageToArchive(const Shared::Message& msg) +{ + if (msg.getId().size() > 0 && msg.getBody().size() > 0) { + switch (archiveState) { + case empty: + if (archive->addElement(msg)) { + archiveState = end; + }; + requestHistory(-1, msg.getId()); + break; + case beginning: + appendCache.emplace_back(msg); + requestHistory(-1, msg.getId()); + break; + case end: + archive->addElement(msg); + break; + case chunk: + appendCache.emplace_back(msg); + requestHistory(-1, msg.getId()); + break; + case complete: + archive->addElement(msg); + break; + } + + } +} diff --git a/core/contact.h b/core/contact.h index cc3f413..49346c6 100644 --- a/core/contact.h +++ b/core/contact.h @@ -60,7 +60,7 @@ signals: void nameChanged(const QString& name); void subscriptionStateChanged(Shared::SubscriptionState state); void historyResponse(const std::list& messages); - void needEarlierHistory(const QString& before, const QString& after, int count, const QDateTime& from, const QDateTime& to); + void needEarlierHistory(const QString& before, const QString& after, const QDateTime& from, const QDateTime& to); public: const QString jid; @@ -77,7 +77,12 @@ private: int receivedCount; std::list hisoryCache; std::list appendCache; + std::list responseCache; std::list> requestCache; + +private: + void nextRequest(); + void performRequest(int count, const QString& before); }; } From 1f450ad3e0f17666133289a8dc2458be4ddbb169 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 25 Apr 2019 08:27:01 +0300 Subject: [PATCH 3/6] still trying --- core/contact.cpp | 116 ++++++++++++++++++++++++++--------------------- core/contact.h | 1 + 2 files changed, 66 insertions(+), 51 deletions(-) diff --git a/core/contact.cpp b/core/contact.cpp index d1a490d..441218b 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -148,12 +148,8 @@ void Core::Contact::performRequest(int count, const QString& before) emit needEarlierHistory(before, "", QDateTime(), QDateTime()); break; case beginning: { - QString lBefore; bool found = false; if (appendCache.size() != 0) { - if (before.size() == 0) { - lBefore = appendCache.back().getId(); - } for (std::list::const_reverse_iterator itr = appendCache.rbegin(), end = appendCache.rend(); itr != end; ++itr) { const Shared::Message& msg = *itr; if (found) { @@ -177,58 +173,35 @@ void Core::Contact::performRequest(int count, const QString& before) } } if (found) { - lBefore = responseCache.front().getId(); - emit needEarlierHistory(lBefore, "", QDateTime(), QDateTime()); + emit needEarlierHistory(responseCache.front().getId(), "", QDateTime(), QDateTime()); } else { - std::list arc; - if (responseCache.size() > 0) { - lBefore = responseCache.front().getId(); - } else { - lBefore = before; - } - if (count != -1) { - try { - arc = archive->getBefore(requestedCount - receivedCount, lBefore); - responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - emit historyResponse(responseCache); - nextRequest(); - } catch (Archive::NotFound e) { - requestCache.emplace_back(count, before); - requestedCount = -1; - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); - } - } else { - try { - arc = archive->getBefore(1, lBefore); - //just do nothing since response is not required - nextRequest(); //may be even it's a signal that the history is now complete? - } catch (Archive::NotFound e) { - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); - } + if (requiestFromArchive(before)) { + nextRequest(); } } } break; - case end: - std::list arc; - if (count != -1) { - try { - arc = archive->getBefore(requestedCount - receivedCount, before); - responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - emit historyResponse(responseCache); - nextRequest(); - } catch (Archive::NotFound e) { - requestCache.emplace_back(count, before); - requestedCount = -1; - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); - } - } else { - try { - arc = archive->getBefore(1, before); - //just do nothing since response is not required - nextRequest(); //may be even it's a signal that the history is now complete? - } catch (Archive::NotFound e) { - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + case end: { + std::list arc; + if (count != -1) { + try { + arc = archive->getBefore(requestedCount - receivedCount, before); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + emit historyResponse(responseCache); + nextRequest(); + } catch (Archive::NotFound e) { + requestCache.emplace_back(count, before); + requestedCount = -1; + emit needEarlierHistory(archive->oldestId(), "", QDateTime(), QDateTime()); + } + } else { + try { + arc = archive->getBefore(1, before); + //just do nothing since response is not required + nextRequest(); //may be even it's a signal that the history is now complete? + } catch (Archive::NotFound e) { + emit needEarlierHistory(archive->oldestId(), "", QDateTime(), QDateTime()); + } } } break; @@ -241,6 +214,47 @@ void Core::Contact::performRequest(int count, const QString& before) } } +bool Core::Contact::requiestFromArchive(const QString& before) +{ + std::list arc; + QString lBefore; + if (responseCache.size() > 0) { + lBefore = responseCache.front().getId(); + } else { + lBefore = before; + } + if (requestedCount != -1) { + try { + arc = archive->getBefore(requestedCount - receivedCount, lBefore); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + emit historyResponse(responseCache); + return true; + } catch (Archive::NotFound e) { + requestCache.emplace_back(requestedCount, before); + requestedCount = -1; + switch (archiveState) { + case empty: + case beginning: + case end: + case chunk: + case complete: + } + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + return false; + } + } else { + try { + arc = archive->getBefore(1, lBefore); + //just do nothing since response is not required + //may be even it's a signal that the history is now complete? + return true; + } catch (Archive::NotFound e) { + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + return false; + } + } +} + void Core::Contact::appendMessageToArchive(const Shared::Message& msg) { diff --git a/core/contact.h b/core/contact.h index 49346c6..12b85cd 100644 --- a/core/contact.h +++ b/core/contact.h @@ -83,6 +83,7 @@ private: private: void nextRequest(); void performRequest(int count, const QString& before); + bool requiestFromArchive(const QString& before); }; } From 88ea4cf3ace7731d4b11b6548d5f3c4ee28f6708 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 26 Apr 2019 15:52:34 +0300 Subject: [PATCH 4/6] still trying --- core/archive.h | 1 + core/contact.cpp | 121 +++++++++++++++++++++++++++++------------------ core/contact.h | 8 ++-- 3 files changed, 82 insertions(+), 48 deletions(-) diff --git a/core/archive.h b/core/archive.h index 025d927..b887359 100644 --- a/core/archive.h +++ b/core/archive.h @@ -38,6 +38,7 @@ public: void close(); bool addElement(const Shared::Message& message); + unsigned int addElements(const std::list& messages); Shared::Message getElement(const QString& id); Shared::Message oldest(); QString oldestId(); diff --git a/core/contact.cpp b/core/contact.cpp index 441218b..777ec6c 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -28,11 +28,11 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* par subscriptionState(Shared::unknown), syncronizing(false), requestedCount(0), - receivedCount(0), + requestedBefore(), hisoryCache(), appendCache(), - requestCache(), - responseCache() + responseCache(), + requestCache() { archive->open(account); if (archive->isFromTheBeginning()) { @@ -108,7 +108,7 @@ unsigned int Core::Contact::groupsCount() const void Core::Contact::addMessageToArchive(const Shared::Message& msg) { - + hisoryCache.emplace_back(msg); } void Core::Contact::requestHistory(int count, const QString& before) @@ -122,6 +122,11 @@ void Core::Contact::requestHistory(int count, const QString& before) void Core::Contact::nextRequest() { + if (syncronizing) { + if (requestedCount != -1) { + emit historyResponse(responseCache); + } + } if (requestCache.size() > 0) { std::pair request = requestCache.front(); requestCache.pop_front(); @@ -129,7 +134,7 @@ void Core::Contact::nextRequest() } else { syncronizing = false; requestedCount = 0; - receivedCount = 0; + requestedBefore = ""; hisoryCache.clear(); responseCache.clear(); } @@ -139,7 +144,7 @@ void Core::Contact::performRequest(int count, const QString& before) { syncronizing = true; requestedCount = count; - receivedCount = 0; + requestedBefore = before; hisoryCache.clear(); responseCache.clear(); @@ -147,6 +152,7 @@ void Core::Contact::performRequest(int count, const QString& before) case empty: emit needEarlierHistory(before, "", QDateTime(), QDateTime()); break; + case chunk: case beginning: { bool found = false; if (appendCache.size() != 0) { @@ -154,12 +160,10 @@ void Core::Contact::performRequest(int count, const QString& before) const Shared::Message& msg = *itr; if (found) { responseCache.emplace_front(msg); - ++receivedCount; } else { if (msg.getId() == before) { found = true; responseCache.emplace_front(*itr); - ++receivedCount; } } if (responseCache.size() == count) { @@ -167,54 +171,48 @@ void Core::Contact::performRequest(int count, const QString& before) } } if (responseCache.size() == count) { - emit historyResponse(responseCache); nextRequest(); break; } } if (found) { - emit needEarlierHistory(responseCache.front().getId(), "", QDateTime(), QDateTime()); + requestedBefore = responseCache.front().getId(); + emit needEarlierHistory(requestedBefore, "", QDateTime(), QDateTime()); } else { - if (requiestFromArchive(before)) { + if (requestFromArchive(before)) { nextRequest(); } } } break; case end: { - std::list arc; - if (count != -1) { - try { - arc = archive->getBefore(requestedCount - receivedCount, before); - responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - emit historyResponse(responseCache); + bool found = requestFromArchive(before); + if (found) { + int rSize = responseCache.size(); + if (rSize < count) { + if (rSize != 0) { + requestedBefore = responseCache.front().getId(); + emit needEarlierHistory(responseCache.front().getId(), "", QDateTime(), QDateTime()); + } else { + requestedBefore = before; + emit needEarlierHistory(before, "", QDateTime(), QDateTime()); + } + } else { nextRequest(); - } catch (Archive::NotFound e) { - requestCache.emplace_back(count, before); - requestedCount = -1; - emit needEarlierHistory(archive->oldestId(), "", QDateTime(), QDateTime()); - } - } else { - try { - arc = archive->getBefore(1, before); - //just do nothing since response is not required - nextRequest(); //may be even it's a signal that the history is now complete? - } catch (Archive::NotFound e) { - emit needEarlierHistory(archive->oldestId(), "", QDateTime(), QDateTime()); } } } break; - case chunk: - //from last - break; case complete: - //just give + if (!requestFromArchive(before)) { + qDebug("requesting id hasn't been found in archive, skipping"); + } + nextRequest(); break; } } -bool Core::Contact::requiestFromArchive(const QString& before) +bool Core::Contact::requestFromArchive(const QString& before) { std::list arc; QString lBefore; @@ -225,21 +223,13 @@ bool Core::Contact::requiestFromArchive(const QString& before) } if (requestedCount != -1) { try { - arc = archive->getBefore(requestedCount - receivedCount, lBefore); + arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - emit historyResponse(responseCache); return true; } catch (Archive::NotFound e) { requestCache.emplace_back(requestedCount, before); requestedCount = -1; - switch (archiveState) { - case empty: - case beginning: - case end: - case chunk: - case complete: - } - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + requestEarlierToSync(); return false; } } else { @@ -249,12 +239,28 @@ bool Core::Contact::requiestFromArchive(const QString& before) //may be even it's a signal that the history is now complete? return true; } catch (Archive::NotFound e) { - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + requestEarlierToSync(); return false; } } } +void Core::Contact::requestEarlierToSync() +{ + switch (archiveState) { + case empty: + break; + case beginning: //need to reach complete + case chunk: //need to reach end + emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); + break; + case end: //need to reach complete + emit needEarlierHistory(archive->oldestId(), "", QDateTime(), QDateTime()); + break; + case complete: //nothing to sync + break; + } +} void Core::Contact::appendMessageToArchive(const Shared::Message& msg) { @@ -284,3 +290,28 @@ void Core::Contact::appendMessageToArchive(const Shared::Message& msg) } } + +void Core::Contact::flushMessagesToArchive(bool finished, const QString& lastId) +{ + unsigned int amount(0); + if (hisoryCache.size() > 0) { + amount = archive->addElements(hisoryCache); + } + + if (requestedCount == -1) { + if (amount >= requestedCount - responseCache.size()) { + if (requestFromArchive(requestedBefore)){ + nextRequest(); + return; + } + } + if (!finished) { + if (lastId.size() != 0) { + + } + } + + } else { + + } +} diff --git a/core/contact.h b/core/contact.h index 12b85cd..8e2d35a 100644 --- a/core/contact.h +++ b/core/contact.h @@ -21,6 +21,7 @@ #include #include +#include #include "archive.h" #include "../global.h" #include @@ -51,7 +52,7 @@ public: unsigned int groupsCount() const; void addMessageToArchive(const Shared::Message& msg); void appendMessageToArchive(const Shared::Message& msg); - void flushMessagesToArchive(); + void flushMessagesToArchive(bool finished, const QString& lastId); void requestHistory(int count, const QString& before); signals: @@ -74,7 +75,7 @@ private: bool syncronizing; int requestedCount; - int receivedCount; + QString requestedBefore; std::list hisoryCache; std::list appendCache; std::list responseCache; @@ -83,7 +84,8 @@ private: private: void nextRequest(); void performRequest(int count, const QString& before); - bool requiestFromArchive(const QString& before); + bool requestFromArchive(const QString& before); + void requestEarlierToSync(); }; } From d09fef5a34774a1964ae7f46d20fcdbc6da9a1a3 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 29 Apr 2019 00:34:28 +0300 Subject: [PATCH 5/6] a bit better appoach --- core/archive.cpp | 56 +++++++++++++ core/contact.cpp | 201 +++++++++++++++++++++++++---------------------- core/contact.h | 15 ++-- 3 files changed, 168 insertions(+), 104 deletions(-) diff --git a/core/archive.cpp b/core/archive.cpp index ac8a852..5efe17e 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -206,6 +206,62 @@ Shared::Message Core::Archive::oldest() return getElement(oldestId()); } +unsigned int Core::Archive::addElements(const std::list& messages) +{ + if (!opened) { + throw Closed("addElements", jid.toStdString()); + } + + int success = 0; + int rc = 0; + MDB_val lmdbKey, lmdbData; + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + std::list::const_iterator itr = messages.begin(); + while (rc == 0 && itr != messages.end()) { + const Shared::Message& message = *itr; + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + message.serialize(ds); + quint64 stamp = message.getTime().toMSecsSinceEpoch(); + const std::string& id = message.getId().toStdString(); + + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); + if (rc == 0) { + MDB_val orderKey; + orderKey.mv_size = 8; + orderKey.mv_data = (uint8_t*) &stamp; + + rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0); + if (rc) { + qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc); + } else { + success++; + } + } else { + if (rc == MDB_KEYEXIST) { + rc = 0; + } else { + qDebug() << "An element couldn't been added to the archive, aborting the transaction" << mdb_strerror(rc); + } + } + } + + if (rc != 0) { + mdb_txn_abort(txn); + success = 0; + } else { + mdb_txn_commit(txn); + } + + return success; +} + + QString Core::Archive::oldestId() { if (!opened) { diff --git a/core/contact.cpp b/core/contact.cpp index 777ec6c..2b29003 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -108,7 +108,9 @@ unsigned int Core::Contact::groupsCount() const void Core::Contact::addMessageToArchive(const Shared::Message& msg) { - hisoryCache.emplace_back(msg); + if (msg.getId().size() > 0 && msg.getBody().size() > 0) { + hisoryCache.emplace_back(msg); + } } void Core::Contact::requestHistory(int count, const QString& before) @@ -150,57 +152,37 @@ void Core::Contact::performRequest(int count, const QString& before) switch (archiveState) { case empty: - emit needEarlierHistory(before, "", QDateTime(), QDateTime()); + emit needHistory(before, "", QDateTime(), QDateTime()); break; case chunk: - case beginning: { - bool found = false; - if (appendCache.size() != 0) { - for (std::list::const_reverse_iterator itr = appendCache.rbegin(), end = appendCache.rend(); itr != end; ++itr) { - const Shared::Message& msg = *itr; - if (found) { - responseCache.emplace_front(msg); - } else { - if (msg.getId() == before) { - found = true; - responseCache.emplace_front(*itr); - } - } - if (responseCache.size() == count) { - break; - } - } - if (responseCache.size() == count) { - nextRequest(); - break; - } - } - if (found) { - requestedBefore = responseCache.front().getId(); - emit needEarlierHistory(requestedBefore, "", QDateTime(), QDateTime()); - } else { - if (requestFromArchive(before)) { - nextRequest(); - } - } + case beginning: + if (count != -1) { + requestCache.emplace_back(requestedCount, before); + requestedCount = -1; } + emit needHistory("", archive->newestId(), QDateTime(), QDateTime()); break; - case end: { + case end: + if (count != -1) { bool found = requestFromArchive(before); if (found) { int rSize = responseCache.size(); if (rSize < count) { if (rSize != 0) { - requestedBefore = responseCache.front().getId(); - emit needEarlierHistory(responseCache.front().getId(), "", QDateTime(), QDateTime()); + emit needHistory(responseCache.front().getId(), "", QDateTime(), QDateTime()); } else { - requestedBefore = before; - emit needEarlierHistory(before, "", QDateTime(), QDateTime()); + emit needHistory(before, "", QDateTime(), QDateTime()); } } else { nextRequest(); } + } else { + requestCache.emplace_back(requestedCount, before); + requestedCount = -1; + emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); } + } else { + emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); } break; case complete: @@ -212,8 +194,7 @@ void Core::Contact::performRequest(int count, const QString& before) } } -bool Core::Contact::requestFromArchive(const QString& before) -{ +bool Core::Contact::requestFromArchive(const QString& before) { std::list arc; QString lBefore; if (responseCache.size() > 0) { @@ -229,7 +210,7 @@ bool Core::Contact::requestFromArchive(const QString& before) } catch (Archive::NotFound e) { requestCache.emplace_back(requestedCount, before); requestedCount = -1; - requestEarlierToSync(); + emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); return false; } } else { @@ -239,79 +220,107 @@ bool Core::Contact::requestFromArchive(const QString& before) //may be even it's a signal that the history is now complete? return true; } catch (Archive::NotFound e) { - requestEarlierToSync(); + emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); return false; } } } -void Core::Contact::requestEarlierToSync() -{ - switch (archiveState) { - case empty: - break; - case beginning: //need to reach complete - case chunk: //need to reach end - emit needEarlierHistory("", archive->newestId(), QDateTime(), QDateTime()); - break; - case end: //need to reach complete - emit needEarlierHistory(archive->oldestId(), "", QDateTime(), QDateTime()); - break; - case complete: //nothing to sync - break; - } -} - void Core::Contact::appendMessageToArchive(const Shared::Message& msg) { - if (msg.getId().size() > 0 && msg.getBody().size() > 0) { - switch (archiveState) { - case empty: - if (archive->addElement(msg)) { - archiveState = end; - }; - requestHistory(-1, msg.getId()); - break; - case beginning: - appendCache.emplace_back(msg); - requestHistory(-1, msg.getId()); - break; - case end: - archive->addElement(msg); - break; - case chunk: - appendCache.emplace_back(msg); - requestHistory(-1, msg.getId()); - break; - case complete: - archive->addElement(msg); - break; + const QString& id = msg.getId(); + if (id.size() > 0) { + if (msg.getBody().size() > 0) { + switch (archiveState) { + case empty: + if (archive->addElement(msg)) { + archiveState = end; + }; + if (!syncronizing) { + requestHistory(-1, id); + } + break; + case beginning: + appendCache.emplace_back(msg); + if (!syncronizing) { + requestHistory(-1, id); + } + break; + case end: + archive->addElement(msg); + break; + case chunk: + appendCache.emplace_back(msg); + if (!syncronizing) { + requestHistory(-1, id); + } + break; + case complete: + archive->addElement(msg); + break; + } + } else if (!syncronizing && archiveState == empty) { + requestHistory(-1, id); } - } } -void Core::Contact::flushMessagesToArchive(bool finished, const QString& lastId) +void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId) { - unsigned int amount(0); if (hisoryCache.size() > 0) { - amount = archive->addElements(hisoryCache); + archive->addElements(hisoryCache); + hisoryCache.clear(); } - if (requestedCount == -1) { - if (amount >= requestedCount - responseCache.size()) { - if (requestFromArchive(requestedBefore)){ + switch (archiveState) { + break; + case beginning: + if (finished) { + archiveState = complete; nextRequest(); - return; + } else { + emit needHistory("", lastId, QDateTime(), QDateTime()); } - } - if (!finished) { - if (lastId.size() != 0) { - + case chunk: + if (finished) { + archiveState = end; + nextRequest(); + } else { + emit needHistory("", lastId, QDateTime(), QDateTime()); } - } - - } else { - + break; + case empty: + archiveState = end; + case end: + if (finished) { + archiveState = complete; + archive->setFromTheBeginning(true); + } + if (requestedCount != -1) { + QString before; + if (responseCache.size() > 0) { + before = responseCache.front().getId(); + } else { + before = requestedBefore; + } + if (!requestFromArchive(before)) { + qDebug("Something went terrible wrong flushing messages to the archive"); + } + if (requestedCount < responseCache.size()) { + if (archiveState == complete) { + nextRequest(); + } else { + emit needHistory(firstId, "", QDateTime(), QDateTime()); + } + } else { + nextRequest(); + } + } else { + nextRequest(); + } + break; + case complete: + nextRequest(); + break; } } diff --git a/core/contact.h b/core/contact.h index 8e2d35a..80c2db0 100644 --- a/core/contact.h +++ b/core/contact.h @@ -41,7 +41,7 @@ public: }; Contact(const QString& pJid, const QString& account, QObject* parent = 0); ~Contact(); - + ArchiveState getArchiveState() const; QString getName() const; void setName(const QString& n); @@ -52,17 +52,17 @@ public: unsigned int groupsCount() const; void addMessageToArchive(const Shared::Message& msg); void appendMessageToArchive(const Shared::Message& msg); - void flushMessagesToArchive(bool finished, const QString& lastId); + void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId); void requestHistory(int count, const QString& before); - + signals: void groupAdded(const QString& name); void groupRemoved(const QString& name); void nameChanged(const QString& name); void subscriptionStateChanged(Shared::SubscriptionState state); void historyResponse(const std::list& messages); - void needEarlierHistory(const QString& before, const QString& after, const QDateTime& from, const QDateTime& to); - + void needHistory(const QString& before, const QString& after, const QDateTime& from, const QDateTime& to); + public: const QString jid; @@ -72,7 +72,7 @@ private: ArchiveState archiveState; Archive* archive; Shared::SubscriptionState subscriptionState; - + bool syncronizing; int requestedCount; QString requestedBefore; @@ -80,12 +80,11 @@ private: std::list appendCache; std::list responseCache; std::list> requestCache; - + private: void nextRequest(); void performRequest(int count, const QString& before); bool requestFromArchive(const QString& before); - void requestEarlierToSync(); }; } From da3151391b3c4835c4378f9ba8382d76cc727168 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 15 May 2019 20:36:37 +0300 Subject: [PATCH 6/6] First working prototype of history requesting, dates in messages, some screen scrolling handling --- core/account.cpp | 165 ++++++++++++++++++++++++++++++++------------ core/account.h | 5 ++ core/archive.cpp | 97 ++++++++++++++++++++------ core/archive.h | 4 +- core/contact.cpp | 119 ++++++++++++++++++-------------- core/contact.h | 4 +- core/squawk.cpp | 13 +++- core/squawk.h | 4 +- global.cpp | 23 ++++-- global.h | 3 + main.cpp | 5 +- ui/CMakeLists.txt | 7 ++ ui/conversation.cpp | 47 ++++++++++++- ui/conversation.h | 5 ++ ui/conversation.ui | 9 ++- ui/messageline.cpp | 20 +++++- ui/messageline.h | 5 ++ ui/squawk.cpp | 18 ++++- ui/squawk.h | 5 +- 19 files changed, 422 insertions(+), 136 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index d6d36e4..22b8e2e 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -1,6 +1,7 @@ #include "account.h" #include #include +#include using namespace Core; @@ -25,6 +26,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); QObject::connect(&client, SIGNAL(presenceReceived(const QXmppPresence&)), this, SLOT(onPresenceReceived(const QXmppPresence&))); QObject::connect(&client, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onMessageReceived(const QXmppMessage&))); + QObject::connect(&client, SIGNAL(error(QXmppClient::Error)), this, SLOT(onClientError(QXmppClient::Error))); QXmppRosterManager& rm = client.rosterManager(); @@ -212,6 +214,8 @@ void Core::Account::addedAccount(const QString& jid) QObject::connect(contact, SIGNAL(nameChanged(const QString&)), this, SLOT(onContactNameChanged(const QString&))); QObject::connect(contact, SIGNAL(subscriptionStateChanged(Shared::SubscriptionState)), this, SLOT(onContactSubscriptionStateChanged(Shared::SubscriptionState))); + QObject::connect(contact, SIGNAL(needHistory(const QString&, const QString&)), this, SLOT(onContactNeedHistory(const QString&, const QString&))); + QObject::connect(contact, SIGNAL(historyResponse(const std::list&)), this, SLOT(onContactHistoryResponse(const std::list&))); } } @@ -368,6 +372,10 @@ void Core::Account::sendMessage(const Shared::Message& data) QXmppMessage msg(data.getFrom(), data.getTo(), data.getBody(), data.getThread()); msg.setId(data.getId()); msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible + + std::map::const_iterator itr = contacts.find(data.getPenPalJid()); + itr->second->appendMessageToArchive(data); + client.sendPacket(msg); } else { qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; @@ -386,27 +394,14 @@ void Core::Account::onCarbonMessageSent(const QXmppMessage& msg) bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { - QString body(msg.body()); + const QString& body(msg.body()); if (body.size() != 0) { - QString id(msg.id()); - QDateTime time(msg.stamp()); + const QString& id(msg.id()); Shared::Message sMsg(Shared::Message::chat); - sMsg.setId(id); - sMsg.setFrom(msg.from()); - sMsg.setTo(msg.to()); - sMsg.setBody(body); - sMsg.setForwarded(forwarded); - if (guessing) { - if (sMsg.getFromJid() == getLogin() + "@" + getServer()) { - outgoing = true; - } else { - outgoing = false; - } - } - sMsg.setOutgoing(outgoing); - if (time.isValid()) { - sMsg.setTime(time); - } + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + std::map::const_iterator itr = contacts.find(sMsg.getPenPalJid()); + itr->second->appendMessageToArchive(sMsg); + emit message(sMsg); if (!forwarded && !outgoing) { @@ -422,13 +417,51 @@ bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bo return false; } -void Core::Account::onMamMessageReceived(const QString& bareJid, const QXmppMessage& msg) +void Core::Account::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { - handleChatMessage(msg, false, true, true); + const QDateTime& time(source.stamp()); + target.setId(source.id()); + target.setFrom(source.from()); + target.setTo(source.to()); + target.setBody(source.body()); + target.setForwarded(forwarded); + if (guessing) { + if (target.getFromJid() == getLogin() + "@" + getServer()) { + outgoing = true; + } else { + outgoing = false; + } + } + target.setOutgoing(outgoing); + if (time.isValid()) { + target.setTime(time); + } else { + target.setCurrentTime(); + } +} + +void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) +{ + std::map::const_iterator itr = achiveQueries.find(queryId); + QString jid = itr->second; + std::map::const_iterator citr = contacts.find(jid); + + if (citr != contacts.end()) { + Contact* cnt = citr->second; + if (msg.id().size() > 0 && msg.body().size() > 0) { + Shared::Message sMsg(Shared::Message::chat); + initializeMessage(sMsg, msg, false, true, true); + + cnt->addMessageToArchive(sMsg); + } + + } + //handleChatMessage(msg, false, true, true); } void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { + qDebug() << "An archive request for " << jid << ", before " << before; std::map::const_iterator itr = contacts.find(jid); if (itr == contacts.end()) { qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping"; @@ -436,40 +469,56 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString& } Contact* contact = itr->second; - contact->requestHistory(count, before); - Contact::ArchiveState as = contact->getArchiveState(); - switch (as) { - case Contact::empty: - break; - case Contact::beginning: - break; - case Contact::chunk: - break; - case Contact::complete: - break; - case Contact::end: - break; + if (contact->getArchiveState() == Contact::empty && before.size() == 0) { + QXmppMessage msg(getFullJid(), jid, "", ""); + QString last = Shared::generateUUID(); + msg.setId(last); + msg.setType(QXmppMessage::Chat); + msg.setState(QXmppMessage::Active); + client.sendPacket(msg); + QTimer* timer = new QTimer; + QObject::connect(timer, &QTimer::timeout, [timer, contact, count, last](){ + contact->requestFromEmpty(count, last); + timer->deleteLater(); + }); + + timer->setSingleShot(true); + timer->start(1000); + } else { + contact->requestHistory(count, before); } - +} + +void Core::Account::onContactNeedHistory(const QString& before, const QString& after) +{ + Contact* contact = static_cast(sender()); QXmppResultSetQuery query; query.setMax(100); - QDateTime from = QDateTime::currentDateTime().addDays(-7); - - QString q = am->retrieveArchivedMessages("", "", jid, from, QDateTime(), query); - achiveQueries.insert(std::make_pair(q, jid)); + if (before.size() > 0) { + query.setBefore(before); + } + if (after.size() > 0) { + query.setAfter(after); + } + + qDebug() << "Remote query from\"" << after << "\", to" << before; + + QString q = am->retrieveArchivedMessages("", "", contact->jid, QDateTime(), QDateTime(), query); + achiveQueries.insert(std::make_pair(q, contact->jid)); } + void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) { std::map::const_iterator itr = achiveQueries.find(queryId); QString jid = itr->second; achiveQueries.erase(itr); - if (!complete) { - QXmppResultSetQuery q; - q.setAfter(resultSetReply.last()); - q.setMax(100); - QString nQ = am->retrieveArchivedMessages("", "", jid, QDateTime::currentDateTime().addDays(-7), QDateTime(), q); - achiveQueries.insert(std::make_pair(nQ, jid)); + std::map::const_iterator citr = contacts.find(jid); + if (citr != contacts.end()) { + Contact* cnt = citr->second; + + qDebug() << "Flushing messages for" << jid; + cnt->flushMessagesToArchive(complete, resultSetReply.first(), resultSetReply.last()); } } @@ -556,3 +605,29 @@ Shared::SubscriptionState Core::Account::castSubscriptionState(QXmppRosterIq::It } return state; } + +void Core::Account::onContactHistoryResponse(const std::list& list) +{ + Contact* contact = static_cast(sender()); + + qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; + emit responseArchive(contact->jid, list); +} + +void Core::Account::onClientError(QXmppClient::Error err) +{ + switch (err) { + case QXmppClient::SocketError: + qDebug() << "Client socket error" << client.socketErrorString(); + break; + case QXmppClient::XmppStreamError: + qDebug() << "Client stream error" << client.socketErrorString(); + break; + case QXmppClient::KeepAliveError: + qDebug() << "Client keep alive error"; + break; + } + + //onClientDisconnected(); +} + diff --git a/core/account.h b/core/account.h index 9b41cbf..04f14b6 100644 --- a/core/account.h +++ b/core/account.h @@ -55,6 +55,7 @@ signals: void addPresence(const QString& jid, const QString& name, const QMap& data); void removePresence(const QString& jid, const QString& name); void message(const Shared::Message& data); + void responseArchive(const QString& jid, const std::list& list); private: QString name; @@ -71,6 +72,7 @@ private: private slots: void onClientConnected(); void onClientDisconnected(); + void onClientError(QXmppClient::Error err); void onRosterReceived(); void onRosterItemAdded(const QString& bareJid); @@ -91,12 +93,15 @@ private slots: void onContactGroupRemoved(const QString& group); void onContactNameChanged(const QString& name); void onContactSubscriptionStateChanged(Shared::SubscriptionState state); + void onContactHistoryResponse(const std::list& list); + void onContactNeedHistory(const QString& before, const QString& after); private: void addedAccount(const QString &bareJid); bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); void addToGroup(const QString& jid, const QString& group); void removeFromGroup(const QString& jid, const QString& group); + void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const; Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs) const; }; diff --git a/core/archive.cpp b/core/archive.cpp index 5efe17e..21d1658 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -192,7 +192,7 @@ QString Core::Archive::newestId() qDebug() << "Error geting newestId " << mdb_strerror(rc); mdb_cursor_close(cursor); mdb_txn_abort(txn); - throw new Empty(jid.toStdString()); + throw Empty(jid.toStdString()); } else { std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); mdb_cursor_close(cursor); @@ -220,6 +220,7 @@ unsigned int Core::Archive::addElements(const std::list& messag std::list::const_iterator itr = messages.begin(); while (rc == 0 && itr != messages.end()) { const Shared::Message& message = *itr; + QByteArray ba; QDataStream ds(&ba, QIODevice::WriteOnly); message.serialize(ds); @@ -230,6 +231,7 @@ unsigned int Core::Archive::addElements(const std::list& messag lmdbKey.mv_data = (char*)id.c_str(); lmdbData.mv_size = ba.size(); lmdbData.mv_data = (uint8_t*)ba.data(); + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); if (rc == 0) { MDB_val orderKey; @@ -249,6 +251,7 @@ unsigned int Core::Archive::addElements(const std::list& messag qDebug() << "An element couldn't been added to the archive, aborting the transaction" << mdb_strerror(rc); } } + itr++; } if (rc != 0) { @@ -279,7 +282,7 @@ QString Core::Archive::oldestId() qDebug() << "Error geting oldestId " << mdb_strerror(rc); mdb_cursor_close(cursor); mdb_txn_abort(txn); - throw new Empty(jid.toStdString()); + throw Empty(jid.toStdString()); } else { std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); mdb_cursor_close(cursor); @@ -317,21 +320,21 @@ std::list Core::Archive::getBefore(int count, const QString& id rc = mdb_cursor_open(txn, order, &cursor); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST); if (rc) { - qDebug() << "Error geting before " << mdb_strerror(rc); + qDebug() << "Error getting before " << mdb_strerror(rc) << ", id:" << id; mdb_cursor_close(cursor); mdb_txn_abort(txn); - throw new Empty(jid.toStdString()); - } else { - std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + throw Empty(jid.toStdString()); } } else { - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.toStdString().c_str(); + std::string stdId(id.toStdString()); + lmdbKey.mv_size = stdId.size(); + lmdbKey.mv_data = (char*)stdId.c_str(); rc = mdb_get(txn, main, &lmdbKey, &lmdbData); if (rc) { - qDebug() <<"Error getting before: " << mdb_strerror(rc); + qDebug() <<"Error getting before: no reference message" << mdb_strerror(rc) << ", id:" << id; mdb_txn_abort(txn); - throw NotFound(id.toStdString(), jid.toStdString()); + printKeys(); + throw NotFound(stdId, jid.toStdString()); } else { QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); QDataStream ds(&ba, QIODevice::ReadOnly); @@ -346,29 +349,40 @@ std::list Core::Archive::getBefore(int count, const QString& id rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET); if (rc) { - qDebug() << "Error getting before " << mdb_strerror(rc); + qDebug() << "Error getting before: couldn't set " << mdb_strerror(rc); mdb_cursor_close(cursor); mdb_txn_abort(txn); - throw new NotFound(id.toStdString(), jid.toStdString()); + throw NotFound(stdId, jid.toStdString()); } else { rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV); if (rc) { - qDebug() << "Error getting before " << mdb_strerror(rc); + qDebug() << "Error getting before, couldn't prev " << mdb_strerror(rc); mdb_cursor_close(cursor); mdb_txn_abort(txn); - throw new NotFound(id.toStdString(), jid.toStdString()); + throw NotFound(stdId, jid.toStdString()); } } } } do { - QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); - QDataStream ds(&ba, QIODevice::ReadOnly); - - res.emplace_back(); - Shared::Message& msg = res.back(); - msg.deserialize(ds); + MDB_val dKey, dData; + dKey.mv_size = lmdbData.mv_size; + dKey.mv_data = lmdbData.mv_data; + rc = mdb_get(txn, main, &dKey, &dData); + if (rc) { + qDebug() <<"Get error: " << mdb_strerror(rc); + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + mdb_txn_abort(txn); + throw NotFound(sId, jid.toStdString()); + } else { + QByteArray ba((char*)dData.mv_data, dData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + + res.emplace_back(); + Shared::Message& msg = res.back(); + msg.deserialize(ds); + } --count; @@ -420,7 +434,7 @@ bool Core::Archive::isFromTheBeginning() return fromTheBeginning; } -bool Core::Archive::setFromTheBeginning(bool is) +void Core::Archive::setFromTheBeginning(bool is) { if (!opened) { throw Closed("setFromTheBeginning", jid.toStdString()); @@ -448,3 +462,44 @@ bool Core::Archive::setFromTheBeginning(bool is) } } } + +void Core::Archive::printOrder() +{ + qDebug() << "Printing order"; + MDB_txn *txn; + int rc; + rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + MDB_cursor* cursor; + rc = mdb_cursor_open(txn, order, &cursor); + MDB_val lmdbKey, lmdbData; + + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); + + do { + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + qDebug() << QString(sId.c_str()); + } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); +} + +void Core::Archive::printKeys() +{ + MDB_txn *txn; + int rc; + rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + MDB_cursor* cursor; + rc = mdb_cursor_open(txn, main, &cursor); + MDB_val lmdbKey, lmdbData; + + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); + + do { + std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size); + qDebug() << QString(sId.c_str()); + } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); +} diff --git a/core/archive.h b/core/archive.h index b887359..a55c2ee 100644 --- a/core/archive.h +++ b/core/archive.h @@ -48,7 +48,7 @@ public: long unsigned int size() const; std::list getBefore(int count, const QString& id); bool isFromTheBeginning(); - bool setFromTheBeginning(bool is); + void setFromTheBeginning(bool is); public: const QString jid; @@ -109,6 +109,8 @@ private: MDB_dbi stats; bool _isFromTheBeginning(); + void printOrder(); + void printKeys(); }; } diff --git a/core/contact.cpp b/core/contact.cpp index 2b29003..1a1203a 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -17,6 +17,7 @@ */ #include "contact.h" +#include Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent): QObject(parent), @@ -152,7 +153,7 @@ void Core::Contact::performRequest(int count, const QString& before) switch (archiveState) { case empty: - emit needHistory(before, "", QDateTime(), QDateTime()); + emit needHistory(before, ""); break; case chunk: case beginning: @@ -160,33 +161,48 @@ void Core::Contact::performRequest(int count, const QString& before) requestCache.emplace_back(requestedCount, before); requestedCount = -1; } - emit needHistory("", archive->newestId(), QDateTime(), QDateTime()); + emit needHistory("", archive->newestId()); break; case end: if (count != -1) { - bool found = requestFromArchive(before); + QString lBefore; + if (responseCache.size() > 0) { + lBefore = responseCache.front().getId(); + } else { + lBefore = before; + } + bool found = false; + try { + std::list arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + found = true; + } catch (Archive::NotFound e) { + requestCache.emplace_back(requestedCount, before); + requestedCount = -1; + emit needHistory(archive->oldestId(), ""); + } + if (found) { int rSize = responseCache.size(); if (rSize < count) { if (rSize != 0) { - emit needHistory(responseCache.front().getId(), "", QDateTime(), QDateTime()); + emit needHistory(responseCache.front().getId(), ""); } else { - emit needHistory(before, "", QDateTime(), QDateTime()); + emit needHistory(before, ""); } } else { nextRequest(); } - } else { - requestCache.emplace_back(requestedCount, before); - requestedCount = -1; - emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); } } else { - emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); + emit needHistory(archive->oldestId(), ""); } break; case complete: - if (!requestFromArchive(before)) { + try { + std::list arc = archive->getBefore(requestedCount - responseCache.size(), before); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + } catch (Archive::NotFound e) { qDebug("requesting id hasn't been found in archive, skipping"); } nextRequest(); @@ -194,38 +210,6 @@ void Core::Contact::performRequest(int count, const QString& before) } } -bool Core::Contact::requestFromArchive(const QString& before) { - std::list arc; - QString lBefore; - if (responseCache.size() > 0) { - lBefore = responseCache.front().getId(); - } else { - lBefore = before; - } - if (requestedCount != -1) { - try { - arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); - responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - return true; - } catch (Archive::NotFound e) { - requestCache.emplace_back(requestedCount, before); - requestedCount = -1; - emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); - return false; - } - } else { - try { - arc = archive->getBefore(1, lBefore); - //just do nothing since response is not required - //may be even it's a signal that the history is now complete? - return true; - } catch (Archive::NotFound e) { - emit needHistory(archive->oldestId(), "", QDateTime(), QDateTime()); - return false; - } - } -} - void Core::Contact::appendMessageToArchive(const Shared::Message& msg) { const QString& id = msg.getId(); @@ -267,8 +251,10 @@ void Core::Contact::appendMessageToArchive(const Shared::Message& msg) void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId) { + unsigned int added(0); if (hisoryCache.size() > 0) { - archive->addElements(hisoryCache); + added = archive->addElements(hisoryCache); + qDebug() << "Added" << added << "messages to the archive"; hisoryCache.clear(); } @@ -279,14 +265,14 @@ void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId archiveState = complete; nextRequest(); } else { - emit needHistory("", lastId, QDateTime(), QDateTime()); + emit needHistory("", lastId); } case chunk: if (finished) { archiveState = end; nextRequest(); } else { - emit needHistory("", lastId, QDateTime(), QDateTime()); + emit needHistory("", lastId); } break; case empty: @@ -303,20 +289,28 @@ void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId } else { before = requestedBefore; } - if (!requestFromArchive(before)) { - qDebug("Something went terrible wrong flushing messages to the archive"); - } - if (requestedCount < responseCache.size()) { + + bool found = false; + try { + std::list arc = archive->getBefore(requestedCount - responseCache.size(), before); + responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); + found = true; + } catch (Archive::NotFound e) {} + if (!found || requestedCount < responseCache.size()) { if (archiveState == complete) { nextRequest(); } else { - emit needHistory(firstId, "", QDateTime(), QDateTime()); + emit needHistory(firstId, ""); } } else { nextRequest(); } } else { - nextRequest(); + if (added != 0) { + nextRequest(); + } else { + emit needHistory(firstId, ""); + } } break; case complete: @@ -324,3 +318,24 @@ void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId break; } } + +void Core::Contact::requestFromEmpty(int count, const QString& before) +{ + if (syncronizing) { + qDebug("perform from empty didn't work, another request queued"); + } else { + if (archiveState != empty) { + qDebug("perform from empty didn't work, the state is not empty"); + requestHistory(count, before); + } else { + syncronizing = true; + requestedCount = count; + requestedBefore = ""; + hisoryCache.clear(); + responseCache.clear(); + + emit needHistory(before, ""); + } + } +} + diff --git a/core/contact.h b/core/contact.h index 80c2db0..650b06d 100644 --- a/core/contact.h +++ b/core/contact.h @@ -54,6 +54,7 @@ public: void appendMessageToArchive(const Shared::Message& msg); void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId); void requestHistory(int count, const QString& before); + void requestFromEmpty(int count, const QString& before); signals: void groupAdded(const QString& name); @@ -61,7 +62,7 @@ signals: void nameChanged(const QString& name); void subscriptionStateChanged(Shared::SubscriptionState state); void historyResponse(const std::list& messages); - void needHistory(const QString& before, const QString& after, const QDateTime& from, const QDateTime& to); + void needHistory(const QString& before, const QString& after); public: const QString jid; @@ -84,7 +85,6 @@ private: private: void nextRequest(); void performRequest(int count, const QString& before); - bool requestFromArchive(const QString& before); }; } diff --git a/core/squawk.cpp b/core/squawk.cpp index 25d8eb5..2d20941 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -96,6 +96,8 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap&))); connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&))); connect(acc, SIGNAL(message(const Shared::Message&)), this, SLOT(onAccountMessage(const Shared::Message&))); + connect(acc, SIGNAL(responseArchive(const QString&, const std::list&)), + this, SLOT(onAccountResponseArchive(const QString&, const std::list&))); QMap map = { {"login", login}, @@ -221,12 +223,19 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da itr->second->sendMessage(data); } -void Core::Squawk::requestArchive(const QString& account, const QString& jid) +void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to request an archive of non existing account, skipping"); return; } - itr->second->requestAchive(jid); + itr->second->requestArchive(jid, count, before); } + +void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list& list) +{ + Account* acc = static_cast(sender()); + emit responseArchive(acc->getName(), jid, list); +} + diff --git a/core/squawk.h b/core/squawk.h index 9b8235e..3daf26f 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -36,6 +36,7 @@ signals: void removePresence(const QString& account, const QString& jid, const QString& name); void stateChanged(int state); void accountMessage(const QString& account, const Shared::Message& data); + void responseArchive(const QString& account, const QString& jid, const std::list& list); public slots: void start(); @@ -45,7 +46,7 @@ public slots: void disconnectAccount(const QString& account); void changeState(int state); void sendMessage(const QString& account, const Shared::Message& data); - void requestArchive(const QString& account, const QString& jid); + void requestArchive(const QString& account, const QString& jid, int count, const QString& before); private: typedef std::deque Accounts; @@ -70,6 +71,7 @@ private slots: void onAccountAddPresence(const QString& jid, const QString& name, const QMap& data); void onAccountRemovePresence(const QString& jid, const QString& name); void onAccountMessage(const Shared::Message& data); + void onAccountResponseArchive(const QString& jid, const std::list& list); }; } diff --git a/global.cpp b/global.cpp index ce5556a..5d03fef 100644 --- a/global.cpp +++ b/global.cpp @@ -176,12 +176,7 @@ bool Shared::Message::getForwarded() const void Shared::Message::generateRandomId() { - uuid_t uuid; - uuid_generate(uuid); - - char uuid_str[36]; - uuid_unparse_lower(uuid, uuid_str); - id = uuid_str; + id = generateUUID(); } QString Shared::Message::getThread() const @@ -241,3 +236,19 @@ void Shared::Message::deserialize(QDataStream& data) data >> outgoing; data >> forwarded; } + +QString Shared::generateUUID() +{ + uuid_t uuid; + uuid_generate(uuid); + + char uuid_str[36]; + uuid_unparse_lower(uuid, uuid_str); + return uuid_str; +} + +void Shared::Message::setCurrentTime() +{ + time = QDateTime::currentDateTime(); +} + diff --git a/global.h b/global.h index 922c242..e399907 100644 --- a/global.h +++ b/global.h @@ -53,6 +53,8 @@ static const std::deque availabilityNames = {"Online", "Away", "Absent" static const std::deque subscriptionStateThemeIcons = {"edit-none", "arrow-down-double", "arrow-up-double", "dialog-ok", "question"}; +QString generateUUID(); + class Message { public: enum Type { @@ -78,6 +80,7 @@ public: void setOutgoing(bool og); void setForwarded(bool fwd); void setType(Type t); + void setCurrentTime(); QString getFrom() const; QString getFromJid() const; diff --git a/main.cpp b/main.cpp index 7d0dae2..87ee2b5 100644 --- a/main.cpp +++ b/main.cpp @@ -9,6 +9,7 @@ int main(int argc, char *argv[]) { qRegisterMetaType("Shared::Message"); + qRegisterMetaType>("std::list"); QApplication app(argc, argv); SignalCatcher sc(&app); @@ -35,7 +36,7 @@ int main(int argc, char *argv[]) QObject::connect(&w, SIGNAL(disconnectAccount(const QString&)), squawk, SLOT(disconnectAccount(const QString&))); QObject::connect(&w, SIGNAL(changeState(int)), squawk, SLOT(changeState(int))); QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&)), squawk, SLOT(sendMessage(const QString&, const Shared::Message&))); - QObject::connect(&w, SIGNAL(requestArchive(const QString&, const QString&)), squawk, SLOT(requestArchive(const QString&, const QString&))); + QObject::connect(&w, SIGNAL(requestArchive(const QString&, const QString&, int, const QString&)), squawk, SLOT(requestArchive(const QString&, const QString&, int, const QString&))); QObject::connect(squawk, SIGNAL(newAccount(const QMap&)), &w, SLOT(newAccount(const QMap&))); QObject::connect(squawk, SIGNAL(accountAvailabilityChanged(const QString&, int)), &w, SLOT(accountAvailabilityChanged(const QString&, int))); @@ -53,6 +54,8 @@ int main(int argc, char *argv[]) QObject::connect(squawk, SIGNAL(removePresence(const QString&, const QString&, const QString&)), &w, SLOT(removePresence(const QString&, const QString&, const QString&))); QObject::connect(squawk, SIGNAL(stateChanged(int)), &w, SLOT(stateChanged(int))); QObject::connect(squawk, SIGNAL(accountMessage(const QString&, const Shared::Message&)), &w, SLOT(accountMessage(const QString&, const Shared::Message&))); + QObject::connect(squawk, SIGNAL(responseArchive(const QString&, const QString&, const std::list&)), + &w, SLOT(responseArchive(const QString&, const QString&, const std::list&))); //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index c5f7a42..e2fbf4c 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -8,6 +8,10 @@ set(CMAKE_AUTOUIC ON) # Find the QtWidgets library find_package(Qt5Widgets CONFIG REQUIRED) +find_package(Qt5Qml CONFIG REQUIRED) +find_package(Qt5QuickCompiler) +find_package(Qt5Quick CONFIG REQUIRED) +find_package(Qt5QuickWidgets CONFIG REQUIRED) set(squawkUI_SRC squawk.cpp @@ -28,6 +32,9 @@ add_library(squawkUI ${squawkUI_SRC}) # Use the Widgets module from Qt 5. target_link_libraries(squawkUI Qt5::Widgets) +target_link_libraries(squawkUI Qt5::Quick) +target_link_libraries(squawkUI Qt5::Qml) +target_link_libraries(squawkUI Qt5::QuickWidgets) # Install the executable install(TARGETS squawkUI DESTINATION lib) diff --git a/ui/conversation.cpp b/ui/conversation.cpp index b7ba1d8..6c2cb4f 100644 --- a/ui/conversation.cpp +++ b/ui/conversation.cpp @@ -20,6 +20,7 @@ #include "ui_conversation.h" #include #include +#include Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): QWidget(parent), @@ -30,7 +31,9 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): activePalResource(), thread(), scroll(down), - manualSliderChange(false) + manualSliderChange(false), + requestingHistory(false), + everShown(false) { m_ui->setupUi(this); m_ui->splitter->setSizes({300, 0}); @@ -116,6 +119,7 @@ void Conversation::addMessage(const Shared::Message& data) { int pos = m_ui->scrollArea->verticalScrollBar()->sliderPosition(); int max = m_ui->scrollArea->verticalScrollBar()->maximum(); + MessageLine::Position place = line->message(data); if (place == MessageLine::invalid) { return; @@ -183,6 +187,7 @@ void Conversation::onEnterPressed() msg.setBody(body); msg.setOutgoing(true); msg.generateRandomId(); + msg.setCurrentTime(); addMessage(msg); emit sendMessage(msg); } @@ -195,6 +200,18 @@ void Conversation::onMessagesResize(int amount) case down: m_ui->scrollArea->verticalScrollBar()->setValue(m_ui->scrollArea->verticalScrollBar()->maximum()); break; + case keep: { + int max = m_ui->scrollArea->verticalScrollBar()->maximum(); + int value = m_ui->scrollArea->verticalScrollBar()->value() + amount; + m_ui->scrollArea->verticalScrollBar()->setValue(value); + + if (value == max) { + scroll = down; + } else { + scroll = nothing; + } + } + break; default: break; } @@ -207,7 +224,35 @@ void Conversation::onSliderValueChanged(int value) if (value == m_ui->scrollArea->verticalScrollBar()->maximum()) { scroll = down; } else { + if (!requestingHistory && value == 0) { + m_ui->historyStatus->setPixmap(QIcon::fromTheme("view-refresh").pixmap(25)); + requestingHistory = true; + emit requestArchive(line->firstMessageId()); + } scroll = nothing; } } } + +void Conversation::responseArchive(const std::list list) +{ + requestingHistory = false; + scroll = keep; + + m_ui->historyStatus->clear(); + for (std::list::const_iterator itr = list.begin(), end = list.end(); itr != end; ++itr) { + addMessage(*itr); + } +} + +void Conversation::showEvent(QShowEvent* event) +{ + if (!everShown) { + everShown = true; + m_ui->historyStatus->setPixmap(QIcon::fromTheme("view-refresh").pixmap(25)); + requestingHistory = true; + emit requestArchive(line->firstMessageId()); + } + + QWidget::showEvent(event); +} diff --git a/ui/conversation.h b/ui/conversation.h index e88007b..3f5a315 100644 --- a/ui/conversation.h +++ b/ui/conversation.h @@ -56,9 +56,12 @@ public: void addMessage(const Shared::Message& data); void setPalResource(const QString& res); + void responseArchive(const std::list list); + void showEvent(QShowEvent * event) override; signals: void sendMessage(const Shared::Message& message); + void requestArchive(const QString& before); protected: void setState(Shared::Availability state); @@ -85,6 +88,8 @@ private: QString thread; Scroll scroll; bool manualSliderChange; + bool requestingHistory; + bool everShown; }; #endif // CONVERSATION_H diff --git a/ui/conversation.ui b/ui/conversation.ui index e1b2a52..de6604e 100644 --- a/ui/conversation.ui +++ b/ui/conversation.ui @@ -105,6 +105,13 @@ + + + + + + + @@ -134,7 +141,7 @@ 0 0 572 - 162 + 95 diff --git a/ui/messageline.cpp b/ui/messageline.cpp index 4fd7205..7cb53bb 100644 --- a/ui/messageline.cpp +++ b/ui/messageline.cpp @@ -76,6 +76,12 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) QLabel* body = new QLabel(msg.getBody()); QLabel* sender = new QLabel(); + QLabel* time = new QLabel(msg.getTime().toLocalTime().toString()); + QFont dFont = time->font(); + dFont.setItalic(true); + dFont.setPointSize(dFont.pointSize() - 2); + time->setFont(dFont); + time->setForegroundRole(QPalette::ToolTipText); QFont f; f.setBold(true); sender->setFont(f); @@ -84,10 +90,12 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) vBox->addWidget(sender); vBox->addWidget(body); + vBox->addWidget(time); if (msg.getOutgoing()) { - body->setAlignment(Qt::AlignRight); + //body->setAlignment(Qt::AlignRight); sender->setAlignment(Qt::AlignRight); + time->setAlignment(Qt::AlignRight); sender->setText(myName); hBox->addStretch(); hBox->addWidget(message); @@ -132,3 +140,13 @@ void MessageLine::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); emit resize(event->size().height() - event->oldSize().height()); } + + +QString MessageLine::firstMessageId() const +{ + if (messageOrder.size() == 0) { + return ""; + } else { + return messageOrder.begin()->second->getId(); + } +} diff --git a/ui/messageline.h b/ui/messageline.h index c0fd0d5..9dbc192 100644 --- a/ui/messageline.h +++ b/ui/messageline.h @@ -25,6 +25,7 @@ #include #include #include "../global.h" +#include class MessageLine : public QWidget { @@ -42,6 +43,9 @@ public: Position message(const Shared::Message& msg); void setMyName(const QString& name); void setPalName(const QString& jid, const QString& name); + QString firstMessageId() const; + void showBusyIndicator(); + void hideBusyIndicator(); signals: void resize(int amount); @@ -67,6 +71,7 @@ private: QString myName; std::map palNames; std::deque views; + QQuickWidget busy; }; #endif // MESSAGELINE_H diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 0d91ca3..6dcd78d 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -189,6 +189,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) conv->setAttribute(Qt::WA_DeleteOnClose); connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); connect(conv, SIGNAL(sendMessage(const Shared::Message&)), this, SLOT(onConversationMessage(const Shared::Message&))); + connect(conv, SIGNAL(requestArchive(const QString&)), this, SLOT(onConversationRequestArchive(const QString&))); conversations.insert(std::make_pair(id, conv)); rosterModel.dropMessages(account, jid); @@ -198,7 +199,6 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) if (res.size() > 0) { itr->second->setPalResource(res); } - requestArchive(account, jid); } } } @@ -234,3 +234,19 @@ void Squawk::onConversationMessage(const Shared::Message& msg) emit sendMessage(conv->getAccount(), msg); } + +void Squawk::onConversationRequestArchive(const QString& before) +{ + Conversation* conv = static_cast(sender()); + requestArchive(conv->getAccount(), conv->getJid(), 20, before); //TODO amount as a settings value +} + +void Squawk::responseArchive(const QString& account, const QString& jid, const std::list& list) +{ + Models::Roster::ElId id(account, jid); + + Conversations::const_iterator itr = conversations.find(id); + if (itr != conversations.end()) { + itr->second->responseArchive(list); + } +} diff --git a/ui/squawk.h b/ui/squawk.h index b9d53bd..573e1ac 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "accounts.h" #include "conversation.h" @@ -31,7 +32,7 @@ signals: void disconnectAccount(const QString&); void changeState(int state); void sendMessage(const QString& account, const Shared::Message& data); - void requestArchive(const QString& account, const QString& jid); + void requestArchive(const QString& account, const QString& jid, int count, const QString& before); public slots: void newAccount(const QMap& account); @@ -47,6 +48,7 @@ public slots: void removePresence(const QString& account, const QString& jid, const QString& name); void stateChanged(int state); void accountMessage(const QString& account, const Shared::Message& data); + void responseArchive(const QString& account, const QString& jid, const std::list& list); private: typedef std::map Conversations; @@ -66,6 +68,7 @@ private slots: void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); void onConversationMessage(const Shared::Message& msg); + void onConversationRequestArchive(const QString& before); };