diff --git a/client/.vscode/settings.json b/client/.vscode/settings.json
new file mode 100644
index 0000000..a37597c
--- /dev/null
+++ b/client/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "exportall.config.relExclusion": ["/src/ui/components/ErrorModal"]
+}
diff --git a/client/src/game/scripts/scenes/preloadScene.ts b/client/src/game/scripts/scenes/preloadScene.ts
index 27a0bbc..c7ca4ba 100644
--- a/client/src/game/scripts/scenes/preloadScene.ts
+++ b/client/src/game/scripts/scenes/preloadScene.ts
@@ -144,6 +144,13 @@ export default class PreloadScene extends Phaser.Scene {
});
this.scene.start("MainScene", { channel });
});
+ // channel.onDisconnect(() => {
+ // this.game.outEmitter.emit("connectionError", {
+ // message: "Could not connect to the server",
+ // navigateToMode: "mainMenu",
+ // });
+ // this.game.destroy(true);
+ // });
});
} else {
this.game.outEmitter.emit("loading", { name: "Main Scene", progress: 100 });
diff --git a/client/src/ui/App.tsx b/client/src/ui/App.tsx
index ba50b74..1689c97 100644
--- a/client/src/ui/App.tsx
+++ b/client/src/ui/App.tsx
@@ -1,16 +1,17 @@
-import React, { useEffect } from "react";
+import React from "react";
import { BackgroundImage, Group } from "@mantine/core";
import styled from "@emotion/styled";
-import { syncSettingsToSession, useGame } from "./hooks";
+import { useGame, useMultiplayerDisconnect, useSyncSettingsToSession } from "./hooks";
import { TopLeft } from "./scenes/TopLeft";
import { TopRight } from "./scenes/TopRight";
import { Center } from "./scenes/Center";
import { Right } from "./scenes/Right";
import { BottomLeft } from "./scenes/BottomLeft";
import { BottomRight } from "./scenes/BottomRight";
-import background from "~/assets/ui/background-space.webp";
import { UnderTopRight } from "./scenes/UnderTopRight";
+import background from "~/assets/ui/background-space.webp";
+import { ErrorModal } from "./components";
// @ts-ignore
const StyledUI = styled(BackgroundImage)`
@@ -92,10 +93,12 @@ const StyledBottomRightGroup = styled(Group)`
`;
export const App = () => {
- useEffect(() => {
- const unsub = syncSettingsToSession();
- return unsub;
- }, []);
+ useSyncSettingsToSession();
+
+ const { connectionError, clearConnectionError } = useMultiplayerDisconnect();
+ const clearErrors = () => {
+ clearConnectionError();
+ };
const {
computed: { isLoaded },
@@ -110,6 +113,10 @@ export const App = () => {
{isLoaded && }
+
);
};
diff --git a/client/src/ui/Root.tsx b/client/src/ui/Root.tsx
index 821dc6f..88efe9c 100644
--- a/client/src/ui/Root.tsx
+++ b/client/src/ui/Root.tsx
@@ -1,5 +1,3 @@
-//*
-
import "core-js/actual";
import React from "react";
import { createRoot } from "react-dom/client";
@@ -133,61 +131,3 @@ const events = ["mouseup", "mousedown", "touchstart", "touchmove", "touchend", "
events.forEach((event) => {
container.addEventListener(event, (e) => e.stopPropagation());
});
-
-/*/
-
-// import geckosClient from "@geckos.io/client";
-// // @ts-ignore
-// const channel = geckosClient({ url: "http://127.0.0.1:3010", port: null });
-// console.log("client channel created", channel);
-
-// channel.onConnect((error) => {
-// console.log("CONNECT");
-// if (error) console.error(error.message);
-
-// // listens for a disconnection
-// channel.onDisconnect(() => {
-// console.log("diccsosn");
-// });
-
-// // listens for a custom event from the server
-// channel.on("chat message", (data) => {
-// console.log("is it truly over?", data);
-// });
-
-// // emits a message to the server
-// channel.emit("chat message", "Hi!");
-// });
-
-const gg = async () => {
- const host = `http://localhost:3010/.wrtc/v2`;
-
- let headers: any = { "Content-Type": "application/json" };
- let userData = {};
-
- try {
- console.log("trying");
- const res = await fetch(`${host}/connections`, {
- method: "POST",
- headers,
- });
- console.log("Tried");
- if (res.status >= 300) {
- throw {
- name: "Error",
- message: `Connection failed with status code ${res.status}.`,
- status: res.status,
- statusText: res.statusText,
- };
- }
-
- const json = await res.json();
-
- userData = json.userData;
- } catch (error: any) {
- console.error("WHAT", error.message);
- return { error };
- }
-};
-gg();
-//*/
diff --git a/client/src/ui/components/ErrorModal/ErrorModal.tsx b/client/src/ui/components/ErrorModal/ErrorModal.tsx
new file mode 100644
index 0000000..eccabc9
--- /dev/null
+++ b/client/src/ui/components/ErrorModal/ErrorModal.tsx
@@ -0,0 +1,38 @@
+import React, { useEffect } from "react";
+import { Modal, Space, Title } from "@mantine/core";
+import { useDisclosure } from "@mantine/hooks";
+
+import { Errors } from "./components";
+
+export const ErrorModal = ({ errors, clearErrors }) => {
+ const [opened, { close, open }] = useDisclosure(false);
+
+ useEffect(() => {
+ if (errors.length) {
+ open();
+ }
+ }, [errors]);
+
+ const handleClose = () => {
+ close();
+ clearErrors();
+ };
+
+ return (
+
+
+
+
+
+ Error
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/client/src/ui/scenes/BottomRight/scenes/ProfileModal/components/NonFieldErrors.tsx b/client/src/ui/components/ErrorModal/components/Errors.tsx
similarity index 86%
rename from client/src/ui/scenes/BottomRight/scenes/ProfileModal/components/NonFieldErrors.tsx
rename to client/src/ui/components/ErrorModal/components/Errors.tsx
index d1244bf..a395a29 100644
--- a/client/src/ui/scenes/BottomRight/scenes/ProfileModal/components/NonFieldErrors.tsx
+++ b/client/src/ui/components/ErrorModal/components/Errors.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { Text } from "@mantine/core";
-export const NonFieldErrors = ({ errors }) => {
+export const Errors = ({ errors }) => {
return (
<>
{errors &&
diff --git a/client/src/ui/components/ErrorModal/components/index.ts b/client/src/ui/components/ErrorModal/components/index.ts
new file mode 100644
index 0000000..b8a5016
--- /dev/null
+++ b/client/src/ui/components/ErrorModal/components/index.ts
@@ -0,0 +1 @@
+export * from "./Errors";
diff --git a/client/src/ui/components/ErrorModal/index.ts b/client/src/ui/components/ErrorModal/index.ts
new file mode 100644
index 0000000..a9c806a
--- /dev/null
+++ b/client/src/ui/components/ErrorModal/index.ts
@@ -0,0 +1,2 @@
+export * from "./components";
+export * from "./ErrorModal";
diff --git a/client/src/ui/components/index.ts b/client/src/ui/components/index.ts
index 26d3bd6..3e5eb9f 100644
--- a/client/src/ui/components/index.ts
+++ b/client/src/ui/components/index.ts
@@ -1 +1,2 @@
export * from "./ToggleButton";
+export * from "./ErrorModal";
diff --git a/client/src/ui/hooks/useGame/index.ts b/client/src/ui/hooks/useGame/index.ts
new file mode 100644
index 0000000..af8cabc
--- /dev/null
+++ b/client/src/ui/hooks/useGame/index.ts
@@ -0,0 +1,2 @@
+export * from "./useGame";
+export * from "./useMultiplayerDisconnect";
diff --git a/client/src/ui/hooks/useGame.ts b/client/src/ui/hooks/useGame/useGame.ts
similarity index 100%
rename from client/src/ui/hooks/useGame.ts
rename to client/src/ui/hooks/useGame/useGame.ts
diff --git a/client/src/ui/hooks/useGame/useMultiplayerDisconnect/index.ts b/client/src/ui/hooks/useGame/useMultiplayerDisconnect/index.ts
new file mode 100644
index 0000000..a423477
--- /dev/null
+++ b/client/src/ui/hooks/useGame/useMultiplayerDisconnect/index.ts
@@ -0,0 +1 @@
+export * from "./useMultiplayerDisconnect";
diff --git a/client/src/ui/hooks/useGame/useMultiplayerDisconnect/useMultiplayerDisconnect.ts b/client/src/ui/hooks/useGame/useMultiplayerDisconnect/useMultiplayerDisconnect.ts
new file mode 100644
index 0000000..95826ef
--- /dev/null
+++ b/client/src/ui/hooks/useGame/useMultiplayerDisconnect/useMultiplayerDisconnect.ts
@@ -0,0 +1,31 @@
+import { useDidUpdate } from "@mantine/hooks";
+
+import { useGame } from "../";
+import { useState } from "react";
+
+export const useMultiplayerDisconnect = () => {
+ const [connectionError, setConnectionError] = useState(null);
+ const {
+ gameManager,
+ loadMainMenu,
+ computed: { isLoaded },
+ } = useGame();
+
+ useDidUpdate(() => {
+ if (isLoaded) {
+ console.log("LOADED CHANGED");
+ const { channel } = gameManager.game;
+ channel.onDisconnect(() => {
+ loadMainMenu();
+ console.log("SET ERROR");
+ setConnectionError("Connection to server lost");
+ });
+ }
+ }, [isLoaded]);
+
+ const clearConnectionError = () => {
+ setConnectionError(null);
+ };
+
+ return { connectionError, clearConnectionError };
+};
diff --git a/client/src/ui/hooks/useSettings/hooks/index.ts b/client/src/ui/hooks/useSettings/hooks/index.ts
new file mode 100644
index 0000000..034b974
--- /dev/null
+++ b/client/src/ui/hooks/useSettings/hooks/index.ts
@@ -0,0 +1 @@
+export * from "./useSyncSettingsToSession";
diff --git a/client/src/ui/hooks/useSettings/hooks/useSyncSettingsToSession.ts b/client/src/ui/hooks/useSettings/hooks/useSyncSettingsToSession.ts
new file mode 100644
index 0000000..b5dab07
--- /dev/null
+++ b/client/src/ui/hooks/useSettings/hooks/useSyncSettingsToSession.ts
@@ -0,0 +1,20 @@
+import { useEffect } from "react";
+
+import { useSettings, settingsStorageKey } from "../";
+import { setSessionStorage } from "../services/localStorage";
+
+const syncSettingsToSession = () => {
+ const unsub = useSettings.subscribe(
+ (state) => state.settings,
+ (state) => setSessionStorage(settingsStorageKey, state)
+ );
+
+ return unsub;
+};
+
+export const useSyncSettingsToSession = () => {
+ return useEffect(() => {
+ const unsub = syncSettingsToSession();
+ return unsub;
+ }, []);
+};
diff --git a/client/src/ui/hooks/useSettings/index.ts b/client/src/ui/hooks/useSettings/index.ts
new file mode 100644
index 0000000..dad0b0f
--- /dev/null
+++ b/client/src/ui/hooks/useSettings/index.ts
@@ -0,0 +1,3 @@
+export * from "./hooks";
+export * from "./services";
+export * from "./useSettings";
diff --git a/client/src/ui/hooks/useSettings/services/index.ts b/client/src/ui/hooks/useSettings/services/index.ts
new file mode 100644
index 0000000..9272ff5
--- /dev/null
+++ b/client/src/ui/hooks/useSettings/services/index.ts
@@ -0,0 +1 @@
+export * from "./localStorage";
diff --git a/client/src/ui/hooks/useSettings/services/localStorage.ts b/client/src/ui/hooks/useSettings/services/localStorage.ts
new file mode 100644
index 0000000..b5b6ed7
--- /dev/null
+++ b/client/src/ui/hooks/useSettings/services/localStorage.ts
@@ -0,0 +1,23 @@
+export const setSessionStorage = (key, value) => {
+ sessionStorage.setItem(key, serializeJSON(value));
+};
+
+export const getSessionStorage = (key) => {
+ return deserializeJSON(sessionStorage.getItem(key) ?? "{}");
+};
+
+export const serializeJSON = (value: any) => {
+ try {
+ return JSON.stringify(value);
+ } catch (error) {
+ throw new Error("Failed to serialize the value");
+ }
+};
+
+export const deserializeJSON = (value: string) => {
+ try {
+ return JSON.parse(value);
+ } catch {
+ return value;
+ }
+};
diff --git a/client/src/ui/hooks/useSettings.ts b/client/src/ui/hooks/useSettings/useSettings.ts
similarity index 74%
rename from client/src/ui/hooks/useSettings.ts
rename to client/src/ui/hooks/useSettings/useSettings.ts
index a1dd312..5f0fd63 100644
--- a/client/src/ui/hooks/useSettings.ts
+++ b/client/src/ui/hooks/useSettings/useSettings.ts
@@ -2,6 +2,8 @@ import { create } from "zustand";
import { subscribeWithSelector } from "zustand/middleware";
import { produce } from "immer";
+import { getSessionStorage } from "./services/localStorage";
+
const isTouchDevice = () =>
"ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator["msMaxTouchPoints"] > 0;
@@ -16,30 +18,6 @@ interface SettingsStore {
toggleTouchControlsSetting: () => void;
}
-const setSessionStorage = (key, value) => {
- sessionStorage.setItem(key, serializeJSON(value));
-};
-
-const getSessionStorage = (key) => {
- return deserializeJSON(sessionStorage.getItem(key) ?? "{}");
-};
-
-const serializeJSON = (value: any) => {
- try {
- return JSON.stringify(value);
- } catch (error) {
- throw new Error("Failed to serialize the value");
- }
-};
-
-const deserializeJSON = (value: string) => {
- try {
- return JSON.parse(value);
- } catch {
- return value;
- }
-};
-
const defaultSettings = {
musicMute: false,
effectsMute: false,
@@ -51,11 +29,11 @@ const defaultSettings = {
showDeviceInfo: false,
};
-const key = "settings";
+export const settingsStorageKey = "settings";
export const useSettings = create()(
subscribeWithSelector((set) => ({
- settings: { ...defaultSettings, ...getSessionStorage(key) },
+ settings: { ...defaultSettings, ...getSessionStorage(settingsStorageKey) },
toggleEffectsSetting: () =>
set(
produce((state) => {
@@ -100,12 +78,3 @@ export const useSettings = create()(
),
}))
);
-
-export const syncSettingsToSession = () => {
- const unsub = useSettings.subscribe(
- (state) => state.settings,
- (state) => setSessionStorage(key, state)
- );
-
- return unsub;
-};
diff --git a/client/src/ui/scenes/BottomRight/BottomRight.tsx b/client/src/ui/scenes/BottomRight/BottomRight.tsx
index dbf90c3..fedb849 100644
--- a/client/src/ui/scenes/BottomRight/BottomRight.tsx
+++ b/client/src/ui/scenes/BottomRight/BottomRight.tsx
@@ -26,15 +26,11 @@ export const BottomRight = ({ GroupComponent }) => {
toggleProfileModal();
};
- const handleMainMenu = () => {
- loadMainMenu();
- };
-
return (
<>
{isLoaded && (
-