feat(app): retain workspace screens on sidebar switch

This commit is contained in:
Mohamed Boudra
2026-04-20 12:55:58 +07:00
parent f8411d121e
commit 403fb03c97
11 changed files with 265 additions and 48 deletions
@@ -1,14 +1,21 @@
import { useSyncExternalStore } from "react";
import {
buildHostWorkspaceRoute,
decodeWorkspaceIdFromPathSegment,
parseHostWorkspaceRouteFromPathname,
} from "@/utils/host-routes";
import { isWeb } from "@/constants/platform";
interface ActiveWorkspaceSelection {
export interface ActiveWorkspaceSelection {
serverId: string;
workspaceId: string;
}
interface ActivateWorkspaceSelectionOptions {
updateBrowserHistory?: boolean;
historyMode?: "push" | "replace";
}
type NavigationRouteLike = {
name?: unknown;
params?: unknown;
@@ -45,6 +52,36 @@ function emitIfChanged(next: ActiveWorkspaceSelection | null) {
}
}
function getBrowserLocationWorkspace(): ActiveWorkspaceSelection | null {
if (!isWeb || typeof window === "undefined") {
return null;
}
return parseHostWorkspaceRouteFromPathname(window.location.pathname);
}
function writeBrowserWorkspaceUrl(
next: ActiveWorkspaceSelection,
options: ActivateWorkspaceSelectionOptions,
) {
if (!options.updateBrowserHistory || !isWeb || typeof window === "undefined") {
return;
}
const nextPath = buildHostWorkspaceRoute(next.serverId, next.workspaceId);
const currentUrl = new URL(window.location.href);
if (currentUrl.pathname === nextPath && !currentUrl.search && !currentUrl.hash) {
return;
}
const nextUrl = new URL(nextPath, window.location.origin);
const mode = options.historyMode ?? "push";
if (mode === "replace") {
window.history.replaceState(null, "", nextUrl.toString());
return;
}
window.history.pushState(null, "", nextUrl.toString());
}
function extractActiveWorkspaceFromRoute(
route: NavigationRouteLike | undefined,
): ActiveWorkspaceSelection | null {
@@ -91,6 +128,36 @@ export function syncNavigationActiveWorkspace(navigationRef: NavigationObserverR
);
}
export function activateNavigationWorkspaceSelection(
next: ActiveWorkspaceSelection,
options: ActivateWorkspaceSelectionOptions = {},
) {
emitIfChanged(next);
writeBrowserWorkspaceUrl(next, options);
}
export function getNavigationActiveWorkspaceSelection(): ActiveWorkspaceSelection | null {
return getSnapshot();
}
export function syncBrowserActiveWorkspaceFromLocation() {
emitIfChanged(getBrowserLocationWorkspace());
}
export function addBrowserActiveWorkspaceLocationListener(): () => void {
if (!isWeb || typeof window === "undefined") {
return () => {};
}
const handlePopState = () => {
syncBrowserActiveWorkspaceFromLocation();
};
window.addEventListener("popstate", handlePopState);
return () => {
window.removeEventListener("popstate", handlePopState);
};
}
export function useNavigationActiveWorkspaceSelection(): ActiveWorkspaceSelection | null {
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
}