Skip to content

Commit 15fdfbf

Browse files
committed
chore: add custom workspace duplication hook
1 parent 112bc95 commit 15fdfbf

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { checkAuthorization } from "api/queries/authCheck";
2929
import { CreateWSPermissions, createWorkspaceChecks } from "./permissions";
3030
import { paramsUsedToCreateWorkspace } from "utils/workspace";
3131
import { useEffectEvent } from "hooks/hookPolyfills";
32+
import { workspaceBuildParameters } from "api/queries/workspaceBuilds";
3233

3334
export const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
3435
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
@@ -262,3 +263,64 @@ function getDefaultName(mode: CreateWorkspaceMode, params: URLSearchParams) {
262263

263264
return paramsName ?? "";
264265
}
266+
267+
function getDuplicationUrlParams(
268+
workspaceParams: readonly WorkspaceBuildParameter[],
269+
workspace: Workspace,
270+
): URLSearchParams {
271+
// Record type makes sure that every property key added starts with "param.";
272+
// page is also set up to parse params with this prefix for auto mode
273+
const consolidatedParams: Record<`param.${string}`, string> = {};
274+
275+
for (const p of workspaceParams) {
276+
consolidatedParams[`param.${p.name}`] = p.value;
277+
}
278+
279+
return new URLSearchParams({
280+
...consolidatedParams,
281+
mode: "duplicate" satisfies CreateWorkspaceMode,
282+
name: workspace.name,
283+
version: workspace.template_active_version_id,
284+
});
285+
}
286+
287+
/**
288+
* Takes a workspace, and returns out a function that will navigate the user to
289+
* the 'Create Workspace' page, pre-filling the form with as much information
290+
* about the workspace as possible.
291+
*/
292+
// Meant to be consumed by components outside of this file
293+
export function useWorkspaceDuplication(workspace: Workspace) {
294+
const navigate = useNavigate();
295+
const buildParametersQuery = useQuery(
296+
workspaceBuildParameters(workspace.latest_build.id),
297+
);
298+
299+
// Not using useEffectEvent for this, because useEffect isn't really an
300+
// intended use case for this custom hook
301+
const duplicateWorkspace = useCallback(() => {
302+
const buildParams = buildParametersQuery.data;
303+
if (buildParams === undefined) {
304+
return;
305+
}
306+
307+
const newUrlParams = getDuplicationUrlParams(buildParams, workspace);
308+
309+
// Necessary for giving modals/popups time to flush their state changes and
310+
// close the popup before actually navigating. MUI does provide the
311+
// disablePortal prop, which also side-steps this issue, but you have to
312+
// remember to put it on any component that calls this function. Better to
313+
// code defensively and have some redundancy in case someone forgets
314+
void Promise.resolve().then(() => {
315+
navigate({
316+
pathname: `/templates/${workspace.template_name}/workspace`,
317+
search: newUrlParams.toString(),
318+
});
319+
});
320+
}, [navigate, workspace, buildParametersQuery.data]);
321+
322+
return {
323+
duplicateWorkspace,
324+
duplicationReady: buildParametersQuery.isSuccess,
325+
} as const;
326+
}

0 commit comments

Comments
 (0)