Skip to content

Commit fa122db

Browse files
BC-11049 automatic reload page when websocket is gone (#207)
--------- Co-authored-by: Phillip Wirth <phillip.wirth@dataport.de>
1 parent 402c12a commit fa122db

6 files changed

Lines changed: 94 additions & 18 deletions

File tree

package-lock.json

Lines changed: 28 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@testing-library/react": "^16.3.0",
4141
"@testing-library/user-event": "^14.6.1",
4242
"@types/lodash": "^4.17.20",
43+
"@types/node": "^22.0.0",
4344
"@types/react": "^18.3.12",
4445
"@types/react-dom": "^18.3.1",
4546
"@typescript-eslint/eslint-plugin": "^8.46.4",
@@ -59,4 +60,4 @@
5960
"vite-plugin-top-level-await": "^1.6.0",
6061
"vitest": "^4.0.8"
6162
}
62-
}
63+
}

src/stores/setup.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { WebsocketProvider } from "y-websocket";
44
import { Doc, Map, UndoManager } from "yjs";
55
import { UserPresence } from "../types/UserPresence";
66
import { handleWsClose } from "../utils/closeHandler";
7+
import { showConnectionErrorAndReload } from "../utils/connectionErrorHandler";
78
import { getEnvs } from "../utils/envConfig";
89
import { clearErrorData } from "../utils/errorData";
910
import {
@@ -55,6 +56,15 @@ provider.on("connection-close", (event: CloseEvent | null) => {
5556
handleWsClose(event, provider);
5657
});
5758

59+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
60+
provider.on("connection-error", (_event: Event) => {
61+
// Disconnect to prevent automatic reconnection attempts
62+
provider.disconnect();
63+
showConnectionErrorAndReload(
64+
"Failed to connect to server. The page will reload in 10 seconds...",
65+
);
66+
});
67+
5868
const room = new Room<UserPresence>(provider.awareness, {});
5969
const yShapes: Map<TDShape> = doc.getMap("shapes");
6070
const yBindings: Map<TDBinding> = doc.getMap("bindings");

src/utils/closeHandler.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { WebsocketProvider } from "y-websocket";
22
import { handleWsClose } from "./closeHandler";
3+
import { showConnectionErrorAndReload } from "./connectionErrorHandler";
34
import { setErrorData } from "./errorData";
45
import { redirectToErrorPage } from "./redirectUtils";
56

67
describe("closeHandler", () => {
78
beforeAll(() => {
9+
vi.mock("./connectionErrorHandler", { spy: true });
810
vi.mock("./errorData", { spy: true });
911
vi.mock("./redirectUtils", { spy: true });
1012
});
@@ -27,14 +29,17 @@ describe("closeHandler", () => {
2729
return { event, providerMock };
2830
};
2931

30-
it("should not call setErrorData or redirectToErrorPage nor disconnect", () => {
32+
it("should disconnect and call showConnectionErrorAndReload but not setErrorData or redirectToErrorPage", () => {
3133
const { event, providerMock } = setup();
3234

3335
handleWsClose(event, providerMock);
3436

37+
expect(providerMock.disconnect).toHaveBeenCalled();
38+
expect(showConnectionErrorAndReload).toHaveBeenCalledWith(
39+
"Connection to server lost. The page will reload in 10 seconds...",
40+
);
3541
expect(setErrorData).not.toHaveBeenCalled();
3642
expect(redirectToErrorPage).not.toHaveBeenCalled();
37-
expect(providerMock.disconnect).not.toHaveBeenCalled();
3843
});
3944
});
4045

src/utils/closeHandler.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { WebsocketProvider } from "y-websocket";
22
import { HttpStatusCode, WebsocketCloseCode } from "../types/StatusCodeEnums";
3+
import { showConnectionErrorAndReload } from "./connectionErrorHandler";
34
import { setErrorData } from "./errorData";
45
import { redirectToErrorPage } from "./redirectUtils";
56

@@ -30,11 +31,17 @@ export const handleWsClose = (
3031
(element) => element.websocketCode === event.code,
3132
);
3233

33-
// Any error that is not specified above should lead to a reconnection attempt.
34-
if (!specifiedError) return;
35-
36-
setErrorData(specifiedError.httpCode, specifiedError.translationMessageKey);
37-
redirectToErrorPage();
38-
34+
// Disconnect provider to prevent automatic reconnection.
3935
provider.disconnect();
36+
37+
if (specifiedError) {
38+
// For specified errors, use the original error page redirection
39+
setErrorData(specifiedError.httpCode, specifiedError.translationMessageKey);
40+
redirectToErrorPage();
41+
} else {
42+
// For any other connection failure, show error message and force reload
43+
showConnectionErrorAndReload(
44+
"Connection to server lost. The page will reload in 10 seconds...",
45+
);
46+
}
4047
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { toast } from "react-toastify";
2+
3+
let reloadTimeoutId: number | null = null;
4+
5+
export const showConnectionErrorAndReload = (
6+
message: string = "Connection lost. The page will reload in 10 seconds...",
7+
) => {
8+
// Clear any existing reload timeout
9+
if (reloadTimeoutId) {
10+
window.clearTimeout(reloadTimeoutId);
11+
}
12+
13+
// Show error toast
14+
toast.error(message, {
15+
position: "top-center",
16+
autoClose: false,
17+
hideProgressBar: false,
18+
closeOnClick: false,
19+
pauseOnHover: false,
20+
draggable: false,
21+
});
22+
23+
// Force reload after 10 seconds
24+
reloadTimeoutId = window.setTimeout(() => {
25+
window.location.reload();
26+
}, 10000);
27+
};
28+
29+
export const clearConnectionErrorTimeout = () => {
30+
if (reloadTimeoutId) {
31+
window.clearTimeout(reloadTimeoutId);
32+
reloadTimeoutId = null;
33+
}
34+
};

0 commit comments

Comments
 (0)