* feat(settings): consolidate daemon management into the host page The app-level Daemon section and the localhost host page rendered the same daemon card in two places. The host page is now the single home: the Host sidebar group leads with Overview, and a desktop-only "Enable built-in daemon" option in the host switcher recovers a removed localhost connection. Old settings/daemon deep links redirect (COMPAT-tagged). * fix(settings): hide daemon recovery option when the server id probe fails Review feedback: !query.isLoading treated an errored IPC probe as resolved, so the recovery affordance could show without knowing the local server id. Replace the resolved-boolean hook with a single discriminated state hook (loading | error | resolved) — errors hide the affordance and fall back to the settings root in the redirect. Also collapses the two query subscriptions per caller into one. * fix(settings): keep daemon recovery reachable when no hosts remain Review feedback: removing localhost as the only host takes the settings sidebar down its no-host branch, which never mounts the host picker — so the "Enable built-in daemon" option was unreachable in the most common recovery case. The sidebar now owns the single hook call, passes it to the picker, and renders the same action as a sidebar row next to "Add host" when no hosts exist. * test(e2e): update settings host header to Overview Test drift: the host section's sidebar/header label was intentionally renamed to "Overview"; the spec still asserted the old "Host" title. Also assert the retired app-level Daemon row stays absent.
77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import { getDesktopDaemonStatus, shouldUseDesktopDaemon } from "@/desktop/daemon/desktop-daemon";
|
|
|
|
const DESKTOP_DAEMON_SERVER_ID_QUERY_KEY = ["desktop-daemon-server-id"] as const;
|
|
|
|
interface DesktopDaemonServerIdResult {
|
|
serverId: string | null;
|
|
}
|
|
|
|
async function loadDesktopDaemonServerId(): Promise<DesktopDaemonServerIdResult> {
|
|
const status = await getDesktopDaemonStatus();
|
|
const serverId = status.serverId.trim();
|
|
return {
|
|
serverId: serverId.length > 0 ? serverId : null,
|
|
};
|
|
}
|
|
|
|
function useLocalDaemonServerIdQuery() {
|
|
const isDesktopApp = shouldUseDesktopDaemon();
|
|
|
|
return useQuery({
|
|
queryKey: DESKTOP_DAEMON_SERVER_ID_QUERY_KEY,
|
|
queryFn: loadDesktopDaemonServerId,
|
|
enabled: isDesktopApp,
|
|
staleTime: Infinity,
|
|
gcTime: Infinity,
|
|
refetchInterval: (activeQuery) => (activeQuery.state.data?.serverId ? false : 1000),
|
|
refetchOnMount: false,
|
|
refetchOnReconnect: false,
|
|
refetchOnWindowFocus: false,
|
|
retry: false,
|
|
});
|
|
}
|
|
|
|
export function useLocalDaemonServerId(): string | null {
|
|
const isDesktopApp = shouldUseDesktopDaemon();
|
|
const query = useLocalDaemonServerIdQuery();
|
|
|
|
if (!isDesktopApp) {
|
|
return null;
|
|
}
|
|
|
|
return query.data?.serverId ?? null;
|
|
}
|
|
|
|
export type LocalDaemonServerIdState =
|
|
| { status: "loading" }
|
|
| { status: "error" }
|
|
| { status: "resolved"; serverId: string | null };
|
|
|
|
export function useLocalDaemonServerIdState(): LocalDaemonServerIdState {
|
|
const isDesktopApp = shouldUseDesktopDaemon();
|
|
const query = useLocalDaemonServerIdQuery();
|
|
|
|
if (!isDesktopApp) {
|
|
return { status: "resolved", serverId: null };
|
|
}
|
|
if (query.isError) {
|
|
return { status: "error" };
|
|
}
|
|
if (query.isSuccess) {
|
|
return { status: "resolved", serverId: query.data.serverId };
|
|
}
|
|
return { status: "loading" };
|
|
}
|
|
|
|
export function useIsLocalDaemon(serverId: string): boolean {
|
|
const normalizedServerId = serverId.trim();
|
|
const localServerId = useLocalDaemonServerId();
|
|
|
|
if (localServerId === null || normalizedServerId.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
return localServerId === normalizedServerId;
|
|
}
|