fix: a signed-out tab no longer asks for saved views
The providers mount for the public landing too, and the saved-views query had no enabled gate, so every signed-out load logged a 401 for /api/saved-views. The query now waits for a signed-in account in BootGate's me cache, like WizardProvider.
This commit is contained in:
@@ -67,12 +67,19 @@ function Probe() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The views query waits for a signed-in account in the cache (BootGate's `me`).
|
||||||
|
function signedInClient() {
|
||||||
|
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||||
|
qc.setQueryData(qk.me(), { id: ID, preferences: {} });
|
||||||
|
return qc;
|
||||||
|
}
|
||||||
|
|
||||||
async function mountResolved(views: SavedView[]) {
|
async function mountResolved(views: SavedView[]) {
|
||||||
// Fresh array + object copies per call, like a real fetch — so a refetch changes viewsQuery.data's
|
// Fresh array + object copies per call, like a real fetch — so a refetch changes viewsQuery.data's
|
||||||
// identity and re-runs the seed effect (which is what makes the per-source guard actually load-bearing
|
// identity and re-runs the seed effect (which is what makes the per-source guard actually load-bearing
|
||||||
// in the re-seed test; a shared reference would skip the effect and hide a missing guard).
|
// in the re-seed test; a shared reference would skip the effect and hide a missing guard).
|
||||||
vi.spyOn(api, "savedViews").mockImplementation(async () => views.map((v) => ({ ...v })));
|
vi.spyOn(api, "savedViews").mockImplementation(async () => views.map((v) => ({ ...v })));
|
||||||
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
const qc = signedInClient();
|
||||||
render(
|
render(
|
||||||
<QueryClientProvider client={qc}>
|
<QueryClientProvider client={qc}>
|
||||||
<SavedViewsProvider>
|
<SavedViewsProvider>
|
||||||
@@ -141,7 +148,7 @@ describe("SavedViewsProvider — default-view seeding on first resolve", () => {
|
|||||||
calls += 1;
|
calls += 1;
|
||||||
return calls === 1 ? [def] : [def, view({ id: 9, name: "added-later" })];
|
return calls === 1 ? [def] : [def, view({ id: 9, name: "added-later" })];
|
||||||
});
|
});
|
||||||
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
const qc = signedInClient();
|
||||||
render(
|
render(
|
||||||
<QueryClientProvider client={qc}>
|
<QueryClientProvider client={qc}>
|
||||||
<SavedViewsProvider>
|
<SavedViewsProvider>
|
||||||
@@ -174,3 +181,26 @@ describe("SavedViewsProvider — default-view seeding on first resolve", () => {
|
|||||||
expect(h.setPlexFilters).not.toHaveBeenCalled();
|
expect(h.setPlexFilters).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("SavedViewsProvider — signed-out tab", () => {
|
||||||
|
it("does NOT ask for saved views while no account is signed in (the landing logged a 401)", async () => {
|
||||||
|
const spy = vi.spyOn(api, "savedViews").mockResolvedValue([]);
|
||||||
|
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||||
|
qc.setQueryData(qk.me(), null); // BootGate's "nobody"
|
||||||
|
render(
|
||||||
|
<QueryClientProvider client={qc}>
|
||||||
|
<SavedViewsProvider>
|
||||||
|
<Probe />
|
||||||
|
</SavedViewsProvider>
|
||||||
|
</QueryClientProvider>,
|
||||||
|
);
|
||||||
|
// Let any fetch the mount would start get going before asserting it never happened.
|
||||||
|
await act(async () => {
|
||||||
|
await new Promise((r) => setTimeout(r, 20));
|
||||||
|
});
|
||||||
|
expect(spy).not.toHaveBeenCalled();
|
||||||
|
// …and it asks as soon as an account signs in.
|
||||||
|
act(() => qc.setQueryData(qk.me(), { id: ID, preferences: {} }));
|
||||||
|
await waitFor(() => expect(spy).toHaveBeenCalledWith("feed"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -155,9 +155,14 @@ export function SavedViewsProvider({ children }: { children: ReactNode }) {
|
|||||||
}, [page, feedFilters, plexFilters, setFeedFilters, setPlexFilters]);
|
}, [page, feedFilters, plexFilters, setFeedFilters, setPlexFilters]);
|
||||||
|
|
||||||
const source = cfg.source;
|
const source = cfg.source;
|
||||||
|
// Passive observer of the account BootGate fetches: the providers mount for a signed-out tab too
|
||||||
|
// (the public landing), and saved views are per-account, so asking for "nobody's" views only logs
|
||||||
|
// a 401 on the landing page. Enabled once a signed-in account is cached.
|
||||||
|
const { data: me } = useQuery({ queryKey: qk.me(), queryFn: api.me, enabled: false });
|
||||||
const viewsQuery = useQuery({
|
const viewsQuery = useQuery({
|
||||||
queryKey: qk.savedViews(source),
|
queryKey: qk.savedViews(source),
|
||||||
queryFn: () => api.savedViews(source),
|
queryFn: () => api.savedViews(source),
|
||||||
|
enabled: !!me,
|
||||||
});
|
});
|
||||||
const views = useMemo(() => viewsQuery.data ?? [], [viewsQuery.data]);
|
const views = useMemo(() => viewsQuery.data ?? [], [viewsQuery.data]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user