diff --git a/src/components/map/Map.js b/src/components/map/Map.js index c0a4907..46e2609 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -26,9 +26,11 @@ function Map({ onMapChange, onMapStateChange, onMapDraw, + onMapDrawUndo, + onMapDrawRedo, onFogDraw, - onMapUndo, - onMapRedo, + onFogDrawUndo, + onFogDrawRedo, allowMapDrawing, allowFogDrawing, allowTokenChange, @@ -82,28 +84,40 @@ function Map({ timestamp: Date.now(), }); } + if (action === "mapUndo") { + onMapDrawUndo(); + } + if (action === "mapRedo") { + onMapDrawRedo(); + } + if (action === "fogUndo") { + onFogDrawUndo(); + } + if (action === "fogRedo") { + onFogDrawRedo(); + } } const [mapShapes, setMapShapes] = useState([]); function handleMapShapeAdd(shape) { - onMapDraw({ type: "add", shapes: [shape], timestamp: Date.now() }); + onMapDraw({ type: "add", shapes: [shape] }); } function handleMapShapeRemove(shapeId) { - onMapDraw({ type: "remove", shapeIds: [shapeId], timestamp: Date.now() }); + onMapDraw({ type: "remove", shapeIds: [shapeId] }); } const [fogShapes, setFogShapes] = useState([]); function handleFogShapeAdd(shape) { - onFogDraw({ type: "add", shapes: [shape], timestamp: Date.now() }); + onFogDraw({ type: "add", shapes: [shape] }); } function handleFogShapeRemove(shapeId) { - onFogDraw({ type: "remove", shapeIds: [shapeId], timestamp: Date.now() }); + onFogDraw({ type: "remove", shapeIds: [shapeId] }); } function handleFogShapeEdit(shape) { - onFogDraw({ type: "edit", shapes: [shape], timestamp: Date.now() }); + onFogDraw({ type: "edit", shapes: [shape] }); } // Replay the draw actions and convert them to shapes for the map drawing @@ -141,28 +155,38 @@ function Map({ disabledControls.push("shape"); disabledControls.push("erase"); } - // If no actions that can be undone - if (!allowFogDrawing && !allowMapDrawing) { - disabledControls.push("undo"); - disabledControls.push("redo"); - } if (!map) { disabledControls.push("pan"); } if (mapShapes.length === 0) { disabledControls.push("erase"); } - if (!mapState || mapState.mapDrawActionIndex < 0) { - disabledControls.push("undo"); - } if (!allowFogDrawing) { disabledControls.push("fog"); } + + const disabledSettings = { fog: [], brush: [], shape: [], erase: [] }; + if (!mapState || mapState.mapDrawActionIndex < 0) { + disabledSettings.brush.push("undo"); + disabledSettings.shape.push("undo"); + disabledSettings.erase.push("undo"); + } if ( !mapState || mapState.mapDrawActionIndex === mapState.mapDrawActions.length - 1 ) { - disabledControls.push("redo"); + disabledSettings.brush.push("redo"); + disabledSettings.shape.push("redo"); + disabledSettings.erase.push("redo"); + } + if (fogShapes.length === 0) { + disabledSettings.fog.push("undo"); + } + if ( + !mapState || + mapState.fogDrawActionIndex === mapState.fogDrawActions.length - 1 + ) { + disabledSettings.fog.push("redo"); } /** @@ -262,8 +286,7 @@ function Map({ onToolSettingChange={handleToolSettingChange} onToolAction={handleToolAction} disabledControls={disabledControls} - onUndo={onMapUndo} - onRedo={onMapRedo} + disabledSettings={disabledSettings} /> ); return ( diff --git a/src/components/map/MapControls.js b/src/components/map/MapControls.js index 2431054..7399798 100644 --- a/src/components/map/MapControls.js +++ b/src/components/map/MapControls.js @@ -16,8 +16,6 @@ import FogToolIcon from "../../icons/FogToolIcon"; import BrushToolIcon from "../../icons/BrushToolIcon"; import ShapeToolIcon from "../../icons/ShapeToolIcon"; import EraseToolIcon from "../../icons/EraseToolIcon"; -import UndoIcon from "../../icons/UndoIcon"; -import RedoIcon from "../../icons/RedoIcon"; import ExpandMoreIcon from "../../icons/ExpandMoreIcon"; function MapContols({ @@ -30,8 +28,7 @@ function MapContols({ onToolSettingChange, onToolAction, disabledControls, - onUndo, - onRedo, + disabledSettings, }) { const [isExpanded, setIsExpanded] = useState(false); @@ -93,25 +90,6 @@ function MapContols({ )), }, - { - id: "history", - component: ( - <> - - - - - - - - ), - }, ]; let controls = null; @@ -191,6 +169,7 @@ function MapContols({ onToolSettingChange(selectedToolId, change) } onToolAction={onToolAction} + disabledActions={disabledSettings[selectedToolId]} /> ); @@ -199,13 +178,6 @@ function MapContols({ } } - // Move back to pan tool if selected tool becomes disabled - useEffect(() => { - if (disabledControls.includes(selectedToolId)) { - onSelectedToolChange("pan"); - } - }, [disabledControls]); - // Stop map drawing from happening when selecting controls // Not using react events as they seem to trigger after dom events useEffect(() => { diff --git a/src/components/map/controls/BrushToolSettings.js b/src/components/map/controls/BrushToolSettings.js index b503972..df24bb1 100644 --- a/src/components/map/controls/BrushToolSettings.js +++ b/src/components/map/controls/BrushToolSettings.js @@ -8,9 +8,17 @@ import RadioIconButton from "./RadioIconButton"; import BrushStrokeIcon from "../../../icons/BrushStrokeIcon"; import BrushFillIcon from "../../../icons/BrushFillIcon"; +import UndoButton from "./UndoButton"; +import RedoButton from "./RedoButton"; + import Divider from "./Divider"; -function BrushToolSettings({ settings, onSettingChange }) { +function BrushToolSettings({ + settings, + onSettingChange, + onToolAction, + disabledActions, +}) { return ( onSettingChange({ useBlending })} /> + + onToolAction("mapUndo")} + disabled={disabledActions.includes("undo")} + /> + onToolAction("mapRedo")} + disabled={disabledActions.includes("redo")} + /> ); } diff --git a/src/components/map/controls/EraseToolSettings.js b/src/components/map/controls/EraseToolSettings.js index 01a2aeb..0660570 100644 --- a/src/components/map/controls/EraseToolSettings.js +++ b/src/components/map/controls/EraseToolSettings.js @@ -3,7 +3,12 @@ import { Flex, IconButton } from "theme-ui"; import EraseAllIcon from "../../../icons/EraseAllIcon"; -function EraseToolSettings({ onToolAction }) { +import UndoButton from "./UndoButton"; +import RedoButton from "./RedoButton"; + +import Divider from "./Divider"; + +function EraseToolSettings({ onToolAction, disabledActions }) { return ( + + onToolAction("mapUndo")} + disabled={disabledActions.includes("undo")} + /> + onToolAction("mapRedo")} + disabled={disabledActions.includes("redo")} + /> ); } diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index 43cba40..04183d3 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -9,9 +9,17 @@ import FogAddIcon from "../../../icons/FogAddIcon"; import FogRemoveIcon from "../../../icons/FogRemoveIcon"; import FogToggleIcon from "../../../icons/FogToggleIcon"; +import UndoButton from "./UndoButton"; +import RedoButton from "./RedoButton"; + import Divider from "./Divider"; -function BrushToolSettings({ settings, onSettingChange }) { +function BrushToolSettings({ + settings, + onSettingChange, + onToolAction, + disabledActions, +}) { return ( + + onToolAction("fogUndo")} + disabled={disabledActions.includes("undo")} + /> + onToolAction("fogRedo")} + disabled={disabledActions.includes("redo")} + /> ); } diff --git a/src/components/map/controls/RedoButton.js b/src/components/map/controls/RedoButton.js new file mode 100644 index 0000000..e233143 --- /dev/null +++ b/src/components/map/controls/RedoButton.js @@ -0,0 +1,14 @@ +import React from "react"; +import { IconButton } from "theme-ui"; + +import RedoIcon from "../../../icons/RedoIcon"; + +function RedoButton({ onClick, disabled }) { + return ( + + + + ); +} + +export default RedoButton; diff --git a/src/components/map/controls/ShapeToolSettings.js b/src/components/map/controls/ShapeToolSettings.js index b332ca9..e4a8328 100644 --- a/src/components/map/controls/ShapeToolSettings.js +++ b/src/components/map/controls/ShapeToolSettings.js @@ -9,9 +9,17 @@ import ShapeRectangleIcon from "../../../icons/ShapeRectangleIcon"; import ShapeCircleIcon from "../../../icons/ShapeCircleIcon"; import ShapeTriangleIcon from "../../../icons/ShapeTriangleIcon"; +import UndoButton from "./UndoButton"; +import RedoButton from "./RedoButton"; + import Divider from "./Divider"; -function ShapeToolSettings({ settings, onSettingChange }) { +function ShapeToolSettings({ + settings, + onSettingChange, + onToolAction, + disabledActions, +}) { return ( onSettingChange({ useBlending })} /> + + onToolAction("mapUndo")} + disabled={disabledActions.includes("undo")} + /> + onToolAction("mapRedo")} + disabled={disabledActions.includes("redo")} + /> ); } diff --git a/src/components/map/controls/UndoButton.js b/src/components/map/controls/UndoButton.js new file mode 100644 index 0000000..88d0d26 --- /dev/null +++ b/src/components/map/controls/UndoButton.js @@ -0,0 +1,14 @@ +import React from "react"; +import { IconButton } from "theme-ui"; + +import UndoIcon from "../../../icons/UndoIcon"; + +function UndoButton({ onClick, disabled }) { + return ( + + + + ); +} + +export default UndoButton; diff --git a/src/routes/Game.js b/src/routes/Game.js index 4691e19..e2ee57e 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -118,82 +118,92 @@ function Game() { } } - function addNewMapDrawActions(actions) { + function addMapDrawActions(actions, indexKey, actionsKey) { setMapState((prevMapState) => { const newActions = [ - ...prevMapState.mapDrawActions.slice( - 0, - prevMapState.mapDrawActionIndex + 1 - ), + ...prevMapState[actionsKey].slice(0, prevMapState[indexKey] + 1), ...actions, ]; const newIndex = newActions.length - 1; return { ...prevMapState, - mapDrawActions: newActions, - mapDrawActionIndex: newIndex, + [actionsKey]: newActions, + [indexKey]: newIndex, }; }); } + function updateDrawActionIndex(change, indexKey, actionsKey, peerId) { + const newIndex = Math.min( + Math.max(mapState[indexKey] + change, -1), + mapState[actionsKey].length - 1 + ); + + setMapState((prevMapState) => ({ + ...prevMapState, + [indexKey]: newIndex, + })); + return newIndex; + } + function handleMapDraw(action) { - addNewMapDrawActions([action]); + addMapDrawActions([action], "mapDrawActionIndex", "mapDrawActions"); for (let peer of Object.values(peers)) { peer.connection.send({ id: "mapDraw", data: [action] }); } } - function handleMapUndo() { - // TODO: Check whether to pull from draw actions or fog actions - const newIndex = Math.max(mapState.mapDrawActionIndex - 1, -1); - setMapState((prevMapState) => ({ - ...prevMapState, - mapDrawActionIndex: newIndex, - })); - for (let peer of Object.values(peers)) { - peer.connection.send({ id: "mapDrawIndex", data: newIndex }); - } - } - - function handleMapRedo() { - const newIndex = Math.min( - mapState.mapDrawActionIndex + 1, - mapState.mapDrawActions.length - 1 + function handleMapDrawUndo() { + const index = updateDrawActionIndex( + -1, + "mapDrawActionIndex", + "mapDrawActions" ); - setMapState((prevMapState) => ({ - ...prevMapState, - mapDrawActionIndex: newIndex, - })); for (let peer of Object.values(peers)) { - peer.connection.send({ id: "mapDrawIndex", data: newIndex }); + peer.connection.send({ id: "mapDrawIndex", data: index }); } } - function addNewFogDrawActions(actions) { - setMapState((prevMapState) => { - const newActions = [ - ...prevMapState.fogDrawActions.slice( - 0, - prevMapState.fogDrawActionIndex + 1 - ), - ...actions, - ]; - const newIndex = newActions.length - 1; - return { - ...prevMapState, - fogDrawActions: newActions, - fogDrawActionIndex: newIndex, - }; - }); + function handleMapDrawRedo() { + const index = updateDrawActionIndex( + 1, + "mapDrawActionIndex", + "mapDrawActions" + ); + for (let peer of Object.values(peers)) { + peer.connection.send({ id: "mapDrawIndex", data: index }); + } } function handleFogDraw(action) { - addNewFogDrawActions([action]); + addMapDrawActions([action], "fogDrawActionIndex", "fogDrawActions"); for (let peer of Object.values(peers)) { peer.connection.send({ id: "mapFog", data: [action] }); } } + function handleFogDrawUndo() { + const index = updateDrawActionIndex( + -1, + "fogDrawActionIndex", + "fogDrawActions" + ); + for (let peer of Object.values(peers)) { + peer.connection.send({ id: "fogDrawIndex", data: index }); + } + } + + function handleFogDrawRedo() { + const index = updateDrawActionIndex( + 1, + "fogDrawActionIndex", + "fogDrawActions" + ); + for (let peer of Object.values(peers)) { + peer.connection.send({ id: "fogDrawIndex", data: index }); + } + } + /** * Party state */ @@ -261,7 +271,7 @@ function Game() { })); } if (data.id === "mapDraw") { - addNewMapDrawActions(data.data); + addMapDrawActions(data.data, "mapDrawActionIndex", "mapDrawActions"); } if (data.id === "mapDrawIndex") { setMapState((prevMapState) => ({ @@ -270,7 +280,7 @@ function Game() { })); } if (data.id === "mapFog") { - addNewFogDrawActions(data.data); + addMapDrawActions(data.data, "fogDrawActionIndex", "fogDrawActions"); } if (data.id === "mapFogIndex") { setMapState((prevMapState) => ({ @@ -412,9 +422,11 @@ function Game() { onMapChange={handleMapChange} onMapStateChange={handleMapStateChange} onMapDraw={handleMapDraw} - onMapUndo={handleMapUndo} - onMapRedo={handleMapRedo} + onMapDrawUndo={handleMapDrawUndo} + onMapDrawRedo={handleMapDrawRedo} onFogDraw={handleFogDraw} + onFogDrawUndo={handleFogDrawUndo} + onFogDrawRedo={handleFogDrawRedo} allowMapDrawing={canEditMapDrawing} allowFogDrawing={canEditFogDrawing} allowTokenChange={canEditTokens}