mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-10-15 19:45:42 -04:00
refactor(api): share tracking-dependent query key lists between web and native
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4 files changed
+47
-19
No files matched your search
@@ -4,20 +4,23 @@ import { client, orpc } from "@/lib/orpc";
|
||||
import { queryClient } from "@/lib/query-client";
|
||||
import { toast } from "@/lib/toast";
|
||||
import { refreshWidgets } from "@/lib/widgets";
|
||||
import { trackingDerivedQueryKeys, trackingStateQueryKeys } from "@sofa/api/query-keys";
|
||||
import { i18n } from "@sofa/i18n";
|
||||
|
||||
let widgetRefreshTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
/**
|
||||
* Mark every query that depends on the user's tracking state as stale. Title details
|
||||
* (`titles.get`) are excluded — they don't depend on tracking (same rule as the web's
|
||||
* invalidateTrackingQueries). Resolves once tracking and library queries have refetched,
|
||||
* so mutations can stay pending until the UI reflects the change.
|
||||
* Mark every query that depends on the user's tracking state as stale — the shared
|
||||
* `trackingStateQueryKeys` and `trackingDerivedQueryKeys` lists from `@sofa/api/query-keys`.
|
||||
* Title details (`titles.get`) are excluded — they don't depend on tracking (same rule as the
|
||||
* web's invalidateTrackingQueries). Derived queries are fire-and-forget; resolves once the
|
||||
* tracking-state queries (tracking + library) have refetched, so mutations can stay pending
|
||||
* until the UI reflects the change.
|
||||
*/
|
||||
export function invalidateTitleQueries(): Promise<unknown> {
|
||||
void queryClient.invalidateQueries({ queryKey: orpc.discover.key() });
|
||||
void queryClient.invalidateQueries({ queryKey: orpc.people.key() });
|
||||
void queryClient.invalidateQueries({ queryKey: orpc.titles.similar.key() });
|
||||
for (const queryKey of trackingDerivedQueryKeys(orpc)) {
|
||||
void queryClient.invalidateQueries({ queryKey });
|
||||
}
|
||||
|
||||
// Debounce widget refresh to batch rapid mutations (e.g. watching multiple episodes)
|
||||
if (widgetRefreshTimer) clearTimeout(widgetRefreshTimer);
|
||||
@@ -26,10 +29,9 @@ export function invalidateTitleQueries(): Promise<unknown> {
|
||||
widgetRefreshTimer = null;
|
||||
}, 2000);
|
||||
|
||||
return Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: orpc.tracking.key() }),
|
||||
queryClient.invalidateQueries({ queryKey: orpc.library.key() }),
|
||||
]);
|
||||
return Promise.all(
|
||||
trackingStateQueryKeys(orpc).map((queryKey) => queryClient.invalidateQueries({ queryKey })),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { trackingDerivedQueryKeys, trackingStateQueryKeys } from "@sofa/api/query-keys";
|
||||
|
||||
import { orpc } from "./client";
|
||||
|
||||
/**
|
||||
@@ -9,11 +11,9 @@ import { orpc } from "./client";
|
||||
* (`titles.get`) are deliberately excluded — they don't depend on tracking.
|
||||
*/
|
||||
export function invalidateTrackingQueries(queryClient: QueryClient): Promise<unknown> {
|
||||
return Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: orpc.tracking.key() }),
|
||||
queryClient.invalidateQueries({ queryKey: orpc.library.key() }),
|
||||
queryClient.invalidateQueries({ queryKey: orpc.discover.key() }),
|
||||
queryClient.invalidateQueries({ queryKey: orpc.people.key() }),
|
||||
queryClient.invalidateQueries({ queryKey: orpc.titles.similar.key() }),
|
||||
]);
|
||||
return Promise.all(
|
||||
[...trackingStateQueryKeys(orpc), ...trackingDerivedQueryKeys(orpc)].map((queryKey) =>
|
||||
queryClient.invalidateQueries({ queryKey }),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,8 @@
|
||||
"./contract": "./src/contract.ts",
|
||||
"./schemas": "./src/schemas.ts",
|
||||
"./errors": "./src/errors.ts",
|
||||
"./utils": "./src/utils.ts"
|
||||
"./utils": "./src/utils.ts",
|
||||
"./query-keys": "./src/query-keys.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "oxlint",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
type Keyed = { key(): readonly unknown[] };
|
||||
type TrackingKeyedClient = {
|
||||
tracking: Keyed;
|
||||
library: Keyed;
|
||||
discover: Keyed;
|
||||
people: Keyed;
|
||||
titles: { similar: Keyed };
|
||||
};
|
||||
|
||||
/**
|
||||
* Queries that directly show the user's tracking state (statuses, watches, ratings, library).
|
||||
* Mutations can wait on these so the UI reflects the change before they settle.
|
||||
*/
|
||||
export function trackingStateQueryKeys(orpc: TrackingKeyedClient): (readonly unknown[])[] {
|
||||
return [orpc.tracking.key(), orpc.library.key()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries whose results are derived from tracking state (recommendations, discover statuses,
|
||||
* similar titles, people credits). Refetch them after a change, but they can be slow (TMDB-backed).
|
||||
* `titles.get` is deliberately absent — title details don't depend on tracking.
|
||||
*/
|
||||
export function trackingDerivedQueryKeys(orpc: TrackingKeyedClient): (readonly unknown[])[] {
|
||||
return [orpc.discover.key(), orpc.people.key(), orpc.titles.similar.key()];
|
||||
}
|
||||
Reference in new issue
Block a user