mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
fix: add websocket close handling (#17548)
resolves #17508 Display an error in the UI that the websocket closed if the user is still interacting with the dynamic parameters form <img width="795" alt="Screenshot 2025-04-23 at 17 57 25" src="https://github.com/user-attachments/assets/15362ddb-fe01-462e-8537-a48302c5c621" />
This commit is contained in:
@@ -1015,9 +1015,11 @@ class ApiMethods {
|
||||
{
|
||||
onMessage,
|
||||
onError,
|
||||
onClose,
|
||||
}: {
|
||||
onMessage: (response: TypesGen.DynamicParametersResponse) => void;
|
||||
onError: (error: Error) => void;
|
||||
onClose: () => void;
|
||||
},
|
||||
): WebSocket => {
|
||||
const socket = createWebSocket(
|
||||
@@ -1033,6 +1035,10 @@ class ApiMethods {
|
||||
socket.close();
|
||||
});
|
||||
|
||||
socket.addEventListener("close", () => {
|
||||
onClose();
|
||||
});
|
||||
|
||||
return socket;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ApiErrorResponse } from "api/errors";
|
||||
import { type ApiErrorResponse, DetailedError } from "api/errors";
|
||||
import { checkAuthorization } from "api/queries/authCheck";
|
||||
import {
|
||||
templateByName,
|
||||
@@ -107,6 +107,15 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
onError: (error) => {
|
||||
setWsError(error);
|
||||
},
|
||||
onClose: () => {
|
||||
// There is no reason for the websocket to close while a user is on the page
|
||||
setWsError(
|
||||
new DetailedError(
|
||||
"Websocket connection for dynamic parameters unexpectedly closed.",
|
||||
"Refresh the page to reset the form.",
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -141,7 +150,7 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
} = useExternalAuth(realizedVersionId);
|
||||
|
||||
const isLoadingFormData =
|
||||
ws.current?.readyState !== WebSocket.OPEN ||
|
||||
ws.current?.readyState === WebSocket.CONNECTING ||
|
||||
templateQuery.isLoading ||
|
||||
permissionsQuery.isLoading;
|
||||
const loadFormDataError = templateQuery.error ?? permissionsQuery.error;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { DetailedError } from "api/errors";
|
||||
import { chromatic } from "testHelpers/chromatic";
|
||||
import { MockTemplate, MockUser } from "testHelpers/entities";
|
||||
import { CreateWorkspacePageViewExperimental } from "./CreateWorkspacePageViewExperimental";
|
||||
|
||||
const meta: Meta<typeof CreateWorkspacePageViewExperimental> = {
|
||||
title: "Pages/CreateWorkspacePageViewExperimental",
|
||||
parameters: { chromatic },
|
||||
component: CreateWorkspacePageViewExperimental,
|
||||
args: {
|
||||
autofillParameters: [],
|
||||
diagnostics: [],
|
||||
defaultName: "",
|
||||
defaultOwner: MockUser,
|
||||
externalAuth: [],
|
||||
externalAuthPollingState: "idle",
|
||||
hasAllRequiredExternalAuth: true,
|
||||
mode: "form",
|
||||
parameters: [],
|
||||
permissions: {
|
||||
createWorkspaceForAny: true,
|
||||
},
|
||||
presets: [],
|
||||
sendMessage: () => {},
|
||||
template: MockTemplate,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CreateWorkspacePageViewExperimental>;
|
||||
|
||||
export const WebsocketError: Story = {
|
||||
args: {
|
||||
error: new DetailedError(
|
||||
"Websocket connection for dynamic parameters unexpectedly closed.",
|
||||
"Refresh the page to reset the form.",
|
||||
),
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user