Fix duplicate workspace shell navigation
This commit is contained in:
@@ -19,7 +19,7 @@ import { useKeyboardShortcutOverrides } from "@/hooks/use-keyboard-shortcut-over
|
||||
import { getShortcutOs } from "@/utils/shortcut-platform";
|
||||
import { getIsElectronRuntime } from "@/constants/layout";
|
||||
import { resolveWorkspaceIdByExecutionDirectory } from "@/utils/workspace-execution";
|
||||
import { prepareWorkspaceTab } from "@/utils/workspace-navigation";
|
||||
import { navigateToPreparedWorkspaceTab } from "@/utils/workspace-navigation";
|
||||
import { focusWithRetries } from "@/utils/web-focus";
|
||||
import { useActiveServerId } from "@/hooks/use-active-server-id";
|
||||
|
||||
@@ -123,7 +123,7 @@ function resolveActionShortcutKeys(
|
||||
}
|
||||
|
||||
export function useCommandCenter() {
|
||||
const _pathname = usePathname();
|
||||
const pathname = usePathname();
|
||||
const routeActiveServerId = useActiveServerId();
|
||||
const { overrides } = useKeyboardShortcutOverrides();
|
||||
const open = useKeyboardShortcutsStore((s) => s.commandCenterOpen);
|
||||
@@ -212,14 +212,14 @@ export function useCommandCenter() {
|
||||
router.navigate(buildHostAgentDetailRoute(agent.serverId, agent.id) as Href);
|
||||
return;
|
||||
}
|
||||
const route = prepareWorkspaceTab({
|
||||
navigateToPreparedWorkspaceTab({
|
||||
serverId: agent.serverId,
|
||||
workspaceId,
|
||||
target: { kind: "agent", agentId: agent.id },
|
||||
currentPathname: pathname,
|
||||
});
|
||||
router.navigate(route);
|
||||
},
|
||||
[setOpen],
|
||||
[pathname, setOpen],
|
||||
);
|
||||
|
||||
const openProjectPicker = useOpenProjectPicker(activeServerId);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.hoisted(() => {
|
||||
(globalThis as typeof globalThis & { __DEV__?: boolean }).__DEV__ = false;
|
||||
});
|
||||
|
||||
vi.mock("@react-native-async-storage/async-storage", () => {
|
||||
const storage = new Map<string, string>();
|
||||
return {
|
||||
@@ -19,10 +23,8 @@ const { replaceRoute } = vi.hoisted(() => ({
|
||||
replaceRoute: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("expo-router", () => ({
|
||||
router: {
|
||||
replace: replaceRoute,
|
||||
},
|
||||
vi.mock("@/hooks/use-workspace-navigation", () => ({
|
||||
navigateToWorkspace: replaceRoute,
|
||||
}));
|
||||
|
||||
import { openProjectDirectly } from "@/hooks/use-open-project";
|
||||
@@ -89,7 +91,7 @@ describe("openProjectDirectly", () => {
|
||||
mergeWorkspaces: useSessionStore.getState().mergeWorkspaces,
|
||||
setHasHydratedWorkspaces: useSessionStore.getState().setHasHydratedWorkspaces,
|
||||
openDraftTab: createOpenDraftTab(),
|
||||
replaceRoute,
|
||||
navigateToWorkspace: replaceRoute,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
@@ -115,7 +117,7 @@ describe("openProjectDirectly", () => {
|
||||
const tabs = collectAllTabs(layout.root);
|
||||
expect(tabs).toHaveLength(1);
|
||||
expect(tabs[0]?.target.kind).toBe("draft");
|
||||
expect(replaceRoute).toHaveBeenCalledWith("/h/server-1/workspace/1");
|
||||
expect(replaceRoute).toHaveBeenCalledWith("server-1", "1");
|
||||
});
|
||||
|
||||
it("does not navigate or seed tabs when openProject fails", async () => {
|
||||
@@ -133,7 +135,7 @@ describe("openProjectDirectly", () => {
|
||||
mergeWorkspaces: useSessionStore.getState().mergeWorkspaces,
|
||||
setHasHydratedWorkspaces: useSessionStore.getState().setHasHydratedWorkspaces,
|
||||
openDraftTab: createOpenDraftTab(),
|
||||
replaceRoute,
|
||||
navigateToWorkspace: replaceRoute,
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCallback } from "react";
|
||||
import { router, type Href } from "expo-router";
|
||||
import type { DaemonClient } from "@server/client/daemon-client";
|
||||
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
||||
import {
|
||||
@@ -12,7 +11,7 @@ import {
|
||||
useWorkspaceLayoutStore,
|
||||
} from "@/stores/workspace-layout-store";
|
||||
import { generateDraftId } from "@/stores/draft-keys";
|
||||
import { buildHostWorkspaceRoute } from "@/utils/host-routes";
|
||||
import { navigateToWorkspace } from "@/hooks/use-workspace-navigation";
|
||||
|
||||
interface OpenProjectDirectlyInput {
|
||||
serverId: string;
|
||||
@@ -22,7 +21,7 @@ interface OpenProjectDirectlyInput {
|
||||
mergeWorkspaces: (serverId: string, workspaces: Iterable<WorkspaceDescriptor>) => void;
|
||||
setHasHydratedWorkspaces: (serverId: string, hydrated: boolean) => void;
|
||||
openDraftTab: (workspaceKey: string) => string | null;
|
||||
replaceRoute: (route: string) => void;
|
||||
navigateToWorkspace: (serverId: string, workspaceId: string) => void;
|
||||
}
|
||||
|
||||
export async function openProjectDirectly(input: OpenProjectDirectlyInput): Promise<boolean> {
|
||||
@@ -50,7 +49,7 @@ export async function openProjectDirectly(input: OpenProjectDirectlyInput): Prom
|
||||
}
|
||||
|
||||
input.openDraftTab(workspaceKey);
|
||||
input.replaceRoute(buildHostWorkspaceRoute(normalizedServerId, workspace.id));
|
||||
input.navigateToWorkspace(normalizedServerId, workspace.id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -75,9 +74,7 @@ export function useOpenProject(serverId: string | null): (path: string) => Promi
|
||||
kind: "draft",
|
||||
draftId: generateDraftId(),
|
||||
}),
|
||||
replaceRoute: (route) => {
|
||||
router.replace(route as Href);
|
||||
},
|
||||
navigateToWorkspace,
|
||||
});
|
||||
},
|
||||
[client, isConnected, mergeWorkspaces, normalizedServerId, setHasHydratedWorkspaces],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { navigateMock } = vi.hoisted(() => ({
|
||||
navigateMock: vi.fn(),
|
||||
const { dismissToMock } = vi.hoisted(() => ({
|
||||
dismissToMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("expo-router", () => ({
|
||||
router: {
|
||||
navigate: navigateMock,
|
||||
dismissTo: dismissToMock,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -19,11 +19,11 @@ import {
|
||||
|
||||
describe("navigateToWorkspace", () => {
|
||||
beforeEach(() => {
|
||||
navigateMock.mockReset();
|
||||
dismissToMock.mockReset();
|
||||
syncNavigationActiveWorkspace({ current: null });
|
||||
});
|
||||
|
||||
it("uses router navigation from a non-workspace route even when active selection is stale", () => {
|
||||
it("dismisses to the workspace route from a non-workspace route even when active selection is stale", () => {
|
||||
activateNavigationWorkspaceSelection({
|
||||
serverId: "server-1",
|
||||
workspaceId: "workspace-a",
|
||||
@@ -33,7 +33,7 @@ describe("navigateToWorkspace", () => {
|
||||
currentPathname: "/h/server-1/sessions",
|
||||
});
|
||||
|
||||
expect(navigateMock).toHaveBeenCalledWith("/h/server-1/workspace/workspace-b");
|
||||
expect(dismissToMock).toHaveBeenCalledWith("/h/server-1/workspace/workspace-b");
|
||||
expect(getNavigationActiveWorkspaceSelection()).toEqual({
|
||||
serverId: "server-1",
|
||||
workspaceId: "workspace-a",
|
||||
@@ -50,7 +50,7 @@ describe("navigateToWorkspace", () => {
|
||||
currentPathname: "/h/server-1/workspace/workspace-a",
|
||||
});
|
||||
|
||||
expect(navigateMock).not.toHaveBeenCalled();
|
||||
expect(dismissToMock).not.toHaveBeenCalled();
|
||||
expect(getNavigationActiveWorkspaceSelection()).toEqual({
|
||||
serverId: "server-1",
|
||||
workspaceId: "workspace-b",
|
||||
|
||||
@@ -19,7 +19,7 @@ function shouldUseRetainedWorkspaceSwitch(input: {
|
||||
}
|
||||
|
||||
if (input.currentPathname == null) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return parseHostWorkspaceRouteFromPathname(input.currentPathname) !== null;
|
||||
@@ -49,7 +49,7 @@ export function navigateToWorkspace(
|
||||
}
|
||||
|
||||
const href = buildHostWorkspaceRoute(serverId, workspaceId);
|
||||
router.navigate(href);
|
||||
router.dismissTo(href);
|
||||
}
|
||||
|
||||
export function useWorkspaceNavigation() {
|
||||
|
||||
Reference in New Issue
Block a user