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 | |
8 | namespace 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 |
19 | struct 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) |
24 | public: |
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 | |
112 | QUOTIENT_API QDebug operator<<(QDebug dbg, const EventStats& es); |
113 | |
114 | } |
115 | |