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