aboutsummaryrefslogtreecommitdiffstats
path: root/src/mainwindow.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainwindow.h')
-rw-r--r--src/mainwindow.h94
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.
///