summary refs log tree commit diff
path: root/src/timeline
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2018-07-17 23:50:18 +0300
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2018-07-17 23:50:18 +0300
commite4dedbcaba544b8cd9b7fea20ece4dad262b2c34 (patch)
treed80efcc5a10d0d1747d1fd7113010113f0848103 /src/timeline
parentBump version to v0.5.1 (diff)
downloadnheko-e4dedbcaba544b8cd9b7fea20ece4dad262b2c34.tar.xz
Mark own read messages with a double checkmark (#377)
Diffstat (limited to 'src/timeline')
-rw-r--r--src/timeline/TimelineItem.cpp36
-rw-r--r--src/timeline/TimelineItem.h7
-rw-r--r--src/timeline/TimelineView.cpp51
-rw-r--r--src/timeline/TimelineView.h4
-rw-r--r--src/timeline/TimelineViewManager.cpp11
-rw-r--r--src/timeline/TimelineViewManager.h1
6 files changed, 109 insertions, 1 deletions
diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp

index 88ab1963..696db8de 100644 --- a/src/timeline/TimelineItem.cpp +++ b/src/timeline/TimelineItem.cpp
@@ -42,6 +42,7 @@ StatusIndicator::StatusIndicator(QWidget *parent) lockIcon_.addFile(":/icons/icons/ui/lock.png"); clockIcon_.addFile(":/icons/icons/ui/clock.png"); checkmarkIcon_.addFile(":/icons/icons/ui/checkmark.png"); + doubleCheckmarkIcon_.addFile(":/icons/icons/ui/double-tick-indicator.png"); } void @@ -79,6 +80,10 @@ StatusIndicator::paintEvent(QPaintEvent *) paintIcon(p, checkmarkIcon_); break; } + case StatusIndicatorState::Read: { + paintIcon(p, doubleCheckmarkIcon_); + break; + } case StatusIndicatorState::Empty: break; } @@ -302,6 +307,8 @@ TimelineItem::TimelineItem(ImageItem *image, setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Image>, ImageItem>( image, event, with_sender); + markOwnMessagesAsReceived(event.sender); + addSaveImageAction(image); } @@ -315,6 +322,8 @@ TimelineItem::TimelineItem(StickerItem *image, { setupWidgetLayout<mtx::events::Sticker, StickerItem>(image, event, with_sender); + markOwnMessagesAsReceived(event.sender); + addSaveImageAction(image); } @@ -328,6 +337,8 @@ TimelineItem::TimelineItem(FileItem *file, { setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::File>, FileItem>( file, event, with_sender); + + markOwnMessagesAsReceived(event.sender); } TimelineItem::TimelineItem(AudioItem *audio, @@ -340,6 +351,8 @@ TimelineItem::TimelineItem(AudioItem *audio, { setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Audio>, AudioItem>( audio, event, with_sender); + + markOwnMessagesAsReceived(event.sender); } TimelineItem::TimelineItem(VideoItem *video, @@ -352,6 +365,8 @@ TimelineItem::TimelineItem(VideoItem *video, { setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Video>, VideoItem>( video, event, with_sender); + + markOwnMessagesAsReceived(event.sender); } /* @@ -367,6 +382,8 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice init(); addReplyAction(); + markOwnMessagesAsReceived(event.sender); + event_id_ = QString::fromStdString(event.event_id); const auto sender = QString::fromStdString(event.sender); const auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts); @@ -413,6 +430,8 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote> init(); addReplyAction(); + markOwnMessagesAsReceived(event.sender); + event_id_ = QString::fromStdString(event.event_id); const auto sender = QString::fromStdString(event.sender); @@ -455,6 +474,8 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text> init(); addReplyAction(); + markOwnMessagesAsReceived(event.sender); + event_id_ = QString::fromStdString(event.event_id); const auto sender = QString::fromStdString(event.sender); @@ -496,6 +517,21 @@ TimelineItem::markSent() } void +TimelineItem::markOwnMessagesAsReceived(const std::string &sender) +{ + QSettings settings; + if (sender == settings.value("auth/user_id").toString().toStdString()) + statusIndicator_->setState(StatusIndicatorState::Received); +} + +void +TimelineItem::markRead() +{ + if (statusIndicator_->state() != StatusIndicatorState::Encrypted) + statusIndicator_->setState(StatusIndicatorState::Read); +} + +void TimelineItem::markReceived(bool isEncrypted) { isReceived_ = true; diff --git a/src/timeline/TimelineItem.h b/src/timeline/TimelineItem.h
index d3cab0a0..874c00df 100644 --- a/src/timeline/TimelineItem.h +++ b/src/timeline/TimelineItem.h
@@ -50,6 +50,8 @@ enum class StatusIndicatorState Encrypted, //! The plaintext message was received by the server. Received, + //! At least one of the participants has read the message. + Read, //! The client sent the message. Not yet received. Sent, //! When the message is loaded from cache or backfill. @@ -66,6 +68,7 @@ class StatusIndicator : public QWidget public: explicit StatusIndicator(QWidget *parent); void setState(StatusIndicatorState state); + StatusIndicatorState state() const { return state_; } protected: void paintEvent(QPaintEvent *event) override; @@ -76,6 +79,7 @@ private: QIcon lockIcon_; QIcon clockIcon_; QIcon checkmarkIcon_; + QIcon doubleCheckmarkIcon_; QColor iconColor_ = QColor("#999"); @@ -234,6 +238,7 @@ public: QString eventId() const { return event_id_; } void setEventId(const QString &event_id) { event_id_ = event_id; } void markReceived(bool isEncrypted); + void markRead(); void markSent(); bool isReceived() { return isReceived_; }; void setRoomId(QString room_id) { room_id_ = room_id; } @@ -252,6 +257,8 @@ protected: void contextMenuEvent(QContextMenuEvent *event) override; private: + //! If we are the sender of the message the event wil be marked as received by the server. + void markOwnMessagesAsReceived(const std::string &sender); void init(); //! Add a context menu option to save the image of the timeline item. void addSaveImageAction(ImageItem *image); diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp
index a8c04807..074ba498 100644 --- a/src/timeline/TimelineView.cpp +++ b/src/timeline/TimelineView.cpp
@@ -18,6 +18,7 @@ #include <QApplication> #include <QFileInfo> #include <QTimer> +#include <QtConcurrent> #include "Cache.h" #include "ChatPage.h" @@ -353,6 +354,27 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent<mtx::events: } void +TimelineView::displayReadReceipts(std::vector<TimelineEvent> events) +{ + QtConcurrent::run( + [events = std::move(events), room_id = room_id_, local_user = local_user_, this]() { + std::vector<QString> event_ids; + + for (const auto &e : events) { + if (utils::event_sender(e) == local_user) + event_ids.emplace_back( + QString::fromStdString(utils::event_id(e))); + } + + auto readEvents = + cache::client()->filterReadEvents(room_id, event_ids, local_user.toStdString()); + + if (!readEvents.empty()) + emit markReadEvents(readEvents); + }); +} + +void TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events) { int counter = 0; @@ -373,6 +395,8 @@ TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events) lastMessageDirection_ = TimelineDirection::Bottom; + displayReadReceipts(events); + QApplication::processEvents(); } @@ -407,6 +431,8 @@ TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events) QApplication::processEvents(); + displayReadReceipts(events); + // If this batch is the first being rendered (i.e the first and the last // events originate from this batch), set the last sender. if (lastSender_.isEmpty() && !items.empty()) { @@ -499,6 +525,23 @@ TimelineView::init() connect(this, &TimelineView::messageFailed, this, &TimelineView::handleFailedMessage); connect(this, &TimelineView::messageSent, this, &TimelineView::updatePendingMessage); + connect( + this, &TimelineView::markReadEvents, this, [this](const std::vector<QString> &event_ids) { + for (const auto &event : event_ids) { + if (eventIds_.contains(event)) { + auto widget = eventIds_[event]; + if (!widget) + return; + + auto item = qobject_cast<TimelineItem *>(widget); + if (!item) + return; + + item->markRead(); + } + } + }); + connect(scroll_area_->verticalScrollBar(), SIGNAL(valueChanged(int)), this, @@ -615,6 +658,7 @@ TimelineView::updatePendingMessage(const std::string &txn_id, const QString &eve // we've already marked the widget as received. if (!msg.widget->isReceived()) { msg.widget->markReceived(msg.is_encrypted); + cache::client()->addPendingReceipt(room_id_, event_id); pending_sent_msgs_.append(msg); } } else { @@ -826,9 +870,14 @@ TimelineView::removePendingMessage(const std::string &txn_id) } for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) { if (it->txn_id == txn_id) { - if (it->widget) + if (it->widget) { it->widget->markReceived(it->is_encrypted); + // TODO: update when a solution for encrypted messages is available. + if (!it->is_encrypted) + cache::client()->addPendingReceipt(room_id_, it->event_id); + } + nhlog::ui()->info("[{}] received sync before message response", txn_id); return; } diff --git a/src/timeline/TimelineView.h b/src/timeline/TimelineView.h
index 7b269063..5c42415a 100644 --- a/src/timeline/TimelineView.h +++ b/src/timeline/TimelineView.h
@@ -156,6 +156,7 @@ signals: void messagesRetrieved(const mtx::responses::Messages &res); void messageFailed(const std::string &txn_id); void messageSent(const std::string &txn_id, const QString &event_id); + void markReadEvents(const std::vector<QString> &event_ids); protected: void paintEvent(QPaintEvent *event) override; @@ -165,6 +166,9 @@ protected: private: using TimelineEvent = mtx::events::collections::TimelineEvents; + //! Mark our own widgets as read if they have more than one receipt. + void displayReadReceipts(std::vector<TimelineEvent> events); + QWidget *relativeWidget(QWidget *item, int dt) const; DecryptionResult parseEncryptedEvent( diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 1decab35..1bbb4def 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -37,6 +37,17 @@ TimelineViewManager::TimelineViewManager(QWidget *parent) } void +TimelineViewManager::updateReadReceipts(const QString &room_id, + const std::vector<QString> &event_ids) +{ + if (timelineViewExists(room_id)) { + auto view = views_[room_id]; + if (view) + emit view->markReadEvents(event_ids); + } +} + +void TimelineViewManager::removeTimelineEvent(const QString &room_id, const QString &event_id) { auto view = views_[room_id]; diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index f3c099c1..d23345d3 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h
@@ -57,6 +57,7 @@ signals: void updateRoomsLastMessage(const QString &user, const DescInfo &info); public slots: + void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids); void removeTimelineEvent(const QString &room_id, const QString &event_id); void initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs);