aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mainwindow.cpp72
-rw-r--r--src/mainwindow.h43
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