Open History to see the last commit
1import type { ReactNode } from "react";
2
3import {
4 ensureClientSettingsHydrated,
5 useClientSettingsHydrationStatus,
6} from "../hooks/useSettings";
7import { Button } from "./ui/button";
8import { Empty, EmptyContent, EmptyHeader, EmptyTitle } from "./ui/empty";
9import { RefreshIcon } from "./ui/refresh-icon";
10import { StandalonePage } from "./ui/standalone-page";
11
12/**
13 * Edits made on defaults after a failed read are never saved, so the app waits for a good read.
14 * Until the first read finishes it keeps the boot splash, so no frame paints on defaults.
15 */
16export function ClientSettingsRecoveryGate({ children }: { readonly children: ReactNode }) {
17 const status = useClientSettingsHydrationStatus();
18 if (status === "pending") return <BootSplash />;
19 if (status !== "failed" && status !== "retrying") return children;
20 const retrying = status === "retrying";
21 return (
22 <StandalonePage size="bare">
23 <Empty>
24 <EmptyHeader>
25 <EmptyTitle>Couldn't load your settings</EmptyTitle>
26 </EmptyHeader>
27 <EmptyContent>
28 <Button
29 size="standard"
30 disabled={retrying}
31 aria-busy={retrying}
32 onClick={() => void ensureClientSettingsHydrated().catch(() => undefined)}
33 >
34 <RefreshIcon refreshing={retrying} />
35 {retrying ? "Retrying…" : "Retry"}
36 </Button>
37 </EmptyContent>
38 </Empty>
39 </StandalonePage>
40 );
41}
42
43/** The splash from index.html, which the first React commit replaces. */
44function BootSplash() {
45 return (
46 <div id="boot-shell">
47 <div id="boot-shell-card" aria-label="T3 Code splash screen">
48 <img id="boot-shell-logo" src="/apple-touch-icon.png" alt="T3 Code" />
49 </div>
50 </div>
51 );
52}