Keep composer autocomplete visible after route hops (#1851)

* fix(app): keep composer autocomplete visible after route hops

Workspace callers now express only the target workspace. The navigation helper chooses whether to pop to a mounted host route or fall back, and active-workspace tracking ignores stale hidden route params while app-wide routes are foregrounded.

* test(app): address workspace navigation review

* fix(app): parse decoded legacy workspace routes

* fix(app): ignore decoded legacy tab routes

* test(app): use canonical offline workspace route
This commit is contained in:
Mohamed Boudra
2026-07-01 21:55:17 +02:00
committed by GitHub
parent 48d07dedb5
commit d3e8f77914
19 changed files with 196 additions and 103 deletions
@@ -20,10 +20,6 @@ import { navigateToHostWorkspaceRoute } from "@/navigation/workspace-route-navig
export type { ActiveWorkspaceSelection } from "@/stores/last-workspace-selection";
interface NavigateToWorkspaceOptions {
currentPathname?: string | null;
}
const lastWorkspaceSelectionStorage: LastWorkspaceSelectionStorage = {
read: () => AsyncStorage.getItem(LAST_WORKSPACE_SELECTION_STORAGE_KEY),
write: (value) => AsyncStorage.setItem(LAST_WORKSPACE_SELECTION_STORAGE_KEY, value),
@@ -33,13 +29,7 @@ const lastWorkspaceSelectionStore = createLastWorkspaceSelectionStore(
lastWorkspaceSelectionStorage,
);
function shouldPopToExistingHostRoute(options: NavigateToWorkspaceOptions): boolean {
// Only /new needs POP_TO to avoid hidden deck entries; regular workspace switches
// should use router navigation so route observers like the sidebar selection update.
return options.currentPathname === "/new";
}
function navigateDeps(options: NavigateToWorkspaceOptions): NavigateToWorkspaceDeps {
function navigateDeps(): NavigateToWorkspaceDeps {
return {
getSessionWorkspaces: (serverId) => useSessionStore.getState().sessions[serverId]?.workspaces,
getSessionAgents: (serverId) =>
@@ -49,9 +39,7 @@ function navigateDeps(options: NavigateToWorkspaceOptions): NavigateToWorkspaceD
},
rememberLastWorkspace: (selection) => lastWorkspaceSelectionStore.remember(selection),
navigateToRoute: (route) => {
navigateToHostWorkspaceRoute(route, {
popToExistingHostRoute: shouldPopToExistingHostRoute(options),
});
navigateToHostWorkspaceRoute(route);
stripHostWorkspaceRouteEchoSearchFromBrowserUrlAfterCommit();
},
};
@@ -69,17 +57,13 @@ export function getIsLastWorkspaceSelectionHydrated(): boolean {
return lastWorkspaceSelectionStore.isHydrated();
}
export function navigateToWorkspace(
serverId: string,
workspaceId: string,
options: NavigateToWorkspaceOptions = {},
) {
navigateToWorkspacePure(serverId, workspaceId, navigateDeps(options));
export function navigateToWorkspace(serverId: string, workspaceId: string) {
navigateToWorkspacePure(serverId, workspaceId, navigateDeps());
}
export function navigateToLastWorkspace(): boolean {
return navigateToLastWorkspacePure({
...navigateDeps({}),
...navigateDeps(),
getLastWorkspaceSelection: () => lastWorkspaceSelectionStore.getSelection(),
});
}
@@ -115,6 +115,18 @@ describe("workspace navigation", () => {
});
});
it("ignores stale workspace route params while an app-wide route is active", () => {
const selection = parseActiveWorkspaceSelection({
pathname: "/settings/general",
params: {
serverId: "server-1",
workspaceId: "workspace-a",
},
});
expect(selection).toBeNull();
});
it("navigates to the last workspace once a route observation has been remembered", () => {
const { deps, navigations } = createLastSelectionDeps(null);
@@ -58,10 +58,16 @@ function parseWorkspaceSelectionFromRouteParams(params: {
export function parseActiveWorkspaceSelection(
input: RouteSelectionInput,
): ActiveWorkspaceSelection | null {
return (
parseHostWorkspaceRouteFromPathname(input.pathname) ??
parseWorkspaceSelectionFromRouteParams(input.params)
);
const routeSelection = parseHostWorkspaceRouteFromPathname(input.pathname);
if (routeSelection) {
return routeSelection;
}
if (input.pathname !== "/" && input.pathname !== "") {
return null;
}
return parseWorkspaceSelectionFromRouteParams(input.params);
}
export function navigateToWorkspace(