Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d9af4b2

Browse files
committed
fix: get things working correctly
1 parent a0505bb commit d9af4b2

File tree

1 file changed

+135
-120
lines changed

1 file changed

+135
-120
lines changed

site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPageExperimental.tsx

Lines changed: 135 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import CheckOutlined from "@mui/icons-material/CheckOutlined";
22
import FileCopyOutlined from "@mui/icons-material/FileCopyOutlined";
3-
import FormControlLabel from "@mui/material/FormControlLabel";
4-
import Radio from "@mui/material/Radio";
5-
import RadioGroup from "@mui/material/RadioGroup";
63
import { API } from "api/api";
74
import { DetailedError } from "api/errors";
85
import type {
@@ -11,56 +8,48 @@ import type {
118
FriendlyDiagnostic,
129
PreviewParameter,
1310
Template,
14-
User,
1511
} from "api/typesGenerated";
1612
import { ErrorAlert } from "components/Alert/ErrorAlert";
1713
import { Button } from "components/Button/Button";
18-
import { FormSection } from "components/Form/Form";
19-
import { Loader } from "components/Loader/Loader";
14+
import { Label } from "components/Label/Label";
15+
import { RadioGroup, RadioGroupItem } from "components/RadioGroup/RadioGroup";
16+
import { Skeleton } from "components/Skeleton/Skeleton";
17+
import { useAuthenticated } from "hooks";
2018
import { useEffectEvent } from "hooks/hookPolyfills";
2119
import { useClipboard } from "hooks/useClipboard";
2220
import {
2321
Diagnostics,
2422
DynamicParameter,
2523
} from "modules/workspaces/DynamicParameter/DynamicParameter";
2624
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
27-
import {
28-
type FC,
29-
useCallback,
30-
useEffect,
31-
useMemo,
32-
useRef,
33-
useState,
34-
} from "react";
25+
import { type FC, useEffect, useMemo, useRef, useState } from "react";
3526
import { Helmet } from "react-helmet-async";
36-
import { useQuery } from "react-query";
3727
import { pageTitle } from "utils/page";
3828

3929
type ButtonValues = Record<string, string>;
4030

4131
const TemplateEmbedPageExperimental: FC = () => {
4232
const { template } = useTemplateLayoutContext();
33+
const { user: me } = useAuthenticated();
4334
const [latestResponse, setLatestResponse] =
4435
useState<DynamicParametersResponse | null>(null);
4536
const wsResponseId = useRef<number>(-1);
4637
const ws = useRef<WebSocket | null>(null);
4738
const [wsError, setWsError] = useState<Error | null>(null);
4839

49-
const { data: authenticatedUser } = useQuery<User>({
50-
queryKey: ["authenticatedUser"],
51-
queryFn: () => API.getAuthenticatedUser(),
52-
});
53-
54-
const sendMessage = useCallback((formValues: Record<string, string>) => {
55-
const request: DynamicParametersRequest = {
56-
id: wsResponseId.current + 1,
57-
inputs: formValues,
58-
};
59-
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
60-
ws.current.send(JSON.stringify(request));
61-
wsResponseId.current = wsResponseId.current + 1;
62-
}
63-
}, []);
40+
const sendMessage = useEffectEvent(
41+
(formValues: Record<string, string>, ownerId?: string) => {
42+
const request: DynamicParametersRequest = {
43+
id: wsResponseId.current + 1,
44+
owner_id: me.id,
45+
inputs: formValues,
46+
};
47+
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
48+
ws.current.send(JSON.stringify(request));
49+
wsResponseId.current = wsResponseId.current + 1;
50+
}
51+
},
52+
);
6453

6554
const onMessage = useEffectEvent((response: DynamicParametersResponse) => {
6655
if (latestResponse && latestResponse?.id >= response.id) {
@@ -71,25 +60,29 @@ const TemplateEmbedPageExperimental: FC = () => {
7160
});
7261

7362
useEffect(() => {
74-
if (!template.active_version_id || !authenticatedUser) {
63+
if (!template.active_version_id || !me) {
7564
return;
7665
}
7766

7867
const socket = API.templateVersionDynamicParameters(
79-
authenticatedUser.id,
8068
template.active_version_id,
69+
me.id,
8170
{
8271
onMessage,
8372
onError: (error) => {
84-
setWsError(error);
73+
if (ws.current === socket) {
74+
setWsError(error);
75+
}
8576
},
8677
onClose: () => {
87-
setWsError(
88-
new DetailedError(
89-
"Websocket connection for dynamic parameters unexpectedly closed.",
90-
"Refresh the page to reset the form.",
91-
),
92-
);
78+
if (ws.current === socket) {
79+
setWsError(
80+
new DetailedError(
81+
"Websocket connection for dynamic parameters unexpectedly closed.",
82+
"Refresh the page to reset the form.",
83+
),
84+
);
85+
}
9386
},
9487
},
9588
);
@@ -99,7 +92,7 @@ const TemplateEmbedPageExperimental: FC = () => {
9992
return () => {
10093
socket.close();
10194
};
102-
}, [authenticatedUser, template.active_version_id, onMessage]);
95+
}, [template.active_version_id, onMessage, me]);
10396

10497
const sortedParams = useMemo(() => {
10598
if (!latestResponse?.parameters) {
@@ -108,6 +101,9 @@ const TemplateEmbedPageExperimental: FC = () => {
108101
return [...latestResponse.parameters].sort((a, b) => a.order - b.order);
109102
}, [latestResponse?.parameters]);
110103

104+
const isLoading =
105+
ws.current?.readyState === WebSocket.CONNECTING || !latestResponse;
106+
111107
return (
112108
<>
113109
<Helmet>
@@ -119,6 +115,7 @@ const TemplateEmbedPageExperimental: FC = () => {
119115
diagnostics={latestResponse?.diagnostics ?? []}
120116
error={wsError}
121117
sendMessage={sendMessage}
118+
isLoading={isLoading}
122119
/>
123120
</>
124121
);
@@ -130,6 +127,7 @@ interface TemplateEmbedPageViewProps {
130127
diagnostics: readonly FriendlyDiagnostic[];
131128
error: unknown;
132129
sendMessage: (message: Record<string, string>) => void;
130+
isLoading: boolean;
133131
}
134132

135133
const TemplateEmbedPageView: FC<TemplateEmbedPageViewProps> = ({
@@ -138,45 +136,46 @@ const TemplateEmbedPageView: FC<TemplateEmbedPageViewProps> = ({
138136
diagnostics,
139137
error,
140138
sendMessage,
139+
isLoading,
141140
}) => {
142-
const [buttonValues, setButtonValues] = useState<ButtonValues | undefined>();
143-
const [localParameters, setLocalParameters] = useState<
144-
Record<string, string>
145-
>({});
141+
const [formState, setFormState] = useState<{
142+
mode: "manual" | "auto";
143+
paramValues: Record<string, string>;
144+
}>({
145+
mode: "manual",
146+
paramValues: {},
147+
});
146148

147149
useEffect(() => {
148150
if (parameters) {
149-
const initialInputs: Record<string, string> = {};
150-
const currentMode = buttonValues?.mode || "manual";
151-
const initialButtonParamValues: ButtonValues = { mode: currentMode };
152-
151+
const serverParamValues: Record<string, string> = {};
153152
for (const p of parameters) {
154153
const initialVal = p.value?.valid ? p.value.value : "";
155-
initialInputs[p.name] = initialVal;
156-
initialButtonParamValues[`param.${p.name}`] = initialVal;
154+
serverParamValues[p.name] = initialVal;
157155
}
158-
setLocalParameters(initialInputs);
156+
setFormState((prev) => ({ ...prev, paramValues: serverParamValues }));
157+
}
158+
}, [parameters]);
159159

160-
setButtonValues(initialButtonParamValues);
160+
const buttonValues = useMemo(() => {
161+
const values: ButtonValues = { mode: formState.mode };
162+
for (const [key, value] of Object.entries(formState.paramValues)) {
163+
values[`param.${key}`] = value;
161164
}
162-
}, [parameters, buttonValues?.mode]);
165+
return values;
166+
}, [formState]);
163167

164168
const handleChange = (
165169
changedParamInfo: PreviewParameter,
166170
newValue: string,
167171
) => {
168-
const newFormInputs = {
169-
...localParameters,
172+
const newParamValues = {
173+
...formState.paramValues,
170174
[changedParamInfo.name]: newValue,
171175
};
172-
setLocalParameters(newFormInputs);
173-
174-
setButtonValues((prevButtonValues) => ({
175-
...(prevButtonValues || {}),
176-
[`param.${changedParamInfo.name}`]: newValue,
177-
}));
176+
setFormState((prev) => ({ ...prev, paramValues: newParamValues }));
178177

179-
const formInputsToSend: Record<string, string> = { ...newFormInputs };
178+
const formInputsToSend: Record<string, string> = { ...newParamValues };
180179
for (const p of parameters) {
181180
if (!(p.name in formInputsToSend)) {
182181
formInputsToSend[p.name] = p.value?.valid ? p.value.value : "";
@@ -186,68 +185,84 @@ const TemplateEmbedPageView: FC<TemplateEmbedPageViewProps> = ({
186185
sendMessage(formInputsToSend);
187186
};
188187

189-
useEffect(() => {
190-
if (!buttonValues && parameters.length === 0) {
191-
setButtonValues({ mode: "manual" });
192-
} else if (buttonValues && !buttonValues.mode && parameters.length > 0) {
193-
setButtonValues((prev) => ({ ...prev, mode: "manual" }));
194-
}
195-
}, [buttonValues, parameters]);
196-
197-
if (!buttonValues || (!parameters && !error)) {
198-
return <Loader />;
199-
}
200-
201188
return (
202189
<>
203190
<div className="flex items-start gap-12">
204-
<div className="flex flex-col gap-5 max-w-screen-md">
205-
{Boolean(error) && <ErrorAlert error={error} />}
206-
{diagnostics.length > 0 && <Diagnostics diagnostics={diagnostics} />}
207-
<div className="flex flex-col">
208-
<FormSection
209-
title="Creation mode"
210-
description="By changing the mode to automatic, when the user clicks the button, the workspace will be created automatically instead of showing a form to the user."
211-
>
212-
<RadioGroup
213-
defaultValue={buttonValues?.mode || "manual"}
214-
onChange={(_, v) => {
215-
setButtonValues((prevButtonValues) => ({
216-
...(prevButtonValues || {}),
217-
mode: v,
218-
}));
219-
}}
220-
>
221-
<FormControlLabel
222-
value="manual"
223-
control={<Radio size="small" />}
224-
label="Manual"
225-
/>
226-
<FormControlLabel
227-
value="auto"
228-
control={<Radio size="small" />}
229-
label="Automatic"
230-
/>
231-
</RadioGroup>
232-
</FormSection>
233-
234-
{parameters.length > 0 && (
191+
<div className="w-full flex flex-col gap-5 max-w-screen-md">
192+
{isLoading ? (
193+
<div className="flex flex-col gap-9">
194+
<div className="flex flex-col gap-2">
195+
<Skeleton className="h-5 w-1/3" />
196+
<Skeleton className="h-9 w-full" />
197+
</div>
198+
<div className="flex flex-col gap-2">
199+
<Skeleton className="h-5 w-1/3" />
200+
<Skeleton className="h-9 w-full" />
201+
</div>
202+
<div className="flex flex-col gap-2">
203+
<Skeleton className="h-5 w-1/3" />
204+
<Skeleton className="h-9 w-full" />
205+
</div>
206+
</div>
207+
) : (
208+
<>
209+
{Boolean(error) && <ErrorAlert error={error} />}
210+
{diagnostics.length > 0 && (
211+
<Diagnostics diagnostics={diagnostics} />
212+
)}
235213
<div className="flex flex-col gap-9">
236-
{parameters.map((parameter) => {
237-
const isDisabled = parameter.styling?.disabled;
238-
return (
239-
<DynamicParameter
240-
key={parameter.name}
241-
parameter={parameter}
242-
onChange={(value) => handleChange(parameter, value)}
243-
disabled={isDisabled}
244-
value={localParameters[parameter.name] || ""}
245-
/>
246-
);
247-
})}
214+
<section className="flex flex-col gap-2">
215+
<div>
216+
<h2 className="text-lg font-bold m-0">Creation mode</h2>
217+
<p className="text-sm text-content-secondary m-0">
218+
When set to automatic mode, clicking the button will
219+
create the workspace automatically without displaying a
220+
form to the user.
221+
</p>
222+
</div>
223+
<RadioGroup
224+
value={formState.mode}
225+
onValueChange={(v) => {
226+
setFormState((prev) => ({
227+
...prev,
228+
mode: v as "manual" | "auto",
229+
}));
230+
}}
231+
>
232+
<div className="flex items-center gap-3">
233+
<RadioGroupItem value="manual" id="manual" />
234+
<Label htmlFor={"manual"} className="cursor-pointer">
235+
Manual
236+
</Label>
237+
</div>
238+
<div className="flex items-center gap-3">
239+
<RadioGroupItem value="auto" id="automatic" />
240+
<Label htmlFor={"automatic"} className="cursor-pointer">
241+
Automatic
242+
</Label>
243+
</div>
244+
</RadioGroup>
245+
</section>
246+
247+
{parameters.length > 0 && (
248+
<div className="flex flex-col gap-9">
249+
{parameters.map((parameter) => {
250+
const isDisabled = parameter.styling?.disabled;
251+
return (
252+
<DynamicParameter
253+
key={parameter.name}
254+
parameter={parameter}
255+
onChange={(value) => handleChange(parameter, value)}
256+
disabled={isDisabled}
257+
value={formState.paramValues[parameter.name] || ""}
258+
/>
259+
);
260+
})}
261+
</div>
262+
)}
248263
</div>
249-
)}
250-
</div>
264+
</>
265+
)}
251266
</div>
252267

253268
<ButtonPreview template={template} buttonValues={buttonValues} />
@@ -285,7 +300,7 @@ const ButtonPreview: FC<ButtonPreviewProps> = ({ template, buttonValues }) => {
285300

286301
return (
287302
<div
288-
className="sticky top-10 flex gap-16 h-80 p-14 flex-1 flex-col items-center justify-center
303+
className="sticky top-10 flex gap-16 h-96 flex-1 flex-col items-center justify-center
289304
rounded-lg border border-border border-solid bg-surface-secondary"
290305
>
291306
<img src="/open-in-coder.svg" alt="Open in Coder button" />

0 commit comments

Comments
 (0)