fix(projects): separate registration from workspace setup
Adding or cloning a project now registers it and opens workspace setup instead of creating a workspace implicitly. Keep Add Project independently mounted from Search so closing one cannot control the other.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { AddProjectFlow } from "@/components/add-project-flow";
|
||||
import { useAddProjectFlowStore } from "@/stores/add-project-flow-store";
|
||||
|
||||
export function AddProjectFlowHost() {
|
||||
const request = useAddProjectFlowStore((state) => state.request);
|
||||
const close = useAddProjectFlowStore((state) => state.close);
|
||||
|
||||
if (!request) return null;
|
||||
|
||||
return <AddProjectFlow key={request.id} request={request} onClose={close} />;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { router } from "expo-router";
|
||||
import type { WorkspaceProjectDescriptorPayload } from "@getpaseo/protocol/messages";
|
||||
import {
|
||||
ArrowLeft,
|
||||
Folder,
|
||||
@@ -63,7 +64,7 @@ import { pickDirectory } from "@/desktop/pick-directory";
|
||||
import { useFetchQuery } from "@/data/query";
|
||||
import { getOpenProjectFailureReason, registerProjectDescriptor } from "@/hooks/open-project";
|
||||
import { useIsLocalDaemon, useLocalDaemonServerId } from "@/hooks/use-is-local-daemon";
|
||||
import { useOpenGithubRepo, useOpenProject } from "@/hooks/use-open-project";
|
||||
import { useCloneGithubProject, useOpenProject } from "@/hooks/use-open-project";
|
||||
import {
|
||||
useHosts,
|
||||
useHostRuntimeClient,
|
||||
@@ -75,7 +76,7 @@ import { useRecommendedProjectPaths } from "@/stores/session-store-hooks";
|
||||
import type { AddProjectFlowRequest } from "@/stores/add-project-flow-store";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
import { shortenPath } from "@/utils/shorten-path";
|
||||
import { buildSettingsAddHostRoute } from "@/utils/host-routes";
|
||||
import { buildNewWorkspaceRoute, buildSettingsAddHostRoute } from "@/utils/host-routes";
|
||||
|
||||
interface AddProjectFlowProps {
|
||||
request: AddProjectFlowRequest;
|
||||
@@ -197,7 +198,7 @@ function pageTitle(page: AddProjectPage): string {
|
||||
case "github-search":
|
||||
return "Clone from GitHub";
|
||||
case "github-location":
|
||||
return `Where should Paseo create ${pathBaseName(page.repository.nameWithOwner)}?`;
|
||||
return "Choose destination";
|
||||
case "new-directory-parent":
|
||||
return "Choose parent directory";
|
||||
case "new-directory-name":
|
||||
@@ -296,8 +297,8 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
const hostIds = useMemo(() => hosts.map((host) => host.serverId), [hosts]);
|
||||
const connectionStatuses = useHostRuntimeConnectionStatuses(hostIds);
|
||||
const projectAddByHost = useHostFeatureMap(hostIds, "projectAdd");
|
||||
// COMPAT(workspaceGithubClone): added in v0.1.108, remove gate after 2027-01-15.
|
||||
const githubCloneByHost = useHostFeatureMap(hostIds, "workspaceGithubClone");
|
||||
// COMPAT(projectGithubClone): added in v0.1.108, remove gate after 2027-01-15.
|
||||
const githubCloneByHost = useHostFeatureMap(hostIds, "projectGithubClone");
|
||||
// COMPAT(workspaceGithubRepositorySearch): added in v0.1.108, remove gate after 2027-01-15.
|
||||
const githubSearchByHost = useHostFeatureMap(hostIds, "workspaceGithubRepositorySearch");
|
||||
// COMPAT(projectCreateDirectory): added in v0.1.108, remove gate after 2027-01-15.
|
||||
@@ -343,7 +344,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
const isLocalDaemon = useIsLocalDaemon(hostId ?? "");
|
||||
const recommendedPaths = useRecommendedProjectPaths(hostId);
|
||||
const openProject = useOpenProject(hostId);
|
||||
const openGithubRepo = useOpenGithubRepo(hostId);
|
||||
const cloneGithubProject = useCloneGithubProject(hostId);
|
||||
const addEmptyProject = useSessionStore((store) => store.addEmptyProject);
|
||||
const setHasHydratedWorkspaces = useSessionStore((store) => store.setHasHydratedWorkspaces);
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
@@ -416,9 +417,24 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
});
|
||||
}, [onClose]);
|
||||
|
||||
const openNewWorkspaceForProject = useCallback(
|
||||
(serverId: string, project: WorkspaceProjectDescriptorPayload) => {
|
||||
onClose();
|
||||
router.push(
|
||||
buildNewWorkspaceRoute({
|
||||
serverId,
|
||||
projectId: project.projectId,
|
||||
sourceDirectory: project.projectRootPath,
|
||||
displayName: project.projectDisplayName,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[onClose],
|
||||
);
|
||||
|
||||
const openAddedProject = useCallback(
|
||||
async (path: string, sourceKind: "directory-search" | "method") => {
|
||||
if (submissionInFlightRef.current) return;
|
||||
if (!hostId || submissionInFlightRef.current) return;
|
||||
submissionInFlightRef.current = true;
|
||||
setState((current) =>
|
||||
setPageStatus(current, sourceKind, { isSubmitting: true, error: null }),
|
||||
@@ -426,7 +442,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
try {
|
||||
const result = await openProject(path);
|
||||
if (result.ok) {
|
||||
onClose();
|
||||
openNewWorkspaceForProject(hostId, result.project);
|
||||
return;
|
||||
}
|
||||
const reason = getOpenProjectFailureReason(result);
|
||||
@@ -446,7 +462,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
submissionInFlightRef.current = false;
|
||||
}
|
||||
},
|
||||
[onClose, openProject],
|
||||
[hostId, openNewWorkspaceForProject, openProject],
|
||||
);
|
||||
|
||||
const browse = useCallback(async () => {
|
||||
@@ -501,34 +517,34 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
setPageStatus(current, "github-location", { isSubmitting: true, error: null }),
|
||||
);
|
||||
try {
|
||||
const opened = await openGithubRepo(
|
||||
const result = await cloneGithubProject(
|
||||
locationPage.repository.cloneUrl,
|
||||
parentPath,
|
||||
locationPage.repository.cloneProtocol,
|
||||
);
|
||||
if (opened) {
|
||||
if (result.ok) {
|
||||
lastCloneParentByHost.set(locationPage.hostId, parentPath);
|
||||
onClose();
|
||||
openNewWorkspaceForProject(locationPage.hostId, result.project);
|
||||
return;
|
||||
}
|
||||
setState((current) =>
|
||||
setPageStatus(current, "github-location", {
|
||||
isSubmitting: false,
|
||||
error: "Unable to clone repository",
|
||||
error: result.error ?? "Unable to clone repository",
|
||||
}),
|
||||
);
|
||||
} catch {
|
||||
} catch (error) {
|
||||
setState((current) =>
|
||||
setPageStatus(current, "github-location", {
|
||||
isSubmitting: false,
|
||||
error: "Unable to clone repository",
|
||||
error: error instanceof Error ? error.message : "Unable to clone repository",
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
submissionInFlightRef.current = false;
|
||||
}
|
||||
},
|
||||
[onClose, openGithubRepo],
|
||||
[cloneGithubProject, openNewWorkspaceForProject],
|
||||
);
|
||||
const rows = useMemo<FlowRowOption[]>(() => {
|
||||
if (page.kind === "host") {
|
||||
@@ -702,7 +718,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
addEmptyProject,
|
||||
setHasHydratedWorkspaces,
|
||||
});
|
||||
onClose();
|
||||
openNewWorkspaceForProject(page.hostId, payload.project);
|
||||
} catch {
|
||||
setState((current) =>
|
||||
setPageStatus(current, "new-directory-name", {
|
||||
@@ -713,7 +729,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
} finally {
|
||||
submissionInFlightRef.current = false;
|
||||
}
|
||||
}, [addEmptyProject, client, onClose, page, setHasHydratedWorkspaces]);
|
||||
}, [addEmptyProject, client, openNewWorkspaceForProject, page, setHasHydratedWorkspaces]);
|
||||
|
||||
const submitActive = useCallback(() => {
|
||||
if (page.kind === "new-directory-name") {
|
||||
|
||||
@@ -21,8 +21,6 @@ import {
|
||||
import { AgentStatusDot } from "@/components/agent-status-dot";
|
||||
import { Shortcut } from "@/components/ui/shortcut";
|
||||
import { isNative, isWeb } from "@/constants/platform";
|
||||
import { AddProjectFlow } from "@/components/add-project-flow";
|
||||
import { useAddProjectFlowStore } from "@/stores/add-project-flow-store";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import {
|
||||
IsolatedBottomSheetModal,
|
||||
@@ -338,8 +336,6 @@ export function CommandCenter() {
|
||||
handleSelectItem,
|
||||
handleKeyEvent,
|
||||
} = useCommandCenter();
|
||||
const addProjectRequest = useAddProjectFlowStore((state) => state.request);
|
||||
|
||||
const isCompact = useIsCompactFormFactor();
|
||||
const showBottomSheet = isCompact && isNative;
|
||||
const rowRefs = useRef<Map<number, View>>(new Map());
|
||||
@@ -493,16 +489,6 @@ export function CommandCenter() {
|
||||
|
||||
const snapPoints = useMemo(() => ["60%", "90%"], []);
|
||||
|
||||
if (open && addProjectRequest) {
|
||||
return (
|
||||
<AddProjectFlow
|
||||
key={addProjectRequest.id}
|
||||
request={addProjectRequest}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const resultList =
|
||||
items.length === 0 ? (
|
||||
<Text style={emptyTextStyle}>{t("shell.commandCenter.noMatches")}</Text>
|
||||
|
||||
Reference in New Issue
Block a user