1// SPDX-FileCopyrightText: 2021 Quotient contributors
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
6#include "room.h"
7
8namespace Quotient {
9
10//! \brief Counters of unread events and highlights with a precision flag
11//!
12//! This structure contains a static snapshot with values of unread counters
13//! returned by Room::partiallyReadStats and Room::unreadStats (properties
14//! or methods).
15//!
16//! \note It's just a simple grouping of counters and is not automatically
17//! updated from the room as subsequent syncs arrive.
18//! \sa Room::unreadStats, Room::partiallyReadStats, Room::isEventNotable
19struct QUOTIENT_API EventStats {
20 Q_GADGET
21 Q_PROPERTY(qsizetype notableCount MEMBER notableCount CONSTANT)
22 Q_PROPERTY(qsizetype highlightCount MEMBER highlightCount CONSTANT)
23 Q_PROPERTY(bool isEstimate MEMBER isEstimate CONSTANT)
24public:
25 //! The number of "notable" events in an events range
26 //! \sa Room::isEventNotable
27 qsizetype notableCount = 0;
28 qsizetype highlightCount = 0;
29 //! \brief Whether the counter values above are exact
30 //!
31 //! This is false when the end marker (m.read receipt or m.fully_read) used
32 //! to collect the stats points to an event loaded locally and the counters
33 //! can therefore be calculated exactly using the locally available segment
34 //! of the timeline; true when the marker points to an event outside of
35 //! the local timeline (in which case the estimation is made basing on
36 //! the data supplied by the homeserver as well as counters saved from
37 //! the previous run of the client).
38 bool isEstimate = true;
39
40 // TODO: replace with = default once C++20 becomes a requirement on clients
41 bool operator==(const EventStats& rhs) const
42 {
43 return notableCount == rhs.notableCount
44 && highlightCount == rhs.highlightCount
45 && isEstimate == rhs.isEstimate;
46 }
47 bool operator!=(const EventStats& rhs) const { return !operator==(rhs); }
48
49 //! \brief Check whether the event statistics are empty
50 //!
51 //! Empty statistics have notable and highlight counters of zero and
52 //! isEstimate set to false.
53 Q_INVOKABLE bool empty() const
54 {
55 return notableCount == 0 && !isEstimate && highlightCount == 0;
56 }
57
58 using marker_t = Room::rev_iter_t;
59
60 //! \brief Build event statistics on a range of events
61 //!
62 //! This is a factory that returns an EventStats instance with counts of
63 //! notable and highlighted events between \p from and \p to reverse
64 //! timeline iterators; the \p init parameter allows to override
65 //! the initial statistics object and start from other values.
66 static EventStats fromRange(const Room* room, const marker_t& from,
67 const marker_t& to,
68 const EventStats& init = { .notableCount: .notableCount: 0, .highlightCount: .highlightCount: 0, .isEstimate: .isEstimate: false });
69
70 //! \brief Build event statistics on a range from sync edge to marker
71 //!
72 //! This is mainly a shortcut for \code
73 //! <tt>fromRange(room, marker_t(room->syncEdge()), marker)</tt>
74 //! \endcode except that it also sets isEstimate to true if (and only if)
75 //! <tt>to == room->historyEdge()</tt>.
76 static EventStats fromMarker(const Room* room, const marker_t& marker);
77
78 //! \brief Loads a statistics object from the cached counters
79 //!
80 //! Sets isEstimate to `true` unless both notableCount and highlightCount
81 //! are equal to -1.
82 static EventStats fromCachedCounters(Omittable<int> notableCount,
83 Omittable<int> highlightCount = none);
84
85 //! \brief Update statistics when a read marker moves down the timeline
86 //!
87 //! Removes events between oldMarker and newMarker from statistics
88 //! calculation if \p oldMarker points to an existing event in the timeline,
89 //! or recalculates the statistics entirely if \p oldMarker points
90 //! to <tt>room->historyEdge()</tt>. Always results in exact statistics
91 //! (<tt>isEstimate == false</tt>.
92 //! \param oldMarker Must point correspond to the _current_ statistics
93 //! isEstimate state, i.e. it should point to
94 //! <tt>room->historyEdge()</tt> if <tt>isEstimate == true</tt>, or
95 //! to a valid position within the timeline otherwise
96 //! \param newMarker Must point to a valid position in the timeline (not to
97 //! <tt>room->historyEdge()</tt> that is equal to or closer to
98 //! the sync edge than \p oldMarker
99 //! \return true if either notableCount or highlightCount changed, or if
100 //! the statistics was completely recalculated; false otherwise
101 bool updateOnMarkerMove(const Room* room, const marker_t& oldMarker,
102 const marker_t& newMarker);
103
104 //! \brief Validate the statistics object against the given marker
105 //!
106 //! Checks whether the statistics object data are valid for a given marker.
107 //! No stats recalculation takes place, only isEstimate and zero-ness
108 //! of notableCount are checked.
109 bool isValidFor(const Room* room, const marker_t& marker) const;
110};
111
112QUOTIENT_API QDebug operator<<(QDebug dbg, const EventStats& es);
113
114}
115