Files
paseo/packages/app/src/components/worktree-setup-callout-source.tsx
T
Mohamed BoudraandGitHub 868479c493 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.
2026-05-06 13:22:31 +08:00

102 lines
3.4 KiB
TypeScript

import { useQuery } from "@tanstack/react-query";
import type { PaseoConfigRaw } from "@server/shared/messages";
import { useRouter } from "expo-router";
import { useEffect } from "react";
import { useSidebarCallouts } from "@/contexts/sidebar-callout-context";
import { useStableEvent } from "@/hooks/use-stable-event";
import { useHostRuntimeClient } from "@/runtime/host-runtime";
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
import { useWorkspaceFields } from "@/stores/session-store-hooks";
import type { WorkspaceDescriptor } from "@/stores/session-store";
import { buildProjectSettingsRoute } from "@/utils/host-routes";
interface ActiveGitWorkspaceProject {
serverId: string;
projectKey: string;
repoRoot: string;
}
function selectActiveGitWorkspaceProject(
serverId: string,
workspace: WorkspaceDescriptor,
): ActiveGitWorkspaceProject | null {
if (workspace.projectKind !== "git") {
return null;
}
const projectKey = workspace.projectId.trim();
const repoRoot = (workspace.project?.checkout.mainRepoRoot ?? workspace.projectRootPath).trim();
if (!projectKey || !repoRoot) {
return null;
}
return { serverId, projectKey, repoRoot };
}
function hasSetupCommands(config: PaseoConfigRaw): boolean {
const setup = config.worktree?.setup;
if (typeof setup === "string") {
return setup.trim().length > 0;
}
if (Array.isArray(setup)) {
return setup.some((command) => typeof command === "string" && command.trim().length > 0);
}
return false;
}
export function WorktreeSetupCalloutSource() {
const selection = useActiveWorkspaceSelection();
const activeProject = useWorkspaceFields(
selection?.serverId ?? null,
selection?.workspaceId ?? null,
(workspace) => selectActiveGitWorkspaceProject(selection?.serverId ?? "", workspace),
);
const client = useHostRuntimeClient(activeProject?.serverId ?? "");
const callouts = useSidebarCallouts();
const router = useRouter();
const openProjectSettings = useStableEvent(() => {
if (!activeProject) {
return;
}
router.navigate(buildProjectSettingsRoute(activeProject.projectKey));
});
const readQuery = useQuery({
queryKey: ["project-config", activeProject?.serverId ?? "", activeProject?.repoRoot ?? ""],
queryFn: () => {
if (!client || !activeProject) {
throw new Error("Project config query requires an active git workspace");
}
return client.readProjectConfig(activeProject.repoRoot);
},
enabled: Boolean(client && activeProject),
retry: false,
});
const shouldShow =
activeProject !== null &&
readQuery.data?.ok === true &&
!hasSetupCommands(readQuery.data.config ?? {});
useEffect(() => {
if (!shouldShow || !activeProject) {
return;
}
return callouts.show({
id: `worktree-setup-missing:${activeProject.projectKey}`,
dismissalKey: `worktree-setup-missing:${activeProject.projectKey}`,
priority: 100,
title: "Set up worktree scripts",
description:
"Add setup commands so new worktrees can install dependencies and prepare themselves automatically.",
actions: [
{ label: "Open project settings", onPress: openProjectSettings, variant: "primary" },
],
testID: `worktree-setup-callout-${activeProject.projectKey}`,
});
}, [activeProject, callouts, openProjectSettings, shouldShow]);
return null;
}