Files
app/frontend/src/components/Common/ConnectionNotice.tsx
T
stroblmeandClaude Opus 5 d01a8dad37 Rename Installation to Instance
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
2026-08-31 10:12:01 +02:00

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
}