From 409faeadf013952420963fa1b740d44526d8a95e Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 26 Aug 2026 16:13:06 +0200 Subject: feat: load the business-senders list at startup --- src/mainwindow.cpp | 35 ++++++++++++++++++++++++++++++++++- src/mainwindow.h | 31 +++++++++++++++++++++++++++++++ tests/test_mainwindow.cpp | 18 ++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9166588..ec43b3c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -520,6 +520,17 @@ MainWindow::MainWindow(const Config &config, QWidget *parent) buildUi(); registerActions(); + // Both need the delegate, which buildUi() just created. The list is loaded + // once at startup and again only on an explicit reload, never per repaint: + // the painting path runs on every row of every scroll. + loadBusinessSenders(); + applyCurrentAccountToDelegate(); + // A change takes effect without a restart. Its own connect, not the one in + // buildSavedQueryRow(), which belongs to the filter-buttons row and is + // rebuilt with it. + connect(m_accountBox, &QComboBox::currentIndexChanged, this, + [this]() { applyCurrentAccountToDelegate(); }); + // After registerActions(), not inside buildUi(): the query bar exists by // then but the action does not, so wiring this where the field is built // silently connected nothing and left Save query enabled on an empty @@ -831,7 +842,8 @@ void MainWindow::buildUi() // delegate is confined to one column's rectangle. m_threadView = new ThreadListView(central); m_threadView->setModel(m_model); - m_threadView->setItemDelegate(new CardDelegate(this)); + m_cardDelegate = new CardDelegate(this); + m_threadView->setItemDelegate(m_cardDelegate); m_threadView->setHeaderHidden(true); m_threadView->setSelectionBehavior(QAbstractItemView::SelectRows); m_threadView->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -2794,6 +2806,27 @@ void MainWindow::selectAccountForTesting(const QString &key) m_accountBox->setCurrentIndex(index); } +void MainWindow::loadBusinessSenders(const QString &path) +{ + m_businessSenders = BusinessSenders::load( + path.isEmpty() ? BusinessSenders::defaultPath() : path); + m_cardDelegate->setBusinessSenders(m_businessSenders); +} + +void MainWindow::applyCurrentAccountToDelegate() +{ + const QString key = m_accountBox->currentData().toString(); + if (key.isEmpty()) { + m_cardDelegate->setAccountAddress(QString()); + m_cardDelegate->setAccountLabel(QString()); + return; + } + const Account account = m_config.account(key); + m_cardDelegate->setAccountAddress(account.address); + m_cardDelegate->setAccountLabel( + account.name.isEmpty() ? account.key : account.name); +} + void MainWindow::onRulePreviewRequested(const QString &query) { // Unscoped, deliberately. runQuery() wraps the bar's text in the selected diff --git a/src/mainwindow.h b/src/mainwindow.h index cb8cec4..e65c07d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -43,6 +43,10 @@ #include "tagcolors.h" #include "types.h" +// Held by value: the parsed business-senders list is a member, and the load is +// asserted through it without reaching into the delegate. +#include "businesssenders.h" + class QAction; class QLineEdit; class QMenu; @@ -57,6 +61,7 @@ class QToolButton; class QVBoxLayout; class BusyIndicator; +class CardDelegate; class ThreadListModel; class MessageView; class MailSync; @@ -358,6 +363,20 @@ public: /// The Maildir root as the worker reported it, for the split-index test. QString mailRootForTesting() const { return m_mailRoot; } + /// Reads the business-senders list and hands it to the delegate. + /// + /// Once at startup and on an explicit reload, never per repaint and never + /// stat-per-row: the file is small and the painting path runs on every + /// row of every scroll. + void loadBusinessSenders(const QString &path = QString()); + + /// Test accessor, so the load can be asserted without reaching into the + /// delegate. + const BusinessSenders::List &businessSendersForTest() const + { + return m_businessSenders; + } + /// Runs save_message into \p directory instead of asking for one. /// /// The file dialog is a modal the offscreen platform cannot click, and the @@ -625,6 +644,11 @@ private slots: private: void buildUi(); + /// Pushes the selected account's identity into the card delegate, so the + /// fallback avatar is seeded from it. Empty selection ("All accounts") + /// clears both: the delegate then falls back to "??". + void applyCurrentAccountToDelegate(); + /// Restores window geometry, splitter and thread-list header widths. /// A missing or rejected blob leaves the buildUi() defaults in place. void restoreUiState(); @@ -1278,6 +1302,13 @@ private: /// expander column are ThreadListView's, and holding the base here only /// hid that from every reader. ThreadListView *m_threadView = nullptr; + /// The card delegate, stored rather than discarded so the window can hand + /// it the account identity and the business-senders list. + CardDelegate *m_cardDelegate = nullptr; + + /// The parsed business-senders list, for the delegate. Empty until + /// loadBusinessSenders() runs. + BusinessSenders::List m_businessSenders; /// Right-click menu for the thread list, holding the same QActions the /// menu bar does. diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index 2032793..887fead 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -543,6 +543,7 @@ private slots: void aCloseDuringTheCountdownIsRefused(); void aFailedSendKeepsTheTextThatFailedToGo(); void aSmallSizeLimitIsNotDescribedAsZeroMegabytes(); + void theBusinessSenderListIsLoadedAtStartup(); private: /// Owns the throwaway lock table init() points every test at. A pointer @@ -14886,4 +14887,21 @@ void TestMainWindow::aSmallSizeLimitIsNotDescribedAsZeroMegabytes() "1.5 MB lost its decimal"); } +void TestMainWindow::theBusinessSenderListIsLoadedAtStartup() +{ + QTemporaryDir dir; + const QString path = dir.filePath(QStringLiteral("business-senders")); + QFile file(path); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + file.write("@cofidis.it\n"); + file.close(); + + const Config config; + MainWindow window(config); + window.loadBusinessSenders(path); + + QVERIFY(window.businessSendersForTest().domains.contains( + QStringLiteral("cofidis.it"))); +} + #include "test_mainwindow.moc" -- cgit v1.2.3