From 3f563e1e6e5e73b0eb50f53cc4568064a0f2f780 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Fri, 9 Aug 2019 23:34:44 -0400 Subject: Cache User Mentions Cache user mentions when they are retrieved from the server. This logic currently isn't being utilized by the UI. Additionally, the app should use a 'since' value to only get mentions newer than those stored in the DB, to avoid excessive web requests. This will be implemented in a future commit. --- src/popups/UserMentions.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/popups/UserMentions.cpp (limited to 'src/popups/UserMentions.cpp') diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp new file mode 100644 index 00000000..77e5260e --- /dev/null +++ b/src/popups/UserMentions.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include "UserMentions.h" +#include "timeline/TimelineItem.h" + +using namespace popups; + +UserMentions::UserMentions(QWidget *parent) + : QWidget{parent} +{ + tab_layout_ = new QTabWidget(this); + + top_layout_ = new QVBoxLayout(this); + top_layout_->setSpacing(0); + top_layout_->setMargin(0); + + local_scroll_area_ = new QScrollArea(this); + local_scroll_area_->setWidgetResizable(true); + local_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + local_scroll_widget_ = new QWidget(this); + local_scroll_widget_->setObjectName("local_scroll_widget"); + + all_scroll_area_ = new QScrollArea(this); + all_scroll_area_->setWidgetResizable(true); + all_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + all_scroll_widget_ = new QWidget(this); + all_scroll_widget_->setObjectName("all_scroll_widget"); + + // Height of the typing display. + QFont f; + f.setPointSizeF(f.pointSizeF() * 0.9); + const int bottomMargin = QFontMetrics(f).height() + 6; + + local_scroll_layout_ = new QVBoxLayout(local_scroll_widget_); + local_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); + local_scroll_layout_->setSpacing(0); + local_scroll_layout_->setObjectName("localcrollarea"); + + all_scroll_layout_ = new QVBoxLayout(all_scroll_widget_); + all_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); + all_scroll_layout_->setSpacing(0); + all_scroll_layout_->setObjectName("allcrollarea"); + + local_scroll_area_->setWidget(local_scroll_widget_); + local_scroll_area_->setAlignment(Qt::AlignBottom); + + all_scroll_area_->setWidget(all_scroll_widget_); + all_scroll_area_->setAlignment(Qt::AlignBottom); + + tab_layout_->addTab(local_scroll_area_, tr("This Room")); + tab_layout_->addTab(all_scroll_area_, tr("All Rooms")); + top_layout_->addWidget(tab_layout_); + + setLayout(top_layout_); +} + +void +UserMentions::initializeMentions(const std::map ¬ifs) +{ + Q_UNUSED(notifs); + // Very TODO: +} + +void +UserMentions::pushItem(const QString &event_id, + const QString &user_id, + const QString &body, + const QString &room_id, + const QString ¤t_room_id) +{ + setUpdatesEnabled(false); + + // Add to the 'all' section + TimelineItem *view_item = new TimelineItem( + mtx::events::MessageType::Text, user_id, body, true, room_id, all_scroll_widget_); + view_item->setEventId(event_id); + view_item->hide(); + + all_scroll_layout_->addWidget(view_item); + QTimer::singleShot(0, this, [view_item, this]() { + view_item->show(); + view_item->adjustSize(); + setUpdatesEnabled(true); + }); + + // if it matches the current room... add it to the current room as well. + if (QString::compare(room_id, current_room_id, Qt::CaseInsensitive) == 0) { + // Add to the 'local' section + TimelineItem *local_view_item = new TimelineItem(mtx::events::MessageType::Text, + user_id, + body, + true, + room_id, + local_scroll_widget_); + local_view_item->setEventId(event_id); + local_view_item->hide(); + + local_scroll_layout_->addWidget(local_view_item); + + QTimer::singleShot(0, this, [local_view_item]() { + local_view_item->show(); + local_view_item->adjustSize(); + }); + } +} \ No newline at end of file -- cgit 1.5.1 From 52a262177660da2d541236d597fe34212d078094 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 12 Aug 2019 22:09:40 -0400 Subject: Fix issues with caching and loading of mentions. Mentions are now loaded from the cache instead of directly from the web request. Mentions are also properly saved to the cache now (instead of as empty strings). Still lots of tweaks left on this feature. --- src/Cache.cpp | 31 +++++++++++++---------- src/Cache.h | 2 +- src/ChatPage.cpp | 62 ++++++++++++++++++++++++++------------------- src/ChatPage.h | 2 +- src/popups/UserMentions.cpp | 51 +++++++++++++++++++++++++++++++++---- src/popups/UserMentions.h | 10 +++++--- 6 files changed, 108 insertions(+), 50 deletions(-) (limited to 'src/popups/UserMentions.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index 799dd319..d264b541 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -1232,20 +1233,20 @@ Cache::roomMessages() return msgs; } -std::map +QMap Cache::getTimelineMentions() { // TODO: Should be read-only, but getMentionsDb will attempt to create a DB // if it doesn't exist, throwing an error. auto txn = lmdb::txn::begin(env_, nullptr); - std::map notifs; + QMap notifs; auto room_ids = getRoomIds(txn); for (const auto &room_id : room_ids) { - auto roomNotifs = getTimelineMentionsForRoom(txn, room_id); - notifs.emplace(QString::fromStdString(room_id), roomNotifs); + auto roomNotifs = getTimelineMentionsForRoom(txn, room_id); + notifs[QString::fromStdString(room_id)] = roomNotifs; } txn.commit(); @@ -1973,11 +1974,11 @@ Cache::getTimelineMentionsForRoom(lmdb::txn &txn, const std::string &room_id) while (cursor.get(event_id, msg, MDB_NEXT)) { auto obj = json::parse(msg); - if (obj.count("event") == 0 || obj.count("token") == 0) + if (obj.count("event") == 0) continue; mtx::responses::Notification notification; - mtx::responses::from_json(obj.at("notification"), notification); + mtx::responses::from_json(obj, notification); notif.notifications.push_back(notification); } @@ -1992,18 +1993,25 @@ Cache::getTimelineMentionsForRoom(lmdb::txn &txn, const std::string &room_id) void Cache::saveTimelineMentions(const mtx::responses::Notifications &res) { + json notif = res; + QMap> notifsByRoom; // Sort into room-specific 'buckets' for (const auto ¬if : res.notifications) { + json val = notif; notifsByRoom[notif.room_id].push_back(notif); } auto txn = lmdb::txn::begin(env_); // Insert the entire set of mentions for each room at a time. - for (const auto &room : notifsByRoom.keys()) { - nhlog::db()->debug("Storing notifications for " + room); - saveTimelineMentions(txn, room, notifsByRoom[room]); + QMap>::const_iterator it = + notifsByRoom.constBegin(); + auto end = notifsByRoom.constEnd(); + while (it != end) { + nhlog::db()->debug("Storing notifications for " + it.key()); + saveTimelineMentions(txn, it.key(), std::move(it.value())); + ++it; } txn.commit(); @@ -2019,10 +2027,7 @@ Cache::saveTimelineMentions(lmdb::txn &txn, using namespace mtx::events; using namespace mtx::events::state; - int i = 0; for (const auto ¬if : res) { - nhlog::db()->debug("Storing notification " + std::to_string(i++) + " for room " + - room_id); const auto event_id = utils::event_id(notif.event); // double check that we have the correct room_id... @@ -2030,7 +2035,7 @@ Cache::saveTimelineMentions(lmdb::txn &txn, return; } - json obj = json::object(); + json obj = notif; lmdb::dbi_put(txn, db, lmdb::val(event_id), lmdb::val(obj.dump())); } diff --git a/src/Cache.h b/src/Cache.h index 97b264fc..5ec79c3b 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -323,7 +323,7 @@ public: std::map roomMessages(); - std::map getTimelineMentions(); + QMap getTimelineMentions(); //! Retrieve all the user ids from a room. std::vector roomMembers(const std::string &room_id); diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 54562c82..58f23fef 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -153,21 +153,26 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) }); connect(top_bar_, &TopRoomBar::mentionsClicked, this, [this](const QPoint &mentionsPos) { - http::client()->notifications( - 1000, - "", - "highlight", - [this, mentionsPos](const mtx::responses::Notifications &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to retrieve notifications: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); - return; - } + if (user_mentions_popup_->isVisible()) { + user_mentions_popup_->hide(); + } else { + http::client()->notifications( + 1000, + "", + "highlight", + [this, mentionsPos](const mtx::responses::Notifications &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to retrieve notifications: {} ({})", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } - emit highlightedNotifsRetrieved(std::move(res), mentionsPos); - }); + emit highlightedNotifsRetrieved(std::move(res), mentionsPos); + }); + } }); connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL); @@ -1001,28 +1006,32 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) void ChatPage::showNotificationsDialog(const mtx::responses::Notifications &res, const QPoint &widgetPos) { + // TODO: Remove notifications from this function call. + Q_UNUSED(res); + auto notifDialog = user_mentions_popup_; - for (const auto &item : res.notifications) { - const auto event_id = QString::fromStdString(utils::event_id(item.event)); + // for (const auto &item : res.notifications) { + // const auto event_id = QString::fromStdString(utils::event_id(item.event)); - try { - const auto room_id = QString::fromStdString(item.room_id); - const auto user_id = utils::event_sender(item.event); - const auto body = utils::event_body(item.event); + // try { + // const auto room_id = QString::fromStdString(item.room_id); + // const auto user_id = utils::event_sender(item.event); + // const auto body = utils::event_body(item.event); - notifDialog->pushItem(event_id, user_id, body, room_id, current_room_); + // notifDialog->pushItem(event_id, user_id, body, room_id, current_room_); - } catch (const lmdb::error &e) { - nhlog::db()->warn("error while sending desktop notification: {}", e.what()); - } - } + // } catch (const lmdb::error &e) { + // nhlog::db()->warn("error while sending desktop notification: {}", + // e.what()); + // } + // } notifDialog->setGeometry( widgetPos.x() - (width() / 10), widgetPos.y() + 25, width() / 5, height() / 2); // notifDialog->move(widgetPos.x(), widgetPos.y()); // notifDialog->setFixedWidth(width() / 10); // notifDialog->setFixedHeight(height() / 2); notifDialog->raise(); - notifDialog->show(); + notifDialog->showPopup(); } void @@ -1292,6 +1301,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request emit initializeViews(std::move(res.rooms)); emit initializeRoomList(cache::client()->roomInfo()); + emit initializeMentions(cache::client()->getTimelineMentions()); cache::client()->calculateRoomReadStatus(); emit syncTags(cache::client()->roomInfo().toStdMap()); diff --git a/src/ChatPage.h b/src/ChatPage.h index 87876a09..fb6fbb06 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -140,7 +140,7 @@ signals: void initializeRoomList(QMap); void initializeViews(const mtx::responses::Rooms &rooms); void initializeEmptyViews(const std::map &msgs); - void initializeMentions(const std::map ¬ifs); + void initializeMentions(const QMap ¬ifs); void syncUI(const mtx::responses::Rooms &rooms); void syncRoomlist(const std::map &updates); void syncTags(const std::map &updates); diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index 77e5260e..267540cc 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -1,6 +1,8 @@ #include #include +#include "Cache.h" +#include "ChatPage.h" #include "UserMentions.h" #include "timeline/TimelineItem.h" @@ -9,6 +11,9 @@ using namespace popups; UserMentions::UserMentions(QWidget *parent) : QWidget{parent} { + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); + tab_layout_ = new QTabWidget(this); top_layout_ = new QVBoxLayout(this); @@ -37,12 +42,12 @@ UserMentions::UserMentions(QWidget *parent) local_scroll_layout_ = new QVBoxLayout(local_scroll_widget_); local_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); local_scroll_layout_->setSpacing(0); - local_scroll_layout_->setObjectName("localcrollarea"); + local_scroll_layout_->setObjectName("localscrollarea"); all_scroll_layout_ = new QVBoxLayout(all_scroll_widget_); all_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); all_scroll_layout_->setSpacing(0); - all_scroll_layout_->setObjectName("allcrollarea"); + all_scroll_layout_->setObjectName("allscrollarea"); local_scroll_area_->setWidget(local_scroll_widget_); local_scroll_area_->setAlignment(Qt::AlignBottom); @@ -58,10 +63,46 @@ UserMentions::UserMentions(QWidget *parent) } void -UserMentions::initializeMentions(const std::map ¬ifs) +UserMentions::initializeMentions(const QMap ¬ifs) +{ + nhlog::ui()->debug("Initializing " + std::to_string(notifs.size()) + " notifications."); + for (auto widget : all_scroll_layout_->findChildren()) { + delete widget; + } + for (auto widget : local_scroll_layout_->findChildren()) { + delete widget; + } + for (const auto &item : notifs) { + for (const auto notif : item.notifications) { + const auto event_id = QString::fromStdString(utils::event_id(notif.event)); + + try { + const auto room_id = QString::fromStdString(notif.room_id); + const auto user_id = utils::event_sender(notif.event); + const auto body = utils::event_body(notif.event); + + pushItem(event_id, + user_id, + body, + room_id, + ChatPage::instance()->currentRoom()); + + } catch (const lmdb::error &e) { + nhlog::db()->warn("error while sending desktop notification: {}", + e.what()); + } + } + } +} + +void +UserMentions::showPopup() { - Q_UNUSED(notifs); - // Very TODO: + auto notifs = cache::client()->getTimelineMentions(); + + initializeMentions(notifs); + + show(); } void diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h index 5dc475bf..0029eedd 100644 --- a/src/popups/UserMentions.h +++ b/src/popups/UserMentions.h @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -16,15 +17,16 @@ class UserMentions : public QWidget Q_OBJECT public: UserMentions(QWidget *parent = nullptr); + + void showPopup(); + void initializeMentions(const QMap ¬ifs); + +private: void pushItem(const QString &event_id, const QString &user_id, const QString &body, const QString &room_id, const QString ¤t_room_id); - - void initializeMentions(const std::map ¬ifs); - -private: QTabWidget *tab_layout_; QVBoxLayout *top_layout_; QVBoxLayout *local_scroll_layout_; -- cgit 1.5.1 From bcdd97c85fb3b2aeb0cd46c05562f880792737aa Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 19 Aug 2019 18:11:38 -0400 Subject: More updates to mentions functionality --- src/ChatPage.cpp | 27 ++++----------------------- src/ChatPage.h | 2 +- src/popups/UserMentions.cpp | 18 ++++++++++-------- src/popups/UserMentions.h | 7 ++++++- 4 files changed, 21 insertions(+), 33 deletions(-) (limited to 'src/popups/UserMentions.cpp') diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 58f23fef..854d57d7 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -156,6 +156,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) if (user_mentions_popup_->isVisible()) { user_mentions_popup_->hide(); } else { + showNotificationsDialog(mentionsPos); http::client()->notifications( 1000, "", @@ -525,13 +526,12 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::highlightedNotifsRetrieved, this, - [this](const mtx::responses::Notifications ¬if, const QPoint &widgetPos) { + [this](const mtx::responses::Notifications ¬if) { try { cache::client()->saveTimelineMentions(notif); } catch (const lmdb::error &e) { nhlog::db()->error("failed to save mentions: {}", e.what()); } - showNotificationsDialog(notif, widgetPos); }); connect(communitiesList_, @@ -1004,32 +1004,13 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) } void -ChatPage::showNotificationsDialog(const mtx::responses::Notifications &res, const QPoint &widgetPos) +ChatPage::showNotificationsDialog(const QPoint &widgetPos) { - // TODO: Remove notifications from this function call. - Q_UNUSED(res); - auto notifDialog = user_mentions_popup_; - // for (const auto &item : res.notifications) { - // const auto event_id = QString::fromStdString(utils::event_id(item.event)); - - // try { - // const auto room_id = QString::fromStdString(item.room_id); - // const auto user_id = utils::event_sender(item.event); - // const auto body = utils::event_body(item.event); - // notifDialog->pushItem(event_id, user_id, body, room_id, current_room_); - - // } catch (const lmdb::error &e) { - // nhlog::db()->warn("error while sending desktop notification: {}", - // e.what()); - // } - // } notifDialog->setGeometry( widgetPos.x() - (width() / 10), widgetPos.y() + 25, width() / 5, height() / 2); - // notifDialog->move(widgetPos.x(), widgetPos.y()); - // notifDialog->setFixedWidth(width() / 10); - // notifDialog->setFixedHeight(height() / 2); + notifDialog->raise(); notifDialog->showPopup(); } diff --git a/src/ChatPage.h b/src/ChatPage.h index fb6fbb06..189af387 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -210,7 +210,7 @@ private: //! Send desktop notification for the received messages. void sendDesktopNotifications(const mtx::responses::Notifications &); - void showNotificationsDialog(const mtx::responses::Notifications &, const QPoint &point); + void showNotificationsDialog(const QPoint &point); QStringList generateTypingUsers(const QString &room_id, const std::vector &typing_users); diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index 267540cc..152cd82d 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -3,6 +3,7 @@ #include "Cache.h" #include "ChatPage.h" +#include "Logging.h" #include "UserMentions.h" #include "timeline/TimelineItem.h" @@ -12,7 +13,7 @@ UserMentions::UserMentions(QWidget *parent) : QWidget{parent} { setAttribute(Qt::WA_ShowWithoutActivating, true); - setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); + setWindowFlags(Qt::FramelessWindowHint | Qt::Popup); tab_layout_ = new QTabWidget(this); @@ -66,12 +67,7 @@ void UserMentions::initializeMentions(const QMap ¬ifs) { nhlog::ui()->debug("Initializing " + std::to_string(notifs.size()) + " notifications."); - for (auto widget : all_scroll_layout_->findChildren()) { - delete widget; - } - for (auto widget : local_scroll_layout_->findChildren()) { - delete widget; - } + for (const auto &item : notifs) { for (const auto notif : item.notifications) { const auto event_id = QString::fromStdString(utils::event_id(notif.event)); @@ -98,10 +94,16 @@ UserMentions::initializeMentions(const QMapfindChildren()) { + delete widget; + } + for (auto widget : local_scroll_layout_->findChildren()) { + delete widget; + } + auto notifs = cache::client()->getTimelineMentions(); initializeMentions(notifs); - show(); } diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h index 0029eedd..a74bf2ec 100644 --- a/src/popups/UserMentions.h +++ b/src/popups/UserMentions.h @@ -2,6 +2,8 @@ #include +#include +#include #include #include #include @@ -9,6 +11,9 @@ #include #include #include +#include + +#include "Logging.h" namespace popups { @@ -18,8 +23,8 @@ class UserMentions : public QWidget public: UserMentions(QWidget *parent = nullptr); - void showPopup(); void initializeMentions(const QMap ¬ifs); + void showPopup(); private: void pushItem(const QString &event_id, -- cgit 1.5.1 From 98d4f14bd1ccf4c280be6a5fb965c75217c74b7e Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Thu, 29 Aug 2019 18:01:20 -0400 Subject: Fix themeing issues on mentions --- resources/styles/nheko-dark.qss | 11 +++++++++++ resources/styles/nheko.qss | 11 +++++++++++ resources/styles/system.qss | 10 ++++++++++ src/popups/UserMentions.cpp | 12 +++++++++++- src/popups/UserMentions.h | 4 ++++ 5 files changed, 47 insertions(+), 1 deletion(-) (limited to 'src/popups/UserMentions.cpp') diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss index 1e1333b2..1271e39f 100644 --- a/resources/styles/nheko-dark.qss +++ b/resources/styles/nheko-dark.qss @@ -24,6 +24,17 @@ TimelineView > * { border: none; } +UserMentionsWidget, +UserMentionsWidget > * { + background-color: #202228; + border: none; +} + +UserMentionsWidget > TimelineItem { + qproperty-backgroundColor: #202228; + qproperty-hoverColor: rgba(45, 49, 57, 120); +} + #scroll_widget { background-color: #202228; } diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss index a70441be..3c7f3b71 100644 --- a/resources/styles/nheko.qss +++ b/resources/styles/nheko.qss @@ -24,6 +24,17 @@ TimelineView > * { border: none; } +UserMentionsWidget, +UserMentionsWidget > * { + background-color: white; + border: none; +} + +UserMentionsWidget > TimelineItem { + qproperty-backgroundColor: white; + qproperty-hoverColor: rgba(192, 193, 195, 120); +} + #scroll_widget { background-color: white; } diff --git a/resources/styles/system.qss b/resources/styles/system.qss index dfb8ce65..0a8c4b21 100644 --- a/resources/styles/system.qss +++ b/resources/styles/system.qss @@ -12,6 +12,16 @@ TimelineView > * { border: none; } +UserMentionsWidget, +UserMentionsWidget > * { + border: none; +} + +UserMentionsWidget > TimelineItem { + qproperty-backgroundColor: palette(window); + qproperty-hoverColor: palette(base); +} + TextInputWidget { border: none; border-top: 1px solid palette(mid); diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index 152cd82d..3480959a 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include @@ -140,7 +142,6 @@ UserMentions::pushItem(const QString &event_id, local_scroll_widget_); local_view_item->setEventId(event_id); local_view_item->hide(); - local_scroll_layout_->addWidget(local_view_item); QTimer::singleShot(0, this, [local_view_item]() { @@ -148,4 +149,13 @@ UserMentions::pushItem(const QString &event_id, local_view_item->adjustSize(); }); } +} + +void +UserMentions::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } \ No newline at end of file diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h index a74bf2ec..d7dfc575 100644 --- a/src/popups/UserMentions.h +++ b/src/popups/UserMentions.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ public: void initializeMentions(const QMap ¬ifs); void showPopup(); +protected: + void paintEvent(QPaintEvent *) override; + private: void pushItem(const QString &event_id, const QString &user_id, -- cgit 1.5.1 From 8e611abe87dd78095a35d156874a5b70ca72eb3e Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 30 Aug 2019 19:29:25 +0200 Subject: Replace timeline with empty qml view --- .gitignore | 5 ++- CMakeLists.txt | 34 +++++++++------- resources/qml/TimelineView.qml | 11 +++++ resources/res.qrc | 3 ++ src/ChatPage.cpp | 4 +- src/popups/UserMentions.cpp | 77 +++++++++++++++++++---------------- src/timeline2/TimelineViewManager.cpp | 10 +++++ src/timeline2/TimelineViewManager.h | 72 ++++++++++++++++++++++++++++++++ 8 files changed, 163 insertions(+), 53 deletions(-) create mode 100644 resources/qml/TimelineView.qml create mode 100644 src/timeline2/TimelineViewManager.cpp create mode 100644 src/timeline2/TimelineViewManager.h (limited to 'src/popups/UserMentions.cpp') diff --git a/.gitignore b/.gitignore index 43c9b7b4..23b84039 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ -build +/build* tags cscope* .clang_complete *wintoastlib* +/.ccls-cache +/.exrc +.gdb_history # GTAGS GTAGS diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d5aff7a..b9726dd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,7 +69,7 @@ include(LMDB) # # Discover Qt dependencies. # -find_package(Qt5 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia REQUIRED) +find_package(Qt5 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia Qml QuickControls2 REQUIRED) find_package(Qt5DBus) if (APPLE) @@ -191,13 +191,14 @@ set(SRC_FILES src/emoji/Provider.cpp # Timeline - src/timeline/TimelineViewManager.cpp - src/timeline/TimelineItem.cpp - src/timeline/TimelineView.cpp - src/timeline/widgets/AudioItem.cpp - src/timeline/widgets/FileItem.cpp - src/timeline/widgets/ImageItem.cpp - src/timeline/widgets/VideoItem.cpp + src/timeline2/TimelineViewManager.cpp + #src/timeline/TimelineViewManager.cpp + #src/timeline/TimelineItem.cpp + #src/timeline/TimelineView.cpp + #src/timeline/widgets/AudioItem.cpp + #src/timeline/widgets/FileItem.cpp + #src/timeline/widgets/ImageItem.cpp + #src/timeline/widgets/VideoItem.cpp # UI components src/ui/Avatar.cpp @@ -333,13 +334,14 @@ qt5_wrap_cpp(MOC_HEADERS src/emoji/PickButton.h # Timeline - src/timeline/TimelineItem.h - src/timeline/TimelineView.h - src/timeline/TimelineViewManager.h - src/timeline/widgets/AudioItem.h - src/timeline/widgets/FileItem.h - src/timeline/widgets/ImageItem.h - src/timeline/widgets/VideoItem.h + src/timeline2/TimelineViewManager.h + #src/timeline/TimelineItem.h + #src/timeline/TimelineView.h + #src/timeline/TimelineViewManager.h + #src/timeline/widgets/AudioItem.h + #src/timeline/widgets/FileItem.h + #src/timeline/widgets/ImageItem.h + #src/timeline/widgets/VideoItem.h # UI components src/ui/Avatar.h @@ -405,6 +407,8 @@ set(COMMON_LIBS Qt5::Svg Qt5::Concurrent Qt5::Multimedia + Qt5::Qml + Qt5::QuickControls2 nlohmann_json::nlohmann_json) if(APPVEYOR_BUILD) diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml new file mode 100644 index 00000000..d81e3ae1 --- /dev/null +++ b/resources/qml/TimelineView.qml @@ -0,0 +1,11 @@ +import QtQuick 2.1 + +Rectangle { + anchors.fill: parent + + Text { + anchors.centerIn: parent + text: qsTr("No room open") + font.pointSize: 24 + } +} diff --git a/resources/res.qrc b/resources/res.qrc index ad27af5a..65770c8c 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -114,4 +114,7 @@ styles/nheko.qss styles/nheko-dark.qss + + qml/TimelineView.qml + diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 21ded4b3..594a41c2 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -44,7 +44,7 @@ #include "dialogs/ReadReceipts.h" #include "popups/UserMentions.h" -#include "timeline/TimelineViewManager.h" +#include "timeline2/TimelineViewManager.h" // TODO: Needs to be updated with an actual secret. static const std::string STORAGE_SECRET_KEY("secret"); @@ -113,7 +113,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) view_manager_ = new TimelineViewManager(this); contentLayout_->addWidget(top_bar_); - contentLayout_->addWidget(view_manager_); + contentLayout_->addWidget(view_manager_->getWidget()); connect(this, &ChatPage::removeTimelineEvent, diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index 3480959a..3be5c462 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -7,7 +7,7 @@ #include "ChatPage.h" #include "Logging.h" #include "UserMentions.h" -#include "timeline/TimelineItem.h" +//#include "timeline/TimelineItem.h" using namespace popups; @@ -116,39 +116,46 @@ UserMentions::pushItem(const QString &event_id, const QString &room_id, const QString ¤t_room_id) { - setUpdatesEnabled(false); - - // Add to the 'all' section - TimelineItem *view_item = new TimelineItem( - mtx::events::MessageType::Text, user_id, body, true, room_id, all_scroll_widget_); - view_item->setEventId(event_id); - view_item->hide(); - - all_scroll_layout_->addWidget(view_item); - QTimer::singleShot(0, this, [view_item, this]() { - view_item->show(); - view_item->adjustSize(); - setUpdatesEnabled(true); - }); - - // if it matches the current room... add it to the current room as well. - if (QString::compare(room_id, current_room_id, Qt::CaseInsensitive) == 0) { - // Add to the 'local' section - TimelineItem *local_view_item = new TimelineItem(mtx::events::MessageType::Text, - user_id, - body, - true, - room_id, - local_scroll_widget_); - local_view_item->setEventId(event_id); - local_view_item->hide(); - local_scroll_layout_->addWidget(local_view_item); - - QTimer::singleShot(0, this, [local_view_item]() { - local_view_item->show(); - local_view_item->adjustSize(); - }); - } + (void)event_id; + (void)user_id; + (void)body; + (void)room_id; + (void)current_room_id; + // setUpdatesEnabled(false); + // + // // Add to the 'all' section + // TimelineItem *view_item = new TimelineItem( + // mtx::events::MessageType::Text, user_id, body, true, room_id, + // all_scroll_widget_); + // view_item->setEventId(event_id); + // view_item->hide(); + // + // all_scroll_layout_->addWidget(view_item); + // QTimer::singleShot(0, this, [view_item, this]() { + // view_item->show(); + // view_item->adjustSize(); + // setUpdatesEnabled(true); + // }); + // + // // if it matches the current room... add it to the current room as well. + // if (QString::compare(room_id, current_room_id, Qt::CaseInsensitive) == 0) { + // // Add to the 'local' section + // TimelineItem *local_view_item = new + // TimelineItem(mtx::events::MessageType::Text, + // user_id, + // body, + // true, + // room_id, + // local_scroll_widget_); + // local_view_item->setEventId(event_id); + // local_view_item->hide(); + // local_scroll_layout_->addWidget(local_view_item); + // + // QTimer::singleShot(0, this, [local_view_item]() { + // local_view_item->show(); + // local_view_item->adjustSize(); + // }); + // } } void @@ -158,4 +165,4 @@ UserMentions::paintEvent(QPaintEvent *) opt.init(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} \ No newline at end of file +} diff --git a/src/timeline2/TimelineViewManager.cpp b/src/timeline2/TimelineViewManager.cpp new file mode 100644 index 00000000..b932b6ac --- /dev/null +++ b/src/timeline2/TimelineViewManager.cpp @@ -0,0 +1,10 @@ +#include "TimelineViewManager.h" + +TimelineViewManager::TimelineViewManager(QWidget *parent) +{ + view = new QQuickView(); + container = QWidget::createWindowContainer(view, parent); + container->setMinimumSize(200, 200); + view->setSource(QUrl("qrc:///qml/TimelineView.qml")); + // view->rootContext()->setContextProperty(room); +} diff --git a/src/timeline2/TimelineViewManager.h b/src/timeline2/TimelineViewManager.h new file mode 100644 index 00000000..23d30065 --- /dev/null +++ b/src/timeline2/TimelineViewManager.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include + +#include "Cache.h" +#include "Utils.h" + +// temporary for stubs +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +class TimelineViewManager : public QObject +{ + Q_OBJECT +public: + TimelineViewManager(QWidget *parent = 0); + QWidget *getWidget() const { return container; } + + void initialize(const mtx::responses::Rooms &rooms) {} + void addRoom(const QString &room_id) {} + + void sync(const mtx::responses::Rooms &rooms) {} + void clearAll() {} + +signals: + void clearRoomMessageCount(QString roomid); + void updateRoomsLastMessage(const QString &user, const DescInfo &info); + +public slots: + void updateReadReceipts(const QString &room_id, const std::vector &event_ids) {} + void removeTimelineEvent(const QString &room_id, const QString &event_id) {} + void initWithMessages(const std::map &msgs) {} + + void setHistoryView(const QString &room_id) {} + void queueTextMessage(const QString &msg) {} + void queueReplyMessage(const QString &reply, const RelatedInfo &related) {} + void queueEmoteMessage(const QString &msg) {} + void queueImageMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + uint64_t dsize, + const QSize &dimensions) + {} + void queueFileMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + uint64_t dsize) + {} + void queueAudioMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + uint64_t dsize) + {} + void queueVideoMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + uint64_t dsize) + {} + +private: + QQuickView *view; + QWidget *container; +}; + +#pragma GCC diagnostic pop -- cgit 1.5.1 From 5fc1f3bd678cca690268eafbb7e4595657a6e133 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 15 Dec 2019 02:56:04 +0100 Subject: Reduce overhead of Cache.h --- .gitignore | 1 + CMakeLists.txt | 2 +- src/AvatarProvider.cpp | 14 +- src/Cache.cpp | 561 +++++++++++++++++++++++++++++-- src/Cache.h | 744 +++++++++++++++-------------------------- src/Cache_p.h | 494 +++++++++++++++++++++++++++ src/ChatPage.cpp | 87 +++-- src/CommunitiesList.cpp | 4 +- src/MainWindow.cpp | 6 +- src/MxcImageProvider.cpp | 10 +- src/Olm.cpp | 27 +- src/QuickSwitcher.cpp | 3 +- src/TextInputWidget.cpp | 4 +- src/UserSettingsPage.cpp | 4 +- src/Utils.cpp | 2 +- src/Utils.h | 2 +- src/dialogs/MemberList.cpp | 4 +- src/dialogs/ReadReceipts.cpp | 2 +- src/dialogs/RoomSettings.cpp | 12 +- src/dialogs/RoomSettings.h | 4 +- src/dialogs/UserProfile.cpp | 8 +- src/popups/PopupItem.cpp | 6 +- src/popups/PopupItem.h | 3 +- src/popups/ReplyPopup.cpp | 1 + src/popups/ReplyPopup.h | 9 +- src/popups/SuggestionsPopup.h | 7 +- src/popups/UserMentions.cpp | 2 +- src/timeline/TimelineModel.cpp | 23 +- 28 files changed, 1403 insertions(+), 643 deletions(-) create mode 100644 src/Cache_p.h (limited to 'src/popups/UserMentions.cpp') diff --git a/.gitignore b/.gitignore index 2d772e58..6d178679 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ moc_*.cpp qrc_*.cpp ui_*.h *-build-* +/.clangd/ # QtCreator diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e86ee14..5c68d614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,7 +357,7 @@ qt5_wrap_cpp(MOC_HEADERS src/notifications/Manager.h src/AvatarProvider.h - src/Cache.h + src/Cache_p.h src/ChatPage.h src/CommunitiesListItem.h src/CommunitiesList.h diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp index 68b6901e..1587a9a1 100644 --- a/src/AvatarProvider.cpp +++ b/src/AvatarProvider.cpp @@ -35,9 +35,6 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca const auto cacheKey = avatarUrl + "_size_" + size; - if (!cache::client()) - return; - if (avatarUrl.isEmpty()) return; @@ -47,7 +44,7 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca return; } - auto data = cache::client()->image(avatarUrl); + auto data = cache::image(avatarUrl); if (!data.isNull()) { pixmap.loadFromData(data); avatar_cache.insert(cacheKey, pixmap); @@ -82,7 +79,7 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca return; } - cache::client()->saveImage(opts.mxc_url, res); + cache::saveImage(opts.mxc_url, res); emit proxy->avatarDownloaded(QByteArray(res.data(), res.size())); }); @@ -95,12 +92,7 @@ resolve(const QString &room_id, QObject *receiver, AvatarCallback callback) { - const auto key = QString("%1 %2").arg(room_id).arg(user_id); - const auto avatarUrl = Cache::avatarUrl(room_id, user_id); - const auto cacheKey = avatarUrl + "_size_" + size; - - if (!Cache::AvatarUrls.contains(key) || !cache::client()) - return; + const auto avatarUrl = cache::avatarUrl(room_id, user_id); resolve(avatarUrl, size, receiver, callback); } diff --git a/src/Cache.cpp b/src/Cache.cpp index ee53ffbd..79425fa1 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -30,6 +30,7 @@ #include #include "Cache.h" +#include "Cache_p.h" #include "Utils.h" //! Should be changed when a breaking change occurs in the cache format. @@ -89,29 +90,20 @@ namespace { std::unique_ptr instance_ = nullptr; } -namespace cache { -void -init(const QString &user_id) +int +numeric_key_comparison(const MDB_val *a, const MDB_val *b) { - qRegisterMetaType(); - qRegisterMetaType>(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType>(); - qRegisterMetaType>(); - qRegisterMetaType>(); - qRegisterMetaType>(); + auto lhs = std::stoull(std::string((char *)a->mv_data, a->mv_size)); + auto rhs = std::stoull(std::string((char *)b->mv_data, b->mv_size)); - instance_ = std::make_unique(user_id); -} + if (lhs < rhs) + return 1; + else if (lhs == rhs) + return 0; -Cache * -client() -{ - return instance_.get(); + return -1; } -} // namespace cache + Cache::Cache(const QString &userId, QObject *parent) : QObject{parent} @@ -2321,20 +2313,6 @@ from_json(const json &j, RoomInfo &info) info.tags = j.at("tags").get>(); } -int -numeric_key_comparison(const MDB_val *a, const MDB_val *b) -{ - auto lhs = std::stoull(std::string((char *)a->mv_data, a->mv_size)); - auto rhs = std::stoull(std::string((char *)b->mv_data, b->mv_size)); - - if (lhs < rhs) - return 1; - else if (lhs == rhs) - return 0; - - return -1; -} - void to_json(json &j, const ReadReceiptKey &key) { @@ -2407,3 +2385,520 @@ from_json(const nlohmann::json &obj, MegolmSessionIndex &msg) msg.session_id = obj.at("session_id"); msg.sender_key = obj.at("sender_key"); } + +namespace cache { +void +init(const QString &user_id) +{ + qRegisterMetaType(); + qRegisterMetaType>(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType>(); + qRegisterMetaType>(); + qRegisterMetaType>(); + qRegisterMetaType>(); + + instance_ = std::make_unique(user_id); +} + +Cache * +client() +{ + return instance_.get(); +} + +std::string +displayName(const std::string &room_id, const std::string &user_id) +{ + return instance_->displayName(room_id, user_id); +} + +QString +displayName(const QString &room_id, const QString &user_id) +{ + return instance_->displayName(room_id, user_id); +} +QString +avatarUrl(const QString &room_id, const QString &user_id) +{ + return instance_->avatarUrl(room_id, user_id); +} + +QString +userColor(const QString &user_id) +{ + return instance_->userColor(user_id); +} + +void +removeDisplayName(const QString &room_id, const QString &user_id) +{ + instance_->removeDisplayName(room_id, user_id); +} +void +removeAvatarUrl(const QString &room_id, const QString &user_id) +{ + instance_->removeAvatarUrl(room_id, user_id); +} +void +removeUserColor(const QString &user_id) +{ + instance_->removeUserColor(user_id); +} + +void +insertDisplayName(const QString &room_id, const QString &user_id, const QString &display_name) +{ + instance_->insertDisplayName(room_id, user_id, display_name); +} +void +insertAvatarUrl(const QString &room_id, const QString &user_id, const QString &avatar_url) +{ + instance_->insertAvatarUrl(room_id, user_id, avatar_url); +} +void +insertUserColor(const QString &user_id, const QString &color_name) +{ + instance_->insertUserColor(user_id, color_name); +} + +void +clearUserColors() +{ + instance_->clearUserColors(); +} + +//! Load saved data for the display names & avatars. +void +populateMembers() +{ + instance_->populateMembers(); +} + +std::vector +joinedRooms() +{ + return instance_->joinedRooms(); +} + +QMap +roomInfo(bool withInvites) +{ + return instance_->roomInfo(withInvites); +} +std::map +invites() +{ + return instance_->invites(); +} + +QString +getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) +{ + return instance_->getRoomName(txn, statesdb, membersdb); +} +mtx::events::state::JoinRule +getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb) +{ + return instance_->getRoomJoinRule(txn, statesdb); +} +bool +getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb) +{ + return instance_->getRoomGuestAccess(txn, statesdb); +} +QString +getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb) +{ + return instance_->getRoomTopic(txn, statesdb); +} +QString +getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb, const QString &room_id) +{ + return instance_->getRoomAvatarUrl(txn, statesdb, membersdb, room_id); +} + +QString +getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb) +{ + return instance_->getRoomVersion(txn, statesdb); +} + +std::vector +getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) +{ + return instance_->getMembers(room_id, startIndex, len); +} + +void +saveState(const mtx::responses::Sync &res) +{ + instance_->saveState(res); +} +bool +isInitialized() +{ + return instance_->isInitialized(); +} + +std::string +nextBatchToken() +{ + return instance_->nextBatchToken(); +} + +void +deleteData() +{ + instance_->deleteData(); +} + +void +removeInvite(lmdb::txn &txn, const std::string &room_id) +{ + instance_->removeInvite(txn, room_id); +} +void +removeInvite(const std::string &room_id) +{ + instance_->removeInvite(room_id); +} +void +removeRoom(lmdb::txn &txn, const std::string &roomid) +{ + instance_->removeRoom(txn, roomid); +} +void +removeRoom(const std::string &roomid) +{ + instance_->removeRoom(roomid); +} +void +removeRoom(const QString &roomid) +{ + instance_->removeRoom(roomid.toStdString()); +} +void +setup() +{ + instance_->setup(); +} + +bool +isFormatValid() +{ + return instance_->isFormatValid(); +} +void +setCurrentFormat() +{ + instance_->setCurrentFormat(); +} + +std::map +roomMessages() +{ + return instance_->roomMessages(); +} + +QMap +getTimelineMentions() +{ + return instance_->getTimelineMentions(); +} + +//! Retrieve all the user ids from a room. +std::vector +roomMembers(const std::string &room_id) +{ + return instance_->roomMembers(room_id); +} + +//! Check if the given user has power leve greater than than +//! lowest power level of the given events. +bool +hasEnoughPowerLevel(const std::vector &eventTypes, + const std::string &room_id, + const std::string &user_id) +{ + return instance_->hasEnoughPowerLevel(eventTypes, room_id, user_id); +} + +//! Retrieves the saved room avatar. +QImage +getRoomAvatar(const QString &id) +{ + return instance_->getRoomAvatar(id); +} +QImage +getRoomAvatar(const std::string &id) +{ + return instance_->getRoomAvatar(id); +} + +void +updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Receipts &receipts) +{ + instance_->updateReadReceipt(txn, room_id, receipts); +} + +UserReceipts +readReceipts(const QString &event_id, const QString &room_id) +{ + return instance_->readReceipts(event_id, room_id); +} + +//! Filter the events that have at least one read receipt. +std::vector +filterReadEvents(const QString &room_id, + const std::vector &event_ids, + const std::string &excluded_user) +{ + return instance_->filterReadEvents(room_id, event_ids, excluded_user); +} +//! Add event for which we are expecting some read receipts. +void +addPendingReceipt(const QString &room_id, const QString &event_id) +{ + instance_->addPendingReceipt(room_id, event_id); +} +void +removePendingReceipt(lmdb::txn &txn, const std::string &room_id, const std::string &event_id) +{ + instance_->removePendingReceipt(txn, room_id, event_id); +} +void +notifyForReadReceipts(const std::string &room_id) +{ + instance_->notifyForReadReceipts(room_id); +} +std::vector +pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id) +{ + return instance_->pendingReceiptsEvents(txn, room_id); +} + +QByteArray +image(const QString &url) +{ + return instance_->image(url); +} +QByteArray +image(lmdb::txn &txn, const std::string &url) +{ + return instance_->image(txn, url); +} +void +saveImage(const std::string &url, const std::string &data) +{ + instance_->saveImage(url, data); +} +void +saveImage(const QString &url, const QByteArray &data) +{ + instance_->saveImage(url, data); +} + +RoomInfo +singleRoomInfo(const std::string &room_id) +{ + return instance_->singleRoomInfo(room_id); +} +std::vector +roomsWithStateUpdates(const mtx::responses::Sync &res) +{ + return instance_->roomsWithStateUpdates(res); +} +std::vector +roomsWithTagUpdates(const mtx::responses::Sync &res) +{ + return instance_->roomsWithTagUpdates(res); +} +std::map +getRoomInfo(const std::vector &rooms) +{ + return instance_->getRoomInfo(rooms); +} + +//! Calculates which the read status of a room. +//! Whether all the events in the timeline have been read. +bool +calculateRoomReadStatus(const std::string &room_id) +{ + return instance_->calculateRoomReadStatus(room_id); +} +void +calculateRoomReadStatus() +{ + instance_->calculateRoomReadStatus(); +} + +QVector +searchUsers(const std::string &room_id, const std::string &query, std::uint8_t max_items) +{ + return instance_->searchUsers(room_id, query, max_items); +} +std::vector +searchRooms(const std::string &query, std::uint8_t max_items) +{ + return instance_->searchRooms(query, max_items); +} + +void +markSentNotification(const std::string &event_id) +{ + instance_->markSentNotification(event_id); +} +//! Removes an event from the sent notifications. +void +removeReadNotification(const std::string &event_id) +{ + instance_->removeReadNotification(event_id); +} +//! Check if we have sent a desktop notification for the given event id. +bool +isNotificationSent(const std::string &event_id) +{ + return instance_->isNotificationSent(event_id); +} + +//! Add all notifications containing a user mention to the db. +void +saveTimelineMentions(const mtx::responses::Notifications &res) +{ + instance_->saveTimelineMentions(res); +} + +//! Remove old unused data. +void +deleteOldMessages() +{ + instance_->deleteOldMessages(); +} +void +deleteOldData() noexcept +{ + instance_->deleteOldData(); +} +//! Retrieve all saved room ids. +std::vector +getRoomIds(lmdb::txn &txn) +{ + return instance_->getRoomIds(txn); +} + +//! Mark a room that uses e2e encryption. +void +setEncryptedRoom(lmdb::txn &txn, const std::string &room_id) +{ + instance_->setEncryptedRoom(txn, room_id); +} +bool +isRoomEncrypted(const std::string &room_id) +{ + return instance_->isRoomEncrypted(room_id); +} + +//! Check if a user is a member of the room. +bool +isRoomMember(const std::string &user_id, const std::string &room_id) +{ + return instance_->isRoomMember(user_id, room_id); +} + +// +// Outbound Megolm Sessions +// +void +saveOutboundMegolmSession(const std::string &room_id, + const OutboundGroupSessionData &data, + mtx::crypto::OutboundGroupSessionPtr session) +{ + instance_->saveOutboundMegolmSession(room_id, data, std::move(session)); +} +OutboundGroupSessionDataRef +getOutboundMegolmSession(const std::string &room_id) +{ + return instance_->getOutboundMegolmSession(room_id); +} +bool +outboundMegolmSessionExists(const std::string &room_id) noexcept +{ + return instance_->outboundMegolmSessionExists(room_id); +} +void +updateOutboundMegolmSession(const std::string &room_id, int message_index) +{ + instance_->updateOutboundMegolmSession(room_id, message_index); +} + +void +importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys) +{ + instance_->importSessionKeys(keys); +} +mtx::crypto::ExportedSessionKeys +exportSessionKeys() +{ + return instance_->exportSessionKeys(); +} + +// +// Inbound Megolm Sessions +// +void +saveInboundMegolmSession(const MegolmSessionIndex &index, + mtx::crypto::InboundGroupSessionPtr session) +{ + instance_->saveInboundMegolmSession(index, std::move(session)); +} +OlmInboundGroupSession * +getInboundMegolmSession(const MegolmSessionIndex &index) +{ + return instance_->getInboundMegolmSession(index); +} +bool +inboundMegolmSessionExists(const MegolmSessionIndex &index) +{ + return instance_->inboundMegolmSessionExists(index); +} + +// +// Olm Sessions +// +void +saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session) +{ + instance_->saveOlmSession(curve25519, std::move(session)); +} +std::vector +getOlmSessions(const std::string &curve25519) +{ + return instance_->getOlmSessions(curve25519); +} +std::optional +getOlmSession(const std::string &curve25519, const std::string &session_id) +{ + return instance_->getOlmSession(curve25519, session_id); +} + +void +saveOlmAccount(const std::string &pickled) +{ + instance_->saveOlmAccount(pickled); +} +std::string +restoreOlmAccount() +{ + return instance_->restoreOlmAccount(); +} + +void +restoreSessions() +{ + return instance_->restoreSessions(); +} +} // namespace cache + diff --git a/src/Cache.h b/src/Cache.h index 02346287..da4e2040 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -17,501 +17,285 @@ #pragma once -#include -#include - #include #include #include #include #include -#include #include -#include #include "CacheCryptoStructs.h" #include "CacheStructs.h" -#include "Logging.h" -#include "MatrixClient.h" -int -numeric_key_comparison(const MDB_val *a, const MDB_val *b); +namespace cache { +void +init(const QString &user_id); + +std::string +displayName(const std::string &room_id, const std::string &user_id); +QString +displayName(const QString &room_id, const QString &user_id); +QString +avatarUrl(const QString &room_id, const QString &user_id); +QString +userColor(const QString &user_id); + +void +removeDisplayName(const QString &room_id, const QString &user_id); +void +removeAvatarUrl(const QString &room_id, const QString &user_id); +void +removeUserColor(const QString &user_id); + +void +insertDisplayName(const QString &room_id, const QString &user_id, const QString &display_name); +void +insertAvatarUrl(const QString &room_id, const QString &user_id, const QString &avatar_url); +void +insertUserColor(const QString &user_id, const QString &color_name); + +void +clearUserColors(); + +//! Load saved data for the display names & avatars. +void +populateMembers(); +std::vector +joinedRooms(); + +QMap +roomInfo(bool withInvites = true); +std::map +invites(); + +//! Calculate & return the name of the room. +QString +getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); +//! Get room join rules +mtx::events::state::JoinRule +getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb); +bool +getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb); +//! Retrieve the topic of the room if any. +QString +getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); +//! Retrieve the room avatar's url if any. +QString +getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb, const QString &room_id); +//! Retrieve the version of the room if any. +QString +getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb); + +//! Retrieve member info from a room. +std::vector +getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); -class Cache : public QObject +void +saveState(const mtx::responses::Sync &res); +bool +isInitialized(); + +std::string +nextBatchToken(); + +void +deleteData(); + +void +removeInvite(lmdb::txn &txn, const std::string &room_id); +void +removeInvite(const std::string &room_id); +void +removeRoom(lmdb::txn &txn, const std::string &roomid); +void +removeRoom(const std::string &roomid); +void +removeRoom(const QString &roomid); +void +setup(); + +bool +isFormatValid(); +void +setCurrentFormat(); + +std::map +roomMessages(); + +QMap +getTimelineMentions(); + +//! Retrieve all the user ids from a room. +std::vector +roomMembers(const std::string &room_id); + +//! Check if the given user has power leve greater than than +//! lowest power level of the given events. +bool +hasEnoughPowerLevel(const std::vector &eventTypes, + const std::string &room_id, + const std::string &user_id); + +//! Retrieves the saved room avatar. +QImage +getRoomAvatar(const QString &id); +QImage +getRoomAvatar(const std::string &id); + +//! Adds a user to the read list for the given event. +//! +//! There should be only one user id present in a receipt list per room. +//! The user id should be removed from any other lists. +using Receipts = std::map>; +void +updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Receipts &receipts); + +//! Retrieve all the read receipts for the given event id and room. +//! +//! Returns a map of user ids and the time of the read receipt in milliseconds. +using UserReceipts = std::multimap>; +UserReceipts +readReceipts(const QString &event_id, const QString &room_id); + +//! Filter the events that have at least one read receipt. +std::vector +filterReadEvents(const QString &room_id, + const std::vector &event_ids, + const std::string &excluded_user); +//! Add event for which we are expecting some read receipts. +void +addPendingReceipt(const QString &room_id, const QString &event_id); +void +removePendingReceipt(lmdb::txn &txn, const std::string &room_id, const std::string &event_id); +void +notifyForReadReceipts(const std::string &room_id); +std::vector +pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id); + +QByteArray +image(const QString &url); +QByteArray +image(lmdb::txn &txn, const std::string &url); +inline QByteArray +image(const std::string &url) +{ + return image(QString::fromStdString(url)); +} +void +saveImage(const std::string &url, const std::string &data); +void +saveImage(const QString &url, const QByteArray &data); + +RoomInfo +singleRoomInfo(const std::string &room_id); +std::vector +roomsWithStateUpdates(const mtx::responses::Sync &res); +std::vector +roomsWithTagUpdates(const mtx::responses::Sync &res); +std::map +getRoomInfo(const std::vector &rooms); +inline std::map +roomUpdates(const mtx::responses::Sync &sync) { - Q_OBJECT - -public: - Cache(const QString &userId, QObject *parent = nullptr); - - static QHash DisplayNames; - static QHash AvatarUrls; - static QHash UserColors; - - static std::string displayName(const std::string &room_id, const std::string &user_id); - static QString displayName(const QString &room_id, const QString &user_id); - static QString avatarUrl(const QString &room_id, const QString &user_id); - static QString userColor(const QString &user_id); - - static void removeDisplayName(const QString &room_id, const QString &user_id); - static void removeAvatarUrl(const QString &room_id, const QString &user_id); - static void removeUserColor(const QString &user_id); - - static void insertDisplayName(const QString &room_id, - const QString &user_id, - const QString &display_name); - static void insertAvatarUrl(const QString &room_id, - const QString &user_id, - const QString &avatar_url); - static void insertUserColor(const QString &user_id, const QString &color_name); - - static void clearUserColors(); - - //! Load saved data for the display names & avatars. - void populateMembers(); - std::vector joinedRooms(); - - QMap roomInfo(bool withInvites = true); - std::map invites(); - - //! Calculate & return the name of the room. - QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); - //! Get room join rules - mtx::events::state::JoinRule getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb); - bool getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb); - //! Retrieve the topic of the room if any. - QString getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); - //! Retrieve the room avatar's url if any. - QString getRoomAvatarUrl(lmdb::txn &txn, - lmdb::dbi &statesdb, - lmdb::dbi &membersdb, - const QString &room_id); - //! Retrieve the version of the room if any. - QString getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb); - - //! Retrieve member info from a room. - std::vector getMembers(const std::string &room_id, - std::size_t startIndex = 0, - std::size_t len = 30); - - void saveState(const mtx::responses::Sync &res); - bool isInitialized() const; - - std::string nextBatchToken() const; - - void deleteData(); - - void removeInvite(lmdb::txn &txn, const std::string &room_id); - void removeInvite(const std::string &room_id); - void removeRoom(lmdb::txn &txn, const std::string &roomid); - void removeRoom(const std::string &roomid); - void removeRoom(const QString &roomid) { removeRoom(roomid.toStdString()); }; - void setup(); - - bool isFormatValid(); - void setCurrentFormat(); - - std::map roomMessages(); - - QMap getTimelineMentions(); - - //! Retrieve all the user ids from a room. - std::vector roomMembers(const std::string &room_id); - - //! Check if the given user has power leve greater than than - //! lowest power level of the given events. - bool hasEnoughPowerLevel(const std::vector &eventTypes, - const std::string &room_id, - const std::string &user_id); - - //! Retrieves the saved room avatar. - QImage getRoomAvatar(const QString &id); - QImage getRoomAvatar(const std::string &id); - - //! Adds a user to the read list for the given event. - //! - //! There should be only one user id present in a receipt list per room. - //! The user id should be removed from any other lists. - using Receipts = std::map>; - void updateReadReceipt(lmdb::txn &txn, - const std::string &room_id, - const Receipts &receipts); - - //! Retrieve all the read receipts for the given event id and room. - //! - //! Returns a map of user ids and the time of the read receipt in milliseconds. - using UserReceipts = std::multimap>; - UserReceipts readReceipts(const QString &event_id, const QString &room_id); - - //! Filter the events that have at least one read receipt. - std::vector filterReadEvents(const QString &room_id, - const std::vector &event_ids, - const std::string &excluded_user); - //! Add event for which we are expecting some read receipts. - void addPendingReceipt(const QString &room_id, const QString &event_id); - void removePendingReceipt(lmdb::txn &txn, - const std::string &room_id, - const std::string &event_id); - void notifyForReadReceipts(const std::string &room_id); - std::vector pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id); - - QByteArray image(const QString &url) const; - QByteArray image(lmdb::txn &txn, const std::string &url) const; - QByteArray image(const std::string &url) const - { - return image(QString::fromStdString(url)); - } - void saveImage(const std::string &url, const std::string &data); - void saveImage(const QString &url, const QByteArray &data); - - RoomInfo singleRoomInfo(const std::string &room_id); - std::vector roomsWithStateUpdates(const mtx::responses::Sync &res); - std::vector roomsWithTagUpdates(const mtx::responses::Sync &res); - std::map getRoomInfo(const std::vector &rooms); - std::map roomUpdates(const mtx::responses::Sync &sync) - { - return getRoomInfo(roomsWithStateUpdates(sync)); - } - std::map roomTagUpdates(const mtx::responses::Sync &sync) - { - return getRoomInfo(roomsWithTagUpdates(sync)); - } - - //! Calculates which the read status of a room. - //! Whether all the events in the timeline have been read. - bool calculateRoomReadStatus(const std::string &room_id); - void calculateRoomReadStatus(); - - QVector searchUsers(const std::string &room_id, - const std::string &query, - std::uint8_t max_items = 5); - std::vector searchRooms(const std::string &query, - std::uint8_t max_items = 5); - - void markSentNotification(const std::string &event_id); - //! Removes an event from the sent notifications. - void removeReadNotification(const std::string &event_id); - //! Check if we have sent a desktop notification for the given event id. - bool isNotificationSent(const std::string &event_id); - - //! Add all notifications containing a user mention to the db. - void saveTimelineMentions(const mtx::responses::Notifications &res); - - //! Remove old unused data. - void deleteOldMessages(); - void deleteOldData() noexcept; - //! Retrieve all saved room ids. - std::vector getRoomIds(lmdb::txn &txn); - - //! Mark a room that uses e2e encryption. - void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id); - bool isRoomEncrypted(const std::string &room_id); - - //! Save the public keys for a device. - void saveDeviceKeys(const std::string &device_id); - void getDeviceKeys(const std::string &device_id); - - //! Save the device list for a user. - void setDeviceList(const std::string &user_id, const std::vector &devices); - std::vector getDeviceList(const std::string &user_id); - - //! Check if a user is a member of the room. - bool isRoomMember(const std::string &user_id, const std::string &room_id); - - // - // Outbound Megolm Sessions - // - void saveOutboundMegolmSession(const std::string &room_id, - const OutboundGroupSessionData &data, - mtx::crypto::OutboundGroupSessionPtr session); - OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id); - bool outboundMegolmSessionExists(const std::string &room_id) noexcept; - void updateOutboundMegolmSession(const std::string &room_id, int message_index); - - void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys); - mtx::crypto::ExportedSessionKeys exportSessionKeys(); - - // - // Inbound Megolm Sessions - // - void saveInboundMegolmSession(const MegolmSessionIndex &index, - mtx::crypto::InboundGroupSessionPtr session); - OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index); - bool inboundMegolmSessionExists(const MegolmSessionIndex &index); - - // - // Olm Sessions - // - void saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session); - std::vector getOlmSessions(const std::string &curve25519); - std::optional getOlmSession(const std::string &curve25519, - const std::string &session_id); - - void saveOlmAccount(const std::string &pickled); - std::string restoreOlmAccount(); - - void restoreSessions(); - - OlmSessionStorage session_storage; - -signals: - void newReadReceipts(const QString &room_id, const std::vector &event_ids); - void roomReadStatus(const std::map &status); - -private: - //! Save an invited room. - void saveInvite(lmdb::txn &txn, - lmdb::dbi &statesdb, - lmdb::dbi &membersdb, - const mtx::responses::InvitedRoom &room); - - //! Add a notification containing a user mention to the db. - void saveTimelineMentions(lmdb::txn &txn, - const std::string &room_id, - const QList &res); - - //! Get timeline items that a user was mentions in for a given room - mtx::responses::Notifications getTimelineMentionsForRoom(lmdb::txn &txn, - const std::string &room_id); - - QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); - QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); - QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); - - DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id); - void saveTimelineMessages(lmdb::txn &txn, - const std::string &room_id, - const mtx::responses::Timeline &res); - - mtx::responses::Timeline getTimelineMessages(lmdb::txn &txn, const std::string &room_id); - - //! Remove a room from the cache. - // void removeLeftRoom(lmdb::txn &txn, const std::string &room_id); - template - void saveStateEvents(lmdb::txn &txn, - const lmdb::dbi &statesdb, - const lmdb::dbi &membersdb, - const std::string &room_id, - const std::vector &events) - { - for (const auto &e : events) - saveStateEvent(txn, statesdb, membersdb, room_id, e); - } - - template - void saveStateEvent(lmdb::txn &txn, - const lmdb::dbi &statesdb, - const lmdb::dbi &membersdb, - const std::string &room_id, - const T &event) - { - using namespace mtx::events; - using namespace mtx::events::state; - - if (auto e = std::get_if>(&event); e != nullptr) { - switch (e->content.membership) { - // - // We only keep users with invite or join membership. - // - case Membership::Invite: - case Membership::Join: { - auto display_name = e->content.display_name.empty() - ? e->state_key - : e->content.display_name; - - // Lightweight representation of a member. - MemberInfo tmp{display_name, e->content.avatar_url}; - - lmdb::dbi_put(txn, - membersdb, - lmdb::val(e->state_key), - lmdb::val(json(tmp).dump())); - - insertDisplayName(QString::fromStdString(room_id), - QString::fromStdString(e->state_key), - QString::fromStdString(display_name)); - - insertAvatarUrl(QString::fromStdString(room_id), - QString::fromStdString(e->state_key), - QString::fromStdString(e->content.avatar_url)); - - break; - } - default: { - lmdb::dbi_del( - txn, membersdb, lmdb::val(e->state_key), lmdb::val("")); - - removeDisplayName(QString::fromStdString(room_id), - QString::fromStdString(e->state_key)); - removeAvatarUrl(QString::fromStdString(room_id), - QString::fromStdString(e->state_key)); - - break; - } - } - - return; - } else if (std::holds_alternative>(event)) { - setEncryptedRoom(txn, room_id); - return; - } - - if (!isStateEvent(event)) - return; - - std::visit( - [&txn, &statesdb](auto e) { - lmdb::dbi_put( - txn, statesdb, lmdb::val(to_string(e.type)), lmdb::val(json(e).dump())); - }, - event); - } - - template - bool isStateEvent(const T &e) - { - using namespace mtx::events; - using namespace mtx::events::state; - - return std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e); - } - - template - bool containsStateUpdates(const T &e) - { - using namespace mtx::events; - using namespace mtx::events::state; - - return std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e); - } - - bool containsStateUpdates(const mtx::events::collections::StrippedEvents &e) - { - using namespace mtx::events; - using namespace mtx::events::state; - - return std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e) || - std::holds_alternative>(e); - } - - void saveInvites(lmdb::txn &txn, - const std::map &rooms); - - //! Sends signals for the rooms that are removed. - void removeLeftRooms(lmdb::txn &txn, - const std::map &rooms) - { - for (const auto &room : rooms) { - removeRoom(txn, room.first); - - // Clean up leftover invites. - removeInvite(txn, room.first); - } - } - - lmdb::dbi getPendingReceiptsDb(lmdb::txn &txn) - { - return lmdb::dbi::open(txn, "pending_receipts", MDB_CREATE); - } - - lmdb::dbi getMessagesDb(lmdb::txn &txn, const std::string &room_id) - { - auto db = - lmdb::dbi::open(txn, std::string(room_id + "/messages").c_str(), MDB_CREATE); - lmdb::dbi_set_compare(txn, db, numeric_key_comparison); - - return db; - } - - lmdb::dbi getInviteStatesDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open( - txn, std::string(room_id + "/invite_state").c_str(), MDB_CREATE); - } - - lmdb::dbi getInviteMembersDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open( - txn, std::string(room_id + "/invite_members").c_str(), MDB_CREATE); - } - - lmdb::dbi getStatesDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE); - } - - lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE); - } - - lmdb::dbi getMentionsDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open(txn, std::string(room_id + "/mentions").c_str(), MDB_CREATE); - } - - //! Retrieves or creates the database that stores the open OLM sessions between our device - //! and the given curve25519 key which represents another device. - //! - //! Each entry is a map from the session_id to the pickled representation of the session. - lmdb::dbi getOlmSessionsDb(lmdb::txn &txn, const std::string &curve25519_key) - { - return lmdb::dbi::open( - txn, std::string("olm_sessions/" + curve25519_key).c_str(), MDB_CREATE); - } - - QString getDisplayName(const mtx::events::StateEvent &event) - { - if (!event.content.display_name.empty()) - return QString::fromStdString(event.content.display_name); - - return QString::fromStdString(event.state_key); - } - - void setNextBatchToken(lmdb::txn &txn, const std::string &token); - void setNextBatchToken(lmdb::txn &txn, const QString &token); - - lmdb::env env_; - lmdb::dbi syncStateDb_; - lmdb::dbi roomsDb_; - lmdb::dbi invitesDb_; - lmdb::dbi mediaDb_; - lmdb::dbi readReceiptsDb_; - lmdb::dbi notificationsDb_; - - lmdb::dbi devicesDb_; - lmdb::dbi deviceKeysDb_; - - lmdb::dbi inboundMegolmSessionDb_; - lmdb::dbi outboundMegolmSessionDb_; - - QString localUserId_; - QString cacheDirectory_; -}; + return getRoomInfo(roomsWithStateUpdates(sync)); +} +inline std::map +roomTagUpdates(const mtx::responses::Sync &sync) +{ + return getRoomInfo(roomsWithTagUpdates(sync)); +} -namespace cache { +//! Calculates which the read status of a room. +//! Whether all the events in the timeline have been read. +bool +calculateRoomReadStatus(const std::string &room_id); void -init(const QString &user_id); +calculateRoomReadStatus(); + +QVector +searchUsers(const std::string &room_id, const std::string &query, std::uint8_t max_items = 5); +std::vector +searchRooms(const std::string &query, std::uint8_t max_items = 5); + +void +markSentNotification(const std::string &event_id); +//! Removes an event from the sent notifications. +void +removeReadNotification(const std::string &event_id); +//! Check if we have sent a desktop notification for the given event id. +bool +isNotificationSent(const std::string &event_id); + +//! Add all notifications containing a user mention to the db. +void +saveTimelineMentions(const mtx::responses::Notifications &res); -Cache * -client(); +//! Remove old unused data. +void +deleteOldMessages(); +void +deleteOldData() noexcept; +//! Retrieve all saved room ids. +std::vector +getRoomIds(lmdb::txn &txn); + +//! Mark a room that uses e2e encryption. +void +setEncryptedRoom(lmdb::txn &txn, const std::string &room_id); +bool +isRoomEncrypted(const std::string &room_id); + +//! Check if a user is a member of the room. +bool +isRoomMember(const std::string &user_id, const std::string &room_id); + +// +// Outbound Megolm Sessions +// +void +saveOutboundMegolmSession(const std::string &room_id, + const OutboundGroupSessionData &data, + mtx::crypto::OutboundGroupSessionPtr session); +OutboundGroupSessionDataRef +getOutboundMegolmSession(const std::string &room_id); +bool +outboundMegolmSessionExists(const std::string &room_id) noexcept; +void +updateOutboundMegolmSession(const std::string &room_id, int message_index); + +void +importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys); +mtx::crypto::ExportedSessionKeys +exportSessionKeys(); + +// +// Inbound Megolm Sessions +// +void +saveInboundMegolmSession(const MegolmSessionIndex &index, + mtx::crypto::InboundGroupSessionPtr session); +OlmInboundGroupSession * +getInboundMegolmSession(const MegolmSessionIndex &index); +bool +inboundMegolmSessionExists(const MegolmSessionIndex &index); + +// +// Olm Sessions +// +void +saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session); +std::vector +getOlmSessions(const std::string &curve25519); +std::optional +getOlmSession(const std::string &curve25519, const std::string &session_id); + +void +saveOlmAccount(const std::string &pickled); +std::string +restoreOlmAccount(); + +void +restoreSessions(); } diff --git a/src/Cache_p.h b/src/Cache_p.h new file mode 100644 index 00000000..47dd945a --- /dev/null +++ b/src/Cache_p.h @@ -0,0 +1,494 @@ +/* + * nheko Copyright (C) 2019 The nheko authors + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "CacheCryptoStructs.h" +#include "CacheStructs.h" +#include "Logging.h" +#include "MatrixClient.h" + +int +numeric_key_comparison(const MDB_val *a, const MDB_val *b); + +class Cache : public QObject +{ + Q_OBJECT + +public: + Cache(const QString &userId, QObject *parent = nullptr); + + static std::string displayName(const std::string &room_id, const std::string &user_id); + static QString displayName(const QString &room_id, const QString &user_id); + static QString avatarUrl(const QString &room_id, const QString &user_id); + static QString userColor(const QString &user_id); + + static void removeDisplayName(const QString &room_id, const QString &user_id); + static void removeAvatarUrl(const QString &room_id, const QString &user_id); + static void removeUserColor(const QString &user_id); + + static void insertDisplayName(const QString &room_id, + const QString &user_id, + const QString &display_name); + static void insertAvatarUrl(const QString &room_id, + const QString &user_id, + const QString &avatar_url); + static void insertUserColor(const QString &user_id, const QString &color_name); + + static void clearUserColors(); + + //! Load saved data for the display names & avatars. + void populateMembers(); + std::vector joinedRooms(); + + QMap roomInfo(bool withInvites = true); + std::map invites(); + + //! Calculate & return the name of the room. + QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); + //! Get room join rules + mtx::events::state::JoinRule getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb); + bool getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb); + //! Retrieve the topic of the room if any. + QString getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); + //! Retrieve the room avatar's url if any. + QString getRoomAvatarUrl(lmdb::txn &txn, + lmdb::dbi &statesdb, + lmdb::dbi &membersdb, + const QString &room_id); + //! Retrieve the version of the room if any. + QString getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb); + + //! Retrieve member info from a room. + std::vector getMembers(const std::string &room_id, + std::size_t startIndex = 0, + std::size_t len = 30); + + void saveState(const mtx::responses::Sync &res); + bool isInitialized() const; + + std::string nextBatchToken() const; + + void deleteData(); + + void removeInvite(lmdb::txn &txn, const std::string &room_id); + void removeInvite(const std::string &room_id); + void removeRoom(lmdb::txn &txn, const std::string &roomid); + void removeRoom(const std::string &roomid); + void setup(); + + bool isFormatValid(); + void setCurrentFormat(); + + std::map roomMessages(); + + QMap getTimelineMentions(); + + //! Retrieve all the user ids from a room. + std::vector roomMembers(const std::string &room_id); + + //! Check if the given user has power leve greater than than + //! lowest power level of the given events. + bool hasEnoughPowerLevel(const std::vector &eventTypes, + const std::string &room_id, + const std::string &user_id); + + //! Retrieves the saved room avatar. + QImage getRoomAvatar(const QString &id); + QImage getRoomAvatar(const std::string &id); + + //! Adds a user to the read list for the given event. + //! + //! There should be only one user id present in a receipt list per room. + //! The user id should be removed from any other lists. + using Receipts = std::map>; + void updateReadReceipt(lmdb::txn &txn, + const std::string &room_id, + const Receipts &receipts); + + //! Retrieve all the read receipts for the given event id and room. + //! + //! Returns a map of user ids and the time of the read receipt in milliseconds. + using UserReceipts = std::multimap>; + UserReceipts readReceipts(const QString &event_id, const QString &room_id); + + //! Filter the events that have at least one read receipt. + std::vector filterReadEvents(const QString &room_id, + const std::vector &event_ids, + const std::string &excluded_user); + //! Add event for which we are expecting some read receipts. + void addPendingReceipt(const QString &room_id, const QString &event_id); + void removePendingReceipt(lmdb::txn &txn, + const std::string &room_id, + const std::string &event_id); + void notifyForReadReceipts(const std::string &room_id); + std::vector pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id); + + QByteArray image(const QString &url) const; + QByteArray image(lmdb::txn &txn, const std::string &url) const; + void saveImage(const std::string &url, const std::string &data); + void saveImage(const QString &url, const QByteArray &data); + + RoomInfo singleRoomInfo(const std::string &room_id); + std::vector roomsWithStateUpdates(const mtx::responses::Sync &res); + std::vector roomsWithTagUpdates(const mtx::responses::Sync &res); + std::map getRoomInfo(const std::vector &rooms); + + //! Calculates which the read status of a room. + //! Whether all the events in the timeline have been read. + bool calculateRoomReadStatus(const std::string &room_id); + void calculateRoomReadStatus(); + + QVector searchUsers(const std::string &room_id, + const std::string &query, + std::uint8_t max_items = 5); + std::vector searchRooms(const std::string &query, + std::uint8_t max_items = 5); + + void markSentNotification(const std::string &event_id); + //! Removes an event from the sent notifications. + void removeReadNotification(const std::string &event_id); + //! Check if we have sent a desktop notification for the given event id. + bool isNotificationSent(const std::string &event_id); + + //! Add all notifications containing a user mention to the db. + void saveTimelineMentions(const mtx::responses::Notifications &res); + + //! Remove old unused data. + void deleteOldMessages(); + void deleteOldData() noexcept; + //! Retrieve all saved room ids. + std::vector getRoomIds(lmdb::txn &txn); + + //! Mark a room that uses e2e encryption. + void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id); + bool isRoomEncrypted(const std::string &room_id); + + //! Check if a user is a member of the room. + bool isRoomMember(const std::string &user_id, const std::string &room_id); + + // + // Outbound Megolm Sessions + // + void saveOutboundMegolmSession(const std::string &room_id, + const OutboundGroupSessionData &data, + mtx::crypto::OutboundGroupSessionPtr session); + OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id); + bool outboundMegolmSessionExists(const std::string &room_id) noexcept; + void updateOutboundMegolmSession(const std::string &room_id, int message_index); + + void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys); + mtx::crypto::ExportedSessionKeys exportSessionKeys(); + + // + // Inbound Megolm Sessions + // + void saveInboundMegolmSession(const MegolmSessionIndex &index, + mtx::crypto::InboundGroupSessionPtr session); + OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index); + bool inboundMegolmSessionExists(const MegolmSessionIndex &index); + + // + // Olm Sessions + // + void saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session); + std::vector getOlmSessions(const std::string &curve25519); + std::optional getOlmSession(const std::string &curve25519, + const std::string &session_id); + + void saveOlmAccount(const std::string &pickled); + std::string restoreOlmAccount(); + + void restoreSessions(); + +signals: + void newReadReceipts(const QString &room_id, const std::vector &event_ids); + void roomReadStatus(const std::map &status); + +private: + //! Save an invited room. + void saveInvite(lmdb::txn &txn, + lmdb::dbi &statesdb, + lmdb::dbi &membersdb, + const mtx::responses::InvitedRoom &room); + + //! Add a notification containing a user mention to the db. + void saveTimelineMentions(lmdb::txn &txn, + const std::string &room_id, + const QList &res); + + //! Get timeline items that a user was mentions in for a given room + mtx::responses::Notifications getTimelineMentionsForRoom(lmdb::txn &txn, + const std::string &room_id); + + QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); + QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); + QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); + + DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id); + void saveTimelineMessages(lmdb::txn &txn, + const std::string &room_id, + const mtx::responses::Timeline &res); + + mtx::responses::Timeline getTimelineMessages(lmdb::txn &txn, const std::string &room_id); + + //! Remove a room from the cache. + // void removeLeftRoom(lmdb::txn &txn, const std::string &room_id); + template + void saveStateEvents(lmdb::txn &txn, + const lmdb::dbi &statesdb, + const lmdb::dbi &membersdb, + const std::string &room_id, + const std::vector &events) + { + for (const auto &e : events) + saveStateEvent(txn, statesdb, membersdb, room_id, e); + } + + template + void saveStateEvent(lmdb::txn &txn, + const lmdb::dbi &statesdb, + const lmdb::dbi &membersdb, + const std::string &room_id, + const T &event) + { + using namespace mtx::events; + using namespace mtx::events::state; + + if (auto e = std::get_if>(&event); e != nullptr) { + switch (e->content.membership) { + // + // We only keep users with invite or join membership. + // + case Membership::Invite: + case Membership::Join: { + auto display_name = e->content.display_name.empty() + ? e->state_key + : e->content.display_name; + + // Lightweight representation of a member. + MemberInfo tmp{display_name, e->content.avatar_url}; + + lmdb::dbi_put(txn, + membersdb, + lmdb::val(e->state_key), + lmdb::val(json(tmp).dump())); + + insertDisplayName(QString::fromStdString(room_id), + QString::fromStdString(e->state_key), + QString::fromStdString(display_name)); + + insertAvatarUrl(QString::fromStdString(room_id), + QString::fromStdString(e->state_key), + QString::fromStdString(e->content.avatar_url)); + + break; + } + default: { + lmdb::dbi_del( + txn, membersdb, lmdb::val(e->state_key), lmdb::val("")); + + removeDisplayName(QString::fromStdString(room_id), + QString::fromStdString(e->state_key)); + removeAvatarUrl(QString::fromStdString(room_id), + QString::fromStdString(e->state_key)); + + break; + } + } + + return; + } else if (std::holds_alternative>(event)) { + setEncryptedRoom(txn, room_id); + return; + } + + if (!isStateEvent(event)) + return; + + std::visit( + [&txn, &statesdb](auto e) { + lmdb::dbi_put( + txn, statesdb, lmdb::val(to_string(e.type)), lmdb::val(json(e).dump())); + }, + event); + } + + template + bool isStateEvent(const T &e) + { + using namespace mtx::events; + using namespace mtx::events::state; + + return std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e); + } + + template + bool containsStateUpdates(const T &e) + { + using namespace mtx::events; + using namespace mtx::events::state; + + return std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e); + } + + bool containsStateUpdates(const mtx::events::collections::StrippedEvents &e) + { + using namespace mtx::events; + using namespace mtx::events::state; + + return std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e) || + std::holds_alternative>(e); + } + + void saveInvites(lmdb::txn &txn, + const std::map &rooms); + + //! Sends signals for the rooms that are removed. + void removeLeftRooms(lmdb::txn &txn, + const std::map &rooms) + { + for (const auto &room : rooms) { + removeRoom(txn, room.first); + + // Clean up leftover invites. + removeInvite(txn, room.first); + } + } + + lmdb::dbi getPendingReceiptsDb(lmdb::txn &txn) + { + return lmdb::dbi::open(txn, "pending_receipts", MDB_CREATE); + } + + lmdb::dbi getMessagesDb(lmdb::txn &txn, const std::string &room_id) + { + auto db = + lmdb::dbi::open(txn, std::string(room_id + "/messages").c_str(), MDB_CREATE); + lmdb::dbi_set_compare(txn, db, numeric_key_comparison); + + return db; + } + + lmdb::dbi getInviteStatesDb(lmdb::txn &txn, const std::string &room_id) + { + return lmdb::dbi::open( + txn, std::string(room_id + "/invite_state").c_str(), MDB_CREATE); + } + + lmdb::dbi getInviteMembersDb(lmdb::txn &txn, const std::string &room_id) + { + return lmdb::dbi::open( + txn, std::string(room_id + "/invite_members").c_str(), MDB_CREATE); + } + + lmdb::dbi getStatesDb(lmdb::txn &txn, const std::string &room_id) + { + return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE); + } + + lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id) + { + return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE); + } + + lmdb::dbi getMentionsDb(lmdb::txn &txn, const std::string &room_id) + { + return lmdb::dbi::open(txn, std::string(room_id + "/mentions").c_str(), MDB_CREATE); + } + + //! Retrieves or creates the database that stores the open OLM sessions between our device + //! and the given curve25519 key which represents another device. + //! + //! Each entry is a map from the session_id to the pickled representation of the session. + lmdb::dbi getOlmSessionsDb(lmdb::txn &txn, const std::string &curve25519_key) + { + return lmdb::dbi::open( + txn, std::string("olm_sessions/" + curve25519_key).c_str(), MDB_CREATE); + } + + QString getDisplayName(const mtx::events::StateEvent &event) + { + if (!event.content.display_name.empty()) + return QString::fromStdString(event.content.display_name); + + return QString::fromStdString(event.state_key); + } + + void setNextBatchToken(lmdb::txn &txn, const std::string &token); + void setNextBatchToken(lmdb::txn &txn, const QString &token); + + lmdb::env env_; + lmdb::dbi syncStateDb_; + lmdb::dbi roomsDb_; + lmdb::dbi invitesDb_; + lmdb::dbi mediaDb_; + lmdb::dbi readReceiptsDb_; + lmdb::dbi notificationsDb_; + + lmdb::dbi devicesDb_; + lmdb::dbi deviceKeysDb_; + + lmdb::dbi inboundMegolmSessionDb_; + lmdb::dbi outboundMegolmSessionDb_; + + QString localUserId_; + QString cacheDirectory_; + + static QHash DisplayNames; + static QHash AvatarUrls; + static QHash UserColors; + + OlmSessionStorage session_storage; +}; + +namespace cache { +Cache * +client(); +} diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c496acab..ad07efdd 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -22,6 +22,7 @@ #include "AvatarProvider.h" #include "Cache.h" +#include "Cache_p.h" #include "ChatPage.h" #include "Logging.h" #include "MainWindow.h" @@ -319,7 +320,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) auto bin = dev->peek(dev->size()); auto payload = std::string(bin.data(), bin.size()); std::optional encryptedFile; - if (cache::client()->isRoomEncrypted(current_room_.toStdString())) { + if (cache::isRoomEncrypted(current_room_.toStdString())) { mtx::crypto::BinaryBuf buf; std::tie(buf, encryptedFile) = mtx::crypto::encrypt_file(payload); payload = mtx::crypto::to_string(buf); @@ -408,7 +409,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) this, [](const mtx::responses::Notifications ¬if) { try { - cache::client()->saveTimelineMentions(notif); + cache::saveTimelineMentions(notif); } catch (const lmdb::error &e) { nhlog::db()->error("failed to save mentions: {}", e.what()); } @@ -457,7 +458,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) &popups::UserMentions::initializeMentions); connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) { try { - room_list_->cleanupInvites(cache::client()->invites()); + room_list_->cleanupInvites(cache::invites()); } catch (const lmdb::error &e) { nhlog::db()->error("failed to retrieve invites: {}", e.what()); } @@ -575,7 +576,7 @@ ChatPage::deleteConfigs() settings.remove(""); settings.endGroup(); - cache::client()->deleteData(); + cache::deleteData(); http::client()->clear(); } @@ -610,18 +611,18 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) connect( cache::client(), &Cache::roomReadStatus, room_list_, &RoomList::updateReadStatus); - const bool isInitialized = cache::client()->isInitialized(); - const bool isValid = cache::client()->isFormatValid(); + const bool isInitialized = cache::isInitialized(); + const bool isValid = cache::isFormatValid(); if (!isInitialized) { - cache::client()->setCurrentFormat(); + cache::setCurrentFormat(); } else if (isInitialized && !isValid) { // TODO: Deleting session data but keep using the // same device doesn't work. - cache::client()->deleteData(); + cache::deleteData(); cache::init(userid); - cache::client()->setCurrentFormat(); + cache::setCurrentFormat(); } else if (isInitialized) { loadStateFromCache(); return; @@ -629,7 +630,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) } catch (const lmdb::error &e) { nhlog::db()->critical("failure during boot: {}", e.what()); - cache::client()->deleteData(); + cache::deleteData(); nhlog::net()->info("falling back to initial sync"); } @@ -638,7 +639,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) // There isn't a saved olm account to restore. nhlog::crypto()->info("creating new olm account"); olm::client()->create_new_account(); - cache::client()->saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); + cache::saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); } catch (const lmdb::error &e) { nhlog::crypto()->critical("failed to save olm account {}", e.what()); emit dropToLoginPageCb(QString::fromStdString(e.what())); @@ -671,7 +672,7 @@ ChatPage::changeTopRoomInfo(const QString &room_id) } try { - auto room_info = cache::client()->getRoomInfo({room_id.toStdString()}); + auto room_info = cache::getRoomInfo({room_id.toStdString()}); if (room_info.find(room_id) == room_info.end()) return; @@ -682,7 +683,7 @@ ChatPage::changeTopRoomInfo(const QString &room_id) top_bar_->updateRoomName(name); top_bar_->updateRoomTopic(QString::fromStdString(room_info[room_id].topic)); - auto img = cache::client()->getRoomAvatar(room_id); + auto img = cache::getRoomAvatar(room_id); if (img.isNull()) top_bar_->updateRoomAvatarFromName(name); @@ -719,18 +720,17 @@ ChatPage::loadStateFromCache() QtConcurrent::run([this]() { try { - cache::client()->restoreSessions(); - olm::client()->load(cache::client()->restoreOlmAccount(), - STORAGE_SECRET_KEY); + cache::restoreSessions(); + olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY); - cache::client()->populateMembers(); + cache::populateMembers(); - emit initializeEmptyViews(cache::client()->roomMessages()); - emit initializeRoomList(cache::client()->roomInfo()); - emit initializeMentions(cache::client()->getTimelineMentions()); - emit syncTags(cache::client()->roomInfo().toStdMap()); + emit initializeEmptyViews(cache::roomMessages()); + emit initializeRoomList(cache::roomInfo()); + emit initializeMentions(cache::getTimelineMentions()); + emit syncTags(cache::roomInfo().toStdMap()); - cache::client()->calculateRoomReadStatus(); + cache::calculateRoomReadStatus(); } catch (const mtx::crypto::olm_exception &e) { nhlog::crypto()->critical("failed to restore olm account: {}", e.what()); @@ -773,8 +773,8 @@ void ChatPage::removeRoom(const QString &room_id) { try { - cache::client()->removeRoom(room_id); - cache::client()->removeInvite(room_id.toStdString()); + cache::removeRoom(room_id); + cache::removeInvite(room_id.toStdString()); } catch (const lmdb::error &e) { nhlog::db()->critical("failure while removing room: {}", e.what()); // TODO: Notify the user. @@ -807,7 +807,7 @@ ChatPage::generateTypingUsers(const QString &room_id, const std::vectorremoveReadNotification(event_id); + cache::removeReadNotification(event_id); continue; } - if (!cache::client()->isNotificationSent(event_id)) { + if (!cache::isNotificationSent(event_id)) { const auto room_id = QString::fromStdString(item.room_id); const auto user_id = utils::event_sender(item.event); // We should only sent one notification per event. - cache::client()->markSentNotification(event_id); + cache::markSentNotification(event_id); // Don't send a notification when the current room is opened. if (isRoomActive(room_id)) @@ -871,11 +871,10 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) notificationsManager.postNotification( room_id, QString::fromStdString(event_id), - QString::fromStdString( - cache::client()->singleRoomInfo(item.room_id).name), - Cache::displayName(room_id, user_id), + QString::fromStdString(cache::singleRoomInfo(item.room_id).name), + cache::displayName(room_id, user_id), utils::event_body(item.event), - cache::client()->getRoomAvatar(room_id)); + cache::getRoomAvatar(room_id)); } } catch (const lmdb::error &e) { nhlog::db()->warn("error while sending desktop notification: {}", e.what()); @@ -962,7 +961,7 @@ ChatPage::trySync() connectivityTimer_.start(); try { - opts.since = cache::client()->nextBatchToken(); + opts.since = cache::nextBatchToken(); } catch (const lmdb::error &e) { nhlog::db()->error("failed to retrieve next batch token: {}", e.what()); return; @@ -1015,22 +1014,22 @@ ChatPage::trySync() // TODO: fine grained error handling try { - cache::client()->saveState(res); + cache::saveState(res); olm::handle_to_device_messages(res.to_device); emit syncUI(res.rooms); - auto updates = cache::client()->roomUpdates(res); + auto updates = cache::roomUpdates(res); emit syncTopBar(updates); emit syncRoomlist(updates); - emit syncTags(cache::client()->roomTagUpdates(res)); + emit syncTags(cache::roomTagUpdates(res)); - cache::client()->deleteOldData(); + cache::deleteOldData(); } catch (const lmdb::map_full_error &e) { nhlog::db()->error("lmdb is full: {}", e.what()); - cache::client()->deleteOldData(); + cache::deleteOldData(); } catch (const lmdb::error &e) { nhlog::db()->error("saving sync response: {}", e.what()); } @@ -1058,7 +1057,7 @@ ChatPage::joinRoom(const QString &room) // We remove any invites with the same room_id. try { - cache::client()->removeInvite(room_id); + cache::removeInvite(room_id); } catch (const lmdb::error &e) { emit showNotification( QString("Failed to remove invite: %1").arg(e.what())); @@ -1156,16 +1155,16 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request nhlog::net()->info("initial sync completed"); try { - cache::client()->saveState(res); + cache::saveState(res); olm::handle_to_device_messages(res.to_device); emit initializeViews(std::move(res.rooms)); - emit initializeRoomList(cache::client()->roomInfo()); - emit initializeMentions(cache::client()->getTimelineMentions()); + emit initializeRoomList(cache::roomInfo()); + emit initializeMentions(cache::getTimelineMentions()); - cache::client()->calculateRoomReadStatus(); - emit syncTags(cache::client()->roomInfo().toStdMap()); + cache::calculateRoomReadStatus(); + emit syncTags(cache::roomInfo().toStdMap()); } catch (const lmdb::error &e) { nhlog::db()->error("failed to save state after initial sync: {}", e.what()); startInitialSync(); diff --git a/src/CommunitiesList.cpp b/src/CommunitiesList.cpp index 6e46741b..4ea99408 100644 --- a/src/CommunitiesList.cpp +++ b/src/CommunitiesList.cpp @@ -215,7 +215,7 @@ CommunitiesList::highlightSelectedCommunity(const QString &community_id) void CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUrl) { - auto savedImgData = cache::client()->image(avatarUrl); + auto savedImgData = cache::image(avatarUrl); if (!savedImgData.isNull()) { QPixmap pix; pix.loadFromData(savedImgData); @@ -238,7 +238,7 @@ CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUr return; } - cache::client()->saveImage(opts.mxc_url, res); + cache::saveImage(opts.mxc_url, res); auto data = QByteArray(res.data(), res.size()); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 7d9a8902..b13f1b80 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -114,7 +114,7 @@ MainWindow::MainWindow(QWidget *parent) connect( userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool))); connect(userSettingsPage_, &UserSettingsPage::themeChanged, this, []() { - Cache::clearUserColors(); + cache::clearUserColors(); }); connect( userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged); @@ -444,7 +444,7 @@ MainWindow::openReadReceiptsDialog(const QString &event_id) const auto room_id = chat_page_->currentRoom(); try { - dialog->addUsers(cache::client()->readReceipts(event_id, room_id)); + dialog->addUsers(cache::readReceipts(event_id, room_id)); } catch (const lmdb::error &e) { nhlog::db()->warn("failed to retrieve read receipts for {} {}", event_id.toStdString(), @@ -507,4 +507,4 @@ MainWindow::loadJdenticonPlugin() nhlog::ui()->info("jdenticon plugin not found."); return false; -} \ No newline at end of file +} diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp index edf6ceb5..02ca2806 100644 --- a/src/MxcImageProvider.cpp +++ b/src/MxcImageProvider.cpp @@ -1,6 +1,8 @@ #include "MxcImageProvider.h" #include "Cache.h" +#include "MatrixClient.h" +#include "Logging.h" void MxcImageResponse::run() @@ -11,7 +13,7 @@ MxcImageResponse::run() .arg(m_requestedSize.width()) .arg(m_requestedSize.height()); - auto data = cache::client()->image(fileName); + auto data = cache::image(fileName); if (!data.isNull() && m_image.loadFromData(data)) { m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio); m_image.setText("mxc url", "mxc://" + m_id); @@ -36,14 +38,14 @@ MxcImageResponse::run() } auto data = QByteArray(res.data(), res.size()); - cache::client()->saveImage(fileName, data); + cache::saveImage(fileName, data); m_image.loadFromData(data); m_image.setText("mxc url", "mxc://" + m_id); emit finished(); }); } else { - auto data = cache::client()->image(m_id); + auto data = cache::image(m_id); if (!data.isNull() && m_image.loadFromData(data)) { m_image.setText("mxc url", "mxc://" + m_id); emit finished(); @@ -75,7 +77,7 @@ MxcImageResponse::run() m_image.setText("original filename", QString::fromStdString(originalFilename)); m_image.setText("mxc url", "mxc://" + m_id); - cache::client()->saveImage(m_id, data); + cache::saveImage(m_id, data); emit finished(); }); diff --git a/src/Olm.cpp b/src/Olm.cpp index 9c1a25df..5859fe8e 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -121,7 +121,7 @@ handle_pre_key_olm_message(const std::string &sender, // We also remove the one time key used to establish that // session so we'll have to update our copy of the account object. - cache::client()->saveOlmAccount(olm::client()->save("secret")); + cache::saveOlmAccount(olm::client()->save("secret")); } catch (const mtx::crypto::olm_exception &e) { nhlog::crypto()->critical( "failed to create inbound session with {}: {}", sender, e.what()); @@ -149,7 +149,7 @@ handle_pre_key_olm_message(const std::string &sender, nhlog::crypto()->debug("decrypted message: \n {}", plaintext.dump(2)); try { - cache::client()->saveOlmSession(sender_key, std::move(inbound_session)); + cache::saveOlmSession(sender_key, std::move(inbound_session)); } catch (const lmdb::error &e) { nhlog::db()->warn( "failed to save inbound olm session from {}: {}", sender, e.what()); @@ -166,7 +166,7 @@ encrypt_group_message(const std::string &room_id, using namespace mtx::events; // Always chech before for existence. - auto res = cache::client()->getOutboundMegolmSession(room_id); + auto res = cache::getOutboundMegolmSession(room_id); auto payload = olm::client()->encrypt_group_message(res.session, body); // Prepare the m.room.encrypted event. @@ -181,7 +181,7 @@ encrypt_group_message(const std::string &room_id, nhlog::crypto()->info("next message_index {}", message_index); // We need to re-pickle the session after we send a message to save the new message_index. - cache::client()->updateOutboundMegolmSession(room_id, message_index); + cache::updateOutboundMegolmSession(room_id, message_index); return data; } @@ -189,13 +189,13 @@ encrypt_group_message(const std::string &room_id, nlohmann::json try_olm_decryption(const std::string &sender_key, const mtx::events::msg::OlmCipherContent &msg) { - auto session_ids = cache::client()->getOlmSessions(sender_key); + auto session_ids = cache::getOlmSessions(sender_key); nhlog::crypto()->info("attempt to decrypt message with {} known session_ids", session_ids.size()); for (const auto &id : session_ids) { - auto session = cache::client()->getOlmSession(sender_key, id); + auto session = cache::getOlmSession(sender_key, id); if (!session) continue; @@ -204,7 +204,7 @@ try_olm_decryption(const std::string &sender_key, const mtx::events::msg::OlmCip try { text = olm::client()->decrypt_message(session->get(), msg.type, msg.body); - cache::client()->saveOlmSession(id, std::move(session.value())); + cache::saveOlmSession(id, std::move(session.value())); } catch (const mtx::crypto::olm_exception &e) { nhlog::crypto()->debug("failed to decrypt olm message ({}, {}) with {}: {}", msg.type, @@ -252,7 +252,7 @@ create_inbound_megolm_session(const std::string &sender, try { auto megolm_session = olm::client()->init_inbound_group_session(session_key); - cache::client()->saveInboundMegolmSession(index, std::move(megolm_session)); + cache::saveInboundMegolmSession(index, std::move(megolm_session)); } catch (const lmdb::error &e) { nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what()); return; @@ -268,7 +268,7 @@ void mark_keys_as_published() { olm::client()->mark_keys_as_published(); - cache::client()->saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); + cache::saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); } void @@ -355,13 +355,13 @@ handle_key_request_message(const mtx::events::msg::KeyRequest &req) } // Check if we have the keys for the requested session. - if (!cache::client()->outboundMegolmSessionExists(req.room_id)) { + if (!cache::outboundMegolmSessionExists(req.room_id)) { nhlog::crypto()->warn("requested session not found in room: {}", req.room_id); return; } // Check that the requested session_id and the one we have saved match. - const auto session = cache::client()->getOutboundMegolmSession(req.room_id); + const auto session = cache::getOutboundMegolmSession(req.room_id); if (req.session_id != session.data.session_id) { nhlog::crypto()->warn("session id of retrieved session doesn't match the request: " "requested({}), ours({})", @@ -370,7 +370,7 @@ handle_key_request_message(const mtx::events::msg::KeyRequest &req) return; } - if (!cache::client()->isRoomMember(req.sender, req.room_id)) { + if (!cache::isRoomMember(req.sender, req.room_id)) { nhlog::crypto()->warn( "user {} that requested the session key is not member of the room {}", req.sender, @@ -509,8 +509,7 @@ send_megolm_key_to_device(const std::string &user_id, device_msg = olm::client()->create_olm_encrypted_content( olm_session.get(), room_key, pks.curve25519); - cache::client()->saveOlmSession(pks.curve25519, - std::move(olm_session)); + cache::saveOlmSession(pks.curve25519, std::move(olm_session)); } catch (const json::exception &e) { nhlog::crypto()->warn("creating outbound session: {}", e.what()); diff --git a/src/QuickSwitcher.cpp b/src/QuickSwitcher.cpp index f8f6c001..29683bb3 100644 --- a/src/QuickSwitcher.cpp +++ b/src/QuickSwitcher.cpp @@ -93,8 +93,7 @@ QuickSwitcher::QuickSwitcher(QWidget *parent) QtConcurrent::run([this, query = query.toLower()]() { try { - emit queryResults( - cache::client()->searchRooms(query.toStdString())); + emit queryResults(cache::searchRooms(query.toStdString())); } catch (const lmdb::error &e) { qWarning() << "room search failed:" << e.what(); } diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 66700dbc..5e63eca5 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -529,12 +529,12 @@ TextInputWidget::TextInputWidget(QWidget *parent) emit heightChanged(widgetHeight); }); connect(input_, &FilteredTextEdit::showSuggestions, this, [this](const QString &q) { - if (q.isEmpty() || !cache::client()) + if (q.isEmpty()) return; QtConcurrent::run([this, q = q.toLower().toStdString()]() { try { - emit input_->resultsRetrieved(cache::client()->searchUsers( + emit input_->resultsRetrieved(cache::searchUsers( ChatPage::instance()->currentRoom().toStdString(), q)); } catch (const lmdb::error &e) { std::cout << e.what() << '\n'; diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 1caea449..772a8d13 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -555,7 +555,7 @@ UserSettingsPage::importSessionKeys() try { auto sessions = mtx::crypto::decrypt_exported_sessions(payload, password.toStdString()); - cache::client()->importSessionKeys(std::move(sessions)); + cache::importSessionKeys(std::move(sessions)); } catch (const mtx::crypto::sodium_exception &e) { QMessageBox::warning(this, tr("Error"), e.what()); } catch (const lmdb::error &e) { @@ -597,7 +597,7 @@ UserSettingsPage::exportSessionKeys() // Export sessions & save to file. try { auto encrypted_blob = mtx::crypto::encrypt_exported_sessions( - cache::client()->exportSessionKeys(), password.toStdString()); + cache::exportSessionKeys(), password.toStdString()); QString b64 = QString::fromStdString(mtx::crypto::bin2base64(encrypted_blob)); diff --git a/src/Utils.cpp b/src/Utils.cpp index 3d69162f..918e1996 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -142,7 +142,7 @@ utils::getMessageDescription(const TimelineEvent &event, } else if (auto msg = std::get_if(&event); msg != nullptr) { const auto sender = QString::fromStdString(msg->sender); - const auto username = Cache::displayName(room_id, sender); + const auto username = cache::displayName(room_id, sender); const auto ts = QDateTime::fromMSecsSinceEpoch(msg->origin_server_ts); DescInfo info; diff --git a/src/Utils.h b/src/Utils.h index 6c0cf0dd..aa62b8e7 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -168,7 +168,7 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin const auto msg = std::get(event); const auto sender = QString::fromStdString(msg.sender); - const auto username = Cache::displayName(room_id, sender); + const auto username = cache::displayName(room_id, sender); const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts); return DescInfo{QString::fromStdString(msg.event_id), diff --git a/src/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp index f62cf9fe..dfb3d984 100644 --- a/src/dialogs/MemberList.cpp +++ b/src/dialogs/MemberList.cpp @@ -110,11 +110,11 @@ MemberList::MemberList(const QString &room_id, QWidget *parent) const size_t numMembers = list_->count() - 1; if (numMembers > 0) - addUsers(cache::client()->getMembers(room_id_.toStdString(), numMembers)); + addUsers(cache::getMembers(room_id_.toStdString(), numMembers)); }); try { - addUsers(cache::client()->getMembers(room_id_.toStdString())); + addUsers(cache::getMembers(room_id_.toStdString())); } catch (const lmdb::error &e) { qCritical() << e.what(); } diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp index 58ad59c3..0edd1ebf 100644 --- a/src/dialogs/ReadReceipts.cpp +++ b/src/dialogs/ReadReceipts.cpp @@ -35,7 +35,7 @@ ReceiptItem::ReceiptItem(QWidget *parent, QFont nameFont; nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1); - auto displayName = Cache::displayName(room_id, user_id); + auto displayName = cache::displayName(room_id, user_id); avatar_ = new Avatar(this, 44); avatar_->setLetter(utils::firstChar(displayName)); diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp index 1be33d33..fcaa4fdc 100644 --- a/src/dialogs/RoomSettings.cpp +++ b/src/dialogs/RoomSettings.cpp @@ -485,8 +485,8 @@ void RoomSettings::retrieveRoomInfo() { try { - usesEncryption_ = cache::client()->isRoomEncrypted(room_id_.toStdString()); - info_ = cache::client()->singleRoomInfo(room_id_.toStdString()); + usesEncryption_ = cache::isRoomEncrypted(room_id_.toStdString()); + info_ = cache::singleRoomInfo(room_id_.toStdString()); setAvatar(); } catch (const lmdb::error &) { nhlog::db()->warn("failed to retrieve room info from cache: {}", @@ -529,8 +529,7 @@ bool RoomSettings::canChangeJoinRules(const std::string &room_id, const std::string &user_id) const { try { - return cache::client()->hasEnoughPowerLevel( - {EventType::RoomJoinRules}, room_id, user_id); + return cache::hasEnoughPowerLevel({EventType::RoomJoinRules}, room_id, user_id); } catch (const lmdb::error &e) { nhlog::db()->warn("lmdb error: {}", e.what()); } @@ -542,7 +541,7 @@ bool RoomSettings::canChangeNameAndTopic(const std::string &room_id, const std::string &user_id) const { try { - return cache::client()->hasEnoughPowerLevel( + return cache::hasEnoughPowerLevel( {EventType::RoomName, EventType::RoomTopic}, room_id, user_id); } catch (const lmdb::error &e) { nhlog::db()->warn("lmdb error: {}", e.what()); @@ -555,8 +554,7 @@ bool RoomSettings::canChangeAvatar(const std::string &room_id, const std::string &user_id) const { try { - return cache::client()->hasEnoughPowerLevel( - {EventType::RoomAvatar}, room_id, user_id); + return cache::hasEnoughPowerLevel({EventType::RoomAvatar}, room_id, user_id); } catch (const lmdb::error &e) { nhlog::db()->warn("lmdb error: {}", e.what()); } diff --git a/src/dialogs/RoomSettings.h b/src/dialogs/RoomSettings.h index e1807ba1..d71b70db 100644 --- a/src/dialogs/RoomSettings.h +++ b/src/dialogs/RoomSettings.h @@ -5,7 +5,9 @@ #include #include -#include "Cache.h" +#include + +#include "CacheStructs.h" class Avatar; class FlatButton; diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp index 5ad3afa2..50c1c990 100644 --- a/src/dialogs/UserProfile.cpp +++ b/src/dialogs/UserProfile.cpp @@ -203,7 +203,7 @@ UserProfile::init(const QString &userId, const QString &roomId) { resetToDefaults(); - auto displayName = Cache::displayName(roomId, userId); + auto displayName = cache::displayName(roomId, userId); userIdLabel_->setText(userId); displayNameLabel_->setText(displayName); @@ -215,9 +215,9 @@ UserProfile::init(const QString &userId, const QString &roomId) try { bool hasMemberRights = - cache::client()->hasEnoughPowerLevel({mtx::events::EventType::RoomMember}, - roomId.toStdString(), - localUser.toStdString()); + cache::hasEnoughPowerLevel({mtx::events::EventType::RoomMember}, + roomId.toStdString(), + localUser.toStdString()); if (!hasMemberRights) { kickBtn_->hide(); banBtn_->hide(); diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp index c4d4327f..db97e4a3 100644 --- a/src/popups/PopupItem.cpp +++ b/src/popups/PopupItem.cpp @@ -49,7 +49,7 @@ UserItem::UserItem(QWidget *parent, const QString &user_id) : PopupItem(parent) , userId_{user_id} { - auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_); + auto displayName = cache::displayName(ChatPage::instance()->currentRoom(), userId_); avatar_->setLetter(utils::firstChar(displayName)); @@ -70,7 +70,7 @@ UserItem::updateItem(const QString &user_id) { userId_ = user_id; - auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_); + auto displayName = cache::displayName(ChatPage::instance()->currentRoom(), userId_); // If it's a matrix id we use the second letter. if (displayName.size() > 1 && displayName.at(0) == '@') @@ -93,7 +93,7 @@ UserItem::mousePressEvent(QMouseEvent *event) { if (event->buttons() != Qt::RightButton) emit clicked( - Cache::displayName(ChatPage::instance()->currentRoom(), selectedText())); + cache::displayName(ChatPage::instance()->currentRoom(), selectedText())); QWidget::mousePressEvent(event); } diff --git a/src/popups/PopupItem.h b/src/popups/PopupItem.h index cab73a9d..7a710fdb 100644 --- a/src/popups/PopupItem.h +++ b/src/popups/PopupItem.h @@ -6,7 +6,6 @@ #include #include "../AvatarProvider.h" -#include "../Cache.h" #include "../ChatPage.h" class Avatar; @@ -81,4 +80,4 @@ private: QLabel *roomName_; QString roomId_; RoomSearchResult info_; -}; \ No newline at end of file +}; diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp index 0ebf7c88..42a5a6d3 100644 --- a/src/popups/ReplyPopup.cpp +++ b/src/popups/ReplyPopup.cpp @@ -8,6 +8,7 @@ #include "../ui/Avatar.h" #include "../ui/DropShadow.h" #include "../ui/TextLabel.h" +#include "PopupItem.h" #include "ReplyPopup.h" ReplyPopup::ReplyPopup(QWidget *parent) diff --git a/src/popups/ReplyPopup.h b/src/popups/ReplyPopup.h index b28cd0cf..1fa3bb83 100644 --- a/src/popups/ReplyPopup.h +++ b/src/popups/ReplyPopup.h @@ -2,17 +2,14 @@ #include #include -#include #include #include -#include "../AvatarProvider.h" -#include "../Cache.h" -#include "../ChatPage.h" -#include "../Utils.h" #include "../ui/FlatButton.h" #include "../ui/TextLabel.h" -#include "PopupItem.h" + +struct RelatedInfo; +class UserItem; class ReplyPopup : public QWidget { diff --git a/src/popups/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h index 536c82fb..de52760a 100644 --- a/src/popups/SuggestionsPopup.h +++ b/src/popups/SuggestionsPopup.h @@ -5,9 +5,8 @@ #include #include -#include "../AvatarProvider.h" -#include "../Cache.h" -#include "../ChatPage.h" +#include "CacheStructs.h" +#include "ChatPage.h" #include "PopupItem.h" Q_DECLARE_METATYPE(QVector) @@ -28,7 +27,7 @@ public: const auto &widget = qobject_cast(item->widget()); emit itemSelected( - Cache::displayName(ChatPage::instance()->currentRoom(), widget->selectedText())); + cache::displayName(ChatPage::instance()->currentRoom(), widget->selectedText())); resetSelection(); } diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index 3be5c462..763eeffc 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -103,7 +103,7 @@ UserMentions::showPopup() delete widget; } - auto notifs = cache::client()->getTimelineMentions(); + auto notifs = cache::getTimelineMentions(); initializeMentions(notifs); show(); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 1e069d50..d3d1ad34 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -422,7 +422,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj readEvent(event_id.toStdString()); // ask to be notified for read receipts - cache::client()->addPendingReceipt(room_id_, event_id); + cache::addPendingReceipt(room_id_, event_id); isProcessingPending = false; emit dataChanged(index(idx, 0), index(idx, 0)); @@ -575,8 +575,7 @@ TimelineModel::data(const QModelIndex &index, int role) const return qml_mtx_events::Failed; else if (pending.contains(id)) return qml_mtx_events::Sent; - else if (read.contains(id) || - cache::client()->readReceipts(id, room_id_).size() > 1) + else if (read.contains(id) || cache::readReceipts(id, room_id_).size() > 1) return qml_mtx_events::Read; else return qml_mtx_events::Received; @@ -805,13 +804,13 @@ TimelineModel::userColor(QString id, QColor background) QString TimelineModel::displayName(QString id) const { - return Cache::displayName(room_id_, id); + return cache::displayName(room_id_, id); } QString TimelineModel::avatarUrl(QString id) const { - return Cache::avatarUrl(room_id_, id); + return cache::avatarUrl(room_id_, id); } QString @@ -868,7 +867,7 @@ TimelineModel::decryptEvent(const mtx::events::EncryptedEventinboundMegolmSessionExists(index)) { + if (!cache::inboundMegolmSessionExists(index)) { nhlog::crypto()->info("Could not find inbound megolm session ({}, {}, {})", index.room_id, index.session_id, @@ -887,7 +886,7 @@ TimelineModel::decryptEvent(const mtx::events::EncryptedEventgetInboundMegolmSession(index); + auto session = cache::getInboundMegolmSession(index); auto res = olm::client()->decrypt_group_message(session, e.content.ciphertext); msg_str = std::string((char *)res.data.data(), res.data.size()); } catch (const lmdb::error &e) { @@ -1044,7 +1043,7 @@ TimelineModel::sendEncryptedMessage(const std::string &txn_id, nlohmann::json co try { // Check if we have already an outbound megolm session then we can use. - if (cache::client()->outboundMegolmSessionExists(room_id)) { + if (cache::outboundMegolmSessionExists(room_id)) { auto data = olm::encrypt_group_message( room_id, http::client()->device_id(), doc.dump()); @@ -1089,10 +1088,10 @@ TimelineModel::sendEncryptedMessage(const std::string &txn_id, nlohmann::json co session_data.session_id = session_id; session_data.session_key = session_key; session_data.message_index = 0; // TODO Update me - cache::client()->saveOutboundMegolmSession( + cache::saveOutboundMegolmSession( room_id, session_data, std::move(outbound_session)); - const auto members = cache::client()->roomMembers(room_id); + const auto members = cache::roomMembers(room_id); nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); auto keeper = @@ -1311,7 +1310,7 @@ TimelineModel::handleClaimedKeys(std::shared_ptr keeper, s.get(), room_keys.at(device_id), pks.at(device_id).curve25519); try { - cache::client()->saveOlmSession(id_key, std::move(s)); + cache::saveOlmSession(id_key, std::move(s)); } catch (const lmdb::error &e) { nhlog::db()->critical("failed to save outbound olm session: {}", e.what()); } catch (const mtx::crypto::olm_exception &e) { @@ -1353,7 +1352,7 @@ struct SendMessageVisitor void operator()(const mtx::events::RoomEvent &msg) { - if (cache::client()->isRoomEncrypted(model_->room_id_.toStdString())) { + if (cache::isRoomEncrypted(model_->room_id_.toStdString())) { model_->sendEncryptedMessage(txn_id_qstr_.toStdString(), nlohmann::json(msg.content)); } else { -- cgit 1.5.1 From 63f3071445308aaa01c1f04d6c19f15f0bdde3d4 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 31 Jan 2020 16:25:43 +0100 Subject: Remove more unneeded headers --- src/Splitter.cpp | 6 -- src/TextInputWidget.h | 5 +- src/Utils.cpp | 1 - src/dialogs/PreviewUploadOverlay.cpp | 1 - src/popups/UserMentions.cpp | 3 + src/popups/UserMentions.h | 8 +- src/ui/DropShadow.cpp | 188 +++++++++++++++++------------------ src/ui/TextField.cpp | 2 +- src/ui/ToggleButton.cpp | 2 +- 9 files changed, 103 insertions(+), 113 deletions(-) (limited to 'src/popups/UserMentions.cpp') diff --git a/src/Splitter.cpp b/src/Splitter.cpp index 32c67425..6267e591 100644 --- a/src/Splitter.cpp +++ b/src/Splitter.cpp @@ -15,15 +15,10 @@ * along with this program. If not, see . */ -#include -#include #include -#include -#include "Config.h" #include "Logging.h" #include "Splitter.h" -#include "Utils.h" constexpr auto MaxWidth = (1 << 24) - 1; @@ -180,4 +175,3 @@ splitter::calculateSidebarSizes(const QFont &f) return sz; } - diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 2f267a23..3674ee0d 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -18,17 +18,14 @@ #pragma once #include -#include -#include #include -#include +#include #include #include #include #include -#include "Utils.h" #include "dialogs/PreviewUploadOverlay.h" #include "emoji/PickButton.h" #include "popups/ReplyPopup.h" diff --git a/src/Utils.cpp b/src/Utils.cpp index 55eebbc9..91ecfcd7 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -651,4 +651,3 @@ utils::restoreCombobox(QComboBox *combo, const QString &value) } } } - diff --git a/src/dialogs/PreviewUploadOverlay.cpp b/src/dialogs/PreviewUploadOverlay.cpp index 31d01214..42558d67 100644 --- a/src/dialogs/PreviewUploadOverlay.cpp +++ b/src/dialogs/PreviewUploadOverlay.cpp @@ -15,7 +15,6 @@ * along with this program. If not, see . */ -#include #include #include #include diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index 763eeffc..b0223f7f 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -1,7 +1,10 @@ +#include #include +#include #include #include #include +#include #include "Cache.h" #include "ChatPage.h" diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h index 42bdcd60..b7c4e51d 100644 --- a/src/popups/UserMentions.h +++ b/src/popups/UserMentions.h @@ -3,13 +3,13 @@ #include #include -#include -#include #include -#include -#include #include +class QPaintEvent; +class QTabWidget; +class QScrollArea; +class QVBoxLayout; namespace popups { diff --git a/src/ui/DropShadow.cpp b/src/ui/DropShadow.cpp index 93baa02d..d437975c 100644 --- a/src/ui/DropShadow.cpp +++ b/src/ui/DropShadow.cpp @@ -3,108 +3,106 @@ #include #include +void +DropShadow::draw(QPainter &painter, + qint16 margin, + qreal radius, + QColor start, + QColor end, + qreal startPosition, + qreal endPosition0, + qreal endPosition1, + qreal width, + qreal height) +{ + painter.setPen(Qt::NoPen); - void DropShadow::draw(QPainter &painter, - qint16 margin, - qreal radius, - QColor start, - QColor end, - qreal startPosition, - qreal endPosition0, - qreal endPosition1, - qreal width, - qreal height) - { - painter.setPen(Qt::NoPen); + QLinearGradient gradient; + gradient.setColorAt(startPosition, start); + gradient.setColorAt(endPosition0, end); - QLinearGradient gradient; - gradient.setColorAt(startPosition, start); - gradient.setColorAt(endPosition0, end); + // Right + QPointF right0(width - margin, height / 2); + QPointF right1(width, height / 2); + gradient.setStart(right0); + gradient.setFinalStop(right1); + painter.setBrush(QBrush(gradient)); + // Deprecated in 5.13: painter.drawRoundRect( + // QRectF(QPointF(width - margin * radius, margin), QPointF(width, height - + // margin)), 0.0, 0.0); + painter.drawRoundedRect( + QRectF(QPointF(width - margin * radius, margin), QPointF(width, height - margin)), + 0.0, + 0.0); - // Right - QPointF right0(width - margin, height / 2); - QPointF right1(width, height / 2); - gradient.setStart(right0); - gradient.setFinalStop(right1); - painter.setBrush(QBrush(gradient)); - // Deprecated in 5.13: painter.drawRoundRect( - // QRectF(QPointF(width - margin * radius, margin), QPointF(width, height - - // margin)), 0.0, 0.0); - painter.drawRoundedRect( - QRectF(QPointF(width - margin * radius, margin), QPointF(width, height - margin)), - 0.0, - 0.0); + // Left + QPointF left0(margin, height / 2); + QPointF left1(0, height / 2); + gradient.setStart(left0); + gradient.setFinalStop(left1); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect( + QRectF(QPointF(margin * radius, margin), QPointF(0, height - margin)), 0.0, 0.0); - // Left - QPointF left0(margin, height / 2); - QPointF left1(0, height / 2); - gradient.setStart(left0); - gradient.setFinalStop(left1); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect( - QRectF(QPointF(margin * radius, margin), QPointF(0, height - margin)), 0.0, 0.0); + // Top + QPointF top0(width / 2, margin); + QPointF top1(width / 2, 0); + gradient.setStart(top0); + gradient.setFinalStop(top1); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect( + QRectF(QPointF(width - margin, 0), QPointF(margin, margin)), 0.0, 0.0); - // Top - QPointF top0(width / 2, margin); - QPointF top1(width / 2, 0); - gradient.setStart(top0); - gradient.setFinalStop(top1); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect( - QRectF(QPointF(width - margin, 0), QPointF(margin, margin)), 0.0, 0.0); + // Bottom + QPointF bottom0(width / 2, height - margin); + QPointF bottom1(width / 2, height); + gradient.setStart(bottom0); + gradient.setFinalStop(bottom1); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect( + QRectF(QPointF(margin, height - margin), QPointF(width - margin, height)), 0.0, 0.0); - // Bottom - QPointF bottom0(width / 2, height - margin); - QPointF bottom1(width / 2, height); - gradient.setStart(bottom0); - gradient.setFinalStop(bottom1); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect( - QRectF(QPointF(margin, height - margin), QPointF(width - margin, height)), - 0.0, - 0.0); + // BottomRight + QPointF bottomright0(width - margin, height - margin); + QPointF bottomright1(width, height); + gradient.setStart(bottomright0); + gradient.setFinalStop(bottomright1); + gradient.setColorAt(endPosition1, end); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect(QRectF(bottomright0, bottomright1), 0.0, 0.0); - // BottomRight - QPointF bottomright0(width - margin, height - margin); - QPointF bottomright1(width, height); - gradient.setStart(bottomright0); - gradient.setFinalStop(bottomright1); - gradient.setColorAt(endPosition1, end); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect(QRectF(bottomright0, bottomright1), 0.0, 0.0); + // BottomLeft + QPointF bottomleft0(margin, height - margin); + QPointF bottomleft1(0, height); + gradient.setStart(bottomleft0); + gradient.setFinalStop(bottomleft1); + gradient.setColorAt(endPosition1, end); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect(QRectF(bottomleft0, bottomleft1), 0.0, 0.0); - // BottomLeft - QPointF bottomleft0(margin, height - margin); - QPointF bottomleft1(0, height); - gradient.setStart(bottomleft0); - gradient.setFinalStop(bottomleft1); - gradient.setColorAt(endPosition1, end); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect(QRectF(bottomleft0, bottomleft1), 0.0, 0.0); + // TopLeft + QPointF topleft0(margin, margin); + QPointF topleft1(0, 0); + gradient.setStart(topleft0); + gradient.setFinalStop(topleft1); + gradient.setColorAt(endPosition1, end); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect(QRectF(topleft0, topleft1), 0.0, 0.0); - // TopLeft - QPointF topleft0(margin, margin); - QPointF topleft1(0, 0); - gradient.setStart(topleft0); - gradient.setFinalStop(topleft1); - gradient.setColorAt(endPosition1, end); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect(QRectF(topleft0, topleft1), 0.0, 0.0); + // TopRight + QPointF topright0(width - margin, margin); + QPointF topright1(width, 0); + gradient.setStart(topright0); + gradient.setFinalStop(topright1); + gradient.setColorAt(endPosition1, end); + painter.setBrush(QBrush(gradient)); + painter.drawRoundedRect(QRectF(topright0, topright1), 0.0, 0.0); - // TopRight - QPointF topright0(width - margin, margin); - QPointF topright1(width, 0); - gradient.setStart(topright0); - gradient.setFinalStop(topright1); - gradient.setColorAt(endPosition1, end); - painter.setBrush(QBrush(gradient)); - painter.drawRoundedRect(QRectF(topright0, topright1), 0.0, 0.0); - - // Widget - painter.setBrush(QBrush("#FFFFFF")); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawRoundedRect( - QRectF(QPointF(margin, margin), QPointF(width - margin, height - margin)), - radius, - radius); - } + // Widget + painter.setBrush(QBrush("#FFFFFF")); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawRoundedRect( + QRectF(QPointF(margin, margin), QPointF(width - margin, height - margin)), + radius, + radius); +} diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp index c4582085..0ae2516d 100644 --- a/src/ui/TextField.cpp +++ b/src/ui/TextField.cpp @@ -1,6 +1,6 @@ #include "TextField.h" -#include +#include #include #include #include diff --git a/src/ui/ToggleButton.cpp b/src/ui/ToggleButton.cpp index 755f528f..f9411489 100644 --- a/src/ui/ToggleButton.cpp +++ b/src/ui/ToggleButton.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include -- cgit 1.5.1 From c5af3543e64c03eb22912b6317f4bbad4dbe14f5 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 7 Feb 2020 02:52:18 +0100 Subject: fix c++11 range-loop might detach Qt container --- src/popups/UserMentions.cpp | 2 +- src/timeline/DelegateChooser.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/popups/UserMentions.cpp') diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp index b0223f7f..2e70dbd3 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp @@ -74,7 +74,7 @@ UserMentions::initializeMentions(const QMapdebug("Initializing " + std::to_string(notifs.size()) + " notifications."); for (const auto &item : notifs) { - for (const auto notif : item.notifications) { + for (const auto ¬if : item.notifications) { const auto event_id = QString::fromStdString(utils::event_id(notif.event)); try { diff --git a/src/timeline/DelegateChooser.cpp b/src/timeline/DelegateChooser.cpp index 632a2a64..46ab6c0e 100644 --- a/src/timeline/DelegateChooser.cpp +++ b/src/timeline/DelegateChooser.cpp @@ -91,7 +91,7 @@ DelegateChooser::clearChoices(QQmlListProperty *p) void DelegateChooser::recalcChild() { - for (const auto choice : choices_) { + for (const auto choice : qAsConst(choices_)) { auto choiceValue = choice->roleValue(); if (!roleValue_.isValid() || !choiceValue.isValid() || choiceValue == roleValue_) { if (child) { -- cgit 1.5.1