Follows the portal: the noun is "instance" everywhere the app says it — UI strings, CLI output, error details, docs and comments. The wire keys (`instance_id`, `instance_token`) and the hub route this calls move with it. An existing cloud.json is adopted rather than refused: without the key alias the dataclass fails to parse, which the caller swallows and reads as "never enrolled" instead of "reconnect". `instance_key` on a node type becomes `target_key`. It means the outside thing a node points at, which is a different sense of the word, and keeping both would put two meanings of "instance" in one codebase. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YrQnKV3bnQd4K342y8tKj
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { useEffect, useSyncExternalStore } from "react"
|
|
|
|
import { ago } from "@/components/Health/queries"
|
|
import { connectionStore } from "@/lib/connectionStore"
|
|
import { dismiss, notify } from "@/lib/notificationStore"
|
|
import { isPortal } from "@/lib/portal"
|
|
|
|
/** One card, replaced in place, rather than one per poll that confirms it. */
|
|
const ID = "connection-offline"
|
|
|
|
/**
|
|
* Says when the instance cannot be reached, and when it was last heard
|
|
* from.
|
|
*
|
|
* Only ever meaningful under a portal: a local install cannot lose contact with
|
|
* itself. The screen underneath keeps its last data rather than blanking —
|
|
* stale readings with a timestamp are more use than an empty page, which is why
|
|
* this leads with when we last heard anything.
|
|
*
|
|
* Draws nothing of its own: it raises a persistent notification and takes it
|
|
* back when the tunnel returns, so being offline is said in the same place as
|
|
* everything else the app has to say. Still a component rather than a
|
|
* subscription inside the store, because it is what scopes the notice to the
|
|
* shell — a wall panel or a kiosk has nobody to read it.
|
|
*/
|
|
export function ConnectionNotice() {
|
|
const connection = useSyncExternalStore(
|
|
connectionStore.subscribe,
|
|
connectionStore.snapshot,
|
|
connectionStore.snapshot,
|
|
)
|
|
const portal = isPortal()
|
|
|
|
useEffect(() => {
|
|
if (!portal) return
|
|
if (!connection.offline) {
|
|
dismiss(ID)
|
|
return
|
|
}
|
|
notify(
|
|
connection.lastSeen
|
|
? `Instance offline — last seen ${ago(connection.lastSeen / 1000)}, reconnecting…`
|
|
: "Instance offline — reconnecting…",
|
|
"warning",
|
|
{ id: ID, persistent: true },
|
|
)
|
|
}, [portal, connection])
|
|
|
|
return null
|
|
}
|