Skip to content
Source
Sign inSign up

useOpenLocalWorkspace.ts

98 lines3.9 KB

1import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime";
2import {
3 findProjectByPath,
4 inferProjectTitleFromPath,
5} from "@t3tools/client-runtime/state/projects";
6import type { DesktopAppActivationRequest } from "@t3tools/contracts";
7​
8import { handleDesktopAppActivationRequest } from "../desktopAppActivation";
9import { newProjectId } from "../lib/utils";
10import { readProjects, waitForProject } from "../state/entities";
11import { usePrimaryEnvironment } from "../state/environments";
12import { projectEnvironment } from "../state/projects";
13import { useEnvironmentShellHasSnapshot } from "../state/shell";
14import { useAtomCommand } from "../state/use-atom-command";
15import { useOrchestrationCommand } from "../state/use-orchestration-command";
16import { reportProjectFailure } from "./useProjectGroupUpdate";
17import { useNewThreadHandler } from "./useHandleNewThread";
18​
19/**
20 * Opens a folder on this computer as a project in the primary environment: an
21 * existing project opens (and unarchives), a new one is added. Used by `t3 <dir>`
22 * activations and folders dropped on the sidebar. `ready` is false until the
23 * primary environment's projects have loaded, so a known folder is not added twice.
24 */
25export function useOpenLocalWorkspace() {
26 const primaryEnvironment = usePrimaryEnvironment();
27 const createProject = useAtomCommand(projectEnvironment.create, { reportFailure: false });
28 const updateProject = useOrchestrationCommand(projectEnvironment.update, {
29 label: "project unarchive",
30 });
31 const openThread = useNewThreadHandler();
32 const shellHasSnapshot = useEnvironmentShellHasSnapshot(
33 primaryEnvironment?.environmentId ?? null,
34 );
35 const ready =
36 primaryEnvironment?.connection.phase === "connected" &&
37 primaryEnvironment.serverConfig !== null &&
38 shellHasSnapshot;
39​
40 const open = (request: DesktopAppActivationRequest) =>
41 handleDesktopAppActivationRequest(request, {
42 getTarget: () => {
43 if (
44 primaryEnvironment?.connection.phase !== "connected" ||
45 primaryEnvironment.serverConfig === null
46 ) {
47 return null;
48 }
49 return {
50 environmentId: primaryEnvironment.environmentId,
51 platform: primaryEnvironment.serverConfig.environment.platform.os,
52 };
53 },
54 findProject: (environmentId, workspaceRoot) =>
55 findProjectByPath(
56 readProjects().filter((project) => project.environmentId === environmentId),
57 workspaceRoot,
58 ) ?? null,
59 createProject: async (environmentId, workspaceRoot) => {
60 const projectId = newProjectId();
61 const result = await createProject({
62 environmentId,
63 input: {
64 projectId,
65 title: inferProjectTitleFromPath(workspaceRoot),
66 workspaceRoot,
67 createWorkspaceRootIfMissing: false,
68 defaultModelSelection: null,
69 },
70 });
71 if (result._tag === "Failure") {
72 const error = squashAtomCommandFailure(result);
73 throw error instanceof Error ? error : new Error("T3 Code could not add the project.");
74 }
75 return projectId;
76 },
77 waitForProject: async (projectRef) => {
78 await waitForProject(projectRef);
79 },
80 openThread: (projectRef) => {
81 const project = readProjects().find(
82 (candidate) =>
83 candidate.environmentId === projectRef.environmentId &&
84 candidate.id === projectRef.projectId,
85 );
86 // Opening an archived project's folder again brings the project back.
87 if (project?.archivedAt != null) {
88 void updateProject({
89 environmentId: projectRef.environmentId,
90 input: { projectId: projectRef.projectId, archived: false },
91 }).then((result) => reportProjectFailure("Failed to unarchive project", result));
92 }
93 return openThread(projectRef);
94 },
95 });
96​
97 return { ready, open };