summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/types.h31
-rw-r--r--tests/test_threadlistmodel.cpp26
2 files changed, 57 insertions, 0 deletions
diff --git a/src/types.h b/src/types.h
index 999d3f3..6b2ca4c 100644
--- a/src/types.h
+++ b/src/types.h
@@ -63,6 +63,37 @@ struct MessageRef
bool matched = true;
};
+/// One message as a row in the thread list.
+///
+/// Separate from MessageRef, which exists for RENDERING a thread and carries
+/// only what the message pane needs. A row has to be drawn without opening the
+/// message at all, so the display facts live here.
+struct MessageNode
+{
+ QString messageId;
+ QString threadId; ///< The thread this message belongs to.
+ QString from;
+ QString subject;
+ QDateTime date;
+ QStringList tags;
+ QString filePath;
+
+ /// Reply depth within the thread. 0 is the thread's first message, which
+ /// occupies the ROOT row rather than a child row: the user's model is
+ /// "N replies", so a thread of 7 shows 1 root and 6 descendants.
+ int depth = 0;
+
+ bool isUnread() const { return tags.contains(QStringLiteral("unread")); }
+ bool isFlagged() const { return tags.contains(QStringLiteral("flagged")); }
+
+ /// notmuch applies "attachment" while indexing, so this needs no MIME
+ /// parsing, exactly as on ThreadSummary.
+ bool hasAttachment() const
+ {
+ return tags.contains(QStringLiteral("attachment"));
+ }
+};
+
/// One tag mutation, kept so it can be inverted for undo.
struct TagChange
{
diff --git a/tests/test_threadlistmodel.cpp b/tests/test_threadlistmodel.cpp
index f84bbab..e2ca09f 100644
--- a/tests/test_threadlistmodel.cpp
+++ b/tests/test_threadlistmodel.cpp
@@ -27,6 +27,7 @@ class TestThreadListModel : public QObject
{
Q_OBJECT
private slots:
+ void messageNodeHoldsDisplayFacts();
void startsEmpty();
void accountKeysComeFromTheAccountTags();
void accountKeysCoverAThreadSpanningTwoAccounts();
@@ -116,6 +117,31 @@ void TestThreadListModel::accountKeysAreEmptyForAnUnknownThread()
QVERIFY(model.accountKeysForThread(QStringLiteral("nope")).isEmpty());
}
+void TestThreadListModel::messageNodeHoldsDisplayFacts()
+{
+ // A message ROW has to be drawn without opening the message, so the display
+ // facts live on the node itself. MessageRef, which exists for rendering a
+ // thread into the pane, carries none of them.
+ MessageNode node;
+ node.messageId = QStringLiteral("id@example.org");
+ node.from = QStringLiteral("A Sender <sender@example.org>");
+ node.subject = QStringLiteral("Re: a subject");
+ node.date = QDateTime::fromSecsSinceEpoch(1000);
+ node.depth = 2;
+ node.tags = QStringList{ QStringLiteral("unread") };
+
+ QCOMPARE(node.depth, 2);
+ QVERIFY(node.isUnread());
+ QCOMPARE(node.from, QStringLiteral("A Sender <sender@example.org>"));
+ QCOMPARE(node.subject, QStringLiteral("Re: a subject"));
+
+ // Depth 0 is the thread's first message, which the ROOT row stands for.
+ // Defaulting to 0 rather than 1 keeps "is this the root" a plain check.
+ const MessageNode fresh;
+ QCOMPARE(fresh.depth, 0);
+ QVERIFY(!fresh.isUnread());
+}
+
void TestThreadListModel::startsEmpty()
{
ThreadListModel model;