From 494adb9b533303851b1ce4b44c39fa342b9c9a86 Mon Sep 17 00:00:00 2001 From: Michael Richter Date: Tue, 1 Sep 2026 15:08:12 +0200 Subject: [PATCH 1/3] fix: set default addressbook to selected one --- components/contacts/contacts-app.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/contacts/contacts-app.tsx b/components/contacts/contacts-app.tsx index ff7292a0f..40a8ed326 100644 --- a/components/contacts/contacts-app.tsx +++ b/components/contacts/contacts-app.tsx @@ -391,6 +391,13 @@ export function ContactsApp({ linkSegments }: ContactsAppProps = {}) { const handleCreateNew = () => { setSelectedContact(null); + // When a specific address book is being viewed, create the new contact in + // that book instead of falling back to the account's default book. + if (typeof activeCategory === "object" && "addressBookId" in activeCategory) { + setDefaultBookIdForCreate(activeCategory.addressBookId); + } else { + setDefaultBookIdForCreate(undefined); + } setView("create"); }; @@ -961,7 +968,8 @@ export function ContactsApp({ linkSegments }: ContactsAppProps = {}) { } : undefined} onCreateContactInBook={(book) => { setDefaultBookIdForCreate(book.id); - handleCreateNew(); + setSelectedContact(null); + setView("create"); }} onDeleteAddressBook={client ? async (book) => { const ok = await confirmDialog({ From 71dd5f4fedb46640d7250297afc8d8cfaa7c3d29 Mon Sep 17 00:00:00 2001 From: Shuki Vaknin Date: Tue, 1 Sep 2026 19:03:46 +0300 Subject: [PATCH 2/3] fix(list): stop the batch toolbar hiding the message you just selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opening the toolbar feeds its height back into scrollTop so rows keep their position on screen. At the top of the list there are no rows above to hold steady, so that scroll instead slides the first message under the toolbar — select the top message and it disappears behind the bar acting on it. Skip the compensation when the list is already at the top and let the content move down there, which is what the toolbar taking up space should do. Mid-list behaviour is unchanged, and the scroll is clamped at zero so collapsing the toolbar cannot drive it negative. --- components/email/email-list.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index a3c12c4d9..37eae2669 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -156,6 +156,11 @@ export function EmailList({ // open it shrinks the list and would shove every row downwards. Feed each // height change back into scrollTop so the rows stay put on screen (and // slide back when the toolbar collapses again). + // + // Except at the very top: there are no rows above to hold steady, so the + // compensation just scrolls the first message underneath the toolbar and + // reads as the toolbar covering the message you selected. Let the list + // move down there instead. useEffect(() => { const toolbar = batchToolbarRef.current; if (!toolbar || typeof ResizeObserver === 'undefined') return; @@ -166,7 +171,8 @@ export function EmailList({ lastHeight = height; const list = parentRef.current; if (!list || delta === 0) return; - list.scrollTop += delta; + if (delta > 0 && list.scrollTop <= 0) return; + list.scrollTop = Math.max(0, list.scrollTop + delta); }); observer.observe(toolbar); return () => observer.disconnect(); From 12f01409a6a99253cda7e1b34256307a40888606 Mon Sep 17 00:00:00 2001 From: Shuki Vaknin Date: Mon, 17 Aug 2026 23:53:09 +0300 Subject: [PATCH 3/3] feat(search): add "clear search when switching folders" setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switching folders currently keeps an active search applied and re-runs it in the newly selected folder — deliberate behavior from #553, where users wanted the query to follow them across folders. Others expect the opposite: clicking Inbox/Sent should drop the search and just browse the folder. Add an opt-in setting (Settings → email behavior) that reconciles both. Default is off, so #553's search-follows-you behavior is unchanged for everyone. When on, any folder click clears the text query and advanced filters and browses the folder instead of re-running the search. Applied uniformly across the four folder-entry paths in MailApp — normal mailboxes, per-account (Pro sidebar) folders, unified views, and cross views — via one helper so no path diverges. --- components/mail/mail-app.tsx | 44 +++++++++++++++++------- components/settings/reading-settings.tsx | 8 +++++ locales/ar/common.json | 4 +++ locales/ca/common.json | 4 +++ locales/cs/common.json | 4 +++ locales/da/common.json | 4 +++ locales/de/common.json | 4 +++ locales/en/common.json | 4 +++ locales/es/common.json | 4 +++ locales/fa/common.json | 4 +++ locales/fr/common.json | 4 +++ locales/he/common.json | 4 +++ locales/hu/common.json | 4 +++ locales/it/common.json | 4 +++ locales/ja/common.json | 4 +++ locales/ko/common.json | 4 +++ locales/lv/common.json | 4 +++ locales/mn/common.json | 4 +++ locales/nb/common.json | 4 +++ locales/nl/common.json | 4 +++ locales/pl/common.json | 4 +++ locales/pt/common.json | 4 +++ locales/ro/common.json | 4 +++ locales/ru/common.json | 4 +++ locales/sk/common.json | 4 +++ locales/tr/common.json | 4 +++ locales/uk/common.json | 4 +++ locales/zh-TW/common.json | 4 +++ locales/zh/common.json | 4 +++ stores/settings-store.ts | 3 ++ 30 files changed, 151 insertions(+), 12 deletions(-) diff --git a/components/mail/mail-app.tsx b/components/mail/mail-app.tsx index 2f77adda3..923d2fbd3 100644 --- a/components/mail/mail-app.tsx +++ b/components/mail/mail-app.tsx @@ -2364,6 +2364,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 @@ -2378,11 +2392,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); @@ -2432,9 +2448,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); @@ -2465,9 +2482,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); @@ -2501,13 +2519,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 98057559d..4178185e9 100644 --- a/components/settings/reading-settings.tsx +++ b/components/settings/reading-settings.tsx @@ -25,6 +25,7 @@ export function ReadingSettings() { returnToListAfterAction, swipeRightAction, swipeLeftAction, + clearSearchOnFolderChange, showPreview, mailLayout, disableThreading, @@ -245,6 +246,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,