diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-26 13:28:51 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-26 13:30:22 +0200 |
| commit | 0ca4624195cdd8c78ff614e3912af5b914458497 (patch) | |
| tree | 6fc739e77fed6b5a4e178cacf76ac29fd708ab0b /src/threadlistmodel.h | |
| parent | d835cf3c554e9657fffdb91971b66e3a74aee323 (diff) | |
| download | qtmaildir-0ca4624195cdd8c78ff614e3912af5b914458497.tar.gz qtmaildir-0ca4624195cdd8c78ff614e3912af5b914458497.zip | |
feat: flag what you answered, mark what was forwarded to you
Item 68, which turned out to be three things once its premise was
measured. The note asked to extend a "passed" subject rule to "Fw:";
there was no subject rule, and the correlation it rested on did not
exist. What did exist was a gap nobody had reported.
Reply and forward now flag their source. The Maildir R and P flags,
which every other client sets and notmuch reads back as "replied" and
"passed", had never been written here: measured on the developer's
index, all 317 "replied" and all 6 "passed" came from other clients.
ComposeWindow emits sourceMessageAnswered after a successful send and
MainWindow routes it through sendMessageTagChange, message-scoped and
off the undo stack, for the reason auto mark-read is: the flag records
that the mail went, and the send cannot be undone.
ComposeContext carries sourceMessageId rather than reusing inReplyTo,
which is deliberately empty on a forward so the recipient's client does
not file it under the thread it left. Keying on it made the "passed"
half dead code that compiled and never fired. A resumed draft is
excluded: its kind records how the file was opened, not what the user is
doing, so flagging on it would set R from a guess.
A received forward gets its own mark. Derived from the subject at paint
time, storing nothing and reaching no server, because "passed" means "I
forwarded this" and setting it from a guess would assert something false
on 222 existing messages. subjectIsForwarded() shares forwardSubject()'s
prefix table so the two cannot disagree, strips a Re: chain first, and
takes extra locale spellings from [general] forward_prefixes, which
extends the built-in table rather than replacing it.
A mutation survived the first round and corrected a claim in the code:
QRegularExpression::escape already makes a punctuation prefix inert, so
the word guard is not about pattern validity. It stops a configured "-"
matching "-: x". The comment and test say that now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LXCZFLXbAii5n5wtovpdhh
Diffstat (limited to 'src/threadlistmodel.h')
| -rw-r--r-- | src/threadlistmodel.h | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/src/threadlistmodel.h b/src/threadlistmodel.h index 717537c..2e56328 100644 --- a/src/threadlistmodel.h +++ b/src/threadlistmodel.h @@ -135,6 +135,12 @@ public: /// Item 69 draws this as a mark where it used to read as the word /// "passed" in the tag strip. IsPassedRole, + /// True when the SUBJECT reads as a forward someone sent the user. + /// + /// Item 68. Derived from the subject at query time, not from a tag or + /// a Maildir flag: `passed` means "I forwarded this", which is a + /// different fact. Nothing is stored and nothing reaches the server. + IsReceivedForwardRole, /// bool; the message was replied to, from the Maildir "R" flag. IsRepliedRole, @@ -187,6 +193,16 @@ public: /// The pattern DateFormatRole answers with. Empty means the system format. void setDateFormat(const QString &format) { m_dateFormat = format; } + /// Extra subject prefixes counting as a received forward (item 68). + /// + /// Pushed in from the config exactly as setDateFormat() is, rather than + /// giving the model a Config: both are display values the window already + /// holds, and the model draws rather than resolves. + void setForwardPrefixes(const QStringList &prefixes) + { + m_forwardPrefixes = prefixes; + } + /// One row per thread, with no expander and no reply count. /// /// For the Sent view, where a thread is the wrong unit: the user's model of @@ -203,6 +219,29 @@ public: /// The children are not discarded, only hidden. Leaving flat mode restores /// the tree without reloading anything. void setFlatMode(bool flat); + + /// Whether the list is showing the trash view. + /// + /// The doomed fill exists to tell the user a message is on its way out of + /// a view it is still sitting in. In the trash that is redundant: every + /// row is deleted, and a list painted entirely crimson says nothing while + /// costing legibility. Set on EVERY query run, like flat mode, so it + /// cannot leak into the next view. + void setTrashView(bool trash); + + /// Drops any top-level row whose message no longer carries \p tag. + /// + /// The optimistic counterpart to a row simply vanishing at the next query. + /// Delete strips `inbox`, and in the Inbox view the row it stripped it + /// from stops belonging there; leaving it until the next sync is what made + /// a deleted message sit in the inbox looking undeleted. + /// + /// Top-level rows ONLY, and deliberately: a reply that no longer matches + /// still belongs to the conversation the user has open, and removing it + /// would collapse a thread under the reader's hands. \p tag is the tag the + /// CURRENT VIEW requires, so a caller passes what the query filters on and + /// nothing else. + void removeThreadsWithoutTag(const QString &tag); bool flatMode() const { return m_flatMode; } QModelIndex index(int row, int column, @@ -414,5 +453,7 @@ private: QVector<ThreadNode> m_threads; const TagColors *m_tagColors = nullptr; QString m_dateFormat; + QStringList m_forwardPrefixes; bool m_flatMode = false; + bool m_trashView = false; }; |
