WIP: snapshot workspace execution refactor baseline
This commit is contained in:
@@ -78,6 +78,7 @@ import {
|
||||
parseServerIdFromPathname,
|
||||
parseHostAgentRouteFromPathname,
|
||||
parseWorkspaceOpenIntent,
|
||||
decodeWorkspaceIdFromPathSegment,
|
||||
} from "@/utils/host-routes";
|
||||
import { syncNavigationActiveWorkspace } from "@/stores/navigation-active-workspace-store";
|
||||
|
||||
@@ -602,8 +603,6 @@ function FaviconStatusSync() {
|
||||
}
|
||||
|
||||
function RootStack() {
|
||||
const storeReady = useStoreReady();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
screenOptions={{
|
||||
@@ -614,20 +613,32 @@ function RootStack() {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack.Protected guard={storeReady}>
|
||||
<Stack.Screen name="welcome" />
|
||||
<Stack.Screen name="settings" />
|
||||
<Stack.Screen name="h/[serverId]/workspace/[workspaceId]" />
|
||||
<Stack.Screen
|
||||
name="h/[serverId]/agent/[agentId]"
|
||||
options={{ gestureEnabled: false }}
|
||||
/>
|
||||
<Stack.Screen name="h/[serverId]/index" />
|
||||
<Stack.Screen name="h/[serverId]/sessions" />
|
||||
<Stack.Screen name="h/[serverId]/open-project" />
|
||||
<Stack.Screen name="h/[serverId]/settings" />
|
||||
<Stack.Screen name="pair-scan" />
|
||||
</Stack.Protected>
|
||||
<Stack.Screen name="welcome" />
|
||||
<Stack.Screen name="settings" />
|
||||
<Stack.Screen
|
||||
name="h/[serverId]/workspace/[workspaceId]"
|
||||
getId={({ params }) => {
|
||||
const serverValue = Array.isArray(params?.serverId) ? params.serverId[0] : params?.serverId;
|
||||
const workspaceValue = Array.isArray(params?.workspaceId)
|
||||
? params.workspaceId[0]
|
||||
: params?.workspaceId;
|
||||
const serverId = typeof serverValue === "string" ? serverValue.trim() : "";
|
||||
const workspaceId =
|
||||
typeof workspaceValue === "string"
|
||||
? (decodeWorkspaceIdFromPathSegment(workspaceValue) ?? workspaceValue.trim())
|
||||
: "";
|
||||
return `${serverId}:${workspaceId}`;
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="h/[serverId]/agent/[agentId]"
|
||||
options={{ gestureEnabled: false }}
|
||||
/>
|
||||
<Stack.Screen name="h/[serverId]/index" />
|
||||
<Stack.Screen name="h/[serverId]/sessions" />
|
||||
<Stack.Screen name="h/[serverId]/open-project" />
|
||||
<Stack.Screen name="h/[serverId]/settings" />
|
||||
<Stack.Screen name="pair-scan" />
|
||||
<Stack.Screen name="index" />
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
||||
import { buildHostRootRoute } from "@/utils/host-routes";
|
||||
import { resolveHydratedWorkspaceId } from "@/utils/resolve-hydrated-workspace-id";
|
||||
import { prepareWorkspaceTab } from "@/utils/workspace-navigation";
|
||||
|
||||
export default function HostAgentReadyRoute() {
|
||||
@@ -22,6 +23,21 @@ export default function HostAgentReadyRoute() {
|
||||
}
|
||||
return state.sessions[serverId]?.agents?.get(agentId)?.cwd ?? null;
|
||||
});
|
||||
const sessionWorkspaces = useSessionStore((state) =>
|
||||
serverId ? state.sessions[serverId]?.workspaces : undefined,
|
||||
);
|
||||
const hasHydratedWorkspaces = useSessionStore((state) =>
|
||||
serverId ? (state.sessions[serverId]?.hasHydratedWorkspaces ?? false) : false,
|
||||
);
|
||||
const resolvedWorkspaceId = useSessionStore((state) => {
|
||||
if (!serverId || !agentId) {
|
||||
return null;
|
||||
}
|
||||
return resolveHydratedWorkspaceId({
|
||||
workspaces: state.sessions[serverId]?.workspaces?.values(),
|
||||
path: state.sessions[serverId]?.agents?.get(agentId)?.cwd,
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (redirectedRef.current) {
|
||||
@@ -33,18 +49,17 @@ export default function HostAgentReadyRoute() {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedCwd = agentCwd?.trim();
|
||||
if (normalizedCwd) {
|
||||
if (resolvedWorkspaceId) {
|
||||
redirectedRef.current = true;
|
||||
router.replace(
|
||||
prepareWorkspaceTab({
|
||||
serverId,
|
||||
workspaceId: normalizedCwd,
|
||||
workspaceId: resolvedWorkspaceId,
|
||||
target: { kind: "agent", agentId },
|
||||
}) as any,
|
||||
);
|
||||
}
|
||||
}, [agentCwd, agentId, router, serverId]);
|
||||
}, [agentId, resolvedWorkspaceId, router, serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (redirectedRef.current) {
|
||||
@@ -53,14 +68,14 @@ export default function HostAgentReadyRoute() {
|
||||
if (!serverId || !agentId) {
|
||||
return;
|
||||
}
|
||||
if (agentCwd?.trim()) {
|
||||
if (agentCwd?.trim() && !hasHydratedWorkspaces) {
|
||||
return;
|
||||
}
|
||||
if (!client || !isConnected) {
|
||||
redirectedRef.current = true;
|
||||
router.replace(buildHostRootRoute(serverId) as any);
|
||||
}
|
||||
}, [agentCwd, agentId, client, isConnected, router, serverId]);
|
||||
}, [agentCwd, agentId, client, hasHydratedWorkspaces, isConnected, router, serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (redirectedRef.current) {
|
||||
@@ -78,12 +93,19 @@ export default function HostAgentReadyRoute() {
|
||||
return;
|
||||
}
|
||||
const cwd = result?.agent?.cwd?.trim();
|
||||
const workspaceId = resolveHydratedWorkspaceId({
|
||||
workspaces: sessionWorkspaces?.values(),
|
||||
path: cwd,
|
||||
});
|
||||
if (!workspaceId && !hasHydratedWorkspaces) {
|
||||
return;
|
||||
}
|
||||
redirectedRef.current = true;
|
||||
if (cwd) {
|
||||
if (workspaceId) {
|
||||
router.replace(
|
||||
prepareWorkspaceTab({
|
||||
serverId,
|
||||
workspaceId: cwd,
|
||||
workspaceId,
|
||||
target: { kind: "agent", agentId },
|
||||
}) as any,
|
||||
);
|
||||
@@ -102,7 +124,7 @@ export default function HostAgentReadyRoute() {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [agentId, client, isConnected, router, serverId]);
|
||||
}, [agentId, client, hasHydratedWorkspaces, isConnected, router, serverId, sessionWorkspaces]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,23 @@ import { useLocalSearchParams, usePathname, useRouter } from "expo-router";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useFormPreferences } from "@/hooks/use-form-preferences";
|
||||
import {
|
||||
buildHostAgentDetailRoute,
|
||||
buildHostOpenProjectRoute,
|
||||
buildHostRootRoute,
|
||||
buildHostWorkspaceOpenRoute,
|
||||
buildHostWorkspaceRoute,
|
||||
} from "@/utils/host-routes";
|
||||
import { resolveHydratedWorkspaceId } from "@/utils/resolve-hydrated-workspace-id";
|
||||
import { prepareWorkspaceTab } from "@/utils/workspace-navigation";
|
||||
|
||||
const HOST_ROOT_REDIRECT_DELAY_MS = 300;
|
||||
|
||||
function getCurrentPathname(fallbackPathname: string): string {
|
||||
if (typeof window === "undefined") {
|
||||
return fallbackPathname;
|
||||
}
|
||||
return window.location.pathname || fallbackPathname;
|
||||
}
|
||||
|
||||
export default function HostIndexRoute() {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
@@ -33,11 +41,13 @@ export default function HostIndexRoute() {
|
||||
return;
|
||||
}
|
||||
const rootRoute = buildHostRootRoute(serverId);
|
||||
if (pathname !== rootRoute && pathname !== `${rootRoute}/`) {
|
||||
const currentPathname = getCurrentPathname(pathname);
|
||||
if (currentPathname !== rootRoute && currentPathname !== `${rootRoute}/`) {
|
||||
return;
|
||||
}
|
||||
const timer = setTimeout(() => {
|
||||
if (pathname !== rootRoute && pathname !== `${rootRoute}/`) {
|
||||
const latestPathname = getCurrentPathname(pathname);
|
||||
if (latestPathname !== rootRoute && latestPathname !== `${rootRoute}/`) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,16 +66,24 @@ export default function HostIndexRoute() {
|
||||
});
|
||||
|
||||
const primaryAgent = visibleAgents[0];
|
||||
if (primaryAgent?.cwd?.trim()) {
|
||||
const primaryAgentWorkspaceId = resolveHydratedWorkspaceId({
|
||||
workspaces: sessionWorkspaces?.values(),
|
||||
path: primaryAgent?.cwd,
|
||||
});
|
||||
if (primaryAgent && primaryAgentWorkspaceId) {
|
||||
router.replace(
|
||||
prepareWorkspaceTab({
|
||||
serverId,
|
||||
workspaceId: primaryAgent.cwd.trim(),
|
||||
workspaceId: primaryAgentWorkspaceId,
|
||||
target: { kind: "agent", agentId: primaryAgent.id },
|
||||
}) as any,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (primaryAgent) {
|
||||
router.replace(buildHostAgentDetailRoute(serverId, primaryAgent.id) as any);
|
||||
return;
|
||||
}
|
||||
|
||||
const primaryWorkspace = visibleWorkspaces[0];
|
||||
if (primaryWorkspace?.id?.trim()) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useGlobalSearchParams, useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useGlobalSearchParams, usePathname, useRouter } from "expo-router";
|
||||
import type { WorkspaceTabTarget } from "@/stores/workspace-tabs-store";
|
||||
import { WorkspaceScreen } from "@/screens/workspace/workspace-screen";
|
||||
import {
|
||||
buildHostWorkspaceRoute,
|
||||
decodeWorkspaceIdFromPathSegment,
|
||||
parseHostWorkspaceRouteFromPathname,
|
||||
parseWorkspaceOpenIntent,
|
||||
type WorkspaceOpenIntent,
|
||||
} from "@/utils/host-routes";
|
||||
@@ -37,18 +37,13 @@ function getOpenIntentTarget(openIntent: WorkspaceOpenIntent): WorkspaceTabTarge
|
||||
export default function HostWorkspaceLayout() {
|
||||
const router = useRouter();
|
||||
const consumedIntentRef = useRef<string | null>(null);
|
||||
const params = useLocalSearchParams<{
|
||||
serverId?: string | string[];
|
||||
workspaceId?: string | string[];
|
||||
}>();
|
||||
const pathname = usePathname();
|
||||
const globalParams = useGlobalSearchParams<{
|
||||
open?: string | string[];
|
||||
}>();
|
||||
const serverId = getParamValue(params.serverId);
|
||||
const workspaceValue = getParamValue(params.workspaceId);
|
||||
const workspaceId = workspaceValue
|
||||
? (decodeWorkspaceIdFromPathSegment(workspaceValue) ?? "")
|
||||
: "";
|
||||
const parsedWorkspaceRoute = parseHostWorkspaceRouteFromPathname(pathname);
|
||||
const serverId = parsedWorkspaceRoute?.serverId ?? "";
|
||||
const workspaceId = parsedWorkspaceRoute?.workspaceId ?? "";
|
||||
const openValue = getParamValue(globalParams.open);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -14,6 +14,13 @@ import { buildHostRootRoute } from "@/utils/host-routes";
|
||||
|
||||
const WELCOME_ROUTE = "/welcome";
|
||||
|
||||
function getCurrentPathname(fallbackPathname: string): string {
|
||||
if (typeof window === "undefined") {
|
||||
return fallbackPathname;
|
||||
}
|
||||
return window.location.pathname || fallbackPathname;
|
||||
}
|
||||
|
||||
function useAnyOnlineHostServerId(serverIds: string[]): string | null {
|
||||
const runtime = getHostRuntimeStore();
|
||||
|
||||
@@ -51,7 +58,8 @@ export default function Index() {
|
||||
if (!storeReady) {
|
||||
return;
|
||||
}
|
||||
if (pathname !== "/" && pathname !== "") {
|
||||
const currentPathname = getCurrentPathname(pathname);
|
||||
if (currentPathname !== "/" && currentPathname !== "") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user