Fix workspace navigation regression on web (#772)
Centralize workspace navigation behind a small route-derived API so the workspace shell reads active selection from the current Expo Router route instead of a separately synchronized store.
Remove the stale layout-driven active-workspace write that regressed web after 02e5b564.
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import { router } from "expo-router";
|
||||
import { getNavigationActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
|
||||
import type { ActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { buildWorkspaceArchiveRedirectRoute } from "@/utils/workspace-archive-navigation";
|
||||
|
||||
export function redirectIfArchivingActiveWorkspace(input: {
|
||||
serverId: string;
|
||||
workspaceId: string;
|
||||
activeWorkspaceSelection: ActiveWorkspaceSelection | null;
|
||||
}): boolean {
|
||||
const activeWorkspaceSelection = getNavigationActiveWorkspaceSelection();
|
||||
if (
|
||||
activeWorkspaceSelection?.serverId !== input.serverId ||
|
||||
activeWorkspaceSelection.workspaceId !== input.workspaceId
|
||||
input.activeWorkspaceSelection?.serverId !== input.serverId ||
|
||||
input.activeWorkspaceSelection.workspaceId !== input.workspaceId
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,6 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildWorkspaceArchiveRedirectRoute } from "@/utils/workspace-archive-navigation";
|
||||
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import {
|
||||
activateNavigationWorkspaceSelection,
|
||||
syncNavigationActiveWorkspace,
|
||||
} from "@/stores/navigation-active-workspace-store";
|
||||
import { redirectIfArchivingActiveWorkspace } from "@/utils/sidebar-workspace-archive-redirect";
|
||||
|
||||
const { replaceMock } = vi.hoisted(() => ({
|
||||
@@ -96,7 +92,6 @@ describe("buildWorkspaceArchiveRedirectRoute", () => {
|
||||
describe("redirectIfArchivingActiveWorkspace", () => {
|
||||
afterEach(() => {
|
||||
replaceMock.mockClear();
|
||||
syncNavigationActiveWorkspace({ current: null });
|
||||
useSessionStore.getState().clearSession("server-1");
|
||||
});
|
||||
|
||||
@@ -109,12 +104,11 @@ describe("redirectIfArchivingActiveWorkspace", () => {
|
||||
["feature", workspace({ id: "feature", name: "feature" })],
|
||||
]),
|
||||
);
|
||||
activateNavigationWorkspaceSelection({ serverId: "server-1", workspaceId: "main" });
|
||||
|
||||
expect(
|
||||
redirectIfArchivingActiveWorkspace({
|
||||
serverId: "server-1",
|
||||
workspaceId: "feature",
|
||||
activeWorkspaceSelection: { serverId: "server-1", workspaceId: "main" },
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
@@ -130,12 +124,11 @@ describe("redirectIfArchivingActiveWorkspace", () => {
|
||||
["feature", workspace({ id: "feature", name: "feature" })],
|
||||
]),
|
||||
);
|
||||
activateNavigationWorkspaceSelection({ serverId: "server-1", workspaceId: "feature" });
|
||||
|
||||
expect(
|
||||
redirectIfArchivingActiveWorkspace({
|
||||
serverId: "server-1",
|
||||
workspaceId: "feature",
|
||||
activeWorkspaceSelection: { serverId: "server-1", workspaceId: "feature" },
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
|
||||
@@ -2,12 +2,9 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { platformState, routerMock } = vi.hoisted(() => ({
|
||||
platformState: {
|
||||
isNative: false,
|
||||
isWeb: true,
|
||||
},
|
||||
routerMock: {
|
||||
back: vi.fn(),
|
||||
canGoBack: vi.fn(() => false),
|
||||
dismissTo: vi.fn(),
|
||||
navigate: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
@@ -16,12 +13,11 @@ const { platformState, routerMock } = vi.hoisted(() => ({
|
||||
|
||||
vi.mock("expo-router", () => ({
|
||||
router: routerMock,
|
||||
useLocalSearchParams: () => ({}),
|
||||
usePathname: () => "/",
|
||||
}));
|
||||
|
||||
vi.mock("@/constants/platform", () => ({
|
||||
get isNative() {
|
||||
return platformState.isNative;
|
||||
},
|
||||
get isWeb() {
|
||||
return platformState.isWeb;
|
||||
},
|
||||
@@ -43,10 +39,6 @@ vi.mock("@react-native-async-storage/async-storage", () => {
|
||||
});
|
||||
|
||||
import { useWorkspaceLayoutStore } from "@/stores/workspace-layout-store";
|
||||
import {
|
||||
getNavigationActiveWorkspaceSelection,
|
||||
syncNavigationActiveWorkspace,
|
||||
} from "@/stores/navigation-active-workspace-store";
|
||||
import { navigateToPreparedWorkspaceTab, prepareWorkspaceTab } from "@/utils/workspace-navigation";
|
||||
|
||||
const SERVER_ID = "server-1";
|
||||
@@ -56,15 +48,10 @@ const AGENT_ID = "agent-1";
|
||||
describe("prepareWorkspaceTab", () => {
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
platformState.isNative = false;
|
||||
platformState.isWeb = true;
|
||||
routerMock.back.mockReset();
|
||||
routerMock.canGoBack.mockReset();
|
||||
routerMock.canGoBack.mockReturnValue(false);
|
||||
routerMock.dismissTo.mockReset();
|
||||
routerMock.navigate.mockReset();
|
||||
routerMock.replace.mockReset();
|
||||
syncNavigationActiveWorkspace({ current: null });
|
||||
useWorkspaceLayoutStore.setState({
|
||||
layoutByWorkspace: {},
|
||||
splitSizesByWorkspace: {},
|
||||
@@ -85,37 +72,17 @@ describe("prepareWorkspaceTab", () => {
|
||||
expect(useWorkspaceLayoutStore.getState().getWorkspaceTabs(key)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("pops back to the retained workspace shell for native replace navigation", () => {
|
||||
vi.useFakeTimers();
|
||||
platformState.isNative = true;
|
||||
platformState.isWeb = false;
|
||||
routerMock.canGoBack.mockReturnValue(true);
|
||||
|
||||
syncNavigationActiveWorkspace({
|
||||
current: {
|
||||
getCurrentRoute: () => ({
|
||||
path: "/h/server-1/workspace/source-workspace",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
it("prepares a tab and navigates through the workspace navigation helper", () => {
|
||||
const route = navigateToPreparedWorkspaceTab({
|
||||
serverId: SERVER_ID,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
target: { kind: "agent", agentId: AGENT_ID },
|
||||
navigationMethod: "replace",
|
||||
});
|
||||
|
||||
expect(route).toBe("/h/server-1/workspace/b64_L3JlcG8vd29ya3RyZWU");
|
||||
expect(routerMock.back).toHaveBeenCalledOnce();
|
||||
expect(routerMock.dismissTo).not.toHaveBeenCalled();
|
||||
expect(routerMock.dismissTo).toHaveBeenCalledWith(
|
||||
"/h/server-1/workspace/b64_L3JlcG8vd29ya3RyZWU",
|
||||
);
|
||||
expect(routerMock.replace).not.toHaveBeenCalled();
|
||||
|
||||
vi.runAllTimers();
|
||||
|
||||
expect(getNavigationActiveWorkspaceSelection()).toEqual({
|
||||
serverId: SERVER_ID,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { router } from "expo-router";
|
||||
import { isNative } from "@/constants/platform";
|
||||
import { navigateToWorkspace } from "@/hooks/use-workspace-navigation";
|
||||
import {
|
||||
activateNavigationWorkspaceSelection,
|
||||
getLastNavigationWorkspaceRouteSelection,
|
||||
overrideNextNavigationWorkspaceRouteSelection,
|
||||
} from "@/stores/navigation-active-workspace-store";
|
||||
import { useWorkspaceLayoutStore } from "@/stores/workspace-layout-store";
|
||||
import { generateDraftId } from "@/stores/draft-keys";
|
||||
import {
|
||||
@@ -22,7 +15,6 @@ interface PrepareWorkspaceTabInput {
|
||||
}
|
||||
|
||||
interface NavigateToPreparedWorkspaceTabInput extends PrepareWorkspaceTabInput {
|
||||
navigationMethod?: "navigate" | "replace";
|
||||
currentPathname?: string | null;
|
||||
}
|
||||
|
||||
@@ -52,28 +44,8 @@ export function prepareWorkspaceTab(input: PrepareWorkspaceTabInput) {
|
||||
|
||||
export function navigateToPreparedWorkspaceTab(input: NavigateToPreparedWorkspaceTabInput): string {
|
||||
const route = prepareWorkspaceTab(input);
|
||||
if (input.navigationMethod === "replace") {
|
||||
const canReturnToWorkspaceShell =
|
||||
isNative && getLastNavigationWorkspaceRouteSelection() !== null && router.canGoBack();
|
||||
if (canReturnToWorkspaceShell) {
|
||||
const nextSelection = {
|
||||
serverId: input.serverId,
|
||||
workspaceId: input.workspaceId,
|
||||
};
|
||||
overrideNextNavigationWorkspaceRouteSelection(nextSelection);
|
||||
router.back();
|
||||
setTimeout(() => {
|
||||
activateNavigationWorkspaceSelection(nextSelection);
|
||||
}, 0);
|
||||
return route;
|
||||
}
|
||||
navigateToWorkspace(input.serverId, input.workspaceId, {
|
||||
currentPathname: input.currentPathname,
|
||||
});
|
||||
} else {
|
||||
navigateToWorkspace(input.serverId, input.workspaceId, {
|
||||
currentPathname: input.currentPathname,
|
||||
});
|
||||
}
|
||||
navigateToWorkspace(input.serverId, input.workspaceId, {
|
||||
currentPathname: input.currentPathname,
|
||||
});
|
||||
return route;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user