Files
vibe-kanban/packages/remote-web/src/shared/hooks/useRelayAppBarHosts.ts
Louis Knight-Webb 71cdd50bb8 Refactor routing in remote and local web (Vibe Kanban) (#2990)
* remote-web: fix user-system fetch routing during first host pairing

* refactor(remote-web): migrate fully to host-scoped URL routing

Make /hosts/{hostId} the single source of truth for active host context, remove legacy host resolution state, and migrate remote workspace/project routes to host-scoped paths only.

Also update shared web-core navigation/path parsing helpers and callsites to use route builders so remote host-scoped routing typechecks and behaves consistently end-to-end.

* refactor(web-core): replace resolveAppPath with app navigation intents

Introduce runtime-aware app navigation helpers for local and remote web, add useAppNavigation hook, migrate redirect/history callsites, and remove string-based resolveAppPath path guessing.

* refactor(routes): centralize app navigation and remove sidebar path builders

* refactor(routes): delete legacy navigation module and unify on appNavigation

* refactor(navigation): inject app route mapping via local/remote providers

* refactor(remote-nav): define non-host fallback mapping in remote root

* refactor(app-navigation): move route mappings into local and remote apps

* refactor(app-navigation): colocate local and remote navigation modules

* docs: add hard migration plan for app navigation refactor

* docs: define shared semantic destination model in navigation plan

* docs: update navigation plan to move kanban search to local state

* docs: include host scope and precedence in navigation model plan

* docs: specify remote host resolution and no-fallback navigation rules

* docs: switch navigation plan to draft-first create flow without nav state

* docs: add hostless remote behavior decisions to app navigation migration plan

* docs: clarify remote hostless navigation and 404 policy

* docs: define web-core direct route cleanup and Link removal scope

* docs: require typed replacement for to='.' navigation normalization

* docs: add remote host UX consolidation phase and host action policy

* docs: switch app navigation plan to in-memory kanban create defaults

* docs: lock kanban in-memory defaults rules and rollout order

* refactor: add semantic app destination parsing and unify project sidebar route parsing

* refactor: migrate app navigation to imperative destination API

Replace route-object navigation usage with appNavigation.navigate across web-core, move route mapping into local/remote adapters, remove ActionExecutorContext.navigate, and update migration plan phase tracking.

* refactor: consolidate remote host resolution and remove direct web-core route primitives

Add shared remote host selection helper, apply it in HomePage and RemoteAppShell, and replace remaining web-core onboarding/issue Link-Navigate usage with AppNavigation.

* refactor: move kanban create defaults to in-memory state and make app navigation path-only

* refactor: remove workspace-create router state and use scratch-only initialization

* refactor: use semantic first-project destinations in root and onboarding redirects

* chore: add app-navigation any guardrail and complete phases 7/8

* refactor: migrate AppNavigation to goTo API and enforce route guards

* refactor: remove navigate API and enforce destination dispatch

* fix: surface workspace draft creation failures in kanban panel

* refactor: move path resolution to app adapters and remove shared parser

* refactor: remove projectSidebarRoutes and consume AppDestination directly

* refactor: use explicit goTo methods in root and onboarding redirects

* refactor root redirect page to single-effect navigation flow

* remove previous-path replay and simplify project nav to root

* remove useKanbanNavigation and inline typed route-driven kanban nav

* refactor(web-core): centralize destination parsing with shared hooks

* refactor(kanban): hard-cut issue create routes and use composer state

* chore(navigation): move issue-create options to composer domain

Prune legacy allowlist entries and fix guard script behavior when allowlist is empty.

* docs: remove completed app navigation migration plan
2026-03-02 11:22:18 +00:00

99 lines
2.4 KiB
TypeScript

import { useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
import type { AppBarHost } from "@vibe/ui/components/AppBar";
import type { RelayHost } from "shared/remote-types";
import { listPairedRelayHosts } from "@/shared/lib/relayPairingStorage";
import { listRelayHosts } from "@/shared/lib/remoteApi";
const RELAY_APP_BAR_HOSTS_QUERY_KEY = ["relay-app-bar-hosts", "hosts"] as const;
const RELAY_APP_BAR_PAIRED_HOSTS_QUERY_KEY = [
"relay-app-bar-hosts",
"paired-hosts",
] as const;
interface UseRelayAppBarHostsResult {
hosts: AppBarHost[];
isLoading: boolean;
}
export interface ResolveRelayNavigationHostOptions {
routeHostId?: string | null;
}
export function resolveRelayNavigationHostId(
hosts: AppBarHost[],
options?: ResolveRelayNavigationHostOptions,
): string | null {
const routeHostId = options?.routeHostId ?? null;
if (routeHostId) {
return routeHostId;
}
const onlineHost = hosts.find((host) => host.status === "online");
if (onlineHost) {
return onlineHost.id;
}
return null;
}
function mapRelayHostStatus(
host: RelayHost,
pairedHostIds: Set<string>,
): AppBarHost["status"] {
if (!pairedHostIds.has(host.id)) {
return "unpaired";
}
return host.status === "online" ? "online" : "offline";
}
export function useRelayAppBarHosts(
enabled: boolean,
): UseRelayAppBarHostsResult {
const hostsQuery = useQuery({
queryKey: RELAY_APP_BAR_HOSTS_QUERY_KEY,
queryFn: listRelayHosts,
enabled,
staleTime: 30_000,
refetchInterval: 30_000,
});
const pairedHostsQuery = useQuery({
queryKey: RELAY_APP_BAR_PAIRED_HOSTS_QUERY_KEY,
queryFn: async () => {
try {
return await listPairedRelayHosts();
} catch (error) {
console.error("Failed to load paired relay hosts for app bar", error);
return [];
}
},
enabled,
staleTime: 5_000,
refetchInterval: 5_000,
});
const hosts = useMemo<AppBarHost[]>(() => {
if (!enabled) {
return [];
}
const relayHosts = hostsQuery.data ?? [];
const pairedHostIds = new Set(
(pairedHostsQuery.data ?? []).map((host) => host.host_id),
);
return relayHosts.map((host) => ({
id: host.id,
name: host.name,
status: mapRelayHostStatus(host, pairedHostIds),
}));
}, [enabled, hostsQuery.data, pairedHostsQuery.data]);
return {
hosts,
isLoading: enabled && (hostsQuery.isLoading || pairedHostsQuery.isLoading),
};
}