aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2021-11-21 06:55:09 +0100
committerAlexey Rusakov <Kitsune-Ral@users.sf.net>2021-11-21 07:07:00 +0100
commitbf5f209d2d237301c65cc0973f1707b9386f3110 (patch)
tree17eadba44dd3c8950acb22169b66b3c10286984c /lib
parent52e640bce5a8931330fa6d653212e524e7baa2eb (diff)
downloadlibquotient-bf5f209d2d237301c65cc0973f1707b9386f3110.tar.gz
libquotient-bf5f209d2d237301c65cc0973f1707b9386f3110.zip
Room: isEventNotable, notificationFor, checkForNotifications
Room::isEventNotable has been moved out from Room::Private and made compliant with MSC2654. The concept of Room::checkForNotifications is taken from Quaternion where a method with the same name has been in QuaternionRoom for a long time - however, actual body is a stub for now, always returning { Notification::None } (Quaternion's implementation is too crude to be taken to the library). Now we really need a pushrules processor to fill this method with something reasonably good. Internally the library now calls checkForNotifications() on every event added to the timeline, filling up the events-to-notifications map because it is anticipated that calculation of notifications can be rather resource-intensive and should only be done once for a given event. Finally, Room::notificationsFor is an accessor into the mentioned map, standing next to isEventNotable (but unlike isEventNotable, it's not virtual; checkForNotifications is).
Diffstat (limited to 'lib')
-rw-r--r--lib/room.cpp43
-rw-r--r--lib/room.h36
2 files changed, 66 insertions, 13 deletions
diff --git a/lib/room.cpp b/lib/room.cpp
index a2ec228a..67f65472 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -116,6 +116,7 @@ public:
QHash<QPair<QString, QString>, RelatedEvents> relations;
QString displayname;
Avatar avatar;
+ QHash<QString, Notification> notifications;
int highlightCount = 0;
int notificationCount = 0;
members_map_t membersMap;
@@ -241,13 +242,6 @@ public:
// return EventT::content_type()
// }
- bool isEventNotable(const TimelineItem& ti) const
- {
- return !ti->isRedacted() && ti->senderId() != connection->userId()
- && is<RoomMessageEvent>(*ti)
- && ti.viewAs<RoomMessageEvent>()->replacedEvent().isEmpty();
- }
-
template <typename EventArrayT>
Changes updateStateFrom(EventArrayT&& events)
{
@@ -709,7 +703,7 @@ Room::Changes Room::Private::updateUnreadCount(const rev_iter_t& from,
et.start();
const auto newUnreadMessages =
count_if(from, to,
- std::bind(&Room::Private::isEventNotable, this, _1));
+ std::bind(&Room::isEventNotable, q, _1));
if (et.nsecsElapsed() > profilerMinNsecs() / 10)
qCDebug(PROFILER) << "Counting gained unread messages in"
<< q->objectName() << "took" << et;
@@ -742,7 +736,7 @@ Room::Changes Room::Private::recalculateUnreadCount(bool force)
et.start();
unreadMessages =
int(count_if(timeline.crbegin(), q->fullyReadMarker(),
- [this](const auto& ti) { return isEventNotable(ti); }));
+ [this](const auto& ti) { return q->isEventNotable(ti); }));
if (et.nsecsElapsed() > profilerMinNsecs() / 10)
qCDebug(PROFILER) << "Recounting unread messages in" << q->objectName()
<< "took" << et;
@@ -837,6 +831,28 @@ bool Room::canSwitchVersions() const
return true;
}
+bool Room::isEventNotable(const TimelineItem &ti) const
+{
+ const auto& evt = *ti;
+ const auto* rme = ti.viewAs<RoomMessageEvent>();
+ return !evt.isRedacted()
+ && (is<RoomTopicEvent>(evt) || is<RoomNameEvent>(evt)
+ || is<RoomAvatarEvent>(evt) || is<RoomTombstoneEvent>(evt)
+ || (rme && rme->msgtype() != MessageEventType::Notice
+ && rme->replacedEvent().isEmpty()))
+ && evt.senderId() != localUser()->id();
+}
+
+Notification Room::notificationFor(const TimelineItem &ti) const
+{
+ return d->notifications.value(ti->id());
+}
+
+Notification Room::checkForNotifications(const TimelineItem &ti)
+{
+ return { Notification::None };
+}
+
bool Room::hasUnreadMessages() const { return unreadCount() >= 0; }
int Room::unreadCount() const { return d->unreadMessages; }
@@ -1548,11 +1564,12 @@ Room::Private::moveEventsToTimeline(RoomEventsRange events,
!eventsIndex.contains(eId), __FUNCTION__,
makeErrorStr(*e, "Event is already in the timeline; "
"incoming events were not properly deduplicated"));
- if (placement == Older)
- timeline.emplace_front(move(e), --index);
- else
- timeline.emplace_back(move(e), ++index);
+ const auto& ti = placement == Older
+ ? timeline.emplace_front(move(e), --index)
+ : timeline.emplace_back(move(e), ++index);
eventsIndex.insert(eId, index);
+ if (auto n = q->checkForNotifications(ti); n.type != Notification::None)
+ notifications.insert(e->id(), n);
Q_ASSERT(q->findInTimeline(eId)->event()->id() == eId);
}
const auto insertedSize = (index - baseIndex) * placement;
diff --git a/lib/room.h b/lib/room.h
index 8a544e82..124c8cb4 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -96,6 +96,18 @@ inline void swap(ReadReceipt& lhs, ReadReceipt& rhs)
swap(lhs.timestamp, rhs.timestamp);
}
+struct Notification
+{
+ enum Type { None = 0, Basic, Highlight };
+ Q_ENUM(Notification)
+
+ Type type = None;
+
+private:
+ Q_GADGET
+ Q_PROPERTY(Type type MEMBER type CONSTANT)
+};
+
class Room : public QObject {
Q_OBJECT
Q_PROPERTY(Connection* connection READ connection CONSTANT)
@@ -482,6 +494,29 @@ public:
//! \sa markAllMessagesAsRead, fullyReadMarker
Q_INVOKABLE void markMessagesAsRead(QString uptoEventId);
+ //! \brief Determine whether an event should be counted as unread
+ //!
+ //! The criteria of including an event in unread counters are described in
+ //! [MSC2654](https://github.com/matrix-org/matrix-doc/pull/2654); according
+ //! to these, the event should be counted as unread (or, in libQuotient
+ //! parlance, is "notable") if it is:
+ //! - either
+ //! - a message event that is not m.notice, or
+ //! - a state event with type being one of:
+ //! `m.room.topic`, `m.room.name`, `m.room.avatar`, `m.room.tombstone`;
+ //! - neither redacted, nor an edit (redactions cause the redacted event
+ //! to stop being notable, while edits are not notable themselves while
+ //! the original event usually is);
+ //! - from a non-local user (events from other devices of the local
+ //! user are not notable).
+ virtual bool isEventNotable(const TimelineItem& ti) const;
+
+ //! \brief Get notification details for an event
+ //!
+ //! This allows to get details on the kind of notification that should
+ //! generated for \p evt.
+ Notification notificationFor(const TimelineItem& ti) const;
+
//! Check whether there are unread messages in the room
bool hasUnreadMessages() const;
@@ -873,6 +908,7 @@ protected:
{}
virtual QJsonObject toJson() const;
virtual void updateData(SyncRoomData&& data, bool fromCache = false);
+ virtual Notification checkForNotifications(const TimelineItem& ti);
private:
friend class Connection;