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);
|