diff --git a/package-lock.json b/package-lock.json index 8def707..4f5102f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "react-query": "^3.13.4", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", - "react-sync-board": "^0.4.4", + "react-sync-board": "^0.4.5", "react-toastify": "^6.1.0", "react-useportal": "^1.0.14", "recoil": "^0.3.1", @@ -11667,9 +11667,9 @@ } }, "node_modules/react-sync-board": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/react-sync-board/-/react-sync-board-0.4.4.tgz", - "integrity": "sha512-jwxwn33lB8EmiKPAIG4XlsKKFhPD0WjVZI1SeUBlmWuvNmGxt1UFUCl5dLmtaVLmzeArNTBVrb9EP52WO/Ci7A==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/react-sync-board/-/react-sync-board-0.4.5.tgz", + "integrity": "sha512-YsffM8deD3kZpX2S8wWYUtufyhSOd0scxcED61UgDqztYOdrfQY7vobERwvH3vLWa+xN9/BY0rBcctuqMA1Bdg==", "dependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", @@ -23186,9 +23186,9 @@ } }, "react-sync-board": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/react-sync-board/-/react-sync-board-0.4.4.tgz", - "integrity": "sha512-jwxwn33lB8EmiKPAIG4XlsKKFhPD0WjVZI1SeUBlmWuvNmGxt1UFUCl5dLmtaVLmzeArNTBVrb9EP52WO/Ci7A==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/react-sync-board/-/react-sync-board-0.4.5.tgz", + "integrity": "sha512-YsffM8deD3kZpX2S8wWYUtufyhSOd0scxcED61UgDqztYOdrfQY7vobERwvH3vLWa+xN9/BY0rBcctuqMA1Bdg==", "requires": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", diff --git a/package.json b/package.json index bc8ebba..8737c78 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "react-query": "^3.13.4", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", - "react-sync-board": "^0.4.4", + "react-sync-board": "^0.4.5", "react-toastify": "^6.1.0", "react-useportal": "^1.0.14", "recoil": "^0.3.1", diff --git a/src/hooks/useLocalStorage.js b/src/hooks/useLocalStorage.js index dbd8aa8..0586f87 100644 --- a/src/hooks/useLocalStorage.js +++ b/src/hooks/useLocalStorage.js @@ -3,8 +3,8 @@ import { atomFamily, useRecoilState } from "recoil"; const getFromLocalStorage = (key) => { const item = window.localStorage.getItem(key); - if (item === undefined) { - return undefined; + if (item === null) { + return null; } return JSON.parse(item); }; @@ -25,7 +25,7 @@ const useLocalStorage = (key, initialValue) => { // Get from local storage by key const item = window.localStorage.getItem(key); - if (item === undefined) { + if (item === null) { // If missing we add it window.localStorage.setItem(key, JSON.stringify(initialValue)); setStoredValue([true, initialValue]); @@ -44,21 +44,21 @@ const useLocalStorage = (key, initialValue) => { const setValue = React.useCallback( (value) => { try { - // Allow value to be a function so we have same API as useState - const valueToStore = - value instanceof Function ? value(storedValue) : value; - // Save state - setStoredValue([true, valueToStore]); + setStoredValue(([, prev]) => { + // Allow value to be a function so we have same API as useState + const valueToStore = value instanceof Function ? value(prev) : value; - // Save to local storage - window.localStorage.setItem(key, JSON.stringify(valueToStore)); + // Save to local storage + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + return [true, valueToStore]; + }); } catch (error) { // A more advanced implementation would handle the error case console.log(error); } }, - [key, setStoredValue, storedValue] + [key, setStoredValue] ); // React on other tab modifications @@ -74,7 +74,7 @@ const useLocalStorage = (key, initialValue) => { }; }, [key, setStoredValue]); - return [storedValue === undefined ? initialValue : storedValue, setValue]; + return [storedValue === null ? initialValue : storedValue, setValue]; }; export default useLocalStorage; diff --git a/src/views/Session.jsx b/src/views/Session.jsx index 4c67b62..71e31cd 100644 --- a/src/views/Session.jsx +++ b/src/views/Session.jsx @@ -6,6 +6,7 @@ import { BoardWrapper, useWire } from "react-sync-board"; import { itemTemplates, itemLibrary, premadeItems } from "../gameComponents"; import BoardView from "./BoardView"; +import SessionRestoreDim from "./SessionRestoreDim"; import Waiter from "../ui/Waiter"; import { uid } from "../utils"; @@ -159,6 +160,7 @@ export const Session = () => { mediaLibraries={mediaLibraries} itemLibraries={itemLibraries} /> + {isMaster && } ); diff --git a/src/views/SessionRestoreDim.jsx b/src/views/SessionRestoreDim.jsx new file mode 100644 index 0000000..a9de394 --- /dev/null +++ b/src/views/SessionRestoreDim.jsx @@ -0,0 +1,64 @@ +import React from "react"; +import useAsyncEffect from "use-async-effect"; +import { useBoardPosition } from "react-sync-board"; + +import useSession from "../hooks/useSession"; + +import useLocalStorage from "../hooks/useLocalStorage"; + +// 150 days max for session dim +const MAX_SESSION_DIM_RETENTION = 1000 * 60 * 60 * 24 * 150; + +export const SessionRestoreDim = () => { + const { sessionLoaded, sessionId } = useSession(); + + const [sessionDimensions, setSessionDimensions] = useLocalStorage( + "sessionDimensions", + {} + ); + const { getDim, setDim } = useBoardPosition(); + + /** + * Load the previous dimension for this session if exists + */ + React.useEffect(() => { + if (sessionLoaded) { + if (sessionDimensions[sessionId]) { + setTimeout(() => { + const dim = { ...sessionDimensions[sessionId], timestamp: undefined }; + setDim(() => dim); + }, 500); + } + const now = Date.now(); + + const newDim = Object.fromEntries( + Object.entries(sessionDimensions).filter(([, { timestamp }]) => { + return timestamp && now - timestamp < MAX_SESSION_DIM_RETENTION; + }) + ); + setSessionDimensions(newDim); + } + // We want to set dimension only when session is loaded + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sessionLoaded]); + + /** + * Save board dimension in localstorage every 2 seconds for next visit + */ + useAsyncEffect((isMounted) => { + const interval = setInterval(async () => { + const currentDim = await getDim(); + if (isMounted) { + setSessionDimensions((prev) => ({ + ...prev, + [sessionId]: { ...currentDim, timestamp: Date.now() }, + })); + } + }, 2000); + return () => clearInterval(interval); + }, []); + + return null; +}; + +export default SessionRestoreDim;