Open History to see the last commit
1import type { UsageProviderKind, UsageSummary } from "@t3tools/contracts";
2
3interface UsageProgressEnvironment {
4 readonly label: string;
5 readonly isConnected: boolean;
6 readonly isPending: boolean;
7 readonly error: string | null;
8 readonly summary: UsageSummary | null;
9}
10
11export function usageEnvironmentProgress(
12 environment: UsageProgressEnvironment,
13 refreshing = false,
14) {
15 const { summary } = environment;
16 if (!environment.isConnected || environment.error !== null) return { phase: "inactive" } as const;
17 if (summary === null) return { phase: "loading" } as const;
18 if (refreshing) return { phase: "stale" } as const;
19 if (!environment.isPending) return { phase: "ready" } as const;
20 const providers = [
21 ...new Set(
22 summary.sources.flatMap((source) => (source.refreshing ? [source.fingerprint.provider] : [])),
23 ),
24 ];
25 return providers.length > 0
26 ? ({ phase: "partway", providers } as const)
27 : ({ phase: "stale" } as const);
28}
29
30export function updatingProvidersLabel(
31 providers: readonly UsageProviderKind[],
32 providerLabel: (provider: UsageProviderKind) => string,
33) {
34 return providers.length === 1
35 ? `Updating ${providerLabel(providers[0]!)}…`
36 : `Updating ${providers.length} providers…`;
37}
38
39export function usageProgress(
40 environments: readonly UsageProgressEnvironment[],
41 {
42 refreshing = false,
43 providerLabel,
44 }: {
45 readonly refreshing?: boolean;
46 readonly providerLabel: (provider: UsageProviderKind) => string;
47 },
48) {
49 const progress = environments.map((environment) => ({
50 label: environment.label,
51 ...usageEnvironmentProgress(environment, refreshing),
52 }));
53 const waiting = progress.filter(({ phase }) => phase === "loading" || phase === "stale");
54 const providers = [
55 ...new Set(progress.flatMap((entry) => (entry.phase === "partway" ? entry.providers : []))),
56 ];
57 const answered = progress.some(({ phase }) => phase === "partway" || phase === "ready");
58 return {
59 dimmed: !answered && progress.some(({ phase }) => phase === "stale"),
60 label:
61 waiting.length === 0
62 ? providers.length === 0
63 ? null
64 : updatingProvidersLabel(providers, providerLabel)
65 : environments.length === 1
66 ? "Updating…"
67 : waiting.length === 1
68 ? `Updating ${waiting[0]!.label}…`
69 : `Updating ${waiting.length} environments…`,
70 };
71}
72
73export function usageLoadingState(
74 environments: readonly UsageProgressEnvironment[],
75 refreshing = false,
76) {
77 const providers = new Set<UsageProviderKind>();
78 let everyProvider = false;
79 for (const environment of environments) {
80 const progress = usageEnvironmentProgress(environment, refreshing);
81 if (progress.phase === "loading" || progress.phase === "stale") everyProvider = true;
82 if (progress.phase === "partway")
83 for (const provider of progress.providers) providers.add(provider);
84 }
85 return { partial: everyProvider || providers.size > 0, everyProvider, providers };
86}