diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-14 20:06:08 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-14 20:06:08 +0200 |
| commit | 4a4849421ddac044212cd0e17f9aee8ff2606292 (patch) | |
| tree | bedfb35f370b637eedcb3cd4d24be8ac75b257b5 /src/threadlistmodel.cpp | |
| parent | bde7409ef817089298718376e46a57b2d303cf02 (diff) | |
| download | qtmaildir-4a4849421ddac044212cd0e17f9aee8ff2606292.tar.gz qtmaildir-4a4849421ddac044212cd0e17f9aee8ff2606292.zip | |
Revert the message-scoped auto mark-read
Reverts bde7409 and 66f1159. The user hit the worst possible symptom:
clicking one message marked a DIFFERENT, unrelated message read.
The cause is in markCurrentThreadRead, which reads
m_model->threadAt(current.row()). CLAUDE.md records this exact trap: a
tree numbers rows PER PARENT, so a reply's row() indexes its siblings
and threadAt() on it answers about an unrelated thread near the top of
the list. The guards then compared the right ids against the wrong
thread and let a write through for whatever message the timer's state
named.
That fault predates these commits, but they made it reachable and
harmful: while the write was thread-scoped the mismatch was mostly
masked, and scoping it to a single message turned it into "a random
message is now read".
Reverting rather than fixing forward. Marking the wrong mail read syncs
out to the server and cannot be undone from here, so the safe state is
the previous behaviour, which is too broad but predictable. The item 66
work in 4a4f82f stands: a thread root still renders one message.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/threadlistmodel.cpp')
| -rw-r--r-- | src/threadlistmodel.cpp | 81 |
1 files changed, 0 insertions, 81 deletions
diff --git a/src/threadlistmodel.cpp b/src/threadlistmodel.cpp index 04a5493..3e079ed 100644 --- a/src/threadlistmodel.cpp +++ b/src/threadlistmodel.cpp @@ -888,84 +888,3 @@ void ThreadListModel::applyTagChange(const QString &threadId, return; } } - -void ThreadListModel::applyMessageTagChange(const QString &messageId, - const QStringList &added, - const QStringList &removed) -{ - const auto retag = [&added, &removed](QStringList *tags) { - for (const QString &tag : removed) - tags->removeAll(tag); - for (const QString &tag : added) { - if (!tags->contains(tag)) - tags->append(tag); - } - }; - - for (int row = 0; row < m_threads.size(); ++row) { - ThreadNode &node = m_threads[row]; - bool touched = false; - - // `first` is a copy of the opening message rather than an alias into - // children, so both have to be updated when they name the same one. - if (node.first.messageId == messageId) { - retag(&node.first.tags); - touched = true; - } - - for (int child = 0; child < node.children.size(); ++child) { - if (node.children.at(child).messageId != messageId) - continue; - retag(&node.children[child].tags); - touched = true; - const QModelIndex childIndex = index(child, 0, index(row, 0)); - emit dataChanged(childIndex, childIndex); - } - - // The thread has not been expanded and does not open with this - // message, so nothing here holds it. The summary may still need to - // follow, which the totalCount check below decides. - if (!touched && node.summary.firstMessageId != messageId - && node.summary.totalCount > 1) { - continue; - } - - // The thread's own tags follow only when the answer is unambiguous. - // - // A thread carries `unread` while ANY of its messages does, so a - // one-message change can only clear it from the thread when there is - // nothing else left to carry it. With one message in the thread that - // is certain. With more, the honest answer needs every message's tags, - // which are only loaded once the thread has been expanded; until then - // the summary is left alone rather than guessed at, and the next query - // corrects it. - const bool wholeThread = - node.summary.totalCount <= 1 - || (!node.children.isEmpty() - && node.children.size() >= node.summary.totalCount); - if (!wholeThread) { - if (touched) - emit dataChanged(index(row, 0), index(row, 0)); - continue; - } - - for (const QString &tag : removed) { - bool stillHeld = false; - for (const MessageNode &child : node.children) { - if (child.messageId != messageId && child.tags.contains(tag)) { - stillHeld = true; - break; - } - } - if (!stillHeld) - node.summary.tags.removeAll(tag); - } - for (const QString &tag : added) { - if (!node.summary.tags.contains(tag)) - node.summary.tags.append(tag); - } - - emit dataChanged(index(row, 0), index(row, 0)); - return; - } -} |
