From 4a4f82f7709ab6ee5a84ac0f3b191470b6424c36 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 14 Aug 2026 19:16:16 +0200 Subject: feat(pane): always render one message, never the conversation Selecting a thread root used to render the whole conversation, stubs plus the last messages expanded, but only until the thread had been expanded once. After that the identical click rendered a single message. The user reported the inconsistency and asked for the single-message behaviour throughout, and for the conversation view to go. The cause was a timing one, not a race. The root card stands for the thread's first message and onThreadSelected already preferred to load just that, but the model learned the id only when the replies arrived, so a fresh row fell through to a whole-thread render. ThreadSummary now carries firstMessageId from the query itself, so the id is known before any expansion and the fallback is unreachable. It is free: notmuch_thread_get_toplevel_messages reads the index, not the message files, and a walk with it is indistinguishable from one without over a 36,615-thread database. Contrast recipients, which reads every file and stays Sent-only. The Sent view keeps showing what the user sent rather than the thread's opening message, which is often someone else's. There is no matched-messages iterator in libnotmuch, only a count, so that branch walks oldest-first to the first NOTMUCH_MESSAGE_FLAG_MATCH and stops: 0.146s against a 0.143s baseline over 4,515 threads. onThreadLoaded merges into renderMessages, since onMessageLoaded was already delegating to it for the actual painting. It still takes a list because MessageView renders a list; collapsing that is a separate change to a class with its own tests. NotmuchWorker::loadThread is kept and documented as having no UI caller. It is a tested way to read a thread's messages with the match set resolved, used as a helper by the worker's own tests. Co-Authored-By: Claude Opus 5 --- src/notmuchworker.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/notmuchworker.h') diff --git a/src/notmuchworker.h b/src/notmuchworker.h index 9736ab8..f07e563 100644 --- a/src/notmuchworker.h +++ b/src/notmuchworker.h @@ -71,6 +71,19 @@ public slots: /// Loads the messages of one thread, oldest first. matchQuery is the /// user's current query; messages matching it render expanded, the rest /// as stubs. + /// **No UI caller since item 66, and that is deliberate.** This was how a + /// thread root rendered the whole conversation, stubs plus the last few + /// messages expanded. The user asked for that view to go: selecting any + /// row, root or reply, now renders exactly one message via loadMessage, + /// and `ThreadSummary::firstMessageId` is what makes the root's own + /// message known without expanding the thread first. + /// + /// Kept as a worker capability rather than deleted. It is a tested way to + /// read every message of a thread with the match set resolved, which the + /// worker's own tests use as a helper and a future feature may want. If + /// you are adding a caller, be sure you are not rebuilding the + /// conversation pane that was removed on purpose. + /// /// `matchedOnly` drops the messages that did not match `matchQuery` rather /// than rendering them as stubs. For the Sent view, where the thread is not /// the unit the user is reading: a sent message pulls in the replies it -- cgit v1.2.3