Files
walkup/frontend/src/lib/teamSnapCache.ts
2026-04-23 14:14:18 -05:00

87 lines
2.7 KiB
TypeScript

import type { QueryKey } from "@tanstack/react-query";
import { queryClient } from "./queryClient";
import { readCachedValue, writeCachedValue } from "./offlineCache";
const inFlightRequests = new Map<string, Promise<unknown>>();
export function normalizeTeamSnapCacheScope(scope?: string | number | null): string {
const value = scope == null ? "" : String(scope).trim();
return value || "anonymous";
}
function buildCacheParts(scope: string | number | null | undefined, resource: string, parts: readonly unknown[]): readonly unknown[] {
return ["teamsnap", normalizeTeamSnapCacheScope(scope), resource, ...parts];
}
function cacheKey(parts: readonly unknown[]): string {
return JSON.stringify(parts);
}
function startFreshFetch<T>(
cacheParts: readonly unknown[],
queryKey: QueryKey,
fetchFresh: () => Promise<T>,
): Promise<T> {
const key = cacheKey(cacheParts);
const existing = inFlightRequests.get(key);
if (existing) {
return existing as Promise<T>;
}
const request = fetchFresh()
.then((data) => {
writeCachedValue(cacheParts, data);
queryClient.setQueryData(queryKey, data);
return data;
})
.finally(() => {
inFlightRequests.delete(key);
});
inFlightRequests.set(key, request);
return request;
}
export async function staleWhileRevalidateTeamSnap<T>({
cacheParts,
queryKey,
fetchFresh,
}: {
cacheParts: readonly unknown[];
queryKey: QueryKey;
fetchFresh: () => Promise<T>;
}): Promise<T> {
const cached = readCachedValue<T>(cacheParts);
if (cached) {
void startFreshFetch(cacheParts, queryKey, fetchFresh).catch(() => undefined);
return cached.data;
}
return startFreshFetch(cacheParts, queryKey, fetchFresh);
}
export const teamSnapQueryKeys = {
me(scope?: string | number | null): QueryKey {
return buildCacheParts(scope, "me", []);
},
teams(scope?: string | number | null): QueryKey {
return buildCacheParts(scope, "teams", []);
},
members(scope: string | number | null | undefined, teamId: string): QueryKey {
return buildCacheParts(scope, "members", [teamId]);
},
events(scope: string | number | null | undefined, teamId: string): QueryKey {
return buildCacheParts(scope, "events", [teamId]);
},
availabilities(scope: string | number | null | undefined, teamId: string, eventId?: string): QueryKey {
return buildCacheParts(scope, "availabilities", [teamId, eventId ?? ""]);
},
assignments(scope: string | number | null | undefined, teamId: string, eventId?: string): QueryKey {
return buildCacheParts(scope, "assignments", [teamId, eventId ?? ""]);
},
eventLineup(scope: string | number | null | undefined, teamId: string, eventId: string): QueryKey {
return buildCacheParts(scope, "eventLineup", [teamId, eventId]);
},
};