diff options
author | Konstantinos Sideris <sideris.konstantin@gmail.com> | 2018-01-05 00:27:32 +0200 |
---|---|---|
committer | Konstantinos Sideris <sideris.konstantin@gmail.com> | 2018-01-05 00:27:32 +0200 |
commit | 983aea7c76da65214463e725576e8b147fd02cdf (patch) | |
tree | ed652a099c5a7cce7d9cde091c14b72295ac879d | |
parent | Remove an extra colon from the receipt time (diff) | |
download | nheko-983aea7c76da65214463e725576e8b147fd02cdf.tar.xz |
Create widgets on demand for messages added to the end of the timeline
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | include/Utils.h | 20 | ||||
-rw-r--r-- | include/timeline/TimelineItem.h | 11 | ||||
-rw-r--r-- | include/timeline/TimelineView.h | 25 | ||||
-rw-r--r-- | src/Utils.cc | 121 | ||||
-rw-r--r-- | src/timeline/TimelineItem.cc | 28 | ||||
-rw-r--r-- | src/timeline/TimelineView.cc | 109 |
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); |