Fix host switcher status syncing
This commit is contained in:
@@ -5,8 +5,8 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useSyncExternalStore,
|
|
||||||
type Dispatch,
|
type Dispatch,
|
||||||
|
type ReactElement,
|
||||||
type RefObject,
|
type RefObject,
|
||||||
type SetStateAction,
|
type SetStateAction,
|
||||||
} from "react";
|
} from "react";
|
||||||
@@ -44,8 +44,8 @@ import {
|
|||||||
import { useSidebarAnimation } from "@/contexts/sidebar-animation-context";
|
import { useSidebarAnimation } from "@/contexts/sidebar-animation-context";
|
||||||
import { useWindowControlsPadding } from "@/utils/desktop-window";
|
import { useWindowControlsPadding } from "@/utils/desktop-window";
|
||||||
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
|
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
|
||||||
import { Combobox } from "@/components/ui/combobox";
|
import { Combobox, ComboboxItem, type ComboboxOption } from "@/components/ui/combobox";
|
||||||
import { getHostRuntimeStore, useHosts } from "@/runtime/host-runtime";
|
import { useHostRuntimeSnapshot, useHosts } from "@/runtime/host-runtime";
|
||||||
import { formatConnectionStatus } from "@/utils/daemons";
|
import { formatConnectionStatus } from "@/utils/daemons";
|
||||||
import {
|
import {
|
||||||
HEADER_INNER_HEIGHT,
|
HEADER_INNER_HEIGHT,
|
||||||
@@ -69,18 +69,12 @@ interface LeftSidebarProps {
|
|||||||
selectedAgentId?: string;
|
selectedAgentId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HostOption {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SidebarSharedProps {
|
interface SidebarSharedProps {
|
||||||
theme: SidebarTheme;
|
theme: SidebarTheme;
|
||||||
activeServerId: string | null;
|
activeServerId: string | null;
|
||||||
activeHostLabel: string;
|
activeHostLabel: string;
|
||||||
activeHostStatusColor: string;
|
activeHostStatusColor: string;
|
||||||
hostOptions: HostOption[];
|
hostOptions: ComboboxOption[];
|
||||||
hostTriggerRef: RefObject<View | null>;
|
hostTriggerRef: RefObject<View | null>;
|
||||||
isHostPickerOpen: boolean;
|
isHostPickerOpen: boolean;
|
||||||
setIsHostPickerOpen: Dispatch<SetStateAction<boolean>>;
|
setIsHostPickerOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
@@ -95,6 +89,12 @@ interface SidebarSharedProps {
|
|||||||
handleHostSelect: (nextServerId: string) => void;
|
handleHostSelect: (nextServerId: string) => void;
|
||||||
handleOpenProject: () => void;
|
handleOpenProject: () => void;
|
||||||
handleSettings: () => void;
|
handleSettings: () => void;
|
||||||
|
renderHostOption: (input: {
|
||||||
|
option: ComboboxOption;
|
||||||
|
selected: boolean;
|
||||||
|
active: boolean;
|
||||||
|
onPress: () => void;
|
||||||
|
}) => ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MobileSidebarProps extends SidebarSharedProps {
|
interface MobileSidebarProps extends SidebarSharedProps {
|
||||||
@@ -124,28 +124,6 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
const closeToAgent = usePanelStore((state) => state.closeToAgent);
|
const closeToAgent = usePanelStore((state) => state.closeToAgent);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const daemons = useHosts();
|
const daemons = useHosts();
|
||||||
const runtime = getHostRuntimeStore();
|
|
||||||
const runtimeConnectionStatusSignature = useSyncExternalStore(
|
|
||||||
(onStoreChange) => runtime.subscribeAll(onStoreChange),
|
|
||||||
() =>
|
|
||||||
daemons
|
|
||||||
.map(
|
|
||||||
(daemon) =>
|
|
||||||
`${daemon.serverId}:${
|
|
||||||
runtime.getSnapshot(daemon.serverId)?.connectionStatus ?? "connecting"
|
|
||||||
}`,
|
|
||||||
)
|
|
||||||
.join("|"),
|
|
||||||
() =>
|
|
||||||
daemons
|
|
||||||
.map(
|
|
||||||
(daemon) =>
|
|
||||||
`${daemon.serverId}:${
|
|
||||||
runtime.getSnapshot(daemon.serverId)?.connectionStatus ?? "connecting"
|
|
||||||
}`,
|
|
||||||
)
|
|
||||||
.join("|"),
|
|
||||||
);
|
|
||||||
const activeServerIdFromPath = useMemo(() => parseServerIdFromPathname(pathname), [pathname]);
|
const activeServerIdFromPath = useMemo(() => parseServerIdFromPathname(pathname), [pathname]);
|
||||||
const activeDaemon = useMemo(() => {
|
const activeDaemon = useMemo(() => {
|
||||||
if (daemons.length === 0) {
|
if (daemons.length === 0) {
|
||||||
@@ -165,8 +143,9 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
const trimmed = activeDaemon.label?.trim();
|
const trimmed = activeDaemon.label?.trim();
|
||||||
return trimmed && trimmed.length > 0 ? trimmed : activeDaemon.serverId;
|
return trimmed && trimmed.length > 0 ? trimmed : activeDaemon.serverId;
|
||||||
}, [activeDaemon]);
|
}, [activeDaemon]);
|
||||||
|
const activeHostSnapshot = useHostRuntimeSnapshot(activeServerId ?? "");
|
||||||
const activeHostStatus = activeServerId
|
const activeHostStatus = activeServerId
|
||||||
? (runtime.getSnapshot(activeServerId)?.connectionStatus ?? "connecting")
|
? (activeHostSnapshot?.connectionStatus ?? "connecting")
|
||||||
: "idle";
|
: "idle";
|
||||||
const activeHostStatusColor =
|
const activeHostStatusColor =
|
||||||
activeHostStatus === "online"
|
activeHostStatus === "online"
|
||||||
@@ -179,11 +158,25 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
daemons.map((daemon) => ({
|
daemons.map((daemon) => ({
|
||||||
id: daemon.serverId,
|
id: daemon.serverId,
|
||||||
label: daemon.label?.trim() || daemon.serverId,
|
label: daemon.label?.trim() || daemon.serverId,
|
||||||
description: formatConnectionStatus(
|
|
||||||
runtime.getSnapshot(daemon.serverId)?.connectionStatus ?? "connecting",
|
|
||||||
),
|
|
||||||
})),
|
})),
|
||||||
[daemons, runtime, runtimeConnectionStatusSignature],
|
[daemons],
|
||||||
|
);
|
||||||
|
const renderHostOption = useCallback(
|
||||||
|
({ option, selected, active, onPress }: {
|
||||||
|
option: ComboboxOption;
|
||||||
|
selected: boolean;
|
||||||
|
active: boolean;
|
||||||
|
onPress: () => void;
|
||||||
|
}) => (
|
||||||
|
<HostSwitchOption
|
||||||
|
serverId={option.id}
|
||||||
|
label={option.label}
|
||||||
|
selected={selected}
|
||||||
|
active={active}
|
||||||
|
onPress={onPress}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
const hostTriggerRef = useRef<View | null>(null);
|
const hostTriggerRef = useRef<View | null>(null);
|
||||||
const [isHostPickerOpen, setIsHostPickerOpen] = useState(false);
|
const [isHostPickerOpen, setIsHostPickerOpen] = useState(false);
|
||||||
@@ -276,6 +269,7 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
|
renderHostOption,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isCompactLayout) {
|
if (isCompactLayout) {
|
||||||
@@ -305,6 +299,33 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function HostSwitchOption({
|
||||||
|
serverId,
|
||||||
|
label,
|
||||||
|
selected,
|
||||||
|
active,
|
||||||
|
onPress,
|
||||||
|
}: {
|
||||||
|
serverId: string;
|
||||||
|
label: string;
|
||||||
|
selected: boolean;
|
||||||
|
active: boolean;
|
||||||
|
onPress: () => void;
|
||||||
|
}) {
|
||||||
|
const snapshot = useHostRuntimeSnapshot(serverId);
|
||||||
|
const connectionStatus = snapshot?.connectionStatus ?? "connecting";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ComboboxItem
|
||||||
|
label={label}
|
||||||
|
description={formatConnectionStatus(connectionStatus)}
|
||||||
|
selected={selected}
|
||||||
|
active={active}
|
||||||
|
onPress={onPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function SessionsButton({ onPress }: { onPress: () => void }) {
|
function SessionsButton({ onPress }: { onPress: () => void }) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
@@ -361,6 +382,7 @@ function MobileSidebar({
|
|||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
|
renderHostOption,
|
||||||
handleOpenProject,
|
handleOpenProject,
|
||||||
handleSettings,
|
handleSettings,
|
||||||
insetsTop,
|
insetsTop,
|
||||||
@@ -606,6 +628,7 @@ function MobileSidebar({
|
|||||||
options={hostOptions}
|
options={hostOptions}
|
||||||
value={activeServerId ?? ""}
|
value={activeServerId ?? ""}
|
||||||
onSelect={handleHostSelect}
|
onSelect={handleHostSelect}
|
||||||
|
renderOption={renderHostOption}
|
||||||
searchable={false}
|
searchable={false}
|
||||||
title="Switch host"
|
title="Switch host"
|
||||||
searchPlaceholder="Search hosts..."
|
searchPlaceholder="Search hosts..."
|
||||||
@@ -639,6 +662,7 @@ function DesktopSidebar({
|
|||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
|
renderHostOption,
|
||||||
handleOpenProject,
|
handleOpenProject,
|
||||||
handleSettings,
|
handleSettings,
|
||||||
insetsTop,
|
insetsTop,
|
||||||
@@ -789,6 +813,7 @@ function DesktopSidebar({
|
|||||||
options={hostOptions}
|
options={hostOptions}
|
||||||
value={activeServerId ?? ""}
|
value={activeServerId ?? ""}
|
||||||
onSelect={handleHostSelect}
|
onSelect={handleHostSelect}
|
||||||
|
renderOption={renderHostOption}
|
||||||
searchable={false}
|
searchable={false}
|
||||||
title="Switch host"
|
title="Switch host"
|
||||||
searchPlaceholder="Search hosts..."
|
searchPlaceholder="Search hosts..."
|
||||||
|
|||||||
Reference in New Issue
Block a user