tray: selectable exit nodes + push-based network list refresh

Make the tray Exit Node submenu selectable (mutually exclusive, sourced from
ListNetworks by NetID) instead of read-only.

Add networksRevision to the status snapshot, bumped by the route manager on
network-map and selection changes, so the tray and the React NetworksContext
re-fetch ListNetworks via the push stream instead of polling. The peer-status
route list only carries chosen routes, so a candidate exit node appearing or
disappearing would otherwise never reach the UI.
This commit is contained in:
Zoltan Papp
2026-05-27 20:48:16 +02:00
parent 09f4109b01
commit f693d268b4
12 changed files with 259 additions and 100 deletions
@@ -9,6 +9,7 @@ import {
} from "react";
import { Networks as NetworksSvc } from "@bindings/services";
import type { Network } from "@bindings/services/models.js";
import { useStatus } from "@/modules/daemon-status/StatusContext";
// A range is treated as an exit-node candidate when any of its CIDRs is a
// default route (v4 or v6). The daemon may merge a v4+v6 pair into a single
@@ -40,6 +41,7 @@ export const useNetworks = () => {
};
export const NetworksProvider = ({ children }: { children: ReactNode }) => {
const { status } = useStatus();
const [routes, setRoutes] = useState<Network[]>([]);
const refresh = useCallback(async () => {
@@ -51,9 +53,15 @@ export const NetworksProvider = ({ children }: { children: ReactNode }) => {
}
}, []);
// The daemon bumps networksRevision whenever the routed-network set or a
// selection changes (from any surface) and pushes it on the status stream.
// Refetch on every bump so the list stays live without polling — and on
// mount, since the revision is already defined by the time this provider
// renders (StatusProvider only mounts children once the daemon is reachable).
const networksRevision = status?.networksRevision;
useEffect(() => {
void refresh();
}, [refresh]);
}, [refresh, networksRevision]);
const toggleNetwork = useCallback(
async (id: string, selected: boolean) => {
@@ -120,7 +128,5 @@ export const NetworksProvider = ({ children }: { children: ReactNode }) => {
};
}, [routes, refresh, toggleNetwork, toggleExitNode]);
return (
<NetworksContext.Provider value={value}>{children}</NetworksContext.Provider>
);
return <NetworksContext.Provider value={value}>{children}</NetworksContext.Provider>;
};