summary refs log tree commit diff
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2018-01-05 00:27:32 +0200
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2018-01-05 00:27:32 +0200
commit983aea7c76da65214463e725576e8b147fd02cdf (patch)
treeed652a099c5a7cce7d9cde091c14b72295ac879d
parentRemove an extra colon from the receipt time (diff)
downloadnheko-983aea7c76da65214463e725576e8b147fd02cdf.tar.xz
Create widgets on demand for messages added to the end of the timeline
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/Utils.h20
-rw-r--r--include/timeline/TimelineItem.h11
-rw-r--r--include/timeline/TimelineView.h25
-rw-r--r--src/Utils.cc121
-rw-r--r--src/timeline/TimelineItem.cc28
-rw-r--r--src/timeline/TimelineView.cc109
7 files changed, 260 insertions, 55 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c669262b..b0a5c610 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -207,6 +207,7 @@ set(SRC_FILES
     src/TopRoomBar.cc
     src/TrayIcon.cc
     src/TypingDisplay.cc
+    src/Utils.cc
     src/UserInfoWidget.cc
     src/UserSettingsPage.cc
     src/WelcomePage.cc
diff --git a/include/Utils.h b/include/Utils.h
new file mode 100644
index 00000000..183ebbbe
--- /dev/null
+++ b/include/Utils.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "RoomInfoListItem.h"
+
+#include <QDateTime>
+#include <mtx/events/collections.hpp>
+
+namespace utils {
+
+using TimelineEvent = mtx::events::collections::TimelineEvents;
+
+//! Human friendly timestamp representation.
+QString
+descriptiveTime(const QDateTime &then);
+
+//! Generate a message description from the event to be displayed
+//! in the RoomList.
+DescInfo
+getMessageDescription(const TimelineEvent &event, const QString &localUser);
+}
diff --git a/include/timeline/TimelineItem.h b/include/timeline/TimelineItem.h
index 78fb95c9..d7e7911a 100644
--- a/include/timeline/TimelineItem.h
+++ b/include/timeline/TimelineItem.h
@@ -28,6 +28,7 @@
 #include "AvatarProvider.h"
 #include "RoomInfoListItem.h"
 #include "TimelineViewManager.h"
+#include "Utils.h"
 
 class ImageItem;
 class AudioItem;
@@ -107,7 +108,6 @@ private:
         void generateBody(const QString &body);
         void generateBody(const QString &userid, const QString &body);
         void generateTimestamp(const QDateTime &time);
-        QString descriptiveTime(const QDateTime &then);
 
         void setupAvatarLayout(const QString &userName);
         void setupSimpleLayout();
@@ -145,8 +145,11 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget,
         auto displayName = TimelineViewManager::displayName(userid);
         auto timestamp   = QDateTime::currentDateTime();
 
-        descriptionMsg_ = {
-          "You", userid, QString(" %1").arg(msgDescription), descriptiveTime(timestamp), timestamp};
+        descriptionMsg_ = {"You",
+                           userid,
+                           QString(" %1").arg(msgDescription),
+                           utils::descriptiveTime(timestamp),
+                           timestamp};
 
         generateTimestamp(timestamp);
 
@@ -187,7 +190,7 @@ TimelineItem::setupWidgetLayout(Widget *widget,
         descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
                            sender,
                            msgDescription,
-                           descriptiveTime(timestamp),
+                           utils::descriptiveTime(timestamp),
                            timestamp};
 
         generateTimestamp(timestamp);
diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h
index caf4634a..cde64148 100644
--- a/include/timeline/TimelineView.h
+++ b/include/timeline/TimelineView.h
@@ -23,7 +23,6 @@
 #include <QList>
 #include <QQueue>
 #include <QScrollArea>
-#include <QSettings>
 #include <QStyle>
 #include <QStyleOption>
 
@@ -119,10 +118,13 @@ protected:
         bool event(QEvent *event) override;
 
 private:
+        using TimelineEvent = mtx::events::collections::TimelineEvents;
+
         void init();
         void addTimelineItem(TimelineItem *item, TimelineDirection direction);
         void updateLastSender(const QString &user_id, TimelineDirection direction);
         void notifyForLastEvent();
+        void notifyForLastEvent(const TimelineEvent &event);
         void readLastEvent() const;
         bool isScrollbarActivated() { return scroll_area_->verticalScrollBar()->value() != 0; }
         QString getLastEventId() const;
@@ -193,6 +195,18 @@ private:
 
         TimelineDirection lastMessageDirection_;
 
+        //! Messages received by sync not added to the timeline.
+        std::vector<TimelineEvent> bottomMessages_;
+
+        //! Render the given timeline events to the bottom of the timeline.
+        void renderBottomEvents(const std::vector<TimelineEvent> &events);
+
+        //! Decide if the given timeline event can be rendered.
+        inline bool isViewable(const TimelineEvent &event) const;
+
+        //! Decide if the given event should trigger a notification.
+        inline bool isNotifiable(const TimelineEvent &event) const;
+
         // The events currently rendered. Used for duplicate detection.
         QMap<QString, bool> eventIds_;
         QQueue<PendingMessage> pending_msgs_;
@@ -204,20 +218,19 @@ template<class Widget, mtx::events::MessageType MsgType>
 void
 TimelineView::addUserMessage(const QString &url, const QString &filename)
 {
-        QSettings settings;
-        auto user_id     = settings.value("auth/user_id").toString();
-        auto with_sender = lastSender_ != user_id;
+        auto with_sender = lastSender_ != local_user_;
 
         auto widget = new Widget(client_, url, filename, this);
 
-        TimelineItem *view_item = new TimelineItem(widget, user_id, with_sender, scroll_widget_);
+        TimelineItem *view_item =
+          new TimelineItem(widget, local_user_, with_sender, scroll_widget_);
         scroll_layout_->addWidget(view_item);
 
         lastMessageDirection_ = TimelineDirection::Bottom;
 
         QApplication::processEvents();
 
-        lastSender_ = user_id;
+        lastSender_ = local_user_;
 
         int txn_id = client_->incrementTransactionId();
 
diff --git a/src/Utils.cc b/src/Utils.cc
new file mode 100644
index 00000000..663f7196
--- /dev/null
+++ b/src/Utils.cc
@@ -0,0 +1,121 @@
+#include "Utils.h"
+#include "timeline/TimelineViewManager.h"
+
+#include <variant.hpp>
+
+using TimelineEvent = mtx::events::collections::TimelineEvents;
+
+QString
+utils::descriptiveTime(const QDateTime &then)
+{
+        const auto now  = QDateTime::currentDateTime();
+        const auto days = then.daysTo(now);
+
+        if (days == 0)
+                return then.toString("HH:mm");
+        else if (days < 2)
+                return QString("Yesterday");
+        else if (days < 365)
+                return then.toString("dd/MM");
+
+        return then.toString("dd/MM/yy");
+}
+
+DescInfo
+utils::getMessageDescription(const TimelineEvent &event, const QString &localUser)
+{
+        using Audio  = mtx::events::RoomEvent<mtx::events::msg::Audio>;
+        using Emote  = mtx::events::RoomEvent<mtx::events::msg::Emote>;
+        using File   = mtx::events::RoomEvent<mtx::events::msg::File>;
+        using Image  = mtx::events::RoomEvent<mtx::events::msg::Image>;
+        using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
+        using Text   = mtx::events::RoomEvent<mtx::events::msg::Text>;
+        using Video  = mtx::events::RoomEvent<mtx::events::msg::Video>;
+
+        if (mpark::holds_alternative<Audio>(event)) {
+                const auto msg = mpark::get<Audio>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+
+                return DescInfo{sender == localUser ? "You" : username,
+                                sender,
+                                " sent an audio clip",
+                                utils::descriptiveTime(ts),
+                                ts};
+        } else if (mpark::holds_alternative<Emote>(event)) {
+                auto msg       = mpark::get<Emote>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+                const auto body     = QString::fromStdString(msg.content.body).trimmed();
+
+                return DescInfo{"",
+                                sender,
+                                QString("* %1 %2").arg(username).arg(body),
+                                utils::descriptiveTime(ts),
+                                ts};
+        } else if (mpark::holds_alternative<File>(event)) {
+                const auto msg = mpark::get<File>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+
+                return DescInfo{sender == localUser ? "You" : username,
+                                sender,
+                                " sent a file",
+                                utils::descriptiveTime(ts),
+                                ts};
+        } else if (mpark::holds_alternative<Image>(event)) {
+                const auto msg = mpark::get<Image>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+
+                return DescInfo{sender == localUser ? "You" : username,
+                                sender,
+                                " sent an image",
+                                utils::descriptiveTime(ts),
+                                ts};
+        } else if (mpark::holds_alternative<Notice>(event)) {
+                const auto msg = mpark::get<Notice>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+
+                return DescInfo{
+                  username, sender, " sent a notification", utils::descriptiveTime(ts), ts};
+        } else if (mpark::holds_alternative<Text>(event)) {
+                const auto msg = mpark::get<Text>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+                const auto body     = QString::fromStdString(msg.content.body).trimmed();
+
+                return DescInfo{sender == localUser ? "You" : username,
+                                sender,
+                                QString(": %1").arg(body),
+                                utils::descriptiveTime(ts),
+                                ts};
+        } else if (mpark::holds_alternative<Video>(event)) {
+                const auto msg = mpark::get<Video>(event);
+                QString sender = QString::fromStdString(msg.sender);
+
+                const auto username = TimelineViewManager::displayName(sender);
+                const auto ts       = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
+
+                return DescInfo{sender == localUser ? "You" : username,
+                                sender,
+                                " sent a video clip",
+                                utils::descriptiveTime(ts),
+                                ts};
+        }
+
+        return DescInfo{};
+}
diff --git a/src/timeline/TimelineItem.cc b/src/timeline/TimelineItem.cc
index 202b331d..eeac668d 100644
--- a/src/timeline/TimelineItem.cc
+++ b/src/timeline/TimelineItem.cc
@@ -84,9 +84,10 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
 
         if (ty == mtx::events::MessageType::Emote) {
                 body            = QString("* %1 %2").arg(displayName).arg(body);
-                descriptionMsg_ = {"", userid, body, descriptiveTime(timestamp), timestamp};
+                descriptionMsg_ = {"", userid, body, utils::descriptiveTime(timestamp), timestamp};
         } else {
-                descriptionMsg_ = {"You: ", userid, body, descriptiveTime(timestamp), timestamp};
+                descriptionMsg_ = {
+                  "You: ", userid, body, utils::descriptiveTime(timestamp), timestamp};
         }
 
         body = body.toHtmlEscaped();
@@ -207,7 +208,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
         descriptionMsg_ = {TimelineViewManager::displayName(sender),
                            sender,
                            " sent a notification",
-                           descriptiveTime(timestamp),
+                           utils::descriptiveTime(timestamp),
                            timestamp};
 
         generateTimestamp(timestamp);
@@ -251,7 +252,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
         auto displayName = TimelineViewManager::displayName(sender);
         auto emoteMsg    = QString("* %1 %2").arg(displayName).arg(body);
 
-        descriptionMsg_ = {"", sender, emoteMsg, descriptiveTime(timestamp), timestamp};
+        descriptionMsg_ = {"", sender, emoteMsg, utils::descriptiveTime(timestamp), timestamp};
 
         generateTimestamp(timestamp);
         emoteMsg = emoteMsg.toHtmlEscaped();
@@ -293,7 +294,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
         descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
                            sender,
                            QString(": %1").arg(body),
-                           descriptiveTime(timestamp),
+                           utils::descriptiveTime(timestamp),
                            timestamp};
 
         generateTimestamp(timestamp);
@@ -463,23 +464,6 @@ TimelineItem::setUserAvatar(const QImage &avatar)
         userAvatar_->setImage(avatar);
 }
 
-QString
-TimelineItem::descriptiveTime(const QDateTime &then)
-{
-        auto now = QDateTime::currentDateTime();
-
-        auto days = then.daysTo(now);
-
-        if (days == 0)
-                return then.toString("HH:mm");
-        else if (days < 2)
-                return QString("Yesterday");
-        else if (days < 365)
-                return then.toString("dd/MM");
-
-        return then.toString("dd/MM/yy");
-}
-
 TimelineItem::~TimelineItem() {}
 
 void
diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc
index 64505d42..3c8b3604 100644
--- a/src/timeline/TimelineView.cc
+++ b/src/timeline/TimelineView.cc
@@ -22,6 +22,7 @@
 #include "Config.h"
 #include "FloatingButton.h"
 #include "RoomMessages.h"
+#include "Utils.h"
 
 #include "timeline/TimelineView.h"
 #include "timeline/widgets/AudioItem.h"
@@ -29,6 +30,8 @@
 #include "timeline/widgets/ImageItem.h"
 #include "timeline/widgets/VideoItem.h"
 
+using TimelineEvent = mtx::events::collections::TimelineEvents;
+
 TimelineView::TimelineView(const mtx::responses::Timeline &timeline,
                            QSharedPointer<MatrixClient> client,
                            const QString &room_id,
@@ -251,44 +254,90 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &
         return nullptr;
 }
 
-int
-TimelineView::addEvents(const mtx::responses::Timeline &timeline)
+void
+TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events)
 {
-        int message_count = 0;
-
-        QSettings settings;
-        QString localUser = settings.value("auth/user_id").toString();
-
-        for (const auto &event : timeline.events) {
+        for (const auto &event : events) {
                 TimelineItem *item = parseMessageEvent(event, TimelineDirection::Bottom);
 
-                if (item != nullptr) {
+                if (item != nullptr)
                         addTimelineItem(item, TimelineDirection::Bottom);
-
-                        if (localUser != getEventSender(event))
-                                message_count += 1;
-                }
         }
 
         lastMessageDirection_ = TimelineDirection::Bottom;
 
         QApplication::processEvents();
+}
+
+int
+TimelineView::addEvents(const mtx::responses::Timeline &timeline)
+{
+        int message_count = 0;
 
         if (isInitialSync) {
                 prev_batch_token_ = QString::fromStdString(timeline.prev_batch);
                 isInitialSync     = false;
         }
 
-        // Exclude the top stretch.
-        if (timeline.events.size() != 0 && scroll_layout_->count() > 1)
-                notifyForLastEvent();
+        for (const auto &e : timeline.events) {
+                // Save the message if it can be rendered.
+                if (isViewable(e))
+                        bottomMessages_.push_back(e);
+
+                // Calculate notifications.
+                if (isNotifiable(e))
+                        message_count += 1;
+        }
+
+        if (!bottomMessages_.empty())
+                notifyForLastEvent(bottomMessages_[bottomMessages_.size() - 1]);
 
-        if (isActiveWindow() && isVisible() && timeline.events.size() > 0)
-                readLastEvent();
+        // If the current timeline is open and there are messages to be rendered.
+        if (isVisible() && !bottomMessages_.empty()) {
+                renderBottomEvents(bottomMessages_);
+
+                // Free up space for new messages.
+                bottomMessages_.clear();
+
+                // Send a read receipt for the last event.
+                if (isActiveWindow())
+                        readLastEvent();
+        }
 
         return message_count;
 }
 
+inline bool
+TimelineView::isViewable(const TimelineEvent &event) const
+{
+        namespace msg = mtx::events::msg;
+
+        return mpark::holds_alternative<mtx::events::RoomEvent<msg::Audio>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Emote>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::File>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Image>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Notice>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Text>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Video>>(event);
+}
+
+inline bool
+TimelineView::isNotifiable(const TimelineEvent &event) const
+{
+        namespace msg = mtx::events::msg;
+
+        if (local_user_ == getEventSender(event))
+                return false;
+
+        return mpark::holds_alternative<mtx::events::RoomEvent<msg::Audio>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Emote>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::File>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Image>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Notice>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Text>>(event) ||
+               mpark::holds_alternative<mtx::events::RoomEvent<msg::Video>>(event);
+}
+
 void
 TimelineView::init()
 {
@@ -420,18 +469,17 @@ TimelineView::updatePendingMessage(int txn_id, QString event_id)
 void
 TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
 {
-        QSettings settings;
-        auto user_id     = settings.value("auth/user_id").toString();
-        auto with_sender = lastSender_ != user_id;
+        auto with_sender = lastSender_ != local_user_;
 
-        TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_);
+        TimelineItem *view_item =
+          new TimelineItem(ty, local_user_, body, with_sender, scroll_widget_);
         scroll_layout_->addWidget(view_item);
 
         lastMessageDirection_ = TimelineDirection::Bottom;
 
         QApplication::processEvents();
 
-        lastSender_ = user_id;
+        lastSender_ = local_user_;
 
         int txn_id = client_->incrementTransactionId();
         PendingMessage message(ty, txn_id, body, "", "", view_item);
@@ -483,6 +531,15 @@ TimelineView::notifyForLastEvent()
                 qWarning() << "Cast to TimelineView failed" << room_id_;
 }
 
+void
+TimelineView::notifyForLastEvent(const TimelineEvent &event)
+{
+        auto descInfo = utils::getMessageDescription(event, local_user_);
+
+        if (!descInfo.timestamp.isEmpty())
+                emit updateLastTimelineMessage(room_id_, descInfo);
+}
+
 bool
 TimelineView::isPendingMessage(const QString &txnid,
                                const QString &sender,
@@ -575,6 +632,12 @@ TimelineView::getLastEventId() const
 void
 TimelineView::showEvent(QShowEvent *event)
 {
+        if (!bottomMessages_.empty()) {
+                renderBottomEvents(bottomMessages_);
+                bottomMessages_.clear();
+                scrollDown();
+        }
+
         readLastEvent();
 
         QWidget::showEvent(event);