From b7f2a4e0f8858b1aa0d86755ebab6826306f3eff Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 24 Aug 2026 21:57:28 +0200 Subject: fix(drafts): index a saved draft so it appears without a sync Autosave writes the draft to the Maildir drafts folder and stops, while the Drafts view is a notmuch path: query, so a freshly saved draft was invisible until notmuch new ran. saveDraftNow() now emits draftSaved, and MainWindow connects it to a new NotmuchWorker::indexDraftFile() that indexes the one file the way moveMessages() does, with the previous revision removed so a rewrite leaves no ghost. The send path unlinks a draft that was indexed while being composed, so draftRemoved -> removeIndexedFile() drops its entry too. Measured: notmuch_database_index_file assigns NO tags (unlike notmuch new, which adds draft inbox unread), so no tag-stripping is needed and the draft cannot leak into a tag:inbox view. Item 158. --- tests/test_composewindow.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'tests/test_composewindow.cpp') diff --git a/tests/test_composewindow.cpp b/tests/test_composewindow.cpp index 47f7d87..472c103 100644 --- a/tests/test_composewindow.cpp +++ b/tests/test_composewindow.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ private slots: void changingTheAccountFollowsItsSignature(); void changingTheAccountStopsFollowingOnceTheSwitchIsUsed(); void aResumedDraftDoesNotReseedOnAnAccountChange(); + void savingADraftEmitsItsPathAndTheReplacedOne(); private: /// A config pointing at a signatures directory holding \p files, with one @@ -393,5 +395,57 @@ void TestComposeWindow::aResumedDraftDoesNotReseedOnAnAccountChange() QVERIFY(!body->toPlainText().contains(QStringLiteral("Home sig"))); } +void TestComposeWindow::savingADraftEmitsItsPathAndTheReplacedOne() +{ + // A config whose account has a drafts folder, which makeConfig() does not + // set, so the save can actually write somewhere. + const QString confPath = m_dir->path() + QStringLiteral("/qtmaildir.conf"); + { + QString conf; + QTextStream out(&conf); + out << "[account.work]\n" + << "name = Someone\n" + << "address = someone@example.org\n" + << "maildir = work\n" + << "drafts = Drafts\n" + << "send_command = /bin/cat\n"; + writeFile(confPath, conf); + } + Config config; + config.load(confPath); + + ComposeContext context; + context.kind = ComposeContext::Kind::New; + context.accountKey = QStringLiteral("work"); + + ComposeWindow window(context, config, m_dir->path()); + auto *body = window.findChild(QStringLiteral("body")); + QVERIFY(body); + + QSignalSpy saved(&window, &ComposeWindow::draftSaved); + + body->setPlainText(QStringLiteral("First revision.")); + QVERIFY(window.saveDraftNow()); + + QCOMPARE(saved.size(), 1); + const QString first = saved.first().at(0).toString(); + const QString firstPrevious = saved.first().at(1).toString(); + QVERIFY(!first.isEmpty()); + QVERIFY(firstPrevious.isEmpty()); + QVERIFY(QFile::exists(first)); + + // A rewrite writes a fresh file and unlinks the old; the previous path + // comes back so the owner can drop the old index entry. + body->setPlainText(QStringLiteral("Second revision.")); + QVERIFY(window.saveDraftNow()); + + QCOMPARE(saved.size(), 2); + const QString second = saved.at(1).at(0).toString(); + const QString secondPrevious = saved.at(1).at(1).toString(); + QVERIFY(!second.isEmpty()); + QCOMPARE(secondPrevious, first); + QVERIFY2(second != first, "a rewrite reused the old filename"); +} + QTEST_MAIN(TestComposeWindow) #include "test_composewindow.moc" -- cgit v1.2.3