From eaf05748ff1fc2b1ced8fdb329661ff20d6b7b85 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Wed, 3 Jan 2018 18:05:49 +0200 Subject: Initial support for read receipts --- include/AvatarProvider.h | 4 +-- include/Cache.h | 55 +++++++++++++++++++++++++++++++++++++++++ include/ChatPage.h | 12 +++++++++ include/Config.h | 4 +++ include/MainWindow.h | 2 +- include/dialogs/ReadReceipts.h | 50 +++++++++++++++++++++++++++++++++++++ include/timeline/TimelineItem.h | 8 ++++-- include/ui/OverlayModal.h | 2 ++ 8 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 include/dialogs/ReadReceipts.h (limited to 'include') diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h index 906f2593..44bf1ad2 100644 --- a/include/AvatarProvider.h +++ b/include/AvatarProvider.h @@ -36,7 +36,7 @@ class AvatarProvider : public QObject public: static void init(QSharedPointer client); - static void resolve(const QString &userId, TimelineItem *item); + static void resolve(const QString &userId, std::function callback); static void setAvatarUrl(const QString &userId, const QUrl &url); static void clear(); @@ -48,5 +48,5 @@ private: using UserID = QString; static QMap avatars_; - static QMap> toBeResolved_; + static QMap>> toBeResolved_; }; diff --git a/include/Cache.h b/include/Cache.h index 1f6c59f0..ae58e418 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -18,11 +18,52 @@ #pragma once #include +#include #include #include class RoomState; +//! Used to uniquely identify a list of read receipts. +struct ReadReceiptKey +{ + std::string event_id; + std::string room_id; +}; + +inline void +to_json(json &j, const ReadReceiptKey &key) +{ + j = json{{"event_id", key.event_id}, {"room_id", key.room_id}}; +} + +inline void +from_json(const json &j, ReadReceiptKey &key) +{ + key.event_id = j.at("event_id").get(); + key.room_id = j.at("room_id").get(); +} + +//! Decribes a read receipt stored in cache. +struct ReadReceiptValue +{ + std::string user_id; + uint64_t ts; +}; + +inline void +to_json(json &j, const ReadReceiptValue &value) +{ + j = json{{"user_id", value.user_id}, {"ts", value.ts}}; +} + +inline void +from_json(const json &j, ReadReceiptValue &value) +{ + value.user_id = j.at("user_id").get(); + value.ts = j.at("ts").get(); +} + class Cache { public: @@ -48,6 +89,19 @@ public: bool isFormatValid(); void setCurrentFormat(); + //! 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(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); + QByteArray image(const QString &url) const; void saveImage(const QString &url, const QByteArray &data); @@ -60,6 +114,7 @@ private: lmdb::dbi roomDb_; lmdb::dbi invitesDb_; lmdb::dbi imagesDb_; + lmdb::dbi readReceiptsDb_; bool isMounted_; diff --git a/include/ChatPage.h b/include/ChatPage.h index 24fc6a25..584424c0 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -42,6 +42,10 @@ class TypingDisplay; class UserInfoWidget; class UserSettings; +namespace dialogs { +class ReadReceipts; +} + constexpr int CONSENSUS_TIMEOUT = 1000; constexpr int SHOW_CONTENT_TIMEOUT = 3000; constexpr int TYPING_REFRESH_TIMEOUT = 10000; @@ -59,6 +63,9 @@ public: // Initialize all the components of the UI. void bootstrap(QString userid, QString homeserver, QString token); void showQuickSwitcher(); + void showReadReceipts(const QString &event_id); + + static ChatPage *instance() { return instance_; } signals: void contentLoaded(); @@ -84,6 +91,8 @@ private slots: void removeInvite(const QString &room_id); private: + static ChatPage *instance_; + using UserID = QString; using RoomStates = QMap; using Membership = mtx::events::StateEvent; @@ -150,6 +159,9 @@ private: QSharedPointer quickSwitcher_; QSharedPointer quickSwitcherModal_; + QSharedPointer receiptsDialog_; + QSharedPointer receiptsModal_; + // Matrix Client API provider. QSharedPointer client_; diff --git a/include/Config.h b/include/Config.h index 7d35094e..5492e5fb 100644 --- a/include/Config.h +++ b/include/Config.h @@ -15,6 +15,10 @@ static constexpr int emojiSize = 14; static constexpr int headerFontSize = 21; static constexpr int typingNotificationFontSize = 11; +namespace receipts { +static constexpr int font = 12; +} + namespace dialogs { static constexpr int labelSize = 15; } diff --git a/include/MainWindow.h b/include/MainWindow.h index 2d047b51..d7c5e41d 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -42,7 +42,7 @@ public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); - static MainWindow *instance(); + static MainWindow *instance() { return instance_; }; void saveCurrentWindowSize(); protected: diff --git a/include/dialogs/ReadReceipts.h b/include/dialogs/ReadReceipts.h new file mode 100644 index 00000000..42a9e1b7 --- /dev/null +++ b/include/dialogs/ReadReceipts.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Avatar; + +namespace dialogs { + +class ReceiptItem : public QWidget +{ + Q_OBJECT + +public: + ReceiptItem(QWidget *parent, const QString &user_id, uint64_t timestamp); + +private: + QString dateFormat(const QDateTime &then) const; + + QHBoxLayout *topLayout_; + QVBoxLayout *textLayout_; + + Avatar *avatar_; + + QLabel *userName_; + QLabel *timestamp_; +}; + +class ReadReceipts : public QFrame +{ + Q_OBJECT +public: + explicit ReadReceipts(QWidget *parent = nullptr); + +public slots: + void addUsers(const std::multimap &users); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QLabel *topLabel_; + + QListWidget *userList_; +}; +} // dialogs diff --git a/include/timeline/TimelineItem.h b/include/timeline/TimelineItem.h index f1498d1b..78fb95c9 100644 --- a/include/timeline/TimelineItem.h +++ b/include/timeline/TimelineItem.h @@ -87,6 +87,7 @@ public: protected: void paintEvent(QPaintEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override; private: void init(); @@ -116,6 +117,9 @@ private: DescInfo descriptionMsg_; + QMenu *receiptsMenu_; + QAction *showReadReceipts_; + QHBoxLayout *topLayout_; QVBoxLayout *sideLayout_; // Avatar or Timestamp QVBoxLayout *mainLayout_; // Header & Message body @@ -156,7 +160,7 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget, setupAvatarLayout(displayName); mainLayout_->addLayout(headerLayout_); - AvatarProvider::resolve(userid, this); + AvatarProvider::resolve(userid, [=](const QImage &img) { setUserAvatar(img); }); } else { setupSimpleLayout(); } @@ -199,7 +203,7 @@ TimelineItem::setupWidgetLayout(Widget *widget, mainLayout_->addLayout(headerLayout_); - AvatarProvider::resolve(sender, this); + AvatarProvider::resolve(sender, [=](const QImage &img) { setUserAvatar(img); }); } else { setupSimpleLayout(); } diff --git a/include/ui/OverlayModal.h b/include/ui/OverlayModal.h index 167ba71a..5f6b6eee 100644 --- a/include/ui/OverlayModal.h +++ b/include/ui/OverlayModal.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -37,6 +38,7 @@ public: protected: void paintEvent(QPaintEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; private: int duration_; -- cgit 1.5.1