`ready` was hardcoded false for as long as the sections proxied to an address nobody could set — the connection form was removed and nothing replaced it, so nine doors led to the same "not connected" message. Both halves exist now and neither is typed by a person: the development server runs on a port this plugin chose, holding a FOFOS_API_TOKEN this plugin generated and handed it at start. So the credentials are DERIVED rather than stored. Asking somebody to copy a value out of one part of this plugin into another was never a setting, it was a chore. Development and not production, and that is not a preference. Production is a directory of files behind nginx — no API, no database, nothing listening — so editing against it is impossible rather than merely wrong. Sections read and write dev; Build is what publishes. An edit is visible immediately and public only when somebody decides it should be. The gate asks whether the server ANSWERS, not whether pm2 says online. `bun --hot` survives a crash at startup: the process stays up, pm2 reports online, and nothing is listening. Opening nine sections onto that would make one failed boot look like nine broken screens. And when /status itself cannot be reached, `configured` stays true while `ready` goes false. Telling somebody they have set nothing up is alarming and wrong; opening nine sections onto a server we could not confirm is answering is nine screens that each fail separately. Cannot-tell keeps them shut and lets the next poll open them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 lines
5.0 KiB
TypeScript
107 lines
5.0 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
import { useClient } from 'hooks/useClient';
|
|
import { useFofosChanged } from './channel';
|
|
|
|
/**
|
|
* Is this plugin ready to be USED — that is, may anything but Administration be opened?
|
|
*
|
|
* ── One condition, read in two places ──
|
|
*
|
|
* The nav decides whether a section is clickable and the detail panel decides what to render. Those
|
|
* are two answers to the same question, and a nav that lets you click a row the panel then refuses is
|
|
* worse than one that does not offer it. So the condition lives here and both panels read it, rather
|
|
* than each deciding for itself — the panels are siblings and neither may own the other.
|
|
*
|
|
* ── It is a GATE, not a hint ──
|
|
*
|
|
* Not ready means the site sections cannot be reached at all, by clicking or by typing `?section=` in
|
|
* the address bar. Every one of them is a window onto fofos, and without a working connection there is
|
|
* nothing behind any of them; offering nine doors onto the same "not connected" message taught nobody
|
|
* anything and made a setup step look like nine faults.
|
|
*
|
|
── It is answered by the server ──
|
|
*
|
|
* This was pinned to `false` for as long as nothing was behind the sections. What changed is not this
|
|
* hook: the site's admin API is served by the development server, which this plugin now starts on a
|
|
* port it chose with a token it generated. So there is something to ask about, and `/status` asks it.
|
|
*
|
|
* ── Both panels call this, so `/status` is fetched twice ──
|
|
*
|
|
* Known and accepted for now: two cheap GETs that answer the same question, kept in step because both
|
|
* re-run on the same `useFofosChanged` signal. The alternative is a context provider around the two
|
|
* panels, which is a shared-state boundary this does not yet need — and the duplication is visible
|
|
* here rather than hidden in a wrapper. If the condition grows expensive, that is the moment to fix
|
|
* it, not before.
|
|
*
|
|
* It returns a single boolean and not the parts, and that still matters: callers must not start
|
|
* assembling their own version of the condition, because the condition has already changed shape once
|
|
* — from "a stored HTTP connection" to "the development server is answering" — and every caller that
|
|
* had built its own would have been quietly wrong on the day it did.
|
|
*/
|
|
|
|
type Status = { configured: boolean; lastProbeOk: boolean | null; rootSet: boolean; ready: boolean };
|
|
|
|
export type Readiness = {
|
|
/** The fofos connection works. Gates every section that READS the site. */
|
|
ready: boolean;
|
|
/**
|
|
* A root location has been chosen.
|
|
*
|
|
* Separate from `ready` because the two are genuinely different questions and one section depends on
|
|
* each. Media needs a folder on this machine; it never speaks to fofos, and gating it behind a
|
|
* connection it does not use was the bug that made this distinction necessary.
|
|
*/
|
|
rootSet: boolean;
|
|
/** Still asking. Callers should render nothing rather than guess — see below. */
|
|
loading: boolean;
|
|
};
|
|
|
|
export const useReady = (): Readiness => {
|
|
const client = useClient();
|
|
const clientRef = useRef(client);
|
|
clientRef.current = client;
|
|
|
|
const [status, setStatus] = useState<Status | null>(null);
|
|
const [signal] = useFofosChanged();
|
|
|
|
const check = useCallback(async () => {
|
|
try {
|
|
setStatus(await clientRef.current.get<Status>('/fofos-admin/status'));
|
|
} catch {
|
|
/*
|
|
Cannot tell. Treated as CONFIGURED rather than not — a transient failure must never present as
|
|
"you have not set this up", which is a far more alarming thing to be told and invites somebody
|
|
to re-enter a connection that was never the problem.
|
|
*/
|
|
/*
|
|
`ready: false` here, unlike `configured`, and the asymmetry is deliberate.
|
|
|
|
Being told "you have not set this up" when the request merely failed is alarming and wrong, so
|
|
configured stays true. But OPENING nine sections onto a server we could not confirm is
|
|
answering would show nine screens that each fail on their own — the honest read of "cannot
|
|
tell" is to keep them shut and let the next poll open them.
|
|
*/
|
|
setStatus({ configured: true, lastProbeOk: null, rootSet: false, ready: false });
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
void check();
|
|
}, [check, signal]);
|
|
|
|
/*
|
|
The gate, and it is the server's answer rather than one assembled here.
|
|
|
|
It was `false` for as long as there was nothing behind the sections. The server now knows: the
|
|
development server is what serves the site's admin API, so `ready` is whether that is ANSWERING —
|
|
not whether pm2 says online, because `bun --hot` stays alive after a startup crash and would have
|
|
made one failed boot look like nine broken screens.
|
|
|
|
Computed there and not here on purpose. The note above says callers must not assemble their own
|
|
version out of the parts, and that applies to this hook too: one place decides, everything reads it.
|
|
*/
|
|
const ready = status?.ready ?? false;
|
|
|
|
return { ready, rootSet: status?.rootSet ?? false, loading: status === null };
|
|
};
|