diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mainwindow.cpp | 72 | ||||
| -rw-r--r-- | src/mainwindow.h | 43 |
2 files changed, 111 insertions, 4 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3ae16e9..3c94c5c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3727,6 +3727,8 @@ void MainWindow::runQuery(FlatResult flat, AccountScope scope) // own query does not clear it. m_recoverThreadId.clear(); m_recoverMessageId.clear(); + m_launchMiss.clear(); + m_launchMissNotice.clear(); ++m_generation; m_model->clear(); @@ -3848,6 +3850,26 @@ void MainWindow::onQueryFinished(int total, quint64 generation) m_queryComplete = true; updateViewWideActions(); + // A launch selector's thread:<id> coming back empty: the thread is not in + // the scope it was looked for in, or anywhere. Named, and the view the + // user had comes back, rather than leaving an empty list that makes a + // stale link look like a broken client. + if (!m_launchMiss.isEmpty()) { + const QString miss = m_launchMiss; + m_launchMiss.clear(); + if (total == 0) { + reportLaunchMiss(miss); + return; + } + } + + // The restored view has landed, so its row count is on the bar and the + // miss can go over it. + if (!m_launchMissNotice.isEmpty()) { + showTransientStatus(m_launchMissNotice); + m_launchMissNotice.clear(); + } + // A recovery's own thread:<id> query landing. The rows exist now, so the // thread can be expanded; the message inside it is selected once its // replies arrive. @@ -5265,6 +5287,12 @@ void MainWindow::applySelectors(const LaunchSelectors &requested) selectors.messageId.mid(1, selectors.messageId.size() - 2); } + // Recorded BEFORE anything moves, the dropdown included, so a miss can + // put back exactly what the user was looking at when the launch arrived. + m_launchView = { m_accountBox->currentIndex(), m_queryEdit->text(), + m_lastQuery, m_sentView }; + m_launchAccountGiven = false; + // The account FIRST, and the order matters: a built-in filter composes // with the dropdown, so a query run before the account moved would carry // the old scope. This is the same ordering the startup path uses. @@ -5272,6 +5300,7 @@ void MainWindow::applySelectors(const LaunchSelectors &requested) const int index = m_accountBox->findData(selectors.account); if (index >= 0) { m_accountBox->setCurrentIndex(index); + m_launchAccountGiven = true; } else { // Named, so a caller passing a stale key can be debugged from the // client rather than from the caller. @@ -5323,11 +5352,19 @@ void MainWindow::applySelectors(const LaunchSelectors &requested) static const QRegularExpression hex( QRegularExpression::anchoredPattern(QStringLiteral("[0-9a-fA-F]+"))); if (!hex.match(selectors.threadId).hasMatch()) { - showTransientStatus( + reportLaunchMiss( tr("No thread matched '%1'.").arg(selectors.threadId)); return; } + // Without --account the thread is looked for in EVERY account. The + // recovery runs in the dropdown's scope, so a window left on work + // searched work alone for a personal thread and blanked the list. With + // --account the caller chose the scope, and a thread outside it is a + // miss, reported below when the query comes back empty. + if (!m_launchAccountGiven) + m_accountBox->setCurrentIndex(m_accountBox->findData(QString())); + // recoverStaleThread() is reused whole. It runs thread:<id>, remembers // the target across the queued round trips the load takes, expands // the thread when its row arrives and selects it. Item 91's @@ -5336,6 +5373,8 @@ void MainWindow::applySelectors(const LaunchSelectors &requested) // The empty message id is meaningful to it: land on the ROOT row, // which is the thread's first message. recoverStaleThread(selectors.threadId, QString()); + // After the query, which clears it: this is that query's own miss. + m_launchMiss = tr("No thread matched '%1'.").arg(selectors.threadId); } } @@ -5346,15 +5385,44 @@ void MainWindow::onThreadForMessageResolved(const QString &messageId, // The miss path, and the window stays where it is. A message id from // another program can be stale for every ordinary reason: the mail was // deleted, moved by another client, or never indexed here. - showTransientStatus(tr("No message matched '%1'.").arg(messageId)); + reportLaunchMiss(tr("No message matched '%1'.").arg(messageId)); return; } + // Every account unless the launch named one, as for --thread. + if (!m_launchAccountGiven) + m_accountBox->setCurrentIndex(m_accountBox->findData(QString())); + // The THREAD, with that message selected. An id: query on the message // alone would show one card out of its conversation, which item 91 settled // is the wrong reading of "open this message". The thread id came from // notmuch, so it is safe to hand on unquoted. recoverStaleThread(threadId, messageId); + // Named by the MESSAGE, which is what the caller asked for; the thread id + // is ours and would mean nothing to them. + m_launchMiss = tr("No message matched '%1'.").arg(messageId); +} + +void MainWindow::reportLaunchMiss(const QString &text) +{ + const LaunchView view = m_launchView; + m_accountBox->setCurrentIndex(view.accountIndex); + + // Nothing ran since the launch arrived, so the list is still the view the + // user had and a re-run would only flicker it. A refused thread id and a + // message that did not resolve both land here. + if (m_lastQuery == view.lastQuery || view.lastQuery.isEmpty()) { + showTransientStatus(text); + return; + } + + m_queryEdit->setText(view.queryText); + runQuery(view.flat ? FlatResult::Yes : FlatResult::No, + view.queryText.trimmed() == view.lastQuery + ? AccountScope::AlreadyScoped + : AccountScope::Apply); + // After runQuery(), which clears it. + m_launchMissNotice = text; } void MainWindow::recoverStaleThread(const QString &threadId, diff --git a/src/mainwindow.h b/src/mainwindow.h index d4b0975..87ed171 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -143,8 +143,8 @@ public: /// against a running window means "raise yourself", and a raise is not a /// navigation. The user is looking at something. /// - /// A selector that matches nothing leaves the window on its configured - /// view and names the miss in the status bar. Not an empty result, which + /// A selector that matches nothing leaves the window on the view it was + /// showing and names the miss in the status bar. Not an empty result, which /// makes a stale link look like a broken client; not a refusal, which is /// right for a script and wrong for a desktop launch. /// @@ -758,6 +758,10 @@ private slots: /// drive it through the meta-object. void showTransientStatus(const QString &text); + /// Names a launch selector's miss and puts back the view m_launchView + /// recorded, re-running its query only when the list has since changed. + void reportLaunchMiss(const QString &text); + /// Announces an action that has just been sent, saying so when a sync is /// holding it rather than claiming it landed. /// @@ -1850,6 +1854,41 @@ private: QString m_recoverThreadId; QString m_recoverMessageId; + /// What was on screen before a launch's selectors started changing it, so + /// a miss can put it back (item 200: "the startup view stays and the miss + /// is named"). + /// + /// The bar's text and the query the list was built from are BOTH kept: + /// the scope a re-run needs is read off whether they differ. Equal means + /// the text was already scoped (a filter, or All accounts); different + /// means runQuery() wrapped it in the dropdown's account. + struct LaunchView + { + int accountIndex = -1; + QString queryText; + QString lastQuery; + bool flat = false; + }; + LaunchView m_launchView; + + /// Whether the launch that set m_launchView named an account that exists. + /// Read when a --message resolve lands, which is after applySelectors() + /// has returned: without --account the conversation is looked for in + /// every account, with it the given scope is kept. + bool m_launchAccountGiven = false; + + /// The miss to report if the selector's own thread:<id> query returns no + /// rows. Set AFTER recoverStaleThread(), for the reason the recovery target + /// is, and cleared by runQuery(), so only the selector's query is judged + /// by it and the stale-thread notice and double-click, which share the + /// recovery, keep their behaviour. + QString m_launchMiss; + + /// A miss waiting for the restored view's query to land, since that + /// query's own row count is written to the status bar when it does and + /// would cover a notice shown any earlier. + QString m_launchMissNotice; + /// The thread the pane's current MESSAGE belongs to. /// /// Selecting a message row clears m_currentThreadId (the pane shows one |
