Skip to content

Commit f8ed067

Browse files
committed
fix: get things working correctly
1 parent 05bd144 commit f8ed067

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy