87 lines
2.7 KiB
TypeScript
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]);
|
|
},
|
|
};
|