diff --git a/components/mail/mail-app.tsx b/components/mail/mail-app.tsx
index 85b7036b..021bdcfa 100644
--- a/components/mail/mail-app.tsx
+++ b/components/mail/mail-app.tsx
@@ -2235,6 +2235,20 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
// Pro sidebar: user clicked a folder under a specific account group.
// accountId === null means the active account; non-null means a viewing
// override that fetches via that account's JMAP client.
+ // When "Clear search when switching folders" is on, a folder click drops
+ // any active text query + advanced filters and browses the folder instead
+ // of re-running the search there (#553 is the default: search follows you
+ // across folders). Returns true when it cleared, so the caller browses
+ // rather than re-searching. Reads the current query/filters from the
+ // closure to decide whether there was anything to clear.
+ const clearSearchIfFolderChangeResets = (): boolean => {
+ if (!useSettingsStore.getState().clearSearchOnFolderChange) return false;
+ if (isFilterEmpty(searchFilters) && !searchQuery) return false;
+ setSearchQuery("");
+ clearSearchFilters();
+ return true;
+ };
+
const handleAccountMailboxSelect = async (accountId: string | null, mailboxId: string) => {
const viewingClient = accountId
? useAuthStore.getState().getClientForAccount(accountId) ?? client
@@ -2249,11 +2263,13 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
setTabletListVisible(true);
}
if (viewingClient) {
- // Keep an active search applied when switching folders (#553); the
- // store actions resolve the viewing account's client internally.
- if (!isFilterEmpty(searchFilters)) {
+ // Keep an active search applied when switching folders (#553), unless
+ // the user opted to reset search on folder change. The store actions
+ // resolve the viewing account's client internally.
+ const searchCleared = clearSearchIfFolderChangeResets();
+ if (!searchCleared && !isFilterEmpty(searchFilters)) {
await advancedSearch(viewingClient);
- } else if (searchQuery) {
+ } else if (!searchCleared && searchQuery) {
await searchEmails(viewingClient, searchQuery);
} else {
await fetchEmails(viewingClient, mailboxId);
@@ -2300,9 +2316,10 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
}
const populated = await buildPopulatedUnifiedAccounts();
+ const searchCleared = clearSearchIfFolderChangeResets();
// Keep an active search across the switch and re-run it in this view
// (mirrors normal mailboxes), preserving advanced filters; otherwise browse.
- if (client && (!isFilterEmpty(searchFilters) || searchQuery)) {
+ if (client && !searchCleared && (!isFilterEmpty(searchFilters) || searchQuery)) {
useEmailStore.setState({ isUnifiedView: true, unifiedRole: role, crossView: null });
if (!isFilterEmpty(searchFilters)) {
await advancedSearch(client);
@@ -2333,9 +2350,10 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
}
const populated = await buildPopulatedUnifiedAccounts();
+ const searchCleared = clearSearchIfFolderChangeResets();
// Keep an active search across the switch and re-run it in this view
// (mirrors normal mailboxes), preserving advanced filters; otherwise browse.
- if (client && (!isFilterEmpty(searchFilters) || searchQuery)) {
+ if (client && !searchCleared && (!isFilterEmpty(searchFilters) || searchQuery)) {
useEmailStore.setState({ isUnifiedView: true, crossView: view, unifiedRole: null });
if (!isFilterEmpty(searchFilters)) {
await advancedSearch(client);
@@ -2369,13 +2387,15 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
}
if (client) {
- // If there's an active search, re-run it in the new mailbox. Advanced
- // filters must go through advancedSearch (which also includes the text
- // query) — falling back to fetchEmails would silently drop them while
- // the UI still shows them as active (#553).
- if (!isFilterEmpty(searchFilters)) {
+ // If there's an active search, re-run it in the new mailbox unless the
+ // user opted to reset search on folder change. Advanced filters must go
+ // through advancedSearch (which also includes the text query) — falling
+ // back to fetchEmails would silently drop them while the UI still shows
+ // them as active (#553).
+ const searchCleared = clearSearchIfFolderChangeResets();
+ if (!searchCleared && !isFilterEmpty(searchFilters)) {
await advancedSearch(client);
- } else if (searchQuery) {
+ } else if (!searchCleared && searchQuery) {
await searchEmails(client, searchQuery);
} else {
await fetchEmails(client, mailboxId);
diff --git a/components/settings/reading-settings.tsx b/components/settings/reading-settings.tsx
index 0c959e0d..a2d1c98c 100644
--- a/components/settings/reading-settings.tsx
+++ b/components/settings/reading-settings.tsx
@@ -23,6 +23,7 @@ export function ReadingSettings() {
deleteAction,
permanentlyDeleteJunk,
returnToListAfterAction,
+ clearSearchOnFolderChange,
showPreview,
mailLayout,
disableThreading,
@@ -213,6 +214,13 @@ export function ReadingSettings() {
/>
+
+ updateSetting('clearSearchOnFolderChange', checked)}
+ />
+
+
{!isSettingHidden('showPreview') && (
()(
markAsReadDelay: state.markAsReadDelay,
deleteAction: state.deleteAction,
returnToListAfterAction: state.returnToListAfterAction,
+ clearSearchOnFolderChange: state.clearSearchOnFolderChange,
showPreview: state.showPreview,
mailLayout: state.mailLayout,
emailsPerPage: state.emailsPerPage,