diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-29 09:49:29 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-29 09:49:29 +0200 |
| commit | f051dd657757bca3ac7e36454d7f2fc21f322f73 (patch) | |
| tree | 5804923ed3eb747ee2cd84284e1b36fe0817ab8e /src/notmuchworker.cpp | |
| parent | d128919ad80ecc37b1f58d2ba83a7487362c126f (diff) | |
| download | qtmaildir-f051dd657757bca3ac7e36454d7f2fc21f322f73.tar.gz qtmaildir-f051dd657757bca3ac7e36454d7f2fc21f322f73.zip | |
fix: judge a conversation's trash state on all of its messages
Item 178. everySelectedRowIsInATrashFolder() read
ThreadSummary::firstMessagePath for any row that was not a message row.
That was correct while a thread row MEANT that message (item 108) and
stopped being correct when item 177 made it mean the conversation. A
conversation is in the trash only when ALL of its messages are, so a
partly trashed thread answered on whichever message the query returned
first: Delete could be hidden on a conversation that still had mail
outside the trash, and Restore offered on one that mostly did not.
Not data-affecting. Both actions are no-ops in the wrong direction:
Delete on already-trashed mail takes moveMessages()' already-there
branch, and Restore on mail that was never trashed finds nothing to
move.
qtmaildir cannot produce such a thread itself, since Delete is absent on
a reply row and Restore is thread-scoped. Two things outside it can:
another client trashing a single message, and a reply arriving after the
conversation was trashed.
ThreadDigest already walks every message of the selected conversation
for its sender counts, and a filename is served from the index like
everything else in it, so the paths ride along on a request the
selection already makes rather than costing a walk on every query.
ThreadDigest::messagePaths is relative to the mail root, for the reason
firstMessagePath records: an absolute path matches no account and
silently resolves every row to none. MainWindow keeps them beside the
dashboard's thread id and clears them when the dashboard is left, so a
late digest cannot answer about another row.
One limit, stated in the code rather than hidden. The digest is
requested only for a single selected conversation row, so that is the
only case with a real answer; any other selection falls back to the
summary's one path. That fallback IS the pre-177 answer and is wrong in
exactly the same partial case, which is the point: a multi-row selection
is left no worse than it was, rather than given a second, differently
wrong rule of its own. Making it exhaustive costs a per-query walk over
every message, which is what this avoids.
Two tests, both mutation-checked. The worker test puts its two messages
in different folders, since two in one folder answer identically
whichever way the code resolves them. The window test asserts both
directions, so a fix that simply hid Delete everywhere would fail it,
and sets totalCount explicitly: a summary left at the default is a
message row, and the test would otherwise exercise the other branch and
pass for the wrong reason.
Suite: 42 of 43, with undoMovesTheMessageBack failing as it does on
master (item 136).
Diffstat (limited to 'src/notmuchworker.cpp')
| -rw-r--r-- | src/notmuchworker.cpp | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/src/notmuchworker.cpp b/src/notmuchworker.cpp index 821c575..3a0b2c1 100644 --- a/src/notmuchworker.cpp +++ b/src/notmuchworker.cpp @@ -776,6 +776,11 @@ void NotmuchWorker::loadThreadDigest(const QString &threadId, QVector<MessageNode> unread; QVector<qint64> timestamps; + // mailRootOf(), never notmuch_database_get_path(): under a split index the + // latter returns the XAPIAN directory, and a path relative to that matches + // no account (item 124). + const QString dbRoot = QDir(mailRootOf(m_db)).absolutePath(); + notmuch_messages_t *messages = notmuch_thread_get_messages(thread.get()); for (; notmuch_messages_valid(messages); notmuch_messages_move_to_next(messages)) { @@ -786,6 +791,15 @@ void NotmuchWorker::loadThreadDigest(const QString &threadId, ++digest.totalCount; + // Item 178: which FOLDER each message lives in, so Delete and Restore + // can judge a conversation on all of it rather than on whichever + // message the query returned first. Read from the index by the walk + // that is already running. + if (const char *rawName = notmuch_message_get_filename(message)) { + digest.messagePaths.append( + QDir(dbRoot).relativeFilePath(QString::fromUtf8(rawName))); + } + const qint64 when = notmuch_message_get_date(message); timestamps.append(when); |
