Browse Source

Session working

Jeremie Pardou-Piquemal 2 years ago
parent
commit
b71dbcc54b

+ 2 - 2
src/MainRoute.jsx

@@ -11,7 +11,7 @@ import Home from "./views/Home";
 // import GameView from "./views/GameView";
 import Session from "./views/Session";
 import AuthView from "./views/AuthView";
-// import RoomView from "./views/RoomView";
+import RoomView from "./views/RoomView";
 
 import { Provider as SocketIOProvider } from "@scripters/use-socket.io";
 
@@ -89,7 +89,7 @@ const MainRoute = () => {
           <GameView />
         </WithSocketIO>
       </Route>
-      {/* Room routes}
+      {/*Room routes*/}
       <Route path="/room/:roomId">
         {({
           match: {

+ 49 - 0
src/hooks/SubscribeSessionEvents.jsx

@@ -0,0 +1,49 @@
+import debounce from "lodash.debounce";
+import React from "react";
+
+import { useC2C, useBoardConfig } from "react-sync-board";
+
+export const SubscribeSessionEvents = ({ getSession, setSession }) => {
+  const { c2c, isMaster } = useC2C("board");
+
+  const [, setBoardConfig] = useBoardConfig();
+
+  // Use ref to avoid too fast register and unregister
+  const getSessionRef = React.useRef(getSession);
+  getSessionRef.current = getSession;
+
+  // if first player register callback to allow other user to load game
+  React.useEffect(() => {
+    const unsub = [];
+    if (isMaster) {
+      c2c
+        .register("getSession", async () => {
+          console.log("getsessionCalled");
+          return await getSessionRef.current();
+        })
+        .then((unregister) => {
+          unsub.push(unregister);
+        });
+    }
+    return () => {
+      unsub.forEach((u) => u());
+    };
+  }, [c2c, isMaster]);
+
+  // Subscribe loadSession and updateBoardConfig events
+  React.useEffect(() => {
+    const unsub = [];
+    unsub.push(
+      c2c.subscribe("loadSession", (session) => {
+        setSession(session);
+      })
+    );
+    return () => {
+      unsub.forEach((u) => u());
+    };
+  }, [c2c, setBoardConfig, setSession]);
+
+  return null;
+};
+
+export default SubscribeSessionEvents;

+ 49 - 53
src/hooks/useSession.jsx

@@ -1,5 +1,12 @@
-import { debounce } from "lodash";
 import React, { useContext } from "react";
+import {
+  useItemBaseActions,
+  useMessage,
+  useBoardConfig,
+  useC2C,
+} from "react-sync-board";
+
+import SubscribeSessionEvents from "./SubscribeSessionEvents";
 
 import { updateSession, getSession, getGame } from "../utils/api";
 
@@ -26,16 +33,16 @@ const emtpyBoard = {
 };
 
 export const SessionProvider = ({ sessionId, fromGameId, children }) => {
-  const [initialItems, setInitialItems] = React.useState([]);
+  const { setItemList, getItemList } = useItemBaseActions();
+  const { messages, setMessages } = useMessage();
   const [availableItems, setAvailableItems] = React.useState([]);
-  const [messages, setMessages] = React.useState([]);
-  const [boardConfig, setBoardConfig] = React.useState({});
-  const [currentItems, setCurrentItems] = React.useState([]);
-  const [isMaster, setIsMaster] = React.useState(null);
+  const [boardConfig, setBoardConfig] = useBoardConfig();
 
   const [sessionLoaded, setSessionLoaded] = React.useState(false);
   const [currentGameId, setCurrentGameId] = React.useState(fromGameId);
 
+  const { c2c } = useC2C("board");
+
   const loadSession = React.useCallback(async () => {
     let sessionData;
 
@@ -64,22 +71,28 @@ export const SessionProvider = ({ sessionId, fromGameId, children }) => {
   }, [fromGameId, sessionId]);
 
   const setSession = React.useCallback(
-    async (newData) => {
+    async (newData, sync = false) => {
       const { availableItems, items, board, messages = [] } = newData;
-
       setAvailableItems(availableItems);
       // The filter prevent the empty item bug on reload
-      setInitialItems(items.filter((item) => item));
-      setBoardConfig(board);
+      setItemList(items.filter((item) => item));
+      setBoardConfig(board, false);
       setMessages(messages);
+
+      if (sync) {
+        // Send loadSession event for other user
+        c2c.publish("loadSession", newData);
+      }
+
       setSessionLoaded(true);
     },
-    [setMessages]
+    [c2c, setBoardConfig, setItemList, setMessages]
   );
 
-  /*const getCurrentSession = React.useCallback(() => {
+  const getCurrentSession = React.useCallback(async () => {
+    console.log("getBoard", boardConfig);
     const currentSession = {
-      items: currentItems,
+      items: await getItemList(),
       board: boardConfig,
       availableItems: availableItems,
       messages: messages.slice(-50),
@@ -88,40 +101,29 @@ export const SessionProvider = ({ sessionId, fromGameId, children }) => {
     };
 
     return currentSession;
-  }, [availableItems, boardConfig, currentItems, fromGameId, messages]);*/
-
-  const changeGame = React.useCallback(async (newGameId) => {
-    const newGame = await getGame(newGameId);
-
-    setAvailableItems(newGame.availableItems);
-    // The filter prevent the empty item bug on reload
-    setInitialItems(newGame.items.filter((item) => item));
-    setBoardConfig(newGame.board);
-    //const currentSession = getCurrentSession();
-
-    //setSession({ ...currentSession, ...newGame });
-  }, []);
-
-  const saveSession = React.useCallback(
-    async (currentSession) => {
-      //const currentSession = await getCurrentSession();
-
-      if (currentSession.items.length) {
-        try {
-          return await updateSession(sessionId, currentSession);
-        } catch (e) {
-          console.log(e);
-        }
-      }
+  }, [availableItems, boardConfig, fromGameId, getItemList, messages]);
+
+  const changeGame = React.useCallback(
+    async (newGameId) => {
+      const newGame = await getGame(newGameId);
+      const currentSession = getCurrentSession();
+
+      setSession({ ...currentSession, ...newGame }, true);
     },
-    [sessionId]
+    [getCurrentSession, setSession]
   );
 
-  // eslint-disable-next-line react-hooks/exhaustive-deps
-  const debouncedSetCurrentItems = React.useCallback(
-    debounce(setCurrentItems, 500),
-    []
-  );
+  const saveSession = React.useCallback(async () => {
+    const currentSession = await getCurrentSession();
+
+    if (currentSession.items.length) {
+      try {
+        return await updateSession(sessionId, currentSession);
+      } catch (e) {
+        console.log(e);
+      }
+    }
+  }, [getCurrentSession, sessionId]);
 
   return (
     <SessionContext.Provider
@@ -129,27 +131,21 @@ export const SessionProvider = ({ sessionId, fromGameId, children }) => {
         loadSession,
         changeGame,
         setSession,
-        // getSession: getCurrentSession,
+        getSession: getCurrentSession,
         saveSession,
         sessionLoaded,
         sessionId,
         gameId: currentGameId,
-        initialItems,
         availableItems,
         boardConfig,
         messages,
-        setCurrentItems: debouncedSetCurrentItems,
-        currentItems,
-        isMaster,
-        initialMessages: messages,
-        setIsMaster,
       }}
     >
       {children}
-      {/*<SubscribeSessionEvents
+      <SubscribeSessionEvents
         getSession={getCurrentSession}
         setSession={setSession}
-      />*/}
+      />
     </SessionContext.Provider>
   );
 };

+ 21 - 48
src/views/BoardView/BoardView.jsx

@@ -1,8 +1,8 @@
 import React from "react";
 
-import { SHOW_WELCOME } from "../../utils/settings";
-import { BoardWrapper, Board } from "react-sync-board";
+import { Board, useC2C } from "react-sync-board";
 
+import { SHOW_WELCOME } from "../../utils/settings";
 import WelcomeModal from "./WelcomeModal";
 import NavBar from "./NavBar";
 import BoardForm from "./BoardForm";
@@ -10,24 +10,12 @@ import SelectedItemPane from "./SelectedItemsPane";
 
 import { ItemForm } from "../../gameComponents";
 
-import {
-  uploadResourceImage,
-  listResourceImage,
-  deleteResourceImage,
-} from "../../utils/api";
 import ActionBar from "./ActionBar";
 
 import {
   MediaLibraryProvider,
   ImageDropNPaste,
 } from "../../components/mediaLibrary";
-import AutoSaveSession from "../AutoSaveSession";
-
-const mediaHandlers = {
-  uploadMedia: uploadResourceImage,
-  listMedia: listResourceImage,
-  deleteMedia: deleteResourceImage,
-};
 
 const style = {
   background:
@@ -37,47 +25,32 @@ const style = {
   boxShadow: "0px 3px 6px #00000029",
 };
 
-export const BoardView = (props) => {
-  // TODO open only for master
+export const BoardView = ({ mediaLibraries, edit, itemLibraries }) => {
+  const { isMaster } = useC2C("board");
   const [showWelcomeModal, setShowWelcomeModal] = React.useState(
-    SHOW_WELCOME && !props.editMode
+    SHOW_WELCOME && !edit && isMaster
   );
 
   const [moveFirst, setMoveFirst] = React.useState(false);
   const [hideMenu, setHideMenu] = React.useState(false);
 
   return (
-    <MediaLibraryProvider libraries={props.mediaLibraries} {...mediaHandlers}>
-      <BoardWrapper
-        {...props}
-        mediaHandlers={mediaHandlers}
-        BoardFormComponent={BoardForm}
-        hideMenu={hideMenu}
-        style={{
-          position: "relative",
-          width: "100vw",
-          height: "100vh",
-          overflow: "hidden",
-        }}
-      >
-        <ImageDropNPaste>
-          <Board moveFirst={moveFirst} style={style} />
-          <NavBar editMode={props.edit} />
-          <ActionBar
-            editMode={props.edit}
-            BoardFormComponent={BoardForm}
-            itemLibraries={props.itemLibraries}
-            moveFirst={moveFirst}
-            setMoveFirst={setMoveFirst}
-            hideMenu={hideMenu}
-            setHideMenu={setHideMenu}
-          />
-          <WelcomeModal show={showWelcomeModal} setShow={setShowWelcomeModal} />
-        </ImageDropNPaste>
-
-        <AutoSaveSession />
-        <SelectedItemPane ItemFormComponent={ItemForm} />
-      </BoardWrapper>
+    <MediaLibraryProvider libraries={mediaLibraries}>
+      <ImageDropNPaste>
+        <Board moveFirst={moveFirst} style={style} />
+        <NavBar editMode={edit} />
+        <ActionBar
+          editMode={edit}
+          BoardFormComponent={BoardForm}
+          itemLibraries={itemLibraries}
+          moveFirst={moveFirst}
+          setMoveFirst={setMoveFirst}
+          hideMenu={hideMenu}
+          setHideMenu={setHideMenu}
+        />
+        <WelcomeModal show={showWelcomeModal} setShow={setShowWelcomeModal} />
+      </ImageDropNPaste>
+      <SelectedItemPane ItemFormComponent={ItemForm} hideMenu={hideMenu} />
     </MediaLibraryProvider>
   );
 };

+ 4 - 1
src/views/BoardView/NavBar.jsx

@@ -4,6 +4,7 @@ import styled from "styled-components";
 import { useTranslation } from "react-i18next";
 import { useHistory, useRouteMatch } from "react-router-dom";
 import { confirmAlert } from "react-confirm-alert";
+import { useC2C } from "react-sync-board";
 
 import UserList from "../../components/users/UserList";
 import Touch from "../../components/ui/Touch";
@@ -133,7 +134,9 @@ const StyledNavBar = styled.div.attrs(() => ({ className: "nav" }))`
 
 const NavBar = ({ editMode }) => {
   const { t, i18n } = useTranslation();
-  const { sessionId: room, isMaster, boardConfig } = useSession();
+  const { sessionId: room, boardConfig } = useSession();
+
+  const { isMaster } = useC2C("board");
 
   const history = useHistory();
   const match = useRouteMatch();

+ 2 - 2
src/views/RoomView/RoomNavBar.jsx

@@ -8,7 +8,7 @@ import useLocalStorage from "../../hooks/useLocalStorage";
 import { useC2C } from "react-sync-board";
 
 import Touch from "../../components/ui/Touch";
-import { UserList } from "../../components/users";
+import UserList from "../../components/users/UserList";
 import WebConferenceButton from "../webconf/WebConferenceButton";
 
 import Brand from "../Brand";
@@ -130,7 +130,7 @@ const RoomNavBar = () => {
       </div>
 
       <div className="nav-center">
-        <h3>{"Air Board Game"}</h3>
+        <h3>Air Board Game</h3>
       </div>
 
       <div className="nav-right">

+ 12 - 17
src/views/RoomView/RoomView.jsx

@@ -2,15 +2,11 @@ import React from "react";
 import { nanoid } from "nanoid";
 import styled from "styled-components";
 
-import useC2C, { C2CProvider } from "../../components/hooks/useC2C";
+import { RoomWrapper, useC2C, useUsers } from "react-sync-board";
 
 import { Switch, Route, Link } from "react-router-dom";
-import SessionView from "../SessionView";
-import {
-  useUsers,
-  SubscribeUserEvents,
-  UserCircle,
-} from "../../components/users";
+import Session from "../Session";
+import UserCircle from "../../components/users/UserCircle";
 
 import RoomNavBar from "./RoomNavBar";
 
@@ -184,7 +180,7 @@ const SubscribeRoomEvents = ({ room, setRoom }) => {
   React.useEffect(() => {
     const unsub = [];
     // Master register getRoom for peers
-    if (isMasterRef.current) {
+    if (isMaster) {
       c2c
         .register("getRoom", () => {
           return roomRef.current;
@@ -203,11 +199,11 @@ const SubscribeRoomEvents = ({ room, setRoom }) => {
     return () => {
       unsub.forEach((u) => u());
     };
-  }, [c2c, setRoom]);
+  }, [c2c, isMaster, setRoom]);
 
   // Get Room from master at connection
   React.useEffect(() => {
-    if (!isMasterRef.current) {
+    if (!isMaster) {
       const onGetRoom = (roomData) => {
         setRoom(roomData);
       };
@@ -219,14 +215,14 @@ const SubscribeRoomEvents = ({ room, setRoom }) => {
         }, 2000);
       });
     }
-  }, [c2c, setRoom]);
+  }, [c2c, isMaster, setRoom]);
 
   // Send room update on change if master
   React.useEffect(() => {
-    if (isMasterRef.current) {
+    if (isMaster) {
       c2c.publish("roomUpdate", room);
     }
-  }, [c2c, room]);
+  }, [c2c, isMaster, room]);
 
   return null;
 };
@@ -248,20 +244,19 @@ const RoomView = ({ roomId }) => {
               params: { sessionId },
             },
           }) => {
-            return <SessionView sessionId={sessionId} />;
+            return <Session sessionId={sessionId} />;
           }}
         </Route>
       </Switch>
       <SubscribeRoomEvents room={room} setRoom={setRoom} />
-      <SubscribeUserEvents />
     </>
   );
 };
 
 const ConnectedRoomView = (props) => (
-  <C2CProvider room={props.roomId} channel="room">
+  <RoomWrapper room={props.roomId}>
     <RoomView {...props} />
-  </C2CProvider>
+  </RoomWrapper>
 );
 
 export default ConnectedRoomView;

+ 0 - 23
src/views/RoomWrapperView.jsx

@@ -1,23 +0,0 @@
-import React from "react";
-
-import { C2CProvider } from "../components/hooks/useC2C";
-
-import SessionView from "./SessionView";
-import { SubscribeUserEvents } from "../components/users";
-
-const RoomWrapper = (props) => {
-  return (
-    <>
-      <SessionView {...props} />
-      <SubscribeUserEvents />
-    </>
-  );
-};
-
-const ConnectedRoomWrapper = (props) => (
-  <C2CProvider room={`room__${props.sessionId}`} channel="room">
-    <RoomWrapper {...props} />
-  </C2CProvider>
-);
-
-export default ConnectedRoomWrapper;

+ 45 - 30
src/views/Session.jsx

@@ -1,14 +1,10 @@
 import React from "react";
 import { useTranslation } from "react-i18next";
 import useAsyncEffect from "use-async-effect";
+import { BoardWrapper, useC2C } from "react-sync-board";
 import { nanoid } from "nanoid";
 
-import {
-  itemTemplates,
-  itemLibrary,
-  actionMap,
-  ItemForm,
-} from "../gameComponents";
+import { itemTemplates, itemLibrary, actionMap } from "../gameComponents";
 
 import BoardView from "./BoardView";
 import Waiter from "./Waiter";
@@ -55,21 +51,18 @@ export const Session = () => {
     sessionLoaded,
     gameId,
     sessionId,
-    initialItems,
     availableItems,
-    setCurrentItems,
-    setIsMaster,
-    boardConfig,
-    initialMessages,
   } = useSession();
 
   const gameLoadingRef = React.useRef(false);
 
+  const { c2c, isMaster } = useC2C("board");
+
   const { t } = useTranslation();
 
   useAsyncEffect(
     async (isMounted) => {
-      if (!sessionLoaded && !gameLoadingRef.current) {
+      if (isMaster && !sessionLoaded && !gameLoadingRef.current) {
         gameLoadingRef.current = true;
         const sessionData = await loadSession();
 
@@ -77,9 +70,31 @@ export const Session = () => {
         setSession(sessionData, true);
       }
     },
-    [sessionLoaded]
+    [sessionLoaded, isMaster]
   );
 
+  // Load game from master if any
+  React.useEffect(() => {
+    if (!isMaster && !sessionLoaded && !gameLoadingRef.current) {
+      gameLoadingRef.current = true;
+      const onReceiveGame = (receivedSession) => {
+        console.log("game received", receivedSession);
+        setSession(receivedSession);
+      };
+      c2c.call("getSession").then(onReceiveGame, () => {
+        setTimeout(
+          () =>
+            c2c
+              .call("getSession")
+              .then(onReceiveGame, (error) =>
+                console.log("Failed to call getSession with error", error)
+              ),
+          1000
+        );
+      });
+    }
+  }, [c2c, isMaster, sessionLoaded, setSession]);
+
   const availableItemLibrary = React.useMemo(() => {
     let itemList = availableItems;
     if (itemList.length && itemList[0].groupId) {
@@ -128,28 +143,28 @@ export const Session = () => {
   }
 
   return (
-    <BoardView
-      room={`room_${sessionId}`}
-      initialBoardConfig={boardConfig}
-      session={sessionId}
-      itemTemplates={itemTemplates}
-      mediaLibraries={mediaLibraries}
-      actions={actionMap}
-      itemLibraries={itemLibraries}
-      ItemFormComponent={ItemForm}
-      initialItems={initialItems}
-      initialMessages={initialMessages}
-      onItemsChange={setCurrentItems}
-      onMasterChange={setIsMaster}
-    />
+    <BoardView mediaLibraries={mediaLibraries} itemLibraries={itemLibraries} />
   );
 };
 
 const ConnectedSessionView = ({ sessionId, fromGame }) => {
   return (
-    <SessionProvider sessionId={sessionId} fromGameId={fromGame}>
-      <Session />
-    </SessionProvider>
+    <BoardWrapper
+      room={`room_${sessionId}`}
+      session={sessionId}
+      itemTemplates={itemTemplates}
+      actions={actionMap}
+      style={{
+        position: "relative",
+        width: "100vw",
+        height: "100vh",
+        overflow: "hidden",
+      }}
+    >
+      <SessionProvider sessionId={sessionId} fromGameId={fromGame}>
+        <Session />
+      </SessionProvider>
+    </BoardWrapper>
   );
 };
 

+ 0 - 125
src/views/SessionView_old.jsx

@@ -1,125 +0,0 @@
-import React from "react";
-
-import useC2C, { C2CProvider } from "../components/hooks/useC2C";
-
-import { itemMap, useGameItemActionMap, ItemForm } from "../gameComponents";
-
-import BoardView from "./BoardView";
-import Waiter from "./Waiter";
-
-import { useUsers } from "../components/users";
-
-import useSession, { SessionProvider } from "../hooks/useSession";
-
-import AutoSaveSession from "./AutoSaveSession";
-import { useTranslation } from "react-i18next";
-
-import useAsyncEffect from "use-async-effect";
-
-export const SessionView = () => {
-  const {
-    loadSession,
-    setSession,
-    sessionLoaded,
-    gameId,
-    sessionId,
-  } = useSession();
-
-  const { c2c, isMaster } = useC2C("board");
-
-  const { actionMap } = useGameItemActionMap();
-
-  const { setCurrentUser } = useUsers();
-
-  const gameLoadingRef = React.useRef(false);
-
-  const { t } = useTranslation();
-
-  React.useEffect(() => {
-    setCurrentUser((prev) => ({ ...prev, space: sessionId }));
-  }, [sessionId, setCurrentUser]);
-
-  useAsyncEffect(
-    async (isMounted) => {
-      if (isMaster && !sessionLoaded && !gameLoadingRef.current) {
-        gameLoadingRef.current = true;
-        const sessionData = await loadSession();
-        if (!isMounted) return;
-        setSession(sessionData, true);
-      }
-    },
-    [sessionLoaded, isMaster]
-  );
-
-  // Load game from master if any
-  React.useEffect(() => {
-    if (!isMaster && !sessionLoaded && !gameLoadingRef.current) {
-      gameLoadingRef.current = true;
-      const onReceiveGame = (receivedSession) => {
-        setSession(receivedSession);
-      };
-      c2c.call("getSession").then(onReceiveGame, () => {
-        setTimeout(
-          () =>
-            c2c
-              .call("getSession")
-              .then(onReceiveGame, (error) =>
-                console.log("Failed to call getSession with error", error)
-              ),
-          1000
-        );
-      });
-    }
-  }, [c2c, isMaster, sessionLoaded, setSession]);
-
-  const mediaLibraries = React.useMemo(
-    () =>
-      gameId
-        ? [
-            {
-              id: "session",
-              name: t("Session"),
-              boxId: "session",
-              resourceId: sessionId,
-            },
-          { id: "game", name: t("Game"), boxId: "game", resourceId: gameId },
-          ]
-        : [
-            {
-              id: "session",
-              name: t("Session"),
-              boxId: "session",
-              resourceId: sessionId,
-            },
-        ],
-    [gameId, sessionId, t]
-  );
-
-  if (!sessionLoaded) {
-    return <Waiter message={t("Session loading...")} />;
-  }
-
-  return (
-    <>
-      <AutoSaveSession />
-      <BoardView
-        mediaLibraries={mediaLibraries}
-        itemMap={itemMap}
-        actionMap={actionMap}
-        ItemFormComponent={ItemForm}
-      />
-    </>
-  );
-};
-
-const ConnectedSessionView = ({ sessionId, fromGame }) => {
-  return (
-    <C2CProvider room={sessionId} channel="board">
-      <SessionProvider sessionId={sessionId} fromGameId={fromGame}>
-        <SessionView />
-      </SessionProvider>
-    </C2CProvider>
-  );
-};
-
-export default ConnectedSessionView;