saving a connection now tells the other panels

reported on the first successful save: everything still showed no data and the
form stayed open, and a manual page refresh was the only way through.

two faults, one report. the form did not close itself — nothing cleared
?config, so a save left the settings view sitting on top of a plugin that now
worked. and the two reader panels are SIBLINGS in a workspace layout: they are
mounted independently, neither owns the other, and ConnectionPanel cannot call
into them. so they went on showing the failure they had loaded back when no
connection existed.

a channel is the sanctioned answer for exactly this — docs/navigation-audit.md
puts addressable state in the URL and leaves channels for "something changed,
re-read it". nobody wants to link to the moment after a save.

the nonce is a plain module counter, copied deliberately from officerdev's
channels.ts along with the reason: setSignal(n => n + 1) applies against the
value captured at render, so two bumps in one render window both compute
snapshot + 1 and the second overwrites the first — and nobody re-reads.
Date.now() is the same bug at millisecond scale.

disconnect bumps it too. the panels must stop showing hosts they can no longer
reach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-25 16:58:52 +00:00
co-authored by Claude Opus 5
parent 83a8558e60
commit 3d02cb6229
4 changed files with 76 additions and 2 deletions
+3 -1
View File
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { RefreshCw, ShieldAlert, ShieldCheck } from "lucide-react";
import { useClient } from "hooks/useClient";
import { readableError } from "./errors";
import { useConnectionChanged } from "./channel";
/**
* Certificates, ordered by how soon they expire.
@@ -29,6 +30,7 @@ export const CertificatesPanel = () => {
const clientRef = useRef(client);
clientRef.current = client;
const [connectionSignal] = useConnectionChanged();
const [certificates, setCertificates] = useState<Certificate[] | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -46,7 +48,7 @@ export const CertificatesPanel = () => {
useEffect(() => {
void load();
}, [load]);
}, [load, connectionSignal]);
if (error) return <Centred>{error}</Centred>;
if (!certificates) return <Centred>Loading certificates…</Centred>;