fix: Phase 2 review issues
This commit is contained in:
@@ -20,6 +20,7 @@ import { formatConnectionStatus, getConnectionStatusTone } from "@/utils/daemons
|
|||||||
import { theme as defaultTheme } from "@/styles/theme";
|
import { theme as defaultTheme } from "@/styles/theme";
|
||||||
import { BackHeader } from "@/components/headers/back-header";
|
import { BackHeader } from "@/components/headers/back-header";
|
||||||
import { useSessionForServer } from "@/hooks/use-session-directory";
|
import { useSessionForServer } from "@/hooks/use-session-directory";
|
||||||
|
import type { SessionContextValue } from "@/contexts/session-context";
|
||||||
|
|
||||||
const delay = (ms: number) =>
|
const delay = (ms: number) =>
|
||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
@@ -797,11 +798,18 @@ function DaemonCard({
|
|||||||
: theme.colors.mutedForeground;
|
: theme.colors.mutedForeground;
|
||||||
const badgeText = statusLabel;
|
const badgeText = statusLabel;
|
||||||
const connectionError = typeof lastError === "string" && lastError.trim().length > 0 ? lastError.trim() : null;
|
const connectionError = typeof lastError === "string" && lastError.trim().length > 0 ? lastError.trim() : null;
|
||||||
const daemonSession = useSessionForServer(daemon.id);
|
const daemonWs = useSessionForServer<SessionContextValue["ws"] | null>(
|
||||||
|
daemon.id,
|
||||||
|
useCallback((session) => session?.ws ?? null, [])
|
||||||
|
);
|
||||||
|
const restartServerFn = useSessionForServer<SessionContextValue["restartServer"] | null>(
|
||||||
|
daemon.id,
|
||||||
|
useCallback((session) => session?.restartServer ?? null, [])
|
||||||
|
);
|
||||||
const [isRestarting, setIsRestarting] = useState(false);
|
const [isRestarting, setIsRestarting] = useState(false);
|
||||||
const wsIsConnectedRef = useRef(daemonSession?.ws.isConnected ?? false);
|
const wsIsConnectedRef = useRef(daemonWs?.isConnected ?? false);
|
||||||
const isTesting = testState?.status === "testing";
|
const isTesting = testState?.status === "testing";
|
||||||
const sessionIsConnected = daemonSession?.ws.isConnected ?? false;
|
const sessionIsConnected = daemonWs?.isConnected ?? false;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
wsIsConnectedRef.current = sessionIsConnected;
|
wsIsConnectedRef.current = sessionIsConnected;
|
||||||
@@ -853,7 +861,7 @@ function DaemonCard({
|
|||||||
}, [daemon.label, daemon.wsUrl, isScreenMountedRef, testServerConnection, waitForCondition]);
|
}, [daemon.label, daemon.wsUrl, isScreenMountedRef, testServerConnection, waitForCondition]);
|
||||||
|
|
||||||
const beginServerRestart = useCallback(() => {
|
const beginServerRestart = useCallback(() => {
|
||||||
if (!daemonSession) {
|
if (!restartServerFn) {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Host unavailable",
|
"Host unavailable",
|
||||||
`${daemon.label} is not connected. Wait for it to come online before restarting.`
|
`${daemon.label} is not connected. Wait for it to come online before restarting.`
|
||||||
@@ -871,7 +879,7 @@ function DaemonCard({
|
|||||||
|
|
||||||
setIsRestarting(true);
|
setIsRestarting(true);
|
||||||
try {
|
try {
|
||||||
daemonSession.restartServer(`settings_daemon_restart_${daemon.id}`);
|
restartServerFn(`settings_daemon_restart_${daemon.id}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Settings] Failed to restart daemon ${daemon.label}`, error);
|
console.error(`[Settings] Failed to restart daemon ${daemon.label}`, error);
|
||||||
setIsRestarting(false);
|
setIsRestarting(false);
|
||||||
@@ -883,10 +891,10 @@ function DaemonCard({
|
|||||||
}
|
}
|
||||||
|
|
||||||
void waitForDaemonRestart();
|
void waitForDaemonRestart();
|
||||||
}, [daemon.id, daemon.label, daemonSession, waitForDaemonRestart]);
|
}, [daemon.id, daemon.label, restartServerFn, waitForDaemonRestart]);
|
||||||
|
|
||||||
const handleRestartPress = useCallback(() => {
|
const handleRestartPress = useCallback(() => {
|
||||||
if (!daemonSession) {
|
if (!restartServerFn) {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Host unavailable",
|
"Host unavailable",
|
||||||
`${daemon.label} is not connected. Wait for it to come online before restarting.`
|
`${daemon.label} is not connected. Wait for it to come online before restarting.`
|
||||||
@@ -917,7 +925,7 @@ function DaemonCard({
|
|||||||
onPress: beginServerRestart,
|
onPress: beginServerRestart,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}, [beginServerRestart, daemon.label, daemonSession, restartConfirmationMessage]);
|
}, [beginServerRestart, daemon.label, restartConfirmationMessage, restartServerFn]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.settingCard, styles.daemonCard]}>
|
<View style={[styles.settingCard, styles.daemonCard]}>
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import type { SessionContextValue } from "@/contexts/session-context";
|
|||||||
import { useSessionForServer } from "@/hooks/use-session-directory";
|
import { useSessionForServer } from "@/hooks/use-session-directory";
|
||||||
import type { UseWebSocketReturn } from "@/hooks/use-websocket";
|
import type { UseWebSocketReturn } from "@/hooks/use-websocket";
|
||||||
import { useSessionStore } from "@/stores/session-store";
|
import { useSessionStore } from "@/stores/session-store";
|
||||||
|
import { shallow } from "zustand/shallow";
|
||||||
|
|
||||||
export type CreateAgentInitialValues = {
|
export type CreateAgentInitialValues = {
|
||||||
workingDir?: string;
|
workingDir?: string;
|
||||||
@@ -101,6 +102,18 @@ interface ModalWrapperProps {
|
|||||||
serverId?: string | null;
|
serverId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateAgentSessionSlice = Pick<
|
||||||
|
SessionContextValue,
|
||||||
|
| "serverId"
|
||||||
|
| "ws"
|
||||||
|
| "createAgent"
|
||||||
|
| "resumeAgent"
|
||||||
|
| "sendAgentAudio"
|
||||||
|
| "agents"
|
||||||
|
| "providerModels"
|
||||||
|
| "requestProviderModels"
|
||||||
|
>;
|
||||||
|
|
||||||
const providerDefinitions = AGENT_PROVIDER_DEFINITIONS;
|
const providerDefinitions = AGENT_PROVIDER_DEFINITIONS;
|
||||||
const providerDefinitionMap = new Map<AgentProvider, AgentProviderDefinition>(
|
const providerDefinitionMap = new Map<AgentProvider, AgentProviderDefinition>(
|
||||||
providerDefinitions.map((definition) => [definition.id, definition])
|
providerDefinitions.map((definition) => [definition.id, definition])
|
||||||
@@ -238,8 +251,25 @@ function AgentFlowModal({
|
|||||||
return exists ? serverId : null;
|
return exists ? serverId : null;
|
||||||
}, [serverId, daemonEntries]);
|
}, [serverId, daemonEntries]);
|
||||||
const [selectedServerId, setSelectedServerId] = useState<string | null>(initialServerId);
|
const [selectedServerId, setSelectedServerId] = useState<string | null>(initialServerId);
|
||||||
const selectedSession = useSessionForServer(selectedServerId);
|
const selectSessionSlice = useCallback(
|
||||||
const session = selectedSession ?? null;
|
(session: SessionContextValue | null): CreateAgentSessionSlice | null => {
|
||||||
|
if (!session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
serverId: session.serverId,
|
||||||
|
ws: session.ws,
|
||||||
|
createAgent: session.createAgent,
|
||||||
|
resumeAgent: session.resumeAgent,
|
||||||
|
sendAgentAudio: session.sendAgentAudio,
|
||||||
|
agents: session.agents,
|
||||||
|
providerModels: session.providerModels,
|
||||||
|
requestProviderModels: session.requestProviderModels,
|
||||||
|
} satisfies CreateAgentSessionSlice;
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const session = useSessionForServer<CreateAgentSessionSlice | null>(selectedServerId, selectSessionSlice, shallow);
|
||||||
const getSession = useSessionStore((state) => state.getSession);
|
const getSession = useSessionStore((state) => state.getSession);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -574,7 +604,7 @@ function AgentFlowModal({
|
|||||||
const queueProviderModelFetch = useCallback(
|
const queueProviderModelFetch = useCallback(
|
||||||
(
|
(
|
||||||
serverId: string | null,
|
serverId: string | null,
|
||||||
targetSession: SessionContextValue | null,
|
targetSession: Pick<SessionContextValue, "providerModels" | "requestProviderModels"> | null,
|
||||||
options?: { cwd?: string; delayMs?: number }
|
options?: { cwd?: string; delayMs?: number }
|
||||||
) => {
|
) => {
|
||||||
if (!serverId || !targetSession?.requestProviderModels) {
|
if (!serverId || !targetSession?.requestProviderModels) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
|
import { useStoreWithEqualityFn } from "zustand/traditional";
|
||||||
import { useSessionStore, type SessionData } from "@/stores/session-store";
|
import { useSessionStore, type SessionData } from "@/stores/session-store";
|
||||||
|
|
||||||
export function useSessionDirectory(): Map<string, SessionData> {
|
export function useSessionDirectory(): Map<string, SessionData> {
|
||||||
@@ -9,6 +10,33 @@ export function useSessionDirectory(): Map<string, SessionData> {
|
|||||||
}, [sessions]);
|
}, [sessions]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSessionForServer(serverId: string | null): SessionData | null {
|
type SessionSelector<T> = (session: SessionData | null) => T;
|
||||||
return useSessionStore((state) => (serverId ? state.sessions[serverId] ?? null : null));
|
type EqualityFn<T> = ((left: T, right: T) => boolean) | undefined;
|
||||||
|
|
||||||
|
export function useSessionForServer(serverId: string | null): SessionData | null;
|
||||||
|
export function useSessionForServer<T>(
|
||||||
|
serverId: string | null,
|
||||||
|
selector: SessionSelector<T>,
|
||||||
|
equalityFn?: EqualityFn<T>
|
||||||
|
): T;
|
||||||
|
export function useSessionForServer<T>(
|
||||||
|
serverId: string | null,
|
||||||
|
selector?: SessionSelector<T>,
|
||||||
|
equalityFn?: EqualityFn<T>
|
||||||
|
): SessionData | null | T {
|
||||||
|
const baseSelector = useCallback(
|
||||||
|
(state: ReturnType<typeof useSessionStore.getState>) =>
|
||||||
|
(serverId ? state.sessions[serverId] ?? null : null),
|
||||||
|
[serverId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selector) {
|
||||||
|
const derivedSelector = useCallback(
|
||||||
|
(state: ReturnType<typeof useSessionStore.getState>) => selector(baseSelector(state)),
|
||||||
|
[baseSelector, selector]
|
||||||
|
);
|
||||||
|
return useStoreWithEqualityFn(useSessionStore, derivedSelector, equalityFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return useSessionStore(baseSelector);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,10 @@ function areAgentDirectoriesEqual(left: AgentDirectoryEntry[] | undefined, right
|
|||||||
entry.id === other.id &&
|
entry.id === other.id &&
|
||||||
entry.status === other.status &&
|
entry.status === other.status &&
|
||||||
entry.serverId === other.serverId &&
|
entry.serverId === other.serverId &&
|
||||||
entry.lastActivityAt.getTime() === other.lastActivityAt.getTime()
|
entry.lastActivityAt.getTime() === other.lastActivityAt.getTime() &&
|
||||||
|
entry.title === other.title &&
|
||||||
|
entry.cwd === other.cwd &&
|
||||||
|
entry.provider === other.provider
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user