Initial support for read receipts

This commit is contained in:
Konstantinos Sideris 2018-01-03 18:05:49 +02:00
parent 44ee1b549d
commit eaf05748ff
19 changed files with 434 additions and 33 deletions

View file

@ -36,7 +36,7 @@ class AvatarProvider : public QObject
public:
static void init(QSharedPointer<MatrixClient> client);
static void resolve(const QString &userId, TimelineItem *item);
static void resolve(const QString &userId, std::function<void(QImage)> callback);
static void setAvatarUrl(const QString &userId, const QUrl &url);
static void clear();
@ -48,5 +48,5 @@ private:
using UserID = QString;
static QMap<UserID, AvatarData> avatars_;
static QMap<UserID, QList<TimelineItem *>> toBeResolved_;
static QMap<UserID, QList<std::function<void(QImage)>>> toBeResolved_;
};

View file

@ -18,11 +18,52 @@
#pragma once
#include <QDir>
#include <json.hpp>
#include <lmdb++.h>
#include <mtx/responses.hpp>
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<std::string>();
key.room_id = j.at("room_id").get<std::string>();
}
//! 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<std::string>();
value.ts = j.at("ts").get<uint64_t>();
}
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<std::string, std::map<std::string, uint64_t>>;
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<uint64_t, std::string>;
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_;

View file

@ -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<UserID, RoomState>;
using Membership = mtx::events::StateEvent<mtx::events::state::Member>;
@ -150,6 +159,9 @@ private:
QSharedPointer<QuickSwitcher> quickSwitcher_;
QSharedPointer<OverlayModal> quickSwitcherModal_;
QSharedPointer<dialogs::ReadReceipts> receiptsDialog_;
QSharedPointer<OverlayModal> receiptsModal_;
// Matrix Client API provider.
QSharedPointer<MatrixClient> client_;

View file

@ -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;
}

View file

@ -42,7 +42,7 @@ public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static MainWindow *instance();
static MainWindow *instance() { return instance_; };
void saveCurrentWindowSize();
protected:

View file

@ -0,0 +1,50 @@
#pragma once
#include <QDateTime>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QListWidget>
#include <QVBoxLayout>
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<uint64_t, std::string> &users);
protected:
void paintEvent(QPaintEvent *event) override;
private:
QLabel *topLabel_;
QListWidget *userList_;
};
} // dialogs

View file

@ -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();
}

View file

@ -18,6 +18,7 @@
#pragma once
#include <QGraphicsOpacityEffect>
#include <QKeyEvent>
#include <QPaintEvent>
#include <QPropertyAnimation>
@ -37,6 +38,7 @@ public:
protected:
void paintEvent(QPaintEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private:
int duration_;