Added map state to the database
This commit is contained in:
parent
071cd3ea7f
commit
25b215d4e4
@ -13,10 +13,8 @@ function AddMapButton({ onMapChange }) {
|
||||
setIsAddModalOpen(false);
|
||||
}
|
||||
|
||||
function handleDone(map) {
|
||||
if (map) {
|
||||
onMapChange(map);
|
||||
}
|
||||
function handleDone(map, mapState) {
|
||||
onMapChange(map, mapState);
|
||||
closeModal();
|
||||
}
|
||||
|
||||
|
@ -18,15 +18,13 @@ const maxZoom = 5;
|
||||
|
||||
function Map({
|
||||
map,
|
||||
tokens,
|
||||
mapState,
|
||||
onMapTokenChange,
|
||||
onMapTokenRemove,
|
||||
onMapChange,
|
||||
onMapDraw,
|
||||
onMapDrawUndo,
|
||||
onMapDrawRedo,
|
||||
drawActions,
|
||||
drawActionIndex,
|
||||
}) {
|
||||
function handleProxyDragEnd(isOnMap, token) {
|
||||
if (isOnMap && onMapTokenChange) {
|
||||
@ -63,9 +61,12 @@ function Map({
|
||||
|
||||
// Replay the draw actions and convert them to shapes for the map drawing
|
||||
useEffect(() => {
|
||||
if (!mapState) {
|
||||
return;
|
||||
}
|
||||
let shapesById = {};
|
||||
for (let i = 0; i <= drawActionIndex; i++) {
|
||||
const action = drawActions[i];
|
||||
for (let i = 0; i <= mapState.drawActionIndex; i++) {
|
||||
const action = mapState.drawActions[i];
|
||||
if (action.type === "add") {
|
||||
for (let shape of action.shapes) {
|
||||
shapesById[shape.id] = shape;
|
||||
@ -76,7 +77,7 @@ function Map({
|
||||
}
|
||||
}
|
||||
setDrawnShapes(Object.values(shapesById));
|
||||
}, [drawActions, drawActionIndex]);
|
||||
}, [mapState]);
|
||||
|
||||
const disabledTools = [];
|
||||
if (!map) {
|
||||
@ -233,14 +234,15 @@ function Map({
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
{Object.values(tokens).map((token) => (
|
||||
<MapToken
|
||||
key={token.id}
|
||||
token={token}
|
||||
tokenSizePercent={tokenSizePercent}
|
||||
className={`${mapTokenProxyClassName} ${mapTokenMenuClassName}`}
|
||||
/>
|
||||
))}
|
||||
{mapState &&
|
||||
Object.values(mapState.tokens).map((token) => (
|
||||
<MapToken
|
||||
key={token.id}
|
||||
token={token}
|
||||
tokenSizePercent={tokenSizePercent}
|
||||
className={`${mapTokenProxyClassName} ${mapTokenMenuClassName}`}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@ -299,8 +301,11 @@ function Map({
|
||||
disabledTools={disabledTools}
|
||||
onUndo={onMapDrawUndo}
|
||||
onRedo={onMapDrawRedo}
|
||||
undoDisabled={drawActionIndex < 0}
|
||||
redoDisabled={drawActionIndex === drawActions.length - 1}
|
||||
undoDisabled={!mapState || mapState.drawActionIndex < 0}
|
||||
redoDisabled={
|
||||
!mapState ||
|
||||
mapState.drawActionIndex === mapState.drawActions.length - 1
|
||||
}
|
||||
brushColor={brushColor}
|
||||
onBrushColorChange={setBrushColor}
|
||||
onEraseAll={handleShapeRemoveAll}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Dexie from "dexie";
|
||||
|
||||
const db = new Dexie("OwlbearRodeoDB");
|
||||
db.version(1).stores({ maps: "id" });
|
||||
db.version(1).stores({ maps: "id", states: "mapId" });
|
||||
|
||||
export default db;
|
||||
|
18
src/helpers/useDebounce.js
Normal file
18
src/helpers/useDebounce.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function useDebounce(value, delay) {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [value, delay]);
|
||||
|
||||
return debouncedValue;
|
||||
}
|
||||
|
||||
export default useDebounce;
|
@ -10,6 +10,13 @@ import MapSelect from "../components/map/MapSelect";
|
||||
import * as defaultMaps from "../maps";
|
||||
|
||||
const defaultMapSize = 22;
|
||||
const defaultMapState = {
|
||||
tokens: {},
|
||||
// An index into the draw actions array to which only actions before the
|
||||
// index will be performed (used in undo and redo)
|
||||
drawActionIndex: -1,
|
||||
drawActions: [],
|
||||
};
|
||||
|
||||
function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
const [imageLoading, setImageLoading] = useState(false);
|
||||
@ -81,6 +88,7 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
|
||||
async function handleMapAdd(map) {
|
||||
await db.table("maps").add(map);
|
||||
await db.table("states").add({ ...defaultMapState, mapId: map.id });
|
||||
setMaps((prevMaps) => [map, ...prevMaps]);
|
||||
setCurrentMapId(map.id);
|
||||
setGridX(map.gridX);
|
||||
@ -89,6 +97,7 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
|
||||
async function handleMapRemove(id) {
|
||||
await db.table("maps").delete(id);
|
||||
await db.table("states").delete(id);
|
||||
setMaps((prevMaps) => {
|
||||
const filtered = prevMaps.filter((map) => map.id !== id);
|
||||
setCurrentMapId(filtered[0].id);
|
||||
@ -102,9 +111,14 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
setGridY(map.gridY);
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
onDone(maps.find((map) => map.id === currentMapId));
|
||||
const currentMap = maps.find((map) => map.id === currentMapId);
|
||||
if (currentMap) {
|
||||
let currentMapState =
|
||||
(await db.table("states").get(currentMap.id)) || defaultMapState;
|
||||
onDone(currentMap, currentMapState);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGridXChange(e) {
|
||||
|
@ -2,9 +2,12 @@ import React, { useState, useEffect, useCallback, useContext } from "react";
|
||||
import { Flex, Box, Text, Link } from "theme-ui";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import db from "../database";
|
||||
|
||||
import { omit, isStreamStopped } from "../helpers/shared";
|
||||
import useSession from "../helpers/useSession";
|
||||
import useNickname from "../helpers/useNickname";
|
||||
import useDebounce from "../helpers/useDebounce";
|
||||
|
||||
import Party from "../components/party/Party";
|
||||
import Tokens from "../components/token/Tokens";
|
||||
@ -35,23 +38,32 @@ function Game() {
|
||||
*/
|
||||
|
||||
const [map, setMap] = useState(null);
|
||||
const [mapState, setMapState] = useState(null);
|
||||
|
||||
function handleMapChange(newMap) {
|
||||
// Sync the map state to the database after 500ms of inactivity
|
||||
const debouncedMapState = useDebounce(mapState, 500);
|
||||
useEffect(() => {
|
||||
if (debouncedMapState && debouncedMapState.mapId) {
|
||||
db.table("states").update(debouncedMapState.mapId, debouncedMapState);
|
||||
}
|
||||
}, [debouncedMapState]);
|
||||
|
||||
function handleMapChange(newMap, newMapState) {
|
||||
setMap(newMap);
|
||||
setMapState(newMapState);
|
||||
for (let peer of Object.values(peers)) {
|
||||
peer.connection.send({ id: "mapState", data: newMapState });
|
||||
peer.connection.send({ id: "map", data: newMap });
|
||||
}
|
||||
}
|
||||
|
||||
const [mapTokens, setMapTokens] = useState({});
|
||||
|
||||
function handleMapTokenChange(token) {
|
||||
if (!map.source) {
|
||||
return;
|
||||
}
|
||||
setMapTokens((prevMapTokens) => ({
|
||||
...prevMapTokens,
|
||||
[token.id]: token,
|
||||
async function handleMapTokenChange(token) {
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
tokens: {
|
||||
...prevMapState.tokens,
|
||||
[token.id]: token,
|
||||
},
|
||||
}));
|
||||
for (let peer of Object.values(peers)) {
|
||||
const data = { [token.id]: token };
|
||||
@ -60,9 +72,9 @@ function Game() {
|
||||
}
|
||||
|
||||
function handleMapTokenRemove(token) {
|
||||
setMapTokens((prevMapTokens) => {
|
||||
const { [token.id]: old, ...rest } = prevMapTokens;
|
||||
return rest;
|
||||
setMapState((prevMapState) => {
|
||||
const { [token.id]: old, ...rest } = prevMapState.tokens;
|
||||
return { ...prevMapState, tokens: rest };
|
||||
});
|
||||
for (let peer of Object.values(peers)) {
|
||||
const data = { [token.id]: token };
|
||||
@ -70,19 +82,18 @@ function Game() {
|
||||
}
|
||||
}
|
||||
|
||||
const [mapDrawActions, setMapDrawActions] = useState([]);
|
||||
// An index into the draw actions array to which only actions before the
|
||||
// index will be performed (used in undo and redo)
|
||||
const [mapDrawActionIndex, setMapDrawActionIndex] = useState(-1);
|
||||
function addNewMapDrawActions(actions) {
|
||||
setMapDrawActions((prevActions) => {
|
||||
setMapState((prevMapState) => {
|
||||
const newActions = [
|
||||
...prevActions.slice(0, mapDrawActionIndex + 1),
|
||||
...prevMapState.drawActions.slice(0, prevMapState.drawActionIndex + 1),
|
||||
...actions,
|
||||
];
|
||||
const newIndex = newActions.length - 1;
|
||||
setMapDrawActionIndex(newIndex);
|
||||
return newActions;
|
||||
return {
|
||||
...prevMapState,
|
||||
drawActions: newActions,
|
||||
drawActionIndex: newIndex,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,8 +105,11 @@ function Game() {
|
||||
}
|
||||
|
||||
function handleMapDrawUndo() {
|
||||
const newIndex = Math.max(mapDrawActionIndex - 1, -1);
|
||||
setMapDrawActionIndex(newIndex);
|
||||
const newIndex = Math.max(mapState.drawActionIndex - 1, -1);
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
drawActionIndex: newIndex,
|
||||
}));
|
||||
for (let peer of Object.values(peers)) {
|
||||
peer.connection.send({ id: "mapDrawIndex", data: newIndex });
|
||||
}
|
||||
@ -103,10 +117,13 @@ function Game() {
|
||||
|
||||
function handleMapDrawRedo() {
|
||||
const newIndex = Math.min(
|
||||
mapDrawActionIndex + 1,
|
||||
mapDrawActions.length - 1
|
||||
mapState.drawActionIndex + 1,
|
||||
mapState.drawActions.length - 1
|
||||
);
|
||||
setMapDrawActionIndex(newIndex);
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
drawActionIndex: newIndex,
|
||||
}));
|
||||
for (let peer of Object.values(peers)) {
|
||||
peer.connection.send({ id: "mapDrawIndex", data: newIndex });
|
||||
}
|
||||
@ -145,14 +162,8 @@ function Game() {
|
||||
if (map) {
|
||||
peer.connection.send({ id: "map", data: map });
|
||||
}
|
||||
if (mapTokens) {
|
||||
peer.connection.send({ id: "tokenEdit", data: mapTokens });
|
||||
}
|
||||
if (mapDrawActions) {
|
||||
peer.connection.send({ id: "mapDraw", data: mapDrawActions });
|
||||
}
|
||||
if (mapDrawActionIndex !== mapDrawActions.length - 1) {
|
||||
peer.connection.send({ id: "mapDrawIndex", data: mapDrawActionIndex });
|
||||
if (mapState) {
|
||||
peer.connection.send({ id: "mapState", data: mapState });
|
||||
}
|
||||
}
|
||||
if (data.id === "map") {
|
||||
@ -166,16 +177,20 @@ function Game() {
|
||||
setMap(data.data);
|
||||
}
|
||||
}
|
||||
if (data.id === "mapState") {
|
||||
setMapState(data.data);
|
||||
}
|
||||
if (data.id === "tokenEdit") {
|
||||
setMapTokens((prevMapTokens) => ({
|
||||
...prevMapTokens,
|
||||
...data.data,
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
tokens: { ...prevMapState.tokens, ...data.data },
|
||||
}));
|
||||
}
|
||||
if (data.id === "tokenRemove") {
|
||||
setMapTokens((prevMapTokens) =>
|
||||
omit(prevMapTokens, Object.keys(data.data))
|
||||
);
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
tokens: omit(prevMapState.tokens, Object.keys(data.data)),
|
||||
}));
|
||||
}
|
||||
if (data.id === "nickname") {
|
||||
setPartyNicknames((prevNicknames) => ({
|
||||
@ -187,7 +202,10 @@ function Game() {
|
||||
addNewMapDrawActions(data.data);
|
||||
}
|
||||
if (data.id === "mapDrawIndex") {
|
||||
setMapDrawActionIndex(data.data);
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
drawActionIndex: data.data,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,15 +319,13 @@ function Game() {
|
||||
/>
|
||||
<Map
|
||||
map={map}
|
||||
tokens={mapTokens}
|
||||
mapState={mapState}
|
||||
onMapTokenChange={handleMapTokenChange}
|
||||
onMapTokenRemove={handleMapTokenRemove}
|
||||
onMapChange={handleMapChange}
|
||||
onMapDraw={handleMapDraw}
|
||||
onMapDrawUndo={handleMapDrawUndo}
|
||||
onMapDrawRedo={handleMapDrawRedo}
|
||||
drawActions={mapDrawActions}
|
||||
drawActionIndex={mapDrawActionIndex}
|
||||
/>
|
||||
<Tokens onCreateMapToken={handleMapTokenChange} />
|
||||
</Flex>
|
||||
|
Loading…
Reference in New Issue
Block a user