Skip to content

Commit afdab85

Browse files
committed
chore: move logic for sending initial parameters to websocket
1 parent 03dde8a commit afdab85

File tree

2 files changed

+69
-49
lines changed

2 files changed

+69
-49
lines changed

site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { API } from "api/api";
12
import { type ApiErrorResponse, DetailedError } from "api/errors";
23
import { checkAuthorization } from "api/queries/authCheck";
34
import {
@@ -9,11 +10,13 @@ import { autoCreateWorkspace, createWorkspace } from "api/queries/workspaces";
910
import type {
1011
DynamicParametersRequest,
1112
DynamicParametersResponse,
13+
PreviewParameter,
1214
Workspace,
1315
} from "api/typesGenerated";
1416
import { Loader } from "components/Loader/Loader";
1517
import { useAuthenticated } from "hooks";
1618
import { useEffectEvent } from "hooks/hookPolyfills";
19+
import { getInitialParameterValues } from "modules/workspaces/DynamicParameter/DynamicParameter";
1720
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
1821
import {
1922
type FC,
@@ -29,13 +32,13 @@ import { useNavigate, useParams, useSearchParams } from "react-router-dom";
2932
import { pageTitle } from "utils/page";
3033
import type { AutofillBuildParameter } from "utils/richParameters";
3134
import { CreateWorkspacePageViewExperimental } from "./CreateWorkspacePageViewExperimental";
32-
const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
33-
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
34-
import { API } from "api/api";
3535
import {
3636
type CreateWorkspacePermissions,
3737
createWorkspaceChecks,
3838
} from "./permissions";
39+
40+
const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
41+
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
3942
export type ExternalAuthPollingState = "idle" | "polling" | "abandoned";
4043

4144
const CreateWorkspacePageExperimental: FC = () => {
@@ -50,6 +53,7 @@ const CreateWorkspacePageExperimental: FC = () => {
5053
const [wsResponseId, setWSResponseId] = useState<number>(-1);
5154
const ws = useRef<WebSocket | null>(null);
5255
const [wsError, setWsError] = useState<Error | null>(null);
56+
const initialParamsSentRef = useRef(false);
5357

5458
const customVersionId = searchParams.get("version") ?? undefined;
5559
const defaultName = searchParams.get("name");
@@ -84,15 +88,72 @@ const CreateWorkspacePageExperimental: FC = () => {
8488
const realizedVersionId =
8589
customVersionId ?? templateQuery.data?.active_version_id;
8690

87-
const onMessage = useCallback((response: DynamicParametersResponse) => {
88-
setCurrentResponse((prev) => {
89-
if (prev?.id === response.id) {
90-
return prev;
91+
const autofillParameters = useMemo(
92+
() => getAutofillParameters(searchParams),
93+
[searchParams],
94+
);
95+
96+
const sendMessage = useCallback((formValues: Record<string, string>) => {
97+
setWSResponseId((prevId) => {
98+
const request: DynamicParametersRequest = {
99+
id: prevId + 1,
100+
inputs: formValues,
101+
};
102+
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
103+
ws.current.send(JSON.stringify(request));
104+
return prevId + 1;
91105
}
92-
return response;
106+
return prevId;
93107
});
94108
}, []);
95109

110+
// On sends all initial parameter values to the websocket
111+
// (including defaults and autofilled from the url)
112+
// This ensures the backend has the complete initial state of the form,
113+
// which is vital for correctly rendering dynamic UI elements where parameter visibility
114+
// or options might depend on the initial values of other parameters.
115+
const sendInitialParameters = useEffectEvent(
116+
(parameters: PreviewParameter[]) => {
117+
if (initialParamsSentRef.current) return;
118+
if (parameters.length === 0) return;
119+
120+
const initialFormValues = getInitialParameterValues(
121+
parameters,
122+
autofillParameters,
123+
);
124+
if (initialFormValues.length === 0) return;
125+
126+
const initialParamsToSend: Record<string, string> = {};
127+
for (const param of initialFormValues) {
128+
if (param.name && param.value) {
129+
initialParamsToSend[param.name] = param.value;
130+
}
131+
}
132+
133+
if (Object.keys(initialParamsToSend).length === 0) return;
134+
135+
sendMessage(initialParamsToSend);
136+
initialParamsSentRef.current = true;
137+
},
138+
);
139+
140+
const onMessage = useCallback(
141+
(response: DynamicParametersResponse) => {
142+
setCurrentResponse((prev) => {
143+
if (prev?.id === response.id) {
144+
return prev;
145+
}
146+
147+
if (!initialParamsSentRef.current && response.parameters.length > 0) {
148+
sendInitialParameters([...response.parameters]);
149+
}
150+
151+
return response;
152+
});
153+
},
154+
[sendInitialParameters],
155+
);
156+
96157
// Initialize the WebSocket connection when there is a valid template version ID
97158
useEffect(() => {
98159
if (!realizedVersionId) return;
@@ -127,20 +188,6 @@ const CreateWorkspacePageExperimental: FC = () => {
127188
};
128189
}, [owner.id, realizedVersionId, onMessage]);
129190

130-
const sendMessage = useCallback((formValues: Record<string, string>) => {
131-
setWSResponseId((prevId) => {
132-
const request: DynamicParametersRequest = {
133-
id: prevId + 1,
134-
inputs: formValues,
135-
};
136-
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
137-
ws.current.send(JSON.stringify(request));
138-
return prevId + 1;
139-
}
140-
return prevId;
141-
});
142-
}, []);
143-
144191
const organizationId = templateQuery.data?.organization_id;
145192

146193
const {
@@ -167,9 +214,6 @@ const CreateWorkspacePageExperimental: FC = () => {
167214
[navigate],
168215
);
169216

170-
// Auto fill parameters
171-
const autofillParameters = getAutofillParameters(searchParams);
172-
173217
const autoCreationStartedRef = useRef(false);
174218
const automateWorkspaceCreation = useEffectEvent(async () => {
175219
if (autoCreationStartedRef.current || !organizationId) {

site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -141,30 +141,6 @@ export const CreateWorkspacePageViewExperimental: FC<
141141
},
142142
});
143143

144-
// On component mount, sends all initial parameter values to the websocket
145-
// (including defaults and autofilled from the url)
146-
// This ensures the backend has the complete initial state of the form,
147-
// which is vital for correctly rendering dynamic UI elements where parameter visibility
148-
// or options might depend on the initial values of other parameters.
149-
const hasInitializedWebsocket = useRef(false);
150-
useEffect(() => {
151-
if (hasInitializedWebsocket.current) return;
152-
153-
const formValues = form.values.rich_parameter_values;
154-
if (parameters.length > 0 && formValues && formValues.length > 0) {
155-
const initialParams: Record<string, string> = {};
156-
for (const param of formValues) {
157-
if (param.name && param.value) {
158-
initialParams[param.name] = param.value;
159-
}
160-
}
161-
if (Object.keys(initialParams).length > 0) {
162-
sendMessage(initialParams);
163-
hasInitializedWebsocket.current = true;
164-
}
165-
}
166-
}, [parameters, form.values.rich_parameter_values, sendMessage]);
167-
168144
const autofillByName = Object.fromEntries(
169145
autofillParameters.map((param) => [param.name, param]),
170146
);

0 commit comments

Comments
 (0)