From 9159b9ce22efa4972792b5400fd384457c349caa Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Tue, 11 Jun 2019 21:04:30 -0400 Subject: Initial Support for Rich Replies Add placeholder UI for showing replies in the text entry widget. Existing quoting capability has been removed (Temporarily), as it was replaced with the new reply capability. Replies sent from nheko do not currently appear correctly in the timeline (this will be fixed in a future commit). --- src/popups/ReplyPopup.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/popups/ReplyPopup.cpp (limited to 'src/popups/ReplyPopup.cpp') diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp new file mode 100644 index 00000000..a883739f --- /dev/null +++ b/src/popups/ReplyPopup.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "../Config.h" +#include "../Utils.h" +#include "../ui/Avatar.h" +#include "../ui/DropShadow.h" +#include "ReplyPopup.h" + +ReplyPopup::ReplyPopup(QWidget *parent) + : QWidget(parent) +{ + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); + + layout_ = new QVBoxLayout(this); + layout_->setMargin(0); + layout_->setSpacing(0); +} + +void +ReplyPopup::setReplyContent(const QString &user, const QString &msg, const QString &srcEvent) +{ + QLayoutItem *child; + while ((child = layout_->takeAt(0)) != 0) { + delete child->widget(); + delete child; + } + // Create a new widget if there isn't already one in that + // layout position. + // if (!item) { + auto userItem = new UserItem(this, user); + auto *text = new QLabel(this); + text->setText(msg); + auto *event = new QLabel(this); + event->setText(srcEvent); + connect(userItem, &UserItem::clicked, this, &ReplyPopup::userSelected); + layout_->addWidget(userItem); + layout_->addWidget(text); + layout_->addWidget(event); + // } else { + // Update the current widget with the new data. + // auto userWidget = qobject_cast(item->widget()); + // if (userWidget) + // userWidget->updateItem(users.at(i).user_id); + // } + + adjustSize(); +} + +void +ReplyPopup::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} -- cgit 1.5.1 From 1d4966d5fd916f21bdbbad8080eec69bb2c817d6 Mon Sep 17 00:00:00 2001 From: redsky17 Date: Tue, 11 Jun 2019 23:36:46 -0400 Subject: Add style for reply popup. Fix ALL of the linting issues --- resources/styles/nheko-dark.qss | 4 ++++ resources/styles/nheko.qss | 4 ++++ src/ChatPage.h | 4 +++- src/TextInputWidget.cpp | 6 ++---- src/TextInputWidget.h | 4 ++-- src/popups/PopupItem.cpp | 2 +- src/popups/ReplyPopup.cpp | 4 ++-- src/popups/ReplyPopup.h | 1 - src/popups/SuggestionsPopup.cpp | 2 +- src/popups/SuggestionsPopup.h | 1 - src/timeline/TimelineView.h | 4 +++- src/timeline/TimelineViewManager.cpp | 3 +-- 12 files changed, 23 insertions(+), 16 deletions(-) (limited to 'src/popups/ReplyPopup.cpp') diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss index 5567f32c..c55960f9 100644 --- a/resources/styles/nheko-dark.qss +++ b/resources/styles/nheko-dark.qss @@ -41,6 +41,10 @@ SuggestionsPopup { background-color: #202228; } +ReplyPopup { + background-color: #202228; +} + PopupItem { background-color: #202228; qproperty-hoverColor: rgba(45, 49, 57, 120); diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss index 58e83c22..c352956a 100644 --- a/resources/styles/nheko.qss +++ b/resources/styles/nheko.qss @@ -46,6 +46,10 @@ SuggestionsPopup { background-color: white; } +ReplyPopup { + background-color: white; +} + PopupItem { background-color: white; qproperty-hoverColor: rgba(192, 193, 195, 120); diff --git a/src/ChatPage.h b/src/ChatPage.h index 09e7a2c6..f70f5bdd 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -83,7 +83,9 @@ signals: void connectionLost(); void connectionRestored(); - void messageReply(const QString &username, const QString &msg, const QString &related_event); + void messageReply(const QString &username, + const QString &msg, + const QString &related_event); void notificationsRetrieved(const mtx::responses::Notifications &); diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 7971ab43..dc41b4d3 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -176,18 +176,16 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) } if (replyPopup_.isVisible()) { - switch (event->key()) - { + switch (event->key()) { case Qt::Key_Escape: closeReply(); return; - + default: break; } } - switch (event->key()) { case Qt::Key_At: atTriggerPosition_ = textCursor().position(); diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 2936340b..c462de05 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -28,10 +28,10 @@ #include #include -#include "popups/SuggestionsPopup.h" -#include "popups/ReplyPopup.h" #include "dialogs/PreviewUploadOverlay.h" #include "emoji/PickButton.h" +#include "popups/ReplyPopup.h" +#include "popups/SuggestionsPopup.h" namespace dialogs { class PreviewUploadOverlay; diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp index b10cd32e..94de3a92 100644 --- a/src/popups/PopupItem.cpp +++ b/src/popups/PopupItem.cpp @@ -2,9 +2,9 @@ #include #include -#include "PopupItem.h" #include "../Utils.h" #include "../ui/Avatar.h" +#include "PopupItem.h" constexpr int PopupHMargin = 4; constexpr int PopupItemMargin = 3; diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp index a883739f..04c3cea5 100644 --- a/src/popups/ReplyPopup.cpp +++ b/src/popups/ReplyPopup.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include @@ -32,7 +32,7 @@ ReplyPopup::setReplyContent(const QString &user, const QString &msg, const QStri // layout position. // if (!item) { auto userItem = new UserItem(this, user); - auto *text = new QLabel(this); + auto *text = new QLabel(this); text->setText(msg); auto *event = new QLabel(this); event->setText(srcEvent); diff --git a/src/popups/ReplyPopup.h b/src/popups/ReplyPopup.h index d8355e53..57c2bc8a 100644 --- a/src/popups/ReplyPopup.h +++ b/src/popups/ReplyPopup.h @@ -28,5 +28,4 @@ signals: private: QVBoxLayout *layout_; - }; diff --git a/src/popups/SuggestionsPopup.cpp b/src/popups/SuggestionsPopup.cpp index 6861a76a..ba1f77b9 100644 --- a/src/popups/SuggestionsPopup.cpp +++ b/src/popups/SuggestionsPopup.cpp @@ -3,10 +3,10 @@ #include #include "../Config.h" -#include "SuggestionsPopup.h" #include "../Utils.h" #include "../ui/Avatar.h" #include "../ui/DropShadow.h" +#include "SuggestionsPopup.h" SuggestionsPopup::SuggestionsPopup(QWidget *parent) : QWidget(parent) diff --git a/src/popups/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h index 4fbb97b3..1ef720b2 100644 --- a/src/popups/SuggestionsPopup.h +++ b/src/popups/SuggestionsPopup.h @@ -10,7 +10,6 @@ #include "../ChatPage.h" #include "PopupItem.h" - class SuggestionsPopup : public QWidget { Q_OBJECT diff --git a/src/timeline/TimelineView.h b/src/timeline/TimelineView.h index 450b5dfa..db6087eb 100644 --- a/src/timeline/TimelineView.h +++ b/src/timeline/TimelineView.h @@ -121,7 +121,9 @@ public: // Add new events at the end of the timeline. void addEvents(const mtx::responses::Timeline &timeline); - void addUserMessage(mtx::events::MessageType ty, const QString &body, const QString &related_event); + void addUserMessage(mtx::events::MessageType ty, + const QString &body, + const QString &related_event); void addUserMessage(mtx::events::MessageType ty, const QString &msg); template diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 10c2d747..1ce3794f 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -79,8 +79,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) } void -TimelineViewManager::queueReplyMessage(const QString &reply, - const QString &related_event) +TimelineViewManager::queueReplyMessage(const QString &reply, const QString &related_event) { if (active_room_.isEmpty()) return; -- cgit 1.5.1 From 129beb57c9525439d04fc2bf74f4ccaed30369c9 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Thu, 13 Jun 2019 22:33:04 -0400 Subject: Further Improve Reply Functionality Quoted replies now include matrix.to links for the event and the user. UI Rendering has been (slightly) improved... still very WIP. Restructured the reply structure in the code for future usability improvements. --- src/ChatPage.cpp | 4 +- src/ChatPage.h | 5 +- src/TextInputWidget.cpp | 30 ++++++------ src/TextInputWidget.h | 17 ++++--- src/Utils.cpp | 14 ++++++ src/Utils.h | 13 ++++++ src/popups/PopupItem.cpp | 10 ++++ src/popups/PopupItem.h | 1 + src/popups/ReplyPopup.cpp | 88 ++++++++++++++++++++++++++---------- src/popups/ReplyPopup.h | 16 ++++++- src/timeline/TimelineItem.cpp | 8 +++- src/timeline/TimelineView.cpp | 31 +++++++------ src/timeline/TimelineView.h | 5 +- src/timeline/TimelineViewManager.cpp | 5 +- src/timeline/TimelineViewManager.h | 4 +- 15 files changed, 177 insertions(+), 74 deletions(-) (limited to 'src/popups/ReplyPopup.cpp') diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 5b838259..f7dbf7ca 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -265,9 +265,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) SLOT(queueTextMessage(const QString &))); connect(text_input_, - SIGNAL(sendReplyMessage(const QString &, const QString &)), + SIGNAL(sendReplyMessage(const QString &, const RelatedInfo &)), view_manager_, - SLOT(queueReplyMessage(const QString &, const QString &))); + SLOT(queueReplyMessage(const QString &, const RelatedInfo &))); connect(text_input_, SIGNAL(sendEmoteMessage(const QString &)), diff --git a/src/ChatPage.h b/src/ChatPage.h index f70f5bdd..6e6f5aed 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -30,6 +30,7 @@ #include "Cache.h" #include "CommunitiesList.h" #include "MatrixClient.h" +#include "Utils.h" #include "notifications/Manager.h" class OverlayModal; @@ -83,9 +84,7 @@ signals: void connectionLost(); void connectionRestored(); - void messageReply(const QString &username, - const QString &msg, - const QString &related_event); + void messageReply(const RelatedInfo &related); void notificationsRetrieved(const mtx::responses::Notifications &); diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index dc41b4d3..8becf5ce 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -93,6 +93,8 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) cursor.insertText(text); }); + connect(&replyPopup_, &ReplyPopup::cancel, this, [this]() { closeReply(); }); + // For cycling through the suggestions by hitting tab. connect(this, &FilteredTextEdit::selectNextSuggestion, @@ -219,6 +221,7 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) if (!(event->modifiers() & Qt::ShiftModifier)) { stopTyping(); submit(); + closeReply(); } else { QTextEdit::keyPressEvent(event); } @@ -415,8 +418,8 @@ FilteredTextEdit::submit() auto name = text.mid(1, command_end - 1); auto args = text.mid(command_end + 1); if (name.isEmpty() || name == "/") { - if (!related_event_.isEmpty()) { - reply(args, related_event_); + if (!related_.related_event.empty()) { + reply(args, related_); } else { message(args); } @@ -424,14 +427,14 @@ FilteredTextEdit::submit() command(name, args); } } else { - if (!related_event_.isEmpty()) { - reply(std::move(text), std::move(related_event_)); + if (!related_.related_event.empty()) { + reply(std::move(text), std::move(related_)); } else { message(std::move(text)); } } - related_event_ = ""; + related_ = {}; clear(); } @@ -439,16 +442,8 @@ FilteredTextEdit::submit() void FilteredTextEdit::showReplyPopup(const QString &user, const QString &msg, const QString &event_id) { - QPoint pos; + QPoint pos = viewport()->mapToGlobal(this->pos()); - if (isAnchorValid()) { - auto cursor = textCursor(); - cursor.setPosition(atTriggerPosition_); - pos = viewport()->mapToGlobal(cursorRect(cursor).topLeft()); - } else { - auto rect = cursorRect(); - pos = viewport()->mapToGlobal(rect.topLeft()); - } replyPopup_.setReplyContent(user, msg, event_id); replyPopup_.move(pos.x(), pos.y() - replyPopup_.height() - 10); replyPopup_.show(); @@ -699,14 +694,15 @@ TextInputWidget::paintEvent(QPaintEvent *) } void -TextInputWidget::addReply(const QString &username, const QString &msg, const QString &replied_event) +TextInputWidget::addReply(const RelatedInfo &related) { // input_->setText(QString("> %1: %2\n\n").arg(username).arg(msg)); input_->setFocus(); - input_->showReplyPopup(username, msg, replied_event); + input_->showReplyPopup( + related.quoted_user, related.quoted_body, QString::fromStdString(related.related_event)); auto cursor = input_->textCursor(); cursor.movePosition(QTextCursor::End); input_->setTextCursor(cursor); - input_->setRelatedEvent(replied_event); + input_->setRelated(related); } diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index c462de05..f68560e9 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -28,6 +28,7 @@ #include #include +#include "Utils.h" #include "dialogs/PreviewUploadOverlay.h" #include "emoji/PickButton.h" #include "popups/ReplyPopup.h" @@ -55,7 +56,7 @@ public: QSize minimumSizeHint() const override; void submit(); - void setRelatedEvent(const QString &event) { related_event_ = event; } + void setRelated(const RelatedInfo &related) { related_ = related; } void showReplyPopup(const QString &user, const QString &msg, const QString &event_id); signals: @@ -64,7 +65,7 @@ signals: void stoppedTyping(); void startedUpload(); void message(QString); - void reply(QString, QString); + void reply(QString, const RelatedInfo &); void command(QString name, QString args); void image(QSharedPointer data, const QString &filename); void audio(QSharedPointer data, const QString &filename); @@ -100,7 +101,7 @@ private: ReplyPopup replyPopup_; // Used for replies - QString related_event_; + RelatedInfo related_; enum class AnchorType { @@ -113,7 +114,11 @@ private: int anchorWidth(AnchorType anchor) { return static_cast(anchor); } void closeSuggestions() { suggestionsPopup_.hide(); } - void closeReply() { replyPopup_.hide(); } + void closeReply() + { + replyPopup_.hide(); + related_ = {}; + } void resetAnchor() { atTriggerPosition_ = -1; } bool isAnchorValid() { return atTriggerPosition_ != -1; } bool hasAnchor(int pos, AnchorType anchor) @@ -167,14 +172,14 @@ public slots: void openFileSelection(); void hideUploadSpinner(); void focusLineEdit() { input_->setFocus(); } - void addReply(const QString &username, const QString &msg, const QString &related_event); + void addReply(const RelatedInfo &related); private slots: void addSelectedEmoji(const QString &emoji); signals: void sendTextMessage(QString msg); - void sendReplyMessage(QString msg, QString event_id); + void sendReplyMessage(QString msg, const RelatedInfo &related); void sendEmoteMessage(QString msg); void heightChanged(int height); diff --git a/src/Utils.cpp b/src/Utils.cpp index f8fdfaf9..690a9a9a 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -314,6 +314,20 @@ utils::markdownToHtml(const QString &text) return result; } +QString +utils::getFormattedQuoteBody(const RelatedInfo &related, const QString &html) +{ + return QString("
In reply " + "to%3
%4
") + .arg(QString::fromStdString(related.related_event), + related.quoted_user, + related.quoted_user, + related.quoted_body) + + html; +} + QString utils::linkColor() { diff --git a/src/Utils.h b/src/Utils.h index 8672e7d4..bf941c4c 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -18,6 +18,15 @@ class QComboBox; +// Contains information about related events for +// outgoing messages +struct RelatedInfo +{ + QString quoted_body; + std::string related_event; + QString quoted_user; +}; + namespace utils { using TimelineEvent = mtx::events::collections::TimelineEvents; @@ -225,6 +234,10 @@ linkifyMessage(const QString &body); QString markdownToHtml(const QString &text); +//! Generate a Rich Reply quote message +QString +getFormattedQuoteBody(const RelatedInfo &related, const QString &html); + //! Retrieve the color of the links based on the current theme. QString linkColor(); diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp index 94de3a92..f905983a 100644 --- a/src/popups/PopupItem.cpp +++ b/src/popups/PopupItem.cpp @@ -36,6 +36,16 @@ PopupItem::paintEvent(QPaintEvent *) p.fillRect(rect(), hoverColor_); } +UserItem::UserItem(QWidget *parent) + : PopupItem(parent) +{ + userName_ = new QLabel("Placeholder", this); + avatar_->setSize(conf::popup::avatar); + avatar_->setLetter("P"); + topLayout_->addWidget(avatar_); + topLayout_->addWidget(userName_, 1); +} + UserItem::UserItem(QWidget *parent, const QString &user_id) : PopupItem(parent) , userId_{user_id} diff --git a/src/popups/PopupItem.h b/src/popups/PopupItem.h index 1fc54bf7..cab73a9d 100644 --- a/src/popups/PopupItem.h +++ b/src/popups/PopupItem.h @@ -50,6 +50,7 @@ class UserItem : public PopupItem Q_OBJECT public: + UserItem(QWidget *parent); UserItem(QWidget *parent, const QString &user_id); QString selectedText() const { return userId_; } void updateItem(const QString &user_id); diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp index 04c3cea5..3c7c482a 100644 --- a/src/popups/ReplyPopup.cpp +++ b/src/popups/ReplyPopup.cpp @@ -11,41 +11,69 @@ ReplyPopup::ReplyPopup(QWidget *parent) : QWidget(parent) + , userItem_{0} + , msgLabel_{0} + , eventLabel_{0} { setAttribute(Qt::WA_ShowWithoutActivating, true); setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); - layout_ = new QVBoxLayout(this); - layout_->setMargin(0); - layout_->setSpacing(0); + mainLayout_ = new QVBoxLayout(this); + mainLayout_->setMargin(0); + mainLayout_->setSpacing(0); + + topLayout_ = new QHBoxLayout(); + topLayout_->setSpacing(0); + topLayout_->setContentsMargins(13, 1, 13, 0); + + userItem_ = new UserItem(this); + connect(userItem_, &UserItem::clicked, this, &ReplyPopup::userSelected); + topLayout_->addWidget(userItem_); + + buttonLayout_ = new QHBoxLayout(); + buttonLayout_->setSpacing(0); + buttonLayout_->setMargin(0); + + topLayout_->addLayout(buttonLayout_); + QFont f; + f.setPointSizeF(f.pointSizeF()); + const int fontHeight = QFontMetrics(f).height(); + buttonSize_ = std::min(fontHeight, 20); + + closeBtn_ = new FlatButton(this); + closeBtn_->setToolTip(tr("Logout")); + closeBtn_->setCornerRadius(buttonSize_ / 2); + closeBtn_->setText("X"); + + QIcon icon; + icon.addFile(":/icons/icons/ui/remove-symbol.png"); + + closeBtn_->setIcon(icon); + closeBtn_->setIconSize(QSize(buttonSize_, buttonSize_)); + connect(closeBtn_, &FlatButton::clicked, this, [this]() { emit cancel(); }); + + buttonLayout_->addWidget(closeBtn_); + + topLayout_->addLayout(buttonLayout_); + + mainLayout_->addLayout(topLayout_); + msgLabel_ = new QLabel(this); + mainLayout_->addWidget(msgLabel_); + eventLabel_ = new QLabel(this); + mainLayout_->addWidget(eventLabel_); + + setLayout(mainLayout_); } void ReplyPopup::setReplyContent(const QString &user, const QString &msg, const QString &srcEvent) { - QLayoutItem *child; - while ((child = layout_->takeAt(0)) != 0) { - delete child->widget(); - delete child; - } - // Create a new widget if there isn't already one in that - // layout position. - // if (!item) { - auto userItem = new UserItem(this, user); - auto *text = new QLabel(this); - text->setText(msg); - auto *event = new QLabel(this); - event->setText(srcEvent); - connect(userItem, &UserItem::clicked, this, &ReplyPopup::userSelected); - layout_->addWidget(userItem); - layout_->addWidget(text); - layout_->addWidget(event); - // } else { // Update the current widget with the new data. - // auto userWidget = qobject_cast(item->widget()); - // if (userWidget) - // userWidget->updateItem(users.at(i).user_id); - // } + userItem_->updateItem(user); + + msgLabel_->setText(msg); + + eventLabel_->setText(srcEvent); adjustSize(); } @@ -58,3 +86,13 @@ ReplyPopup::paintEvent(QPaintEvent *) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } + +void +ReplyPopup::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() != Qt::RightButton) { + emit clicked(eventLabel_->text()); + } + + QWidget::mousePressEvent(event); +} diff --git a/src/popups/ReplyPopup.h b/src/popups/ReplyPopup.h index 57c2bc8a..829f707b 100644 --- a/src/popups/ReplyPopup.h +++ b/src/popups/ReplyPopup.h @@ -3,11 +3,13 @@ #include #include #include +#include #include #include "../AvatarProvider.h" #include "../Cache.h" #include "../ChatPage.h" +#include "../ui/FlatButton.h" #include "PopupItem.h" class ReplyPopup : public QWidget @@ -22,10 +24,22 @@ public slots: protected: void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; signals: void userSelected(const QString &user); + void clicked(const QString &text); + void cancel(); private: - QVBoxLayout *layout_; + QHBoxLayout *topLayout_; + QVBoxLayout *mainLayout_; + QHBoxLayout *buttonLayout_; + + UserItem *userItem_; + FlatButton *closeBtn_; + QLabel *msgLabel_; + QLabel *eventLabel_; + + int buttonSize_; }; diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp index dd09ec78..bf5b1b53 100644 --- a/src/timeline/TimelineItem.cpp +++ b/src/timeline/TimelineItem.cpp @@ -874,8 +874,12 @@ TimelineItem::replyAction() if (!body_) return; - emit ChatPage::instance()->messageReply( - Cache::displayName(room_id_, descriptionMsg_.userid), body_->toPlainText(), eventId()); + RelatedInfo related; + related.quoted_body = body_->toPlainText(); + related.quoted_user = descriptionMsg_.userid; + related.related_event = eventId().toStdString(); + + emit ChatPage::instance()->messageReply(related); } void diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp index 6d947c15..fc89fd38 100644 --- a/src/timeline/TimelineView.cpp +++ b/src/timeline/TimelineView.cpp @@ -692,7 +692,7 @@ TimelineView::updatePendingMessage(const std::string &txn_id, const QString &eve void TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body, - const QString &related_event) + const RelatedInfo &related = RelatedInfo()) { auto with_sender = (lastSender_ != local_user_) || isDateDifference(lastMsgTimestamp_); @@ -700,13 +700,11 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, new TimelineItem(ty, local_user_, body, with_sender, room_id_, scroll_widget_); PendingMessage message; - message.ty = ty; - message.txn_id = http::client()->generate_txn_id(); - message.body = body; - message.widget = view_item; - if (!related_event.isEmpty()) { - message.related_event = related_event.toStdString(); - } + message.ty = ty; + message.txn_id = http::client()->generate_txn_id(); + message.body = body; + message.widget = view_item; + message.related = related; try { message.is_encrypted = cache::client()->isRoomEncrypted(room_id_.toStdString()); @@ -730,7 +728,7 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, void TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body) { - addUserMessage(ty, body, ""); + addUserMessage(ty, body, RelatedInfo()); } void @@ -1273,13 +1271,20 @@ toRoomMessage(const PendingMessage &m) auto html = utils::markdownToHtml(m.body); mtx::events::msg::Text text; + text.body = m.body.trimmed().toStdString(); - if (html != m.body.trimmed().toHtmlEscaped()) - text.formatted_body = html.toStdString(); + if (html != m.body.trimmed().toHtmlEscaped()) { + if (!m.related.quoted_body.isEmpty()) { + text.formatted_body = + utils::getFormattedQuoteBody(m.related, html).toStdString(); + } else { + text.formatted_body = html.toStdString(); + } + } - if (!m.related_event.empty()) { - text.relates_to.in_reply_to.event_id = m.related_event; + if (!m.related.related_event.empty()) { + text.relates_to.in_reply_to.event_id = m.related.related_event; } return text; diff --git a/src/timeline/TimelineView.h b/src/timeline/TimelineView.h index db6087eb..35796efd 100644 --- a/src/timeline/TimelineView.h +++ b/src/timeline/TimelineView.h @@ -30,6 +30,7 @@ #include #include +#include "../Utils.h" #include "MatrixClient.h" #include "timeline/TimelineItem.h" @@ -63,7 +64,7 @@ struct PendingMessage { mtx::events::MessageType ty; std::string txn_id; - std::string related_event; + RelatedInfo related; QString body; QString filename; QString mime; @@ -123,7 +124,7 @@ public: void addEvents(const mtx::responses::Timeline &timeline); void addUserMessage(mtx::events::MessageType ty, const QString &body, - const QString &related_event); + const RelatedInfo &related); void addUserMessage(mtx::events::MessageType ty, const QString &msg); template diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 1ce3794f..86505481 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -23,6 +23,7 @@ #include "Cache.h" #include "Logging.h" +#include "Utils.h" #include "timeline/TimelineView.h" #include "timeline/TimelineViewManager.h" #include "timeline/widgets/AudioItem.h" @@ -79,7 +80,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) } void -TimelineViewManager::queueReplyMessage(const QString &reply, const QString &related_event) +TimelineViewManager::queueReplyMessage(const QString &reply, const RelatedInfo &related) { if (active_room_.isEmpty()) return; @@ -87,7 +88,7 @@ TimelineViewManager::queueReplyMessage(const QString &reply, const QString &rela auto room_id = active_room_; auto view = views_[room_id]; - view->addUserMessage(mtx::events::MessageType::Text, reply, related_event); + view->addUserMessage(mtx::events::MessageType::Text, reply, related); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 0ac6d67e..b52136d9 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -22,6 +22,8 @@ #include +#include "Utils.h" + class QFile; class RoomInfoListItem; @@ -63,7 +65,7 @@ public slots: void setHistoryView(const QString &room_id); void queueTextMessage(const QString &msg); - void queueReplyMessage(const QString &reply, const QString &related_event); + void queueReplyMessage(const QString &reply, const RelatedInfo &related); void queueEmoteMessage(const QString &msg); void queueImageMessage(const QString &roomid, const QString &filename, -- cgit 1.5.1 From cfd6c5703a7ca4a22fe1b1e78713b33f32f1a085 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Fri, 14 Jun 2019 20:45:37 -0400 Subject: Further UI Updates to Rich Replies --- src/TextInputWidget.cpp | 8 ++++---- src/TextInputWidget.h | 2 +- src/popups/ReplyPopup.cpp | 16 ++++++++++------ src/popups/ReplyPopup.h | 6 ++++-- src/timeline/TimelineItem.cpp | 2 ++ src/timeline/TimelineView.cpp | 10 ++++++++-- 6 files changed, 29 insertions(+), 15 deletions(-) (limited to 'src/popups/ReplyPopup.cpp') diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 8becf5ce..1ae26c2d 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -440,12 +440,13 @@ FilteredTextEdit::submit() } void -FilteredTextEdit::showReplyPopup(const QString &user, const QString &msg, const QString &event_id) +FilteredTextEdit::showReplyPopup(const RelatedInfo &related) { QPoint pos = viewport()->mapToGlobal(this->pos()); - replyPopup_.setReplyContent(user, msg, event_id); + replyPopup_.setReplyContent(related); replyPopup_.move(pos.x(), pos.y() - replyPopup_.height() - 10); + replyPopup_.setFixedWidth(this->parentWidget()->width()); replyPopup_.show(); } @@ -699,8 +700,7 @@ TextInputWidget::addReply(const RelatedInfo &related) // input_->setText(QString("> %1: %2\n\n").arg(username).arg(msg)); input_->setFocus(); - input_->showReplyPopup( - related.quoted_user, related.quoted_body, QString::fromStdString(related.related_event)); + input_->showReplyPopup(related); auto cursor = input_->textCursor(); cursor.movePosition(QTextCursor::End); input_->setTextCursor(cursor); diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index f68560e9..4a726364 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -57,7 +57,7 @@ public: void submit(); void setRelated(const RelatedInfo &related) { related_ = related; } - void showReplyPopup(const QString &user, const QString &msg, const QString &event_id); + void showReplyPopup(const RelatedInfo &related); signals: void heightChanged(int height); diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp index 3c7c482a..0ebf7c88 100644 --- a/src/popups/ReplyPopup.cpp +++ b/src/popups/ReplyPopup.cpp @@ -7,6 +7,7 @@ #include "../Utils.h" #include "../ui/Avatar.h" #include "../ui/DropShadow.h" +#include "../ui/TextLabel.h" #include "ReplyPopup.h" ReplyPopup::ReplyPopup(QWidget *parent) @@ -42,7 +43,7 @@ ReplyPopup::ReplyPopup(QWidget *parent) closeBtn_ = new FlatButton(this); closeBtn_->setToolTip(tr("Logout")); - closeBtn_->setCornerRadius(buttonSize_ / 2); + closeBtn_->setCornerRadius(buttonSize_ / 4); closeBtn_->setText("X"); QIcon icon; @@ -57,7 +58,8 @@ ReplyPopup::ReplyPopup(QWidget *parent) topLayout_->addLayout(buttonLayout_); mainLayout_->addLayout(topLayout_); - msgLabel_ = new QLabel(this); + msgLabel_ = new TextLabel(this); + msgLabel_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); mainLayout_->addWidget(msgLabel_); eventLabel_ = new QLabel(this); mainLayout_->addWidget(eventLabel_); @@ -66,14 +68,16 @@ ReplyPopup::ReplyPopup(QWidget *parent) } void -ReplyPopup::setReplyContent(const QString &user, const QString &msg, const QString &srcEvent) +ReplyPopup::setReplyContent(const RelatedInfo &related) { // Update the current widget with the new data. - userItem_->updateItem(user); + userItem_->updateItem(related.quoted_user); - msgLabel_->setText(msg); + msgLabel_->setText(utils::getFormattedQuoteBody(related, "") + .replace("", "") + .replace("", "")); - eventLabel_->setText(srcEvent); + // eventLabel_->setText(srcEvent); adjustSize(); } diff --git a/src/popups/ReplyPopup.h b/src/popups/ReplyPopup.h index 829f707b..b28cd0cf 100644 --- a/src/popups/ReplyPopup.h +++ b/src/popups/ReplyPopup.h @@ -9,7 +9,9 @@ #include "../AvatarProvider.h" #include "../Cache.h" #include "../ChatPage.h" +#include "../Utils.h" #include "../ui/FlatButton.h" +#include "../ui/TextLabel.h" #include "PopupItem.h" class ReplyPopup : public QWidget @@ -20,7 +22,7 @@ public: explicit ReplyPopup(QWidget *parent = nullptr); public slots: - void setReplyContent(const QString &user, const QString &msg, const QString &srcEvent); + void setReplyContent(const RelatedInfo &related); protected: void paintEvent(QPaintEvent *event) override; @@ -38,7 +40,7 @@ private: UserItem *userItem_; FlatButton *closeBtn_; - QLabel *msgLabel_; + TextLabel *msgLabel_; QLabel *eventLabel_; int buttonSize_; diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp index bf5b1b53..1094bde5 100644 --- a/src/timeline/TimelineItem.cpp +++ b/src/timeline/TimelineItem.cpp @@ -316,6 +316,8 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty, } formatted_body = utils::linkifyMessage(formatted_body); + formatted_body.replace("mx-reply", "div"); + nhlog::ui()->info("formatted_body: {}", formatted_body.toStdString()); generateTimestamp(timestamp); diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp index fc89fd38..18b73eb0 100644 --- a/src/timeline/TimelineView.cpp +++ b/src/timeline/TimelineView.cpp @@ -696,15 +696,21 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, { auto with_sender = (lastSender_ != local_user_) || isDateDifference(lastMsgTimestamp_); + QString full_body; + if (related.related_event.empty()) { + full_body = body; + } else { + full_body = utils::getFormattedQuoteBody(related, body); + } TimelineItem *view_item = - new TimelineItem(ty, local_user_, body, with_sender, room_id_, scroll_widget_); + new TimelineItem(ty, local_user_, full_body, with_sender, room_id_, scroll_widget_); PendingMessage message; message.ty = ty; message.txn_id = http::client()->generate_txn_id(); message.body = body; - message.widget = view_item; message.related = related; + message.widget = view_item; try { message.is_encrypted = cache::client()->isRoomEncrypted(room_id_.toStdString()); -- 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/ReplyPopup.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 7ccc120f6345bff8dad4abb349669973746aa8da Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 4 Feb 2020 04:58:43 +0100 Subject: modernize: use nullptr --- src/ChatPage.h | 2 +- src/LoginPage.cpp | 2 +- src/LoginPage.h | 2 +- src/MainWindow.h | 2 +- src/RegisterPage.cpp | 8 ++++---- src/RegisterPage.h | 2 +- src/RoomInfoListItem.h | 2 +- src/RoomList.h | 2 +- src/TextInputWidget.h | 2 +- src/TopRoomBar.h | 2 +- src/UserInfoWidget.h | 2 +- src/UserSettingsPage.h | 2 +- src/WelcomePage.h | 2 +- src/dialogs/ImageOverlay.cpp | 2 +- src/popups/ReplyPopup.cpp | 6 +++--- src/popups/SuggestionsPopup.h | 2 +- src/timeline/TimelineModel.h | 2 +- src/timeline/TimelineViewManager.h | 2 +- src/ui/Avatar.h | 2 +- src/ui/Badge.h | 6 +++--- src/ui/FlatButton.h | 6 +++--- src/ui/LoadingIndicator.h | 2 +- src/ui/RaisedButton.h | 4 ++-- src/ui/Ripple.cpp | 2 +- src/ui/Ripple.h | 4 ++-- src/ui/RippleOverlay.h | 2 +- src/ui/TextField.cpp | 8 ++++---- src/ui/TextField.h | 2 +- src/ui/Theme.h | 2 +- 29 files changed, 43 insertions(+), 43 deletions(-) (limited to 'src/popups/ReplyPopup.cpp') diff --git a/src/ChatPage.h b/src/ChatPage.h index 9bc5fb73..8e2e9192 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -65,7 +65,7 @@ class ChatPage : public QWidget Q_OBJECT public: - ChatPage(QSharedPointer userSettings, QWidget *parent = 0); + ChatPage(QSharedPointer userSettings, QWidget *parent = nullptr); // Initialize all the components of the UI. void bootstrap(QString userid, QString homeserver, QString token); diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp index c06137c9..c3919b93 100644 --- a/src/LoginPage.cpp +++ b/src/LoginPage.cpp @@ -111,7 +111,7 @@ LoginPage::LoginPage(QWidget *parent) form_layout_->addLayout(matrixidLayout_); form_layout_->addWidget(password_input_); - form_layout_->addWidget(deviceName_, Qt::AlignHCenter, 0); + form_layout_->addWidget(deviceName_, Qt::AlignHCenter, nullptr); form_layout_->addLayout(serverLayout_); button_layout_ = new QHBoxLayout(); diff --git a/src/LoginPage.h b/src/LoginPage.h index 99c249b1..4b84abfc 100644 --- a/src/LoginPage.h +++ b/src/LoginPage.h @@ -38,7 +38,7 @@ class LoginPage : public QWidget Q_OBJECT public: - LoginPage(QWidget *parent = 0); + LoginPage(QWidget *parent = nullptr); void reset(); diff --git a/src/MainWindow.h b/src/MainWindow.h index 1aadbf4d..59f29d50 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -62,7 +62,7 @@ class MainWindow : public QMainWindow Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0); + explicit MainWindow(QWidget *parent = nullptr); static MainWindow *instance() { return instance_; }; void saveCurrentWindowSize(); diff --git a/src/RegisterPage.cpp b/src/RegisterPage.cpp index 76721036..2688e9a9 100644 --- a/src/RegisterPage.cpp +++ b/src/RegisterPage.cpp @@ -90,10 +90,10 @@ RegisterPage::RegisterPage(QWidget *parent) server_input_ = new TextField(); server_input_->setLabel(tr("Home Server")); - form_layout_->addWidget(username_input_, Qt::AlignHCenter, 0); - form_layout_->addWidget(password_input_, Qt::AlignHCenter, 0); - form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter, 0); - form_layout_->addWidget(server_input_, Qt::AlignHCenter, 0); + form_layout_->addWidget(username_input_, Qt::AlignHCenter, nullptr); + form_layout_->addWidget(password_input_, Qt::AlignHCenter, nullptr); + form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter, nullptr); + form_layout_->addWidget(server_input_, Qt::AlignHCenter, nullptr); button_layout_ = new QHBoxLayout(); button_layout_->setSpacing(0); diff --git a/src/RegisterPage.h b/src/RegisterPage.h index b05cf150..96a5b3ca 100644 --- a/src/RegisterPage.h +++ b/src/RegisterPage.h @@ -30,7 +30,7 @@ class RegisterPage : public QWidget Q_OBJECT public: - RegisterPage(QWidget *parent = 0); + RegisterPage(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; diff --git a/src/RoomInfoListItem.h b/src/RoomInfoListItem.h index 16553c73..5cb9e83c 100644 --- a/src/RoomInfoListItem.h +++ b/src/RoomInfoListItem.h @@ -65,7 +65,7 @@ class RoomInfoListItem : public QWidget Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor) public: - RoomInfoListItem(QString room_id, const RoomInfo &info, QWidget *parent = 0); + RoomInfoListItem(QString room_id, const RoomInfo &info, QWidget *parent = nullptr); void updateUnreadMessageCount(int count, int highlightedCount); void clearUnreadMessageCount() { updateUnreadMessageCount(0, 0); }; diff --git a/src/RoomList.h b/src/RoomList.h index 51a24043..fef552c6 100644 --- a/src/RoomList.h +++ b/src/RoomList.h @@ -35,7 +35,7 @@ class RoomList : public QWidget Q_OBJECT public: - explicit RoomList(QWidget *parent = 0); + explicit RoomList(QWidget *parent = nullptr); void initialize(const QMap &info); void sync(const std::map &info); diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 3674ee0d..ac79be8b 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -142,7 +142,7 @@ class TextInputWidget : public QWidget Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor) public: - TextInputWidget(QWidget *parent = 0); + TextInputWidget(QWidget *parent = nullptr); void stopTyping(); diff --git a/src/TopRoomBar.h b/src/TopRoomBar.h index 5ab25f39..63ce847e 100644 --- a/src/TopRoomBar.h +++ b/src/TopRoomBar.h @@ -40,7 +40,7 @@ class TopRoomBar : public QWidget Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor) public: - TopRoomBar(QWidget *parent = 0); + TopRoomBar(QWidget *parent = nullptr); void updateRoomAvatar(const QString &avatar_image); void updateRoomAvatar(const QIcon &icon); diff --git a/src/UserInfoWidget.h b/src/UserInfoWidget.h index 263dd0c2..e1a925a4 100644 --- a/src/UserInfoWidget.h +++ b/src/UserInfoWidget.h @@ -31,7 +31,7 @@ class UserInfoWidget : public QWidget Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor) public: - UserInfoWidget(QWidget *parent = 0); + UserInfoWidget(QWidget *parent = nullptr); void setDisplayName(const QString &name); void setUserId(const QString &userid); diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h index 8658e80e..299905ab 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h @@ -147,7 +147,7 @@ class UserSettingsPage : public QWidget Q_OBJECT public: - UserSettingsPage(QSharedPointer settings, QWidget *parent = 0); + UserSettingsPage(QSharedPointer settings, QWidget *parent = nullptr); protected: void showEvent(QShowEvent *event) override; diff --git a/src/WelcomePage.h b/src/WelcomePage.h index 480dc702..ae660215 100644 --- a/src/WelcomePage.h +++ b/src/WelcomePage.h @@ -7,7 +7,7 @@ class WelcomePage : public QWidget Q_OBJECT public: - explicit WelcomePage(QWidget *parent = 0); + explicit WelcomePage(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *) override; diff --git a/src/dialogs/ImageOverlay.cpp b/src/dialogs/ImageOverlay.cpp index cbdd351c..a98c39c2 100644 --- a/src/dialogs/ImageOverlay.cpp +++ b/src/dialogs/ImageOverlay.cpp @@ -32,7 +32,7 @@ ImageOverlay::ImageOverlay(QPixmap image, QWidget *parent) , originalImage_{image} { setMouseTracking(true); - setParent(0); + setParent(nullptr); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp index 42a5a6d3..5058c039 100644 --- a/src/popups/ReplyPopup.cpp +++ b/src/popups/ReplyPopup.cpp @@ -13,9 +13,9 @@ ReplyPopup::ReplyPopup(QWidget *parent) : QWidget(parent) - , userItem_{0} - , msgLabel_{0} - , eventLabel_{0} + , userItem_{nullptr} + , msgLabel_{nullptr} + , eventLabel_{nullptr} { setAttribute(Qt::WA_ShowWithoutActivating, true); setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); diff --git a/src/popups/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h index dcd054f8..f84870e7 100644 --- a/src/popups/SuggestionsPopup.h +++ b/src/popups/SuggestionsPopup.h @@ -59,7 +59,7 @@ private: size_t posToRemove = layout_->count() - 1; QLayoutItem *item; - while (startingPos <= posToRemove && (item = layout_->takeAt(posToRemove)) != 0) { + while (startingPos <= posToRemove && (item = layout_->takeAt(posToRemove)) != nullptr) { delete item->widget(); delete item; diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index fb32f0fb..2d0e9627 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -126,7 +126,7 @@ class TimelineModel : public QAbstractListModel typingUsersChanged) public: - explicit TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent = 0); + explicit TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent = nullptr); enum Roles { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 5880a382..8552d6b3 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -29,7 +29,7 @@ class TimelineViewManager : public QObject replyingEventChanged) public: - TimelineViewManager(QSharedPointer userSettings, QWidget *parent = 0); + TimelineViewManager(QSharedPointer userSettings, QWidget *parent = nullptr); QWidget *getWidget() const { return container; } void sync(const mtx::responses::Rooms &rooms); diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h index a643c8c4..d6d0b5c7 100644 --- a/src/ui/Avatar.h +++ b/src/ui/Avatar.h @@ -15,7 +15,7 @@ class Avatar : public QWidget Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor) public: - explicit Avatar(QWidget *parent = 0, int size = ui::AvatarSize); + explicit Avatar(QWidget *parent = nullptr, int size = ui::AvatarSize); void setBackgroundColor(const QColor &color); void setIcon(const QIcon &icon); diff --git a/src/ui/Badge.h b/src/ui/Badge.h index fd73ad30..748b56fd 100644 --- a/src/ui/Badge.h +++ b/src/ui/Badge.h @@ -16,9 +16,9 @@ class Badge : public OverlayWidget Q_PROPERTY(QPointF relativePosition WRITE setRelativePosition READ relativePosition) public: - explicit Badge(QWidget *parent = 0); - explicit Badge(const QIcon &icon, QWidget *parent = 0); - explicit Badge(const QString &text, QWidget *parent = 0); + explicit Badge(QWidget *parent = nullptr); + explicit Badge(const QIcon &icon, QWidget *parent = nullptr); + explicit Badge(const QString &text, QWidget *parent = nullptr); void setBackgroundColor(const QColor &color); void setTextColor(const QColor &color); diff --git a/src/ui/FlatButton.h b/src/ui/FlatButton.h index d29903c2..764df6e9 100644 --- a/src/ui/FlatButton.h +++ b/src/ui/FlatButton.h @@ -91,14 +91,14 @@ class FlatButton : public QPushButton Q_PROPERTY(qreal fontSize WRITE setFontSize READ fontSize) public: - explicit FlatButton(QWidget *parent = 0, + explicit FlatButton(QWidget *parent = nullptr, ui::ButtonPreset preset = ui::ButtonPreset::FlatPreset); explicit FlatButton(const QString &text, - QWidget *parent = 0, + QWidget *parent = nullptr, ui::ButtonPreset preset = ui::ButtonPreset::FlatPreset); FlatButton(const QString &text, ui::Role role, - QWidget *parent = 0, + QWidget *parent = nullptr, ui::ButtonPreset preset = ui::ButtonPreset::FlatPreset); ~FlatButton(); diff --git a/src/ui/LoadingIndicator.h b/src/ui/LoadingIndicator.h index 1585098e..e72a8c48 100644 --- a/src/ui/LoadingIndicator.h +++ b/src/ui/LoadingIndicator.h @@ -12,7 +12,7 @@ class LoadingIndicator : public QWidget Q_PROPERTY(QColor color READ color WRITE setColor) public: - LoadingIndicator(QWidget *parent = 0); + LoadingIndicator(QWidget *parent = nullptr); void paintEvent(QPaintEvent *e); diff --git a/src/ui/RaisedButton.h b/src/ui/RaisedButton.h index edd5ee4a..74543262 100644 --- a/src/ui/RaisedButton.h +++ b/src/ui/RaisedButton.h @@ -11,8 +11,8 @@ class RaisedButton : public FlatButton Q_OBJECT public: - explicit RaisedButton(QWidget *parent = 0); - explicit RaisedButton(const QString &text, QWidget *parent = 0); + explicit RaisedButton(QWidget *parent = nullptr); + explicit RaisedButton(const QString &text, QWidget *parent = nullptr); ~RaisedButton(); protected: diff --git a/src/ui/Ripple.cpp b/src/ui/Ripple.cpp index e22c4a62..ef8a62dd 100644 --- a/src/ui/Ripple.cpp +++ b/src/ui/Ripple.cpp @@ -3,7 +3,7 @@ Ripple::Ripple(const QPoint ¢er, QObject *parent) : QParallelAnimationGroup(parent) - , overlay_(0) + , overlay_(nullptr) , radius_anim_(animate("radius")) , opacity_anim_(animate("opacity")) , radius_(0) diff --git a/src/ui/Ripple.h b/src/ui/Ripple.h index 9184f061..3701fb6c 100644 --- a/src/ui/Ripple.h +++ b/src/ui/Ripple.h @@ -16,8 +16,8 @@ class Ripple : public QParallelAnimationGroup Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity) public: - explicit Ripple(const QPoint ¢er, QObject *parent = 0); - Ripple(const QPoint ¢er, RippleOverlay *overlay, QObject *parent = 0); + explicit Ripple(const QPoint ¢er, QObject *parent = nullptr); + Ripple(const QPoint ¢er, RippleOverlay *overlay, QObject *parent = nullptr); inline void setOverlay(RippleOverlay *overlay); diff --git a/src/ui/RippleOverlay.h b/src/ui/RippleOverlay.h index 9ef91fbf..5d12aff7 100644 --- a/src/ui/RippleOverlay.h +++ b/src/ui/RippleOverlay.h @@ -11,7 +11,7 @@ class RippleOverlay : public OverlayWidget Q_OBJECT public: - explicit RippleOverlay(QWidget *parent = 0); + explicit RippleOverlay(QWidget *parent = nullptr); void addRipple(Ripple *ripple); void addRipple(const QPoint &position, qreal radius = 300); diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp index 0ae2516d..6c1552a8 100644 --- a/src/ui/TextField.cpp +++ b/src/ui/TextField.cpp @@ -16,7 +16,7 @@ TextField::TextField(QWidget *parent) QPalette pal; state_machine_ = new TextFieldStateMachine(this); - label_ = 0; + label_ = nullptr; label_font_size_ = 15; show_label_ = false; background_color_ = pal.color(QPalette::Window); @@ -230,9 +230,9 @@ TextFieldStateMachine::TextFieldStateMachine(TextField *parent) normal_state_ = new QState; focused_state_ = new QState; - label_ = 0; - offset_anim_ = 0; - color_anim_ = 0; + label_ = nullptr; + offset_anim_ = nullptr; + color_anim_ = nullptr; progress_ = 0.0; addState(normal_state_); diff --git a/src/ui/TextField.h b/src/ui/TextField.h index 1675a2e0..100fed31 100644 --- a/src/ui/TextField.h +++ b/src/ui/TextField.h @@ -22,7 +22,7 @@ class TextField : public QLineEdit Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor) public: - explicit TextField(QWidget *parent = 0); + explicit TextField(QWidget *parent = nullptr); void setInkColor(const QColor &color); void setBackgroundColor(const QColor &color); diff --git a/src/ui/Theme.h b/src/ui/Theme.h index d1d7e2a6..ecff02b5 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h @@ -78,7 +78,7 @@ class Theme : public QObject { Q_OBJECT public: - explicit Theme(QObject *parent = 0); + explicit Theme(QObject *parent = nullptr); QColor getColor(const QString &key) const; -- cgit 1.5.1