diff --git a/components/email/__tests__/email-list-plugin-filters.test.tsx b/components/email/__tests__/email-list-plugin-filters.test.tsx index 4bac6c7e3..2a836e864 100644 --- a/components/email/__tests__/email-list-plugin-filters.test.tsx +++ b/components/email/__tests__/email-list-plugin-filters.test.tsx @@ -71,6 +71,14 @@ describe('plugin list filters in the message list', () => { expect(fetchEmails).toHaveBeenCalledTimes(2); }); + it('does not call the folder empty while a filter hides its mail', async () => { + render(); + expect(await screen.findByText('no_emails_description')).toBeInTheDocument(); + act(() => { fireEvent.click(screen.getByRole('button', { name: 'Open' })); }); + expect(screen.getByText('no_emails')).toBeInTheDocument(); + expect(screen.queryByText('no_emails_description')).toBeNull(); + }); + it.each([ ['a search', { searchQuery: 'invoice' }], ['a tag view', { selectedKeyword: 'work' }], diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index bebd4f42c..0755008a8 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -48,7 +48,7 @@ import { getOwnAddresses } from "@/lib/filters/quick-rule-target"; import { emitThreadExpand } from "@/lib/plugin-thread-expand"; import { normalizeAddress } from "@/lib/filters/quick-rules"; import { PluginSlot } from "@/components/plugins/plugin-slot"; -import { PluginListFilters } from "./plugin-list-filters"; +import { PluginListFilters, useFolderListFiltered } from "./plugin-list-filters"; import { PluginActions, useFolderAccountKind } from "@/components/plugins/plugin-actions"; import { useUnreadFirstSection } from "@/hooks/use-unread-first-section"; import { useTimeoutWhileMounted } from "@/hooks/use-timeout-while-mounted"; @@ -532,6 +532,9 @@ export function EmailList({ ); const bulkActionContext = useMemo(() => ({ emailIds: selectedEmailIds }), [selectedEmailIds]); const folderAccountKind = useFolderAccountKind(); + // A filter chip or a category tab on: the folder itself may well have mail. + const pluginFilterOn = useFolderListFiltered(effectiveMailboxRole ?? null, folderAccountKind); + const listNarrowed = pluginFilterOn || useMessageListTabsStore.getState().getCategoryFilter(effectiveMailboxRole) !== null; // "Remove from this folder" (#615): offered for a selection only when every // selected message is also in another folder. const removeFromFolder = useRemoveFromFolder(); @@ -1222,7 +1225,7 @@ export function EmailList({ {tSidebar('clear_search')} ) : !isScheduledView && !isSearching && effectiveMailboxRole === 'drafts' && onCompose ? ( diff --git a/components/email/plugin-list-filters.tsx b/components/email/plugin-list-filters.tsx index 05c2c9cf8..8682560af 100644 --- a/components/email/plugin-list-filters.tsx +++ b/components/email/plugin-list-filters.tsx @@ -5,6 +5,7 @@ import { useTranslations } from "next-intl"; import { iconForName, X } from "@/components/icons"; import { cn } from "@/lib/utils"; import { + activeListFilterCondition, activeListFilterKeys, listFilterApplies, listFilterRevision, @@ -16,6 +17,15 @@ import type { MailAccountKind } from "@/lib/plugin-sandbox/account-kind"; const NONE = new Set(); +/** Whether a plugin's filter is narrowing this folder's list right now. */ +export function useFolderListFiltered(mailboxRole: string | null, accountKind: MailAccountKind | null): boolean { + return useSyncExternalStore( + subscribeListFilters, + () => activeListFilterCondition({ role: mailboxRole, accountKind }) !== null, + () => false, + ); +} + /** * The keyword filters plugins offer for this folder (api.ui.setListFilters), * as chips in the list header. A chip turns its filter on; an active chip