diff options
Diffstat (limited to 'src/mainwindow.h')
| -rw-r--r-- | src/mainwindow.h | 94 |
1 files changed, 86 insertions, 8 deletions
diff --git a/src/mainwindow.h b/src/mainwindow.h index cb8cec4..6321573 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -43,6 +43,10 @@ #include "tagcolors.h" #include "types.h" +// Held by value: the parsed business-senders list is a member, and the load is +// asserted through it without reaching into the delegate. +#include "businesssenders.h" + class QAction; class QLineEdit; class QMenu; @@ -57,6 +61,7 @@ class QToolButton; class QVBoxLayout; class BusyIndicator; +class CardDelegate; class ThreadListModel; class MessageView; class MailSync; @@ -100,6 +105,31 @@ public: /// the worker, which test_mainwindow has no database to drive. bool hasEditAwaitingSend() const { return !m_heldEdits.isEmpty(); } + /// Every outstanding change, as rows, for the list behind the count. + /// + /// A SNAPSHOT: taken once when the user opens the list and never refreshed + /// under them. Subjects are empty here, filled by the resolve step, so + /// this is testable with no worker and no database. + /// + /// Scope follows the ACTION. The three queues already encode it: a held + /// thread edit carries thread ids because a `*_thread` action made it, + /// while a netted tag edit and a held move both carry message ids. Nothing + /// is expanded, and nothing is escalated. + /// Net changes the index holds that a sync has not carried over. + /// + /// Public beside pendingChangeSnapshot(), which must agree with it: the + /// count the user clicks is the count the list has to account for. + int pendingEditCount() const; + + QVector<PendingChange> pendingChangeSnapshot() const; + + /// Opens the list behind the unsynced-changes count. + /// + /// Takes the snapshot, asks the worker to resolve its subjects, and shows + /// the dialog when they arrive. Q_INVOKABLE so a test can open it without + /// synthesising a click on a status-bar label. + Q_INVOKABLE void showPendingChanges(); + /// Whether the undo stack still holds anything. Exposed so a test can show /// that a rejected write did not take unrelated history down with it. bool canUndo() const { return m_undoStack.canUndo(); } @@ -358,6 +388,20 @@ public: /// The Maildir root as the worker reported it, for the split-index test. QString mailRootForTesting() const { return m_mailRoot; } + /// Reads the business-senders list and hands it to the delegate. + /// + /// Once at startup and on an explicit reload, never per repaint and never + /// stat-per-row: the file is small and the painting path runs on every + /// row of every scroll. + void loadBusinessSenders(const QString &path = QString()); + + /// Test accessor, so the load can be asserted without reaching into the + /// delegate. + const BusinessSenders::List &businessSendersForTest() const + { + return m_businessSenders; + } + /// Runs save_message into \p directory instead of asking for one. /// /// The file dialog is a modal the offscreen platform cannot click, and the @@ -581,6 +625,10 @@ private slots: /// A tag mutation the worker has confirmed reached the database. Counts it /// as unsynced, since reaching the index is not reaching the mail store. void onTagsApplied(const TagChange &change); + + /// The subjects for the pending-changes list arrived; show the dialog. + void onPendingSubjectsResolved(const QStringList &subjects, + const QList<int> &messageCounts); void onAllTagsReady(const QStringList &tags); /// The Maildir root, answered once at startup. Enables nothing on its own: @@ -625,6 +673,11 @@ private slots: private: void buildUi(); + /// Pushes the selected account's identity into the card delegate, so the + /// fallback avatar is seeded from it. Empty selection ("All accounts") + /// clears both: the delegate then falls back to "??". + void applyCurrentAccountToDelegate(); + /// Restores window geometry, splitter and thread-list header widths. /// A missing or rejected blob leaves the buildUi() defaults in place. void restoreUiState(); @@ -829,11 +882,14 @@ private: /// Records one confirmed (message, tag) change, cancelling it against an /// opposite change already outstanding for the same pair. + /// + /// `action` is the name the user would recognise, carried through so the + /// list behind the count can say what each change was. It is the + /// TagChange's own description rather than anything derived from the tag. void recordPendingEdit(const QString &messageId, const QString &tag, - bool added); + bool added, const QString &action); + - /// Net changes the index holds that a sync has not carried over. - int pendingEditCount() const; /// Shows or hides the "syncing" state: the progress bar and a disabled /// Sync button. @@ -1278,6 +1334,13 @@ private: /// expander column are ThreadListView's, and holding the base here only /// hid that from every reader. ThreadListView *m_threadView = nullptr; + /// The card delegate, stored rather than discarded so the window can hand + /// it the account identity and the business-senders list. + CardDelegate *m_cardDelegate = nullptr; + + /// The parsed business-senders list, for the delegate. Empty until + /// loadBusinessSenders() runs. + BusinessSenders::List m_businessSenders; /// Right-click menu for the thread list, holding the same QActions the /// menu bar does. @@ -1508,12 +1571,27 @@ private: /// value true for added and false for removed; a pair that reverts is /// erased rather than stored, so an edit and its inverse leave nothing /// behind and the map cannot grow without bound. - QHash<QString, bool> m_pendingTagEdits; + /// What one pending (message, tag) edit is: its direction, and the name of + /// the action that made it. + /// + /// The direction alone was enough while this only had to be counted. The + /// list behind the count (item 119) has to SAY what each change was, and + /// only the action that made it knows: `+deleted` is a Delete and + /// `-unread` is a Mark read, but deriving that here would be a second + /// table of tag names to labels, drifting from the one the actions already + /// pass as TagChange::description. + struct PendingEdit { + bool added = false; + QString action; ///< Translated, from TagChange::description. + }; + QHash<QString, PendingEdit> m_pendingTagEdits; - /// Confirmed changes carrying no message ids, which cannot be netted - /// against anything. Counted separately rather than dropped: understating - /// the indicator is the direction that costs the user work. - int m_unnettablePendingEdits = 0; + /// The snapshot taken when the user clicked the indicator, held while the + /// worker resolves its subjects. Empty when no such request is in flight. + /// + /// One request at a time: a second click before the first answers replaces + /// it, which is right because both would show the same thing. + QVector<PendingChange> m_pendingChangeRequest; /// Marks the open thread read once it has been on screen long enough. /// |
