aboutsummaryrefslogtreecommitdiffstats
path: root/src/mainwindow.h
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-31 12:33:10 +0100
committerDanilo M. <danix@danix.xyz>2026-10-31 12:33:10 +0100
commitc9882b609ade04a8b17a89f6b05d51bf939e330c (patch)
treec8bd66417264edafefaa2077d37b5455c046ff51 /src/mainwindow.h
parentef42103bb1639006c5558eae5f857cd8f9ad2418 (diff)
downloadqtmaildir-c9882b609ade04a8b17a89f6b05d51bf939e330c.tar.gz
qtmaildir-c9882b609ade04a8b17a89f6b05d51bf939e330c.zip
fix: build a backfill's undo from the worker's answer, not the queue
Apply no longer arms commands on the shared confirmation queue. A backfill can run for a long time; a gesture made meanwhile cleared the queue, and the backfill's confirmation was then credited to the user's command, so undoing that command inverted the backfill over every message it touched. MainWindow counts the whole rule, confirms, and sends applyRule() with a token. onRuleApplied() drops a stale token, records each operation's effective change through the bookkeeping onTagsApplied() shares, and pushes one MessageTagCommand per operation with the ids it moved, inside one undo macro. Every configured account is marked edited, since a backfill reaches mail no row shows and a narrowed sync could otherwise clear the pending count without pushing it. Nothing moved shows the nothing-to-change status instead of "done". Also: a count of -1 says the rule could not be counted, and the confirmation says undo lasts until the next search, which is when runQuery() clears the stack. Item 212. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/mainwindow.h')
-rw-r--r--src/mainwindow.h40
1 files changed, 29 insertions, 11 deletions
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 5c9e87b..f962089 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -167,6 +167,12 @@ public:
/// the worker resolves its query.
void applyRuleToExistingMail(const TagRule &rule);
+ /// Arms one MessageTagCommand on the confirmation queue as a user gesture
+ /// would, opening a new gesture, and pushes it. Returned, owned by the
+ /// undo stack, so a test can show a backfill leaves it alone.
+ TagCommand *armTagCommandForTesting(const QStringList &add,
+ const QStringList &remove);
+
/// 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(); }
@@ -787,13 +793,7 @@ private slots:
/// describes state lasting until the sync ends, and a message that expired
/// would leave rows showing a tag the database has not got and no
/// explanation of why.
- ///
- /// `onDone`, when given, runs once the worker has reached the write: the
- /// caller's follow-up (a refresh), not a label, so it runs whichever
- /// announcement owns the label by then. Not run on the sync-held branch;
- /// no caller passes one while a sync holds the lock.
- void announceAction(const QString &text,
- const std::function<void()> &onDone = {});
+ void announceAction(const QString &text);
/// Reacts to a sync started outside this window, by cron or by hand.
///
@@ -857,11 +857,24 @@ private slots:
/// request has superseded this one.
void onRuleCountsReady(const QVector<int> &counts, quint64 generation);
+ /// The worker's answer to applyRule(), one change per single-tag
+ /// operation, each carrying the ids it actually moved. Builds the undo
+ /// entry from those KNOWN ids. A backfill never arms a command on the
+ /// shared confirmation queue: it can run for a long time, a gesture made
+ /// meanwhile clears that queue, and the backfill's confirmation was then
+ /// credited to the user's command, whose undo inverted it over thousands
+ /// of messages.
+ void onRuleApplied(quint64 token, const QVector<TagChange> &changes);
+
private:
- /// The count for applyRuleToExistingMail() arrived: confirm, then write.
- /// `counts` holds the whole rule's count first, then one per single-tag
- /// operation in the order ruleOperations() lists them.
- void confirmRuleApply(const QVector<int> &counts);
+ /// What onTagsApplied() does for any confirmed write, apart from crediting
+ /// an armed undo command: pending edits, the indicator, the auto-sync,
+ /// the dashboard, and the known-tag list. Shared with onRuleApplied().
+ void recordConfirmedTagChange(const TagChange &change);
+
+ /// The count for applyRuleToExistingMail() arrived: confirm, then send
+ /// the rule to the worker's applyRule().
+ void confirmRuleApply(int count);
void buildUi();
@@ -1849,6 +1862,11 @@ private:
TagRule m_pendingApplyRule;
quint64 m_pendingApplyGeneration = 0;
+ /// The token the outstanding applyRule() will answer with, 0 when none.
+ /// A reply carrying any other token is stale and dropped.
+ quint64 m_pendingRuleToken = 0;
+ quint64 m_lastRuleToken = 0;
+
/// The generation of a REFRESH query, run after a sync to bring the list
/// up to date without disturbing it.
///