docs(home): record slow-network acceptance

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-09-24 18:22:44 +08:00
co-authored by Claude Code
parent 25e32fe6fd
commit 1d1983b484
10 changed files with 159 additions and 21 deletions
+2 -3
View File
@@ -84,7 +84,6 @@ export function useBillingPanel(input: {
readonly initialTab: BillingPaneTab;
}) {
const seedAccount = input.account;
const [accountError, setAccountError] = useState("");
const [paymentPackages, setPaymentPackages] = useState<MembershipProduct[]>([]);
const [paymentEnabled, setPaymentEnabled] = useState(false);
const [packagesError, setPackagesError] = useState("");
@@ -160,7 +159,8 @@ export function useBillingPanel(input: {
}, []);
useEffect(() => {
void fetchPackages();
const timer = window.setTimeout(() => void fetchPackages(), 0);
return () => window.clearTimeout(timer);
}, [fetchPackages]);
useEffect(() => {
@@ -294,7 +294,6 @@ export function useBillingPanel(input: {
return {
account: toBillingAccount(seedAccount),
accountError,
activeSubscription,
createPayment,
currentProductCode,
+2 -2
View File
@@ -2,7 +2,7 @@
* Home bootstrap failures are not one "the API is slow" bucket.
*
* `api-error` is the in-bundle account/session path (HTTP status or the 20s
* cloud timeout). The other categories are what the inline dead-screen script
* cloud slow-network timeout). The other categories are what the inline dead-screen script
* can see before that bundle runs. Observation records only category, build
* id, and duration — never a payload, exception string, or birth data.
*/
@@ -28,7 +28,7 @@ export type HomeBootstrapFailureInput = {
resourceFromCache?: boolean;
/** HTTP status from an account/session/model request the bundle actually made. */
apiStatus?: number | null;
/** Bundle-side cloud timeout (page.tsx 8000ms), not the dead-screen timer. */
/** Bundle-side cloud slow-network timeout (page.tsx 20_000ms), not the dead-screen timer. */
apiTimedOut?: boolean;
};
+9 -11
View File
@@ -128,12 +128,11 @@ export function SessionListProvider({ children }: { children: ReactNode }) {
const bootRef = useRef<SessionListBoot | null>(null);
const controllerRef = useRef<AbortController | null>(null);
const reloadRef = useRef<() => void>(() => undefined);
const readyPackRef = useRef(createReadyGate());
const [readyVersion, forceReadyVersion] = useState(0);
const [readyPack, setReadyPack] = useState(createReadyGate);
useEffect(() => {
let disposed = false;
const load = (readyPack = readyPackRef.current) => {
const load = (nextReadyPack: ReturnType<typeof createReadyGate>) => {
controllerRef.current?.abort();
const controller = new AbortController();
controllerRef.current = controller;
@@ -149,22 +148,21 @@ export function SessionListProvider({ children }: { children: ReactNode }) {
setAccount(boot.account);
setSignedOut(boot.signedOut);
setSettled(true);
readyPack.resolve();
nextReadyPack.resolve();
})
.catch(() => {
if (disposed || controller.signal.aborted || controllerRef.current !== controller) return;
bootRef.current = { sessions: [], rawRows: [], cursor: null, account: null, signedOut: false };
setSettled(true);
readyPack.resolve();
nextReadyPack.resolve();
});
};
reloadRef.current = () => {
const nextReadyPack = createReadyGate();
readyPackRef.current = nextReadyPack;
forceReadyVersion((version) => version + 1);
setReadyPack(nextReadyPack);
load(nextReadyPack);
};
load();
load(readyPack);
return () => {
disposed = true;
reloadRef.current = () => undefined;
@@ -180,7 +178,7 @@ export function SessionListProvider({ children }: { children: ReactNode }) {
const boot = useCallback(() => bootRef.current, []);
void readyVersion;
const ready = readyPack.promise;
const value = useMemo<SessionListContextValue>(() => ({
sessions,
setSessions,
@@ -190,7 +188,7 @@ export function SessionListProvider({ children }: { children: ReactNode }) {
setAccount,
signedOut,
settled,
ready: readyPackRef.current.promise,
ready,
boot,
reload,
registerShellControls,
@@ -203,7 +201,7 @@ export function SessionListProvider({ children }: { children: ReactNode }) {
sessionsCursor,
settled,
signedOut,
readyVersion,
ready,
]);
return (