service/NotificationBatcher.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QElapsedTimer> | ||
| 4 | #include <QList> | ||
| 5 | #include <QObject> | ||
| 6 | #include <QString> | ||
| 7 | #include <QTimer> | ||
| 8 | |||
| 9 | // NotificationBatcher (67.A2) coalesces new-mail notifications so a | ||
| 10 | // catch-up sync after hours offline produces one summary popup instead | ||
| 11 | // of one popup per mail. | ||
| 12 | // | ||
| 13 | // Behavior: | ||
| 14 | // - Suppressed until setSyncComplete() is called: mails added before | ||
| 15 | // the first INBOX sync of the session finishes are counted with three | ||
| 16 | // preview rows (they flush as one batch at sync end) or dropped when the sync | ||
| 17 | // was the initial mailbox load (initial load != new mail). | ||
| 18 | // - After that, a debounce timer (default 3 s, restarted on each add) | ||
| 19 | // coalesces bursts: 1-2 mails flush as individual notifications, | ||
| 20 | // >= 3 as a single summary. A burst flushes at the latest 5 debounce | ||
| 21 | // intervals after its first mail, even while mail keeps arriving | ||
| 22 | // (S106-214). | ||
| 23 | // - The newest mail of a burst: the highest UID within one folder; across | ||
| 24 | // folders (separate UID spaces) the later arrival. | ||
| 25 | // | ||
| 26 | // Pure QObject — no D-Bus. MainWindow connects the flush signals to | ||
| 27 | // DesktopNotifier and owns the replaces-id of the on-screen summary. | ||
| 28 | class NotificationBatcher : public QObject { | ||
| 29 | 12 | Q_OBJECT | |
| 30 | |||
| 31 | public: | ||
| 32 | struct PendingMail { | ||
| 33 | QString from; | ||
| 34 | QString subject; | ||
| 35 | qint64 uid = -1; | ||
| 36 | qint64 folderId = -1; | ||
| 37 | }; | ||
| 38 | |||
| 39 | explicit NotificationBatcher(QObject *parent = nullptr); | ||
| 40 | |||
| 41 | void setDebounceMs(int ms); | ||
| 42 | 1 | int debounceMs() const { return m_debounceMs; } | |
| 43 | |||
| 44 | 2 | bool isSuppressed() const { return m_suppressed; } | |
| 45 | 8 | int pendingCount() const { return m_pendingCount; } | |
| 46 | |||
| 47 | // Queue a new mail. While suppressed the mail is buffered; otherwise | ||
| 48 | // the debounce timer is (re)started. | ||
| 49 | void addPending(const QString &from, const QString &subject, qint64 uid, | ||
| 50 | qint64 folderId); | ||
| 51 | |||
| 52 | // First INBOX sync of the session finished. dropBuffered = true when | ||
| 53 | // the sync was the initial mailbox load (cache was empty — nothing in | ||
| 54 | // it is "new"); otherwise the buffer flushes immediately as one batch. | ||
| 55 | void setSyncComplete(bool dropBuffered); | ||
| 56 | |||
| 57 | signals: | ||
| 58 | // Burst of 1-2 mails: notify each mail individually. | ||
| 59 | void notifyIndividual(const QString &from, const QString &subject, | ||
| 60 | qint64 uid, qint64 folderId); | ||
| 61 | // Burst of >= 3 mails: one summary. newestUid/folderId identify the | ||
| 62 | // newest mail of the batch (target for the "open" action). | ||
| 63 | void notifySummary(const QString &title, const QString &body, int count, | ||
| 64 | qint64 folderId, qint64 newestUid); | ||
| 65 | |||
| 66 | private: | ||
| 67 | void flush(); | ||
| 68 | |||
| 69 | // Only the first three entries and the newest identity are needed for | ||
| 70 | // either individual notifications or a summary, even for a huge sync. | ||
| 71 | QList<PendingMail> m_preview; | ||
| 72 | int m_pendingCount = 0; | ||
| 73 | qint64 m_newestUid = -1; | ||
| 74 | qint64 m_newestFolderId = -1; | ||
| 75 | QTimer m_debounce; | ||
| 76 | int m_debounceMs = 3000; | ||
| 77 | QElapsedTimer m_burst; // since the first mail of the pending burst | ||
| 78 | bool m_suppressed = true; | ||
| 79 | }; | ||
| 80 |