controller/PendingFlagIntents.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QHash> | ||
| 4 | #include <QMap> | ||
| 5 | #include <QSet> | ||
| 6 | #include <QString> | ||
| 7 | #include <optional> | ||
| 8 | |||
| 9 | #include "data/MailRequest.h" | ||
| 10 | |||
| 11 | // Unconfirmed local mail intents shared by the controller's flag sync, | ||
| 12 | // polling, user commands and mutation pipeline (Sprint 91 S91.0.7). | ||
| 13 | // | ||
| 14 | // Plain value, GUI thread only, one instance owned by MailMutationPipeline | ||
| 15 | // (S91.4.2: the pipeline creates and retires the intents and fences); the | ||
| 16 | // controller and FolderFlagSync reach it through intents(). Every reader and | ||
| 17 | // writer goes through these query/mark methods; nobody keeps a copy. | ||
| 18 | class PendingFlagIntents { | ||
| 19 | public: | ||
| 20 | // A confirmed Seen readback for a folder. A STATUS answer whose request | ||
| 21 | // started before `completedNs` is older than that readback and must not | ||
| 22 | // replace the folder's count; the fence is void once the epoch changed. | ||
| 23 | struct ReadbackFence { | ||
| 24 | qint64 completedNs = 0; | ||
| 25 | quint32 uidValidity = 0; | ||
| 26 | }; | ||
| 27 | |||
| 28 | // Identities with a pending local Seen/Flagged/keyword change: server flag | ||
| 29 | // snapshots must not revert them before the journal settles. | ||
| 30 |
1/1✓ Branch 2 → 3 taken 494 times.
|
494 | void markFlag(const MailId &id) { m_flags.insert(id); } |
| 31 | 37 | void clearFlag(const MailId &id) { m_flags.remove(id); } | |
| 32 | 102 | bool hasPendingFlag(const MailId &id) const { return m_flags.contains(id); } | |
| 33 | 5 | const QSet<MailId> &pendingFlags() const { return m_flags; } | |
| 34 | |||
| 35 | // Identities moved visually in this session (visual commit until journal | ||
| 36 | // settlement); in-memory, no SQLite read. | ||
| 37 |
1/1✓ Branch 2 → 3 taken 145 times.
|
145 | void markMove(const MailId &id) { m_moves.insert(id); } |
| 38 | 27 | void clearMove(const MailId &id) { m_moves.remove(id); } | |
| 39 | 53 | bool hasPendingMove(const MailId &id) const { return m_moves.contains(id); } | |
| 40 | 21 | const QSet<MailId> &pendingMoves() const { return m_moves; } | |
| 41 | |||
| 42 | 15 | void setReadbackFence(qint64 folderId, ReadbackFence fence) { m_fences.insert(folderId, fence); } | |
| 43 | 29 | std::optional<ReadbackFence> readbackFence(qint64 folderId) const { | |
| 44 | 29 | const auto fence = m_fences.constFind(folderId); | |
| 45 |
2/2✓ Branch 5 → 6 taken 22 times.
✓ Branch 5 → 7 taken 7 times.
|
29 | if (fence == m_fences.cend()) return std::nullopt; |
| 46 | 7 | return *fence; | |
| 47 | } | ||
| 48 | 1 | void clearReadbackFence(qint64 folderId) { m_fences.remove(folderId); } | |
| 49 | 1043 | void clearReadbackFences() { m_fences.clear(); } | |
| 50 | 1 | bool readbackFencesEmpty() const { return m_fences.isEmpty(); } | |
| 51 | |||
| 52 | // T-074: last polled unread count per folder path (keeps badges stable | ||
| 53 | // during folder switches). | ||
| 54 | 15 | int lastPolledUnread(const QString &folder, int fallback) const { | |
| 55 | 15 | return m_lastPolledUnread.value(folder, fallback); | |
| 56 | } | ||
| 57 | 46 | void setLastPolledUnread(const QString &folder, int unread) { m_lastPolledUnread[folder] = unread; } | |
| 58 | 51 | const QMap<QString, int> &lastPolledUnreadCounts() const { return m_lastPolledUnread; } | |
| 59 | 1043 | void clearLastPolledUnread() { m_lastPolledUnread.clear(); } | |
| 60 | |||
| 61 | private: | ||
| 62 | QSet<MailId> m_flags; | ||
| 63 | QSet<MailId> m_moves; | ||
| 64 | QHash<qint64, ReadbackFence> m_fences; | ||
| 65 | QMap<QString, int> m_lastPolledUnread; | ||
| 66 | }; | ||
| 67 |