diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-26 15:55:13 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-26 15:55:13 +0200 |
| commit | ca5b6ca711e73bc22fdf108e06d82241e059c6ee (patch) | |
| tree | 26fc84fb4f5de11fbf71179fe2f3dc7eb9c01f2e | |
| parent | 5e769b073062a8913d2c4250d079aeb2d7d7b0e8 (diff) | |
| download | qtmaildir-ca5b6ca711e73bc22fdf108e06d82241e059c6ee.tar.gz qtmaildir-ca5b6ca711e73bc22fdf108e06d82241e059c6ee.zip | |
feat: expose a row's sender to the delegate
| -rw-r--r-- | src/notmuchworker.cpp | 1 | ||||
| -rw-r--r-- | src/threadlistmodel.cpp | 17 | ||||
| -rw-r--r-- | src/threadlistmodel.h | 9 | ||||
| -rw-r--r-- | src/types.h | 7 | ||||
| -rw-r--r-- | tests/test_threadlistmodel.cpp | 23 |
5 files changed, 57 insertions, 0 deletions
diff --git a/src/notmuchworker.cpp b/src/notmuchworker.cpp index 43f4758..28834df 100644 --- a/src/notmuchworker.cpp +++ b/src/notmuchworker.cpp @@ -222,6 +222,7 @@ void walkReplies(notmuch_messages_t *messages, int depth, QString::fromUtf8(notmuch_message_get_filename(message)); node.from = QString::fromUtf8(notmuch_message_get_header(message, "from")); + node.senderAddress = senderAddressOf(message); node.subject = QString::fromUtf8(notmuch_message_get_header(message, "subject")); node.date = diff --git a/src/threadlistmodel.cpp b/src/threadlistmodel.cpp index fcd8e4f..a6fada3 100644 --- a/src/threadlistmodel.cpp +++ b/src/threadlistmodel.cpp @@ -383,6 +383,13 @@ QVariant ThreadListModel::data(const QModelIndex &index, int role) const } case AccountLabelRole: return QString(); + case SenderAddressRole: + // The REPLY's own sender, exactly as SendersRole serves the node's + // `from` and not the thread's summary. The bare address is what + // the avatar hashes and the business-senders list matches. + return node.senderAddress; + case SenderNameRole: + return node.from; case Qt::DisplayRole: case SubjectRole: return node.subject; @@ -678,6 +685,16 @@ QVariant ThreadListModel::data(const QModelIndex &index, int role) const if (!thread.recipients.isEmpty()) return thread.recipients; return thread.authors; + case SenderAddressRole: + // The bare address of the message this card stands for, carried from + // the query. This is what the avatar's hash and the business-senders + // lookup consume; the display name below is what the card draws. + return thread.firstMessageSender; + case SenderNameRole: + // The same string the card's first line shows: recipients in a flat + // view, where `authors` is the user on every row and says nothing. + return !thread.recipients.isEmpty() ? thread.recipients + : thread.authors; case DateRole: // The QDateTime itself. Formatting belongs to the delegate now: the // card decides how much of a date it has room for, and a pre-formatted diff --git a/src/threadlistmodel.h b/src/threadlistmodel.h index 2e56328..4af09d8 100644 --- a/src/threadlistmodel.h +++ b/src/threadlistmodel.h @@ -52,6 +52,15 @@ public: /// Fill colour for that chip. AccountColourRole, + /// The bare address of the message this row stands for, for the + /// avatar's hash and for the business-senders lookup. Empty when the + /// query did not resolve one, which the delegate handles by falling + /// back to the account. + SenderAddressRole, + /// The display name to take initials from. `authors` for a thread row, + /// `recipients` in a flat view, matching what the card already shows. + SenderNameRole, + /// Every tag on the thread, for the strip under the message pane. TagsRole, diff --git a/src/types.h b/src/types.h index 47ddb78..a1689ab 100644 --- a/src/types.h +++ b/src/types.h @@ -166,6 +166,13 @@ struct MessageNode QString messageId; QString threadId; ///< The thread this message belongs to. QString from; + + /// The BARE address of the message's From, with any display name + /// discarded, like `ThreadSummary::firstMessageSender` is for a thread + /// row. `from` above is the raw header and usually carries a display + /// name, which is what the avatar's hash and the business-senders + /// lookup cannot take initials or match against. + QString senderAddress; QString subject; QDateTime date; QStringList tags; diff --git a/tests/test_threadlistmodel.cpp b/tests/test_threadlistmodel.cpp index 593a777..25c89b0 100644 --- a/tests/test_threadlistmodel.cpp +++ b/tests/test_threadlistmodel.cpp @@ -102,6 +102,7 @@ private slots: void flatModeOffersNoExpanderAndNoReplyCount(); void flatModeIsOffByDefaultAndReversible(); void recipientsReplaceTheSenderWhenPresent(); + void aRowCarriesItsSenderAndAccountAddress(); }; static ThreadSummary makeThread(const QString &id, const QString &subject) @@ -510,6 +511,28 @@ void TestThreadListModel::reportsSubjectAndAuthors() QStringList({ QStringLiteral("inbox"), QStringLiteral("unread") })); } +void TestThreadListModel::aRowCarriesItsSenderAndAccountAddress() +{ + // Task 8: the avatar needs the row's bare sender address to hash and to + // match against the business-senders list, and the display name to take + // initials from. Both come from the row itself, not from the load. + ThreadListModel model; + ThreadSummary summary; + summary.threadId = QStringLiteral("t1"); + summary.subject = QStringLiteral("Subject"); + summary.authors = QStringLiteral("John Doe"); + summary.firstMessageId = QStringLiteral("m1"); + summary.firstMessageSender = QStringLiteral("john@example.org"); + model.appendBatch({ summary }); + + const QModelIndex index = model.index(0, 0); + QCOMPARE(index.data(ThreadListModel::SenderAddressRole).toString(), + QStringLiteral("john@example.org")); + // The display name comes from `authors`, which is all notmuch gives. + QCOMPARE(index.data(ThreadListModel::SenderNameRole).toString(), + QStringLiteral("John Doe")); +} + void TestThreadListModel::theReplyCountExcludesTheRootMessage() { ThreadListModel model; |
