1import { RegistryContext, useAtomValue } from "@effect/atom-react";
2import { scopeProjectRef } from "@t3tools/client-runtime/environment";
3import {
4 isAtomCommandInterrupted,
5 squashAtomCommandFailure,
6} from "@t3tools/client-runtime/state/runtime";
7import { AuthFilesystemReadScope } from "@t3tools/contracts";
8import { useNavigate, useParams, useRouter } from "@tanstack/react-router";
9import { useCallback, useContext } from "react";
10
11import { toastManager } from "../components/ui/toast";
12import { resolveThreadRouteTarget } from "../threadRoutes";
13import { useComposerDraftStore } from "../composerDraftStore";
14import { useEnvironment, usePrimaryEnvironmentId } from "../state/environments";
15import { useProject } from "../state/entities";
16import {
17 createManagedWorkspace,
18 managedWorkspacePending,
19 managedWorkspaceStatus,
20} from "../state/managedWorkspaces";
21import { useEnvironmentQuery } from "../state/query";
22import { useEnvironmentScope } from "../state/session";
23import { useAtomCommand } from "../state/use-atom-command";
24
25export function useNewWorkWorkspace(onNavigated?: () => void) {
26 const target = useParams({ strict: false, select: resolveThreadRouteTarget });
27 const draftEnvironmentId = useComposerDraftStore((store) =>
28 target?.kind === "draft" ? store.getDraftSession(target.draftId)?.environmentId : null,
29 );
30 const primaryEnvironmentId = usePrimaryEnvironmentId();
31 const environmentId =
32 target?.kind === "server"
33 ? target.threadRef.environmentId
34 : (draftEnvironmentId ?? primaryEnvironmentId);
35 const environment = useEnvironment(environmentId);
36 const connected = environment?.connection.phase === "connected";
37 const supported = environment?.serverConfig?.managedWorkspaces === true;
38 const canRead = useEnvironmentScope(environmentId, AuthFilesystemReadScope);
39 const allowed = useAtomValue(createManagedWorkspace.permissionAtom(environmentId));
40 const status = useEnvironmentQuery(
41 environmentId && connected && supported && canRead
42 ? managedWorkspaceStatus({ environmentId, input: {} })
43 : null,
44 );
45 const project = useProject(
46 environmentId && status.isSuccess && status.data?.projectId
47 ? scopeProjectRef(environmentId, status.data.projectId)
48 : null,
49 );
50 const pendingAtom = managedWorkspacePending(environmentId);
51 const pending = useAtomValue(pendingAtom);
52 const registry = useContext(RegistryContext);
53 const create = useAtomCommand(createManagedWorkspace, { reportFailure: false });
54 const navigate = useNavigate();
55 const router = useRouter();
56 const run = useCallback(async () => {
57 if (!environmentId || !connected || !allowed || !project || registry.get(pendingAtom)) return;
58 registry.set(pendingAtom, true);
59 const origin = router.state.location.href;
60 try {
61 const result = await create({ environmentId, input: {} });
62 if (result._tag === "Failure") {
63 if (!isAtomCommandInterrupted(result)) throw squashAtomCommandFailure(result);
64 return;
65 }
66 const open = async () => {
67 await navigate({
68 to: "/$environmentId/$threadId",
69 params: { environmentId: result.value.environmentId, threadId: result.value.threadId },
70 });
71 onNavigated?.();
72 };
73 if (router.state.location.href === origin) await open();
74 else
75 toastManager.add({
76 type: "success",
77 title: "Workspace for my work ready",
78 description: result.value.workspacePath,
79 actionProps: { children: "Open", onClick: () => void open() },
80 });
81 } catch (error) {
82 toastManager.add({
83 type: "error",
84 title: "Could not create workspace",
85 description:
86 error instanceof Error ? error.message : "Try again when the environment is connected.",
87 });
88 } finally {
89 registry.set(pendingAtom, false);
90 }
91 }, [
92 environmentId,
93 connected,
94 allowed,
95 project,
96 registry,
97 pendingAtom,
98 router,
99 create,
100 navigate,
101 onNavigated,
102 ]);
103
104 return {
105 project,
106 pending,
107 label: pending ? "Creating workspace for my work…" : "New workspace for my work",
108 disabled: !connected || !allowed || pending,
109 run,
110 };
111}