quick/QuickMailHost.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "presentation/AboutInfo.h" | ||
| 4 | #include "presentation/QuickSettingsNavigation.h" | ||
| 5 | #include "presentation/SettingsCatalog.h" | ||
| 6 | #include "presentation/DesignTokens.h" | ||
| 7 | #include "presentation/WorkspaceState.h" | ||
| 8 | #include "presentation/MailReaderState.h" | ||
| 9 | #include "presentation/QuickCommandRouter.h" | ||
| 10 | #include "presentation/QuickMailBackend.h" | ||
| 11 | #include "presentation/WorkspacePersistence.h" | ||
| 12 | #include "QuickHtmlReader.h" | ||
| 13 | #include "app/core/BackgroundAccountSession.h" | ||
| 14 | #include "app/core/FolderArrivalWatcher.h" | ||
| 15 | #include "service/DesktopNotifier.h" | ||
| 16 | #include "service/NotificationBatcher.h" | ||
| 17 | #include "presentation/QuickTrayController.h" | ||
| 18 | #include <QQmlApplicationEngine> | ||
| 19 | #include <QHash> | ||
| 20 | #include <QPointer> | ||
| 21 | #include <QQuickItem> | ||
| 22 | #include <QQuickWindow> | ||
| 23 | #include <QSet> | ||
| 24 | #include <memory> | ||
| 25 | |||
| 26 | // A real Quick frontend over the product library, separate from PreviewFixture. | ||
| 27 | class ReminderNotifications; | ||
| 28 | class ProcessTimeZoneWatcher; | ||
| 29 | class QuickMailHost final : public QObject { | ||
| 30 | 18 | Q_OBJECT | |
| 31 | public: | ||
| 32 | QuickMailHost(MailSessionCore &session, QString profileDirectory, QObject *parent = nullptr, | ||
| 33 | MailSearchReadService *searchReader = nullptr); | ||
| 34 | ~QuickMailHost() override; | ||
| 35 | bool initialize(bool online, const QString &theme = {}, const QString &layout = {}, | ||
| 36 | const QString &legacyIni = {}, bool startMinimized = false); | ||
| 37 | bool activate(const QString &mailto = {}, bool compose = false, | ||
| 38 | const QString &activationToken = {}); | ||
| 39 | bool flush(); | ||
| 40 | QQuickWindow *window() const; | ||
| 41 | 209 | QString error() const { return m_error; } | |
| 42 | 582 | QStringList warnings() const { return m_warnings; } | |
| 43 | 2964 | WorkspaceState *workspace() { return &m_workspace; } | |
| 44 | 515 | DesignTokens *tokens() { return &m_tokens; } | |
| 45 | 238 | QuickCommandRouter *commands() { return &m_commands; } | |
| 46 | 5280 | QuickMailBackend *backend() { return &m_backend; } | |
| 47 | 60 | QuickSettingsNavigation *settingsNavigation() { return &m_settingsNavigation; } | |
| 48 | // Null before initialize(); used by measurements of durable state writes. | ||
| 49 | 5 | WorkspacePersistence *persistence() { return m_persistence.get(); } | |
| 50 | |||
| 51 | // ─── Q16: desktop notifications for new mail ─── | ||
| 52 | // Production wiring: INBOX header batches reach enqueueNotification via | ||
| 53 | // the controller; the batcher clusters bursts; DesktopNotifier shows | ||
| 54 | // them. The same entry points are the test seam (the popup rendering | ||
| 55 | // itself is covered by the DesktopNotifier suite). | ||
| 56 | // Queues one new mail for notification (no-op while notifications are | ||
| 57 | // unavailable in this environment). | ||
| 58 | void enqueueNotification(const QString &from, const QString &subject, | ||
| 59 | qint64 uid, qint64 folderId); | ||
| 60 | // F-36/F-50: the folder currently read in the currently selected account | ||
| 61 | // — its arrivals notify individually, everything else summarizes. | ||
| 62 | bool arrivalIsReading(const QString &account, const QString &folder) const; | ||
| 63 | // Marks the first INBOX sync finished so buffered mails release. | ||
| 64 | void completeInboxFirstSync(bool initialLoad); | ||
| 65 | // Executes a notification action ("open"/"mark-read") exactly like the | ||
| 66 | // DesktopNotifier signal would. The activation token rides along for the | ||
| 67 | // compositor focus request. | ||
| 68 | void handleNotificationAction(uint id, const QString &action, | ||
| 69 | const QString &activationToken = {}); | ||
| 70 | // Metadata of the last issued notification ({id, from, subject} or | ||
| 71 | // {id, summary, count}). | ||
| 72 | 67 | QVariantMap lastNotification() const { return m_lastNotification; } | |
| 73 | // Shortens the burst debounce (tests; production default 1000 ms). | ||
| 74 | void setNotificationDebounceMs(int ms); | ||
| 75 | // F-36 (SPRINT-104): the watcher that turns unread-count rises of | ||
| 76 | // folders the user is not reading into arrival summaries. Production | ||
| 77 | // wiring consumes MailController::unreadCountChanged; the entry point | ||
| 78 | // is the test seam for the journey. | ||
| 79 | 8 | FolderArrivalWatcher *arrivalWatcher() { return &m_arrivals; } | |
| 80 | // F-36: arms a folder's individual notification chain (production input | ||
| 81 | // is authoritativeFolderPresented — after the catch-up sync, so older | ||
| 82 | // uncached mail never announces) and decides which headers of a stored | ||
| 83 | // batch are genuinely new, unseen mail (production input is | ||
| 84 | // headersStored). Public as the journey seam, like enqueueNotification. | ||
| 85 | void armFolderNotifications(qint64 folderId); | ||
| 86 | void considerStoredHeadersForNotifications(qint64 folderId, const QList<MailHeader> &headers); | ||
| 87 | // ─── Q16: tray integration ─── | ||
| 88 | 61 | QuickTrayController *tray() { return &m_tray; } | |
| 89 | // Sprint 93 S93.5.1: appointment reminders (null without calendar sync). | ||
| 90 | 5 | ReminderNotifications *reminders() const { return m_reminders.get(); } | |
| 91 | signals: | ||
| 92 | // Render-loop submission of the selected ready revision, not a model callback | ||
| 93 | // or a claim about physical monitor presentation. | ||
| 94 | void readerFrameSubmitted(const QVariantMap &identity, const QString &revision); | ||
| 95 | private: | ||
| 96 | static void installIconFontFallback(); | ||
| 97 | MailSessionCore &m_session; | ||
| 98 | QString m_profileDirectory; | ||
| 99 | WorkspaceState m_workspace; | ||
| 100 | DesignTokens m_tokens; | ||
| 101 | AboutInfo m_about; | ||
| 102 | // Sprint 100: the settings as data and where the settings area is. | ||
| 103 | SettingsCatalog m_settingsCatalog; | ||
| 104 | QuickSettingsNavigation m_settingsNavigation{&m_workspace}; | ||
| 105 | MailReaderState m_reader; | ||
| 106 | QuickHtmlReader m_htmlReader; | ||
| 107 | QuickCommandRouter m_commands; | ||
| 108 | QuickMailBackend m_backend; | ||
| 109 | std::unique_ptr<WorkspacePersistence> m_persistence; | ||
| 110 | // Q16: new-mail notification chain (batcher clusters, notifier shows, | ||
| 111 | // actionInvoked reveals or marks read). F-36 (SPRINT-104): the arrival | ||
| 112 | // watcher summarizes unread rises of folders that only sync in the | ||
| 113 | // background; the per-folder watermark and presentation set keep the | ||
| 114 | // individual chain to genuinely new, unseen mail of folders that were | ||
| 115 | // shown once. | ||
| 116 | DesktopNotifier m_notifier; | ||
| 117 | NotificationBatcher m_batcher{this}; | ||
| 118 | 51 | FolderArrivalWatcher m_arrivals{{[this](const QString &account, const QString &folder) { | |
| 119 | 51 | return arrivalIsReading(account, folder); | |
| 120 | }}, this}; | ||
| 121 | QHash<QString, QString> m_arrivalAccountIds; // account name → config id (F-50) | ||
| 122 | QHash<uint, QString> m_folderRevealAccounts; // summary id → account to switch to | ||
| 123 | QSet<qint64> m_notifyingFolders; // shown once this session | ||
| 124 | QHash<qint64, qint64> m_folderNotifyWatermark; // highest uid that notified | ||
| 125 | QHash<qint64, int> m_summarizedArrivals; // summarized while closed | ||
| 126 | QQuickWindow *m_windowItem = nullptr; | ||
| 127 | QuickTrayController m_tray{nullptr, this}; | ||
| 128 | std::unique_ptr<ReminderNotifications> m_reminders; | ||
| 129 | // Sprint 93 G9: keeps the pinned TZ in step with the system zone. | ||
| 130 | std::unique_ptr<ProcessTimeZoneWatcher> m_zoneWatcher; | ||
| 131 | QHash<uint, QPair<qint64, qint64>> m_notificationTargets; | ||
| 132 | QVariantMap m_lastNotification; | ||
| 133 | // Local ids for environments without a notification service (negative, | ||
| 134 | // so they can never collide with real org.freedesktop.Notifications ids). | ||
| 135 | uint m_localNotificationIds = 0; | ||
| 136 | QList<uint> m_localIds; // local ids still resolvable (no service, S106-358) | ||
| 137 | static constexpr qsizetype LocalNotificationIds = 256; | ||
| 138 | uint localNotificationId(); | ||
| 139 | QString m_error; | ||
| 140 | QStringList m_warnings; | ||
| 141 | MailId m_frameIdentity; | ||
| 142 | quint64 m_frameRevision = 0; | ||
| 143 | QPointer<QQuickItem> m_listView; | ||
| 144 | MailId m_listFrameIdentity; | ||
| 145 | QString m_listFrameGeneration; | ||
| 146 | int m_listFrameRow = -1; | ||
| 147 | QQmlApplicationEngine m_engine; | ||
| 148 | }; | ||
| 149 |