|
| 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 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 = { 0, 0, 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 | +QDebug operator<<(QDebug dbg, const EventStats& es); |
| 113 | + |
| 114 | +} |
0 commit comments