From c7144c15028ffcdfa9ba5a021ef1afa1d35bd5b3 Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Thu, 8 Oct 2026 07:06:25 +0000 Subject: [PATCH] feat: restore the destination workspace mailbox tag filter before saving route state when switching organizations, preserve other query parameters, and cover the mounted Accounts transition without clearing either workspace selection Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- web/src/hooks/useMailboxTagFilter.test.tsx | 25 +++++++++++++++++++--- web/src/hooks/useMailboxTagFilter.ts | 9 ++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/web/src/hooks/useMailboxTagFilter.test.tsx b/web/src/hooks/useMailboxTagFilter.test.tsx index 67a079781..aba896ec8 100644 --- a/web/src/hooks/useMailboxTagFilter.test.tsx +++ b/web/src/hooks/useMailboxTagFilter.test.tsx @@ -1,13 +1,20 @@ import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { createMemoryHistory, createRootRoute, createRoute, createRouter, Outlet, RouterProvider } from "@tanstack/react-router"; +import { createMemoryHistory, createRootRoute, createRoute, createRouter, Outlet, RouterProvider, type AnyRouter } from "@tanstack/react-router"; +import { createContext, useContext } from "react"; import { beforeEach, describe, expect, it } from "vitest"; import { parseSearch, stringifySearch } from "@/lib/routerSearch"; import useMailboxTagFilter from "./useMailboxTagFilter"; let workspace = "workspace"; let tags: readonly { id: string }[] | undefined; +const FilterConfig = createContext({ workspace, tags }); + +function TestRouter({ router }: { router: AnyRouter }) { + return ; +} function Filters() { + const { workspace, tags } = useContext(FilterConfig); const [tag, setTag] = useMailboxTagFilter(`user:${workspace}`, tags); return <>{tag || "All accounts"} @@ -20,7 +27,7 @@ async function open(href = "/app/emails") { const tasks = createRoute({ getParentRoute: () => root, path: "/app/tasks", component: () =>
Tasks
}); const router = createRouter({ routeTree: root.addChildren([emails, tasks]), history: createMemoryHistory({ initialEntries: [href] }), parseSearch, stringifySearch }); await act(() => router.load()); - return { router, ...render() }; + return { router, ...render() }; } describe("remembered mailbox tag", () => { @@ -35,7 +42,7 @@ describe("remembered mailbox tag", () => { const page = await open(); expect(screen.getByLabelText("Tag")).toHaveTextContent("sending"); tags = [{ id: "sending" }]; - page.rerender(); + page.rerender(); expect(screen.getByLabelText("Tag")).toHaveTextContent("sending"); page.unmount(); workspace = "another-workspace"; @@ -57,6 +64,18 @@ describe("remembered mailbox tag", () => { await open(href); expect(screen.getByLabelText("Tag")).toHaveTextContent("sending"); }); + it("restores the new workspace's remembered filter instead of saving the previous URL tag", async () => { + sessionStorage.setItem("warmbly:mailbox-tag:user:another-workspace", "receiving"); + const page = await open("/app/emails?tag=sending&tab=settings"); + workspace = "another-workspace"; + tags = [{ id: "receiving" }]; + page.rerender(); + await waitFor(() => expect(page.router.state.location.searchStr).toContain("tag=receiving")); + expect(screen.getByLabelText("Tag")).toHaveTextContent("receiving"); + expect(page.router.state.location.searchStr).toContain("tab=settings"); + expect(sessionStorage.getItem("warmbly:mailbox-tag:user:another-workspace")).toBe("receiving"); + expect(sessionStorage.getItem("warmbly:mailbox-tag:user:workspace")).toBe("sending"); + }); it("keeps All accounts after clearing and falls back for deleted or other-workspace tags", async () => { sessionStorage.setItem("warmbly:mailbox-tag:other:workspace", "sending"); const page = await open("/app/emails?tag=sending"); diff --git a/web/src/hooks/useMailboxTagFilter.ts b/web/src/hooks/useMailboxTagFilter.ts index 0fff432b0..93a1b4b13 100644 --- a/web/src/hooks/useMailboxTagFilter.ts +++ b/web/src/hooks/useMailboxTagFilter.ts @@ -10,7 +10,8 @@ export default function useMailboxTagFilter(owner: string, validTags: readonly { }, [key]); const [memory, setMemory] = useState(() => ({ key, value: read() })); const remembered = memory.key === key ? memory.value : read(); - const selected = params.has("tag") ? urlTag : remembered; + const restoringOwner = memory.key !== key; + const selected = !restoringOwner && params.has("tag") ? urlTag : remembered; const tag = validTags === undefined || validTags.some((candidate) => candidate.id === selected) ? selected : ""; const remember = useCallback((value: string) => { setMemory({ key, value }); @@ -18,9 +19,13 @@ export default function useMailboxTagFilter(owner: string, validTags: readonly { }, [key]); useEffect(() => { + if (restoringOwner && tag !== urlTag) { + setURLTag(tag); + return; + } remember(tag); if (tag !== urlTag) setURLTag(tag); - }, [tag, urlTag, remember, setURLTag]); + }, [restoringOwner, tag, urlTag, remember, setURLTag]); const setTag = useCallback((value: string) => { remember(value);