Skip to content
70 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge branch 'fast-push' into mirroring1/**
2 * How Docs pages change things: a JSON request to `-/docs/api`, then the
3 * sidebar and page load again so they show it. Returns the service's
4 * `{ ok, value }` or `{ ok: false, error }`.
5 */
6import type { Result } from "@g1t/contracts";
7import { useCallback, useState } from "react";
8import { useRevalidator, useRouteLoaderData } from "react-router";
9
10import type { DocsLayoutData } from "../../routes/workspace/docs/layout";
11
12export function useDocsData(): DocsLayoutData | undefined {
13 return useRouteLoaderData("routes/workspace/docs/layout") as DocsLayoutData | undefined;
14}
15
Merge branch 'fast-push' into mirroring16/** How long a Docs request may take before the page says so instead of waiting on. */
17export const DOCS_TIMEOUT_MS = 20_000;
18
19/** What a request that failed or took too long says. */
20export function docsFailure<T>(error: unknown): Result<T> {
21 const late = error instanceof DOMException && (error.name === "TimeoutError" || error.name === "AbortError");
22 const message = late ? "Docs took too long to answer. Try again in a moment." : "Docs didn't answer. Check your connection and try again.";
23 return { ok: false, error: { code: "conflict", message } };
24}
25
Merge branch 'fast-push' into mirroring26export async function docsRequest<T>(slug: string, intent: string, body: Record<string, unknown> = {}): Promise<Result<T>> {
27 try {
28 const response = await fetch(`/${slug}/-/docs/api`, {
29 method: "POST",
30 headers: { "content-type": "application/json", accept: "application/json" },
31 body: JSON.stringify({ intent, ...body }),
Merge branch 'fast-push' into mirroring32 signal: AbortSignal.timeout(DOCS_TIMEOUT_MS),
Merge branch 'fast-push' into mirroring33 });
34 return (await response.json()) as Result<T>;
Merge branch 'fast-push' into mirroring35 } catch (error) {
36 return docsFailure(error);
Merge branch 'fast-push' into mirroring37 }
38}
39
40export async function docsQuery<T>(slug: string, query: Record<string, string>): Promise<Result<T>> {
41 try {
Merge branch 'fast-push' into mirroring42 const response = await fetch(`/${slug}/-/docs/api?${new URLSearchParams(query)}`, {
43 headers: { accept: "application/json" },
44 signal: AbortSignal.timeout(DOCS_TIMEOUT_MS),
45 });
Merge branch 'fast-push' into mirroring46 return (await response.json()) as Result<T>;
Merge branch 'fast-push' into mirroring47 } catch (error) {
48 return docsFailure(error);
Merge branch 'fast-push' into mirroring49 }
50}
51
52/** `send(intent, body)`: makes the change, then reloads what shows it. `busy` while it works; `error` when it failed. */
53export function useDocsAction(slug: string) {
54 const revalidator = useRevalidator();
55 const [busy, setBusy] = useState(false);
56 const [error, setError] = useState<string | null>(null);
57 const send = useCallback(
58 async <T>(intent: string, body: Record<string, unknown> = {}, options: { reload?: boolean } = {}): Promise<Result<T>> => {
59 setBusy(true);
60 setError(null);
61 const result = await docsRequest<T>(slug, intent, body);
62 setBusy(false);
63 if (!result.ok) setError(result.error.message);
64 else if (options.reload !== false) void revalidator.revalidate();
65 return result;
66 },
67 [slug, revalidator],
68 );
69 return { send, busy, error, setError };
70}

This file's history is long; its oldest lines are credited to the oldest commit read.