{
const [gameLocalSave, setGameLocalSave] = useLocalStorage("savedGame", {});
diff --git a/src/components/BoardMenuEdit.js b/src/components/BoardMenuEdit.js
index bc077ee..bd8d399 100644
--- a/src/components/BoardMenuEdit.js
+++ b/src/components/BoardMenuEdit.js
@@ -4,8 +4,8 @@ import { useTranslation } from "react-i18next";
import { useC2C } from "../hooks/useC2C";
-import { updateGame, createGame } from "../utils/api";
-import { useGame } from "../views/GameProvider";
+import { updateGame } from "../utils/api";
+import { useGame } from "../hooks/useGame";
import DownloadGameLink from "../components/DownloadGameLink";
@@ -83,11 +83,9 @@ const BoardMenuEdit = ({ isOpen, setMenuOpen, setShowLoadGameModal }) => {
const handleSave = async () => {
const currentGame = await getGame();
if (gameId && gameId.length > 8) {
- // FIXME
- console.log(gameId);
await updateGame(gameId, currentGame);
} else {
- await createGame(currentGame);
+ console.log("Game not created. It's not a real one.");
}
setMenuOpen(false);
};
diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js
index 1045dfb..807f5be 100644
--- a/src/hooks/useAuth.js
+++ b/src/hooks/useAuth.js
@@ -8,10 +8,12 @@ const useAuth = () => {
false
);
const [userId, setUserId] = useLocalStorage("userId", null);
+ const mountedRef = React.useRef(true);
const login = React.useCallback(
async (userHash, token) => {
await loginAPI(userHash, token);
+ if (!mountedRef.current) return;
setIsAuthenticated(true);
setUserId(userHash);
},
@@ -20,10 +22,17 @@ const useAuth = () => {
const logout = React.useCallback(async () => {
await logoutAPI();
+ if (!mountedRef.current) return;
setIsAuthenticated(false);
setUserId(null);
}, [setIsAuthenticated, setUserId]);
+ React.useEffect(() => {
+ return () => {
+ mountedRef.current = false;
+ };
+ }, []);
+
return {
isAuthenticated,
userId,
diff --git a/src/hooks/useGame.js b/src/hooks/useGame.js
new file mode 100644
index 0000000..f7abae4
--- /dev/null
+++ b/src/hooks/useGame.js
@@ -0,0 +1,84 @@
+import React, { useContext } from "react";
+import { useSetRecoilState, useRecoilCallback } from "recoil";
+import { nanoid } from "nanoid";
+
+import { getGame } from "../utils/api";
+
+import SubscribeGameEvents from "../components/SubscribeGameEvents";
+import { useItems } from "../components/Board/Items";
+import {
+ AvailableItemListAtom,
+ AllItemsSelector,
+ BoardConfigAtom,
+} from "../components/Board";
+import useBoardConfig from "../components/useBoardConfig";
+
+export const GameContext = React.createContext({});
+
+export const GameProvider = ({ gameId, game, children }) => {
+ const { setItemList } = useItems();
+ const setAvailableItemList = useSetRecoilState(AvailableItemListAtom);
+ const [, setBoardConfig] = useBoardConfig();
+
+ const [gameLoaded, setGameLoaded] = React.useState(false);
+
+ const setGame = React.useCallback(
+ async (newGame) => {
+ try {
+ const originalGame = await getGame(gameId);
+ setAvailableItemList(
+ originalGame.availableItems.map((item) => ({
+ ...item,
+ id: nanoid(),
+ }))
+ );
+ } catch {
+ setAvailableItemList(
+ newGame.availableItems.map((item) => ({ ...item, id: nanoid() }))
+ );
+ }
+ setItemList(newGame.items);
+ setBoardConfig(newGame.board, false);
+ setGameLoaded(true);
+ },
+ [setAvailableItemList, setBoardConfig, setItemList, gameId]
+ );
+
+ const getCurrentGame = useRecoilCallback(
+ ({ snapshot }) => async () => {
+ const availableItemList = await snapshot.getPromise(
+ AvailableItemListAtom
+ );
+ const boardConfig = await snapshot.getPromise(BoardConfigAtom);
+ const itemList = await snapshot.getPromise(AllItemsSelector);
+ const currentGame = {
+ items: itemList,
+ board: boardConfig,
+ availableItems: availableItemList,
+ };
+ return currentGame;
+ },
+ []
+ );
+
+ React.useEffect(() => {
+ if (game) {
+ setGame(game);
+ }
+ }, [game, setGame]);
+
+ return (
+
+ {gameLoaded && children}
+
+
+ );
+};
+
+export const useGame = () => {
+ return useContext(GameContext);
+};
+
+export default GameProvider;
diff --git a/src/ui/formUtils/ImageField.js b/src/ui/formUtils/ImageField.js
index ce11258..3394d85 100644
--- a/src/ui/formUtils/ImageField.js
+++ b/src/ui/formUtils/ImageField.js
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
import styled from "styled-components";
import { useDropzone } from "react-dropzone";
import { uploadImage } from "../../utils/api";
-import { useGame } from "../../views/GameProvider";
+import { useGame } from "../../hooks/useGame";
const Thumbnail = styled.img`
height: 50px;
diff --git a/src/views/AuthView.js b/src/views/AuthView.js
index 6dfefc7..b696626 100644
--- a/src/views/AuthView.js
+++ b/src/views/AuthView.js
@@ -9,11 +9,19 @@ const AuthView = () => {
const { login } = useAuth();
React.useEffect(() => {
+ let isMounted = true;
+
const verify = async () => {
await login(userHash, token);
+ if (!isMounted) return;
setLogged(true);
};
+
verify();
+
+ return () => {
+ isMounted = false;
+ };
}, [login, token, userHash]);
if (logged) {
diff --git a/src/views/BoardView.js b/src/views/BoardView.js
index a049120..90947b6 100644
--- a/src/views/BoardView.js
+++ b/src/views/BoardView.js
@@ -16,7 +16,7 @@ import NavBar from "./NavBar";
import AutoSave from "../components/AutoSave";
import ImageDropNPaste from "../components/ImageDropNPaste";
import { getComponent } from "../components/boardComponents";
-import { useGame } from "../views/GameProvider";
+import { useGame } from "../hooks/useGame";
const StyledBoardView = styled.div`
width: 100vw;
@@ -32,7 +32,7 @@ const BoardContainer = styled.div`
background-color: #202b38;
`;
-export const BoardView = ({ namespace, editMode = false }) => {
+export const BoardView = ({ namespace, edit: editMode = false }) => {
const { currentUser, users } = useUsers();
const [showLoadGameModal, setShowLoadGameModal] = React.useState(false);
const [showHelpModal, setShowHelpModal] = React.useState(false);
diff --git a/src/views/GamesView.js b/src/views/GameListView.js
similarity index 98%
rename from src/views/GamesView.js
rename to src/views/GameListView.js
index 32c3e4c..e5208bf 100644
--- a/src/views/GamesView.js
+++ b/src/views/GameListView.js
@@ -70,7 +70,7 @@ const Game = styled.li`
}
`;
-const GamesView = () => {
+const GameListView = () => {
const { t } = useTranslation();
const [gameList, setGameList] = React.useState([]);
const { isAuthenticated, userId } = useAuth();
@@ -137,4 +137,4 @@ const GamesView = () => {
);
};
-export default GamesView;
+export default GameListView;
diff --git a/src/views/GameProvider.js b/src/views/GameProvider.js
deleted file mode 100644
index dab57ad..0000000
--- a/src/views/GameProvider.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import React, { useContext } from "react";
-import { useSetRecoilState, useRecoilCallback } from "recoil";
-import { nanoid } from "nanoid";
-
-import { useC2C } from "../hooks/useC2C";
-import { getGame } from "../utils/api";
-
-import SubscribeGameEvents from "../components/SubscribeGameEvents";
-import { useItems } from "../components/Board/Items";
-import {
- AvailableItemListAtom,
- AllItemsSelector,
- BoardConfigAtom,
-} from "../components/Board";
-import useBoardConfig from "../components/useBoardConfig";
-
-export const GameContext = React.createContext({});
-
-export const GameProvider = ({ gameId, create, children }) => {
- const [c2c, joined, isMaster] = useC2C();
- const { setItemList } = useItems();
- const setAvailableItemList = useSetRecoilState(AvailableItemListAtom);
- const [, setBoardConfig] = useBoardConfig();
-
- const [gameLoaded, setGameLoaded] = React.useState(false);
- const gameLoadingRef = React.useRef(false);
-
- const sendLoadGameEvent = React.useCallback(
- (game) => {
- game.items = game.items.map((item) => ({ ...item, id: nanoid() }));
- c2c.publish("loadGame", game);
- },
- [c2c]
- );
-
- const setGame = React.useCallback(
- async (game) => {
- const originalGame = await getGame(gameId);
- if (originalGame) {
- setAvailableItemList(
- originalGame.availableItems.map((item) => ({ ...item, id: nanoid() }))
- );
- } else {
- setAvailableItemList(
- game.availableItems.map((item) => ({ ...item, id: nanoid() }))
- );
- }
- setItemList(game.items);
- setBoardConfig(game.board, false);
- setGameLoaded(true);
- },
- [setAvailableItemList, setBoardConfig, setItemList, gameId]
- );
-
- const getCurrentGame = useRecoilCallback(
- ({ snapshot }) => async () => {
- const availableItemList = await snapshot.getPromise(
- AvailableItemListAtom
- );
- const boardConfig = await snapshot.getPromise(BoardConfigAtom);
- const itemList = await snapshot.getPromise(AllItemsSelector);
- const game = {
- items: itemList,
- board: boardConfig,
- availableItems: availableItemList,
- };
- return game;
- },
- []
- );
-
- React.useEffect(() => {
- let isMounted = true;
-
- const loadGameData = async () => {
- try {
- let gameData;
-
- if (create) {
- gameData = {
- board: {
- name: "No name",
- },
- items: [],
- availableItems: [],
- };
- } else {
- gameData = await getGame(gameId);
- }
-
- if (!isMounted) return;
- setGame(gameData);
- sendLoadGameEvent(gameData);
- } catch (e) {
- console.log(e);
- }
- };
-
- if (gameId && isMaster && !gameLoaded) {
- gameLoadingRef.current = true;
- loadGameData();
- }
-
- return () => {
- isMounted = false;
- };
- }, [gameId, sendLoadGameEvent, isMaster, gameLoaded, setGame, create]);
-
- React.useEffect(() => {
- return () => {
- setItemList([]);
- setBoardConfig({}, false);
- setAvailableItemList([]);
- setGameLoaded(false);
- };
- }, [setAvailableItemList, setBoardConfig, setItemList]);
-
- // Load game from master if any
- React.useEffect(() => {
- if (!gameLoaded && joined && !isMaster && !gameLoadingRef.current) {
- gameLoadingRef.current = true;
- c2c.call("getGame").then(setGame, () => {});
- }
- }, [c2c, isMaster, joined, gameLoaded, setGame]);
-
- return (
-
- {children}
-
-
- );
-};
-
-export const useGame = () => {
- return useContext(GameContext);
-};
-
-export default GameProvider;
diff --git a/src/views/GameView.js b/src/views/GameView.js
index 152a3a4..0a6f646 100644
--- a/src/views/GameView.js
+++ b/src/views/GameView.js
@@ -1,16 +1,18 @@
-import React, { useRef } from "react";
-import { useParams, useHistory } from "react-router-dom";
+import React from "react";
+import { useParams } from "react-router-dom";
import { nanoid } from "nanoid";
import { Provider } from "@scripters/use-socket.io";
-import { C2CProvider } from "../hooks/useC2C";
+import { C2CProvider, useC2C } from "../hooks/useC2C";
import { SOCKET_URL, SOCKET_OPTIONS } from "../utils/settings";
-import { createGame } from "../utils/api";
import BoardView from "../views/BoardView";
import Waiter from "../ui/Waiter";
-import GameProvider from "./GameProvider";
+import { getGame } from "../utils/api";
+
+import GameProvider from "../hooks/useGame";
+import { useTranslation } from "react-i18next";
const newGameData = {
items: [],
@@ -18,42 +20,90 @@ const newGameData = {
board: { size: 1000, scale: 1, name: "New game" },
};
-export const ConnectedGameProvider = ({ create = false, editMode = false }) => {
- const { room = nanoid(), gameId } = useParams();
- const history = useHistory();
- const creationRef = useRef(false);
+export const GameView = ({ edit }) => {
+ const [c2c, joined, isMaster] = useC2C();
+ const { gameId } = useParams();
+ const [realGameId, setRealGameId] = React.useState();
+ const [gameLoaded, setGameLoaded] = React.useState(false);
+ const [game, setGame] = React.useState(null);
+ const gameLoadingRef = React.useRef(false);
+ const { t } = useTranslation();
- // 3 cas
- // Create -> jeux vide
- // Edit -> on va chercher du serveur
- // Play master -> on va chercher du serveur
- // Play slave -> on récupère du master
-
- // Create a new game as asked and redirect to it
React.useEffect(() => {
- const createNewGame = async () => {
- const { _id: newGameId } = await createGame(newGameData);
- history.push(`/game/${newGameId}/`);
- };
- if (create && !creationRef.current) {
- createNewGame();
- creationRef.current = true;
- }
- }, [create, history]);
+ let isMounted = true;
- if (create) {
- return
;
+ const loadGameData = async () => {
+ try {
+ let gameData;
+
+ if (!gameId) {
+ // Create new game
+ gameData = JSON.parse(JSON.stringify(newGameData));
+ setRealGameId(nanoid());
+ } else {
+ // Load game from server
+ gameData = await getGame(gameId);
+ setRealGameId(gameId);
+ }
+
+ // Add id if necessary
+ gameData.items = gameData.items.map((item) => ({
+ ...item,
+ id: nanoid(),
+ }));
+
+ if (!isMounted) return;
+
+ setGame(gameData);
+ // Send loadGame event for other user
+ c2c.publish("loadGame", gameData);
+ setGameLoaded(true);
+ } catch (e) {
+ console.log(e);
+ }
+ };
+
+ if (joined && isMaster && !gameLoaded && !gameLoadingRef.current) {
+ gameLoadingRef.current = true;
+ loadGameData();
+ }
+
+ return () => {
+ isMounted = false;
+ };
+ }, [c2c, gameId, gameLoaded, isMaster, joined]);
+
+ // Load game from master if any
+ React.useEffect(() => {
+ if (joined && !isMaster && !gameLoaded && !gameLoadingRef.current) {
+ gameLoadingRef.current = true;
+ c2c.call("getGame").then((receivedGame) => {
+ setGame(receivedGame);
+ setGameLoaded(true);
+ });
+ }
+ }, [c2c, isMaster, joined, gameLoaded]);
+
+ if (!gameLoaded) {
+ return
;
}
+ return (
+
+
+
+ );
+};
+
+const ConnectedGameView = ({ edit = false }) => {
+ const { room = nanoid() } = useParams();
return (
-
-
-
+
);
};
-export default ConnectedGameProvider;
+export default ConnectedGameView;