diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-14 20:02:14 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-14 20:02:14 +0200 |
| commit | bde7409ef817089298718376e46a57b2d303cf02 (patch) | |
| tree | 5fea161c839e30474db0a16c79f20b5fca976ffb /tests/test_mainwindow.cpp | |
| parent | 66f1159136de5e2f032ddc83b5b4b4bde2659291 (diff) | |
| download | qtmaildir-bde7409ef817089298718376e46a57b2d303cf02.tar.gz qtmaildir-bde7409ef817089298718376e46a57b2d303cf02.zip | |
fix(read): repaint the card when one message is marked read
Follows the message-scoped mark-read. The user reported the write going
out with nothing visible changing: the status bar counted an unsynced
edit while the card stayed bold and the message pane still showed the
`unread` tag, until the next query corrected it.
sendMessageTagChange made no optimistic model update on purpose, because
applyTagChange is keyed by THREAD and repainting a whole row for a
one-message edit would claim every reply had changed too. That trade is
right for an explicit tag edit and wrong for auto mark-read, where the
visible change IS the feature and the delay exists to deliver it.
ThreadListModel::applyMessageTagChange updates the message wherever it
is held, as a child row and as `first`, and lets the thread's summary
follow only when the answer is unambiguous: a thread reads as unread
while ANY message does, so the tag is cleared from the thread only when
no other message still carries it. For an unexpanded multi-message
thread the per-message tags are not loaded, so the summary is left for
the next query rather than guessed at.
Mutation checked: without the call the card holds `unread` for the full
timeout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_mainwindow.cpp')
| -rw-r--r-- | tests/test_mainwindow.cpp | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index ea42223..5347493 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -186,6 +186,7 @@ private slots: void anUnexpandedRootRendersOneMessageNotTheConversation(); void aFirstClickIntoAnUnfocusedListStillRenders(); void autoMarkReadTouchesOnlyTheMessageOnScreen(); + void autoMarkReadClearsUnreadOnTheCardImmediately(); void autoSyncIsNotArmedWhenDisabledOrWithNothingPending(); void autoSyncSkipsWhileABackgroundSyncIsRunning(); void aSuccessfulSyncRefreshesRatherThanRerunningTheQuery(); @@ -6555,4 +6556,61 @@ void TestMainWindow::autoMarkReadTouchesOnlyTheMessageOnScreen() QStringList{ QStringLiteral("root@example.org") }); } +void TestMainWindow::autoMarkReadClearsUnreadOnTheCardImmediately() +{ + // Reported by the user against the message-scoped mark-read: the write + // went out, the status bar counted an unsynced edit, and the card stayed + // bold with `unread` still on it. sendMessageTagChange deliberately makes + // no optimistic model update, because applyTagChange is keyed by THREAD + // and repainting a whole row for a one-message edit would be a lie. + // + // For an explicit tag edit that trade is fine. For auto mark-read it is + // not: the visible change IS the feature, and the 2s delay exists to give + // the user that feedback. + // + // One message in the thread, so the thread's own unread state and the + // message's are the same fact and the card must stop reading as unread. + WorkerBackedWindow backed; + backed.setGeneralKey(QStringLiteral("mark_read_delay_ms"), + QStringLiteral("0")); + QVERIFY(backed.fixture().addMessage( + QStringLiteral("inbox"), QStringLiteral("only@example.org"), + QStringLiteral("A single message"), + QStringLiteral("sender@example.org"), + QStringLiteral("Fri, 14 Aug 2026 10:00:00 +0200"), + QStringLiteral("The only message."), /*unread=*/true)); + QVERIFY2(backed.build(), qPrintable(backed.error())); + + MainWindow window(backed.config()); + + QLineEdit *queryEdit = + window.findChild<QLineEdit *>(QStringLiteral("queryEdit")); + QVERIFY2(queryEdit, "no query bar"); + auto *view = window.findChild<ThreadListView *>(); + QVERIFY2(view, "no thread list view"); + auto *model = window.findChild<ThreadListModel *>(); + QVERIFY2(model, "no thread list model"); + + queryEdit->setText(QStringLiteral("tag:inbox")); + queryEdit->returnPressed(); + QTRY_VERIFY_WITH_TIMEOUT(model->rowCount(QModelIndex()) == 1, 15000); + + const QModelIndex root = model->index(0, 0, QModelIndex()); + QVERIFY(root.isValid()); + + // Unread to begin with, or the assertion below proves nothing. + QVERIFY2(model->data(root, ThreadListModel::TagsRole) + .toStringList() + .contains(QStringLiteral("unread")), + "the thread was not unread to begin with"); + + view->setCurrentIndex(root); + + // The card must stop reading as unread without waiting for a new query. + QTRY_VERIFY_WITH_TIMEOUT(!model->data(root, ThreadListModel::TagsRole) + .toStringList() + .contains(QStringLiteral("unread")), + 15000); +} + #include "test_mainwindow.moc" |
