diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-04 20:05:01 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-04 20:05:01 +0200 |
| commit | 8485283bbc30c886e28722516dc6f0b11c3bf82d (patch) | |
| tree | 1caf649b37b8ac693991f4bdf6d12247f9a0eefa /docs | |
| parent | bd1b907ba22464bb869bcc10cc9327ed74c44791 (diff) | |
| download | qtmaildir-8485283bbc30c886e28722516dc6f0b11c3bf82d.tar.gz qtmaildir-8485283bbc30c886e28722516dc6f0b11c3bf82d.zip | |
fix(sync): count unsynced edits as net state, not as writes
Item 28, reported by the user: open a thread, let the automatic
mark-read remove `unread`, then press Ctrl+U to put it back. The
indicator read "2 unsynced change(s)" with the mail store exactly where
it started.
The counter incremented per confirmed write and never decremented, so
any add-then-remove of the same tag inflated it. Mark-read is simply the
path that fires without being asked, which is why it surfaced there.
The user's call was net state: an edit and its inverse are zero
outstanding changes, because what the indicator answers is whether
quitting now would strand work. A QHash keyed "<messageId>\n<tag>"
replaces the int, and a pair that reverts is erased rather than stored
with the new direction, so the map cannot grow without bound across a
long session of tagging and untagging.
Keyed per (message, tag) rather than per message: removing `unread` and
adding `flagged` on one message are independent changes and must not
cancel each other. A change carrying no message ids cannot be netted
against anything and is counted separately, since dropping it would
understate the indicator, which is the direction that costs work.
Both properties item 18 established still hold: a successful sync clears
everything, a failed one clears nothing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index 67668d4..c3a1f4e 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -72,7 +72,7 @@ taking that too literally. | 25 | No select-all, and bulk actions are undiscoverable | workflow | S | **done** | | 26 | No way to add or remove an arbitrary tag from the UI | workflow | S | **done** | | 27 | The UI cannot see a sync it did not start | feedback | S | **done** | -| 28 | Re-adding `unread` counts 2 unsynced changes, not 0 | correctness | S | open | +| 28 | Re-adding `unread` counts 2 unsynced changes, not 0 | correctness | S | **done** | | 29 | Sync button stays enabled during a background sync | feedback | XS | **done** | | 30 | The blank right pane is wasted space | presentation | M | open | | 31 | The quit prompt has no highlighted default button | discoverability | XS | **done** | @@ -1386,6 +1386,28 @@ recognising an inverse exists. - Do not fix this by not counting the automatic mark-read. It is a real write to the index, and hiding it would make the count wrong in the other direction. +### Outcome (done) + +**Decided by the user, 2026-08-04: net state.** "If I undo delete it's 0 edits, +not 2." The counter is replaced by a `QHash<QString, bool>` keyed +`"<messageId>\n<tag>"`, and a pair that reverts is **erased** rather than stored +with the new direction, so an edit and its inverse leave nothing behind and the +map cannot grow without bound over a long session of tagging and untagging. + +**Keyed per (message, tag), not per message.** Removing `unread` and adding +`flagged` on one message are two independent changes; a per-message key would +have cancelled them against each other. A test pins this, and it passed before +the change, so it exists to stop a later simplification from over-netting. + +**A change carrying no message ids still counts**, tracked in a separate +`m_unnettablePendingEdits`. It cannot be netted against anything, and dropping +it would understate the indicator, which is the direction that costs the user +work. This also keeps the older tests honest: they emit a `TagChange` with no +ids, and would otherwise have started reporting zero. + +Both properties item 18 established survive: a successful sync clears everything, +a failed one clears nothing. + ## 29. Sync button stays enabled during a background sync **Observed (user, 2026-08-04):** while a cron sync runs, the Sync button is |
