Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions components/mail/mail-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions components/settings/reading-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function ReadingSettings() {
deleteAction,
permanentlyDeleteJunk,
returnToListAfterAction,
clearSearchOnFolderChange,
showPreview,
mailLayout,
disableThreading,
Expand Down Expand Up @@ -213,6 +214,13 @@ export function ReadingSettings() {
/>
</SettingItem>

<SettingItem label={t('clear_search_on_folder_change.label')} description={t('clear_search_on_folder_change.description')}>
<ToggleSwitch
checked={clearSearchOnFolderChange}
onChange={(checked) => updateSetting('clearSearchOnFolderChange', checked)}
/>
</SettingItem>

{!isSettingHidden('showPreview') && (
<SettingItem
label={t('show_preview.label')}
Expand Down
4 changes: 4 additions & 0 deletions locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,10 @@
"label": "Return to list after delete or mark unread",
"description": "After deleting or marking the open message unread, go back to the message list instead of opening the next message."
},
"clear_search_on_folder_change": {
"label": "Clear search when switching folders",
"description": "Clicking a folder (Inbox, Sent, …) clears the active search and browses that folder. When off, the search stays applied and re-runs in the folder you switch to."
},
"rtl_editing": {
"label": "Right-to-left editing support",
"description": "Adds a direction button to the composer toolbar so you can set paragraphs left-to-right or right-to-left"
Expand Down
3 changes: 3 additions & 0 deletions stores/settings-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ interface SettingsState {
deleteAction: DeleteAction;
permanentlyDeleteJunk: boolean; // Permanently delete emails from junk/spam instead of moving to trash
returnToListAfterAction: boolean; // After delete / mark-unread in an open message, return to the list instead of opening the next message
clearSearchOnFolderChange: boolean; // Reset the search query + advanced filters when switching folders, instead of re-running the search in the newly selected folder (#553 keeps it applied when this is off)
showPreview: boolean;
mailLayout: MailLayout;
emailsPerPage: number;
Expand Down Expand Up @@ -542,6 +543,7 @@ const DEFAULT_SETTINGS = {
deleteAction: 'trash' as DeleteAction,
permanentlyDeleteJunk: false,
returnToListAfterAction: true,
clearSearchOnFolderChange: false,
showPreview: true,
mailLayout: 'split' as MailLayout,
emailsPerPage: 50,
Expand Down Expand Up @@ -762,6 +764,7 @@ export const useSettingsStore = create<SettingsState>()(
markAsReadDelay: state.markAsReadDelay,
deleteAction: state.deleteAction,
returnToListAfterAction: state.returnToListAfterAction,
clearSearchOnFolderChange: state.clearSearchOnFolderChange,
showPreview: state.showPreview,
mailLayout: state.mailLayout,
emailsPerPage: state.emailsPerPage,
Expand Down