From f20173de353dd3e3a4a0da4e760d1742c6cae4ab Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sat, 6 Feb 2021 13:32:38 +1100 Subject: [PATCH] Added shared grid context and moved away from calling useContext directly --- src/components/Grid.js | 79 +++++++---------- src/components/dice/DiceTrayOverlay.js | 14 +-- src/components/map/Map.js | 25 ++---- src/components/map/MapDrawing.js | 43 ++++------ src/components/map/MapEditor.js | 9 +- src/components/map/MapFog.js | 49 +++++------ src/components/map/MapGrid.js | 14 +-- src/components/map/MapGridEditor.js | 9 +- src/components/map/MapInteraction.js | 33 ++++--- src/components/map/MapLoadingOverlay.js | 6 +- src/components/map/MapMeasure.js | 42 +++++---- src/components/map/MapNotes.js | 21 ++--- src/components/map/MapPointer.js | 18 ++-- src/components/map/MapTiles.js | 6 +- src/components/map/MapToken.js | 17 ++-- src/components/map/SelectMapButton.js | 10 +-- .../map/controls/DrawingToolSettings.js | 2 +- .../map/controls/FogToolSettings.js | 2 +- .../map/controls/MeasureToolSettings.js | 2 +- src/components/note/Note.js | 16 ++-- src/components/note/NoteMenu.js | 6 +- src/components/party/Party.js | 15 ++-- src/components/token/ProxyToken.js | 6 +- src/components/token/TokenDragOverlay.js | 10 +-- src/components/token/TokenMenu.js | 6 +- src/components/token/TokenPreview.js | 8 +- src/components/token/TokenTiles.js | 6 +- src/components/token/Tokens.js | 10 +-- src/contexts/AuthContext.js | 12 ++- src/contexts/DatabaseContext.js | 10 ++- src/contexts/DiceLoadingContext.js | 10 ++- src/contexts/GridContext.js | 85 +++++++++++++++++++ src/contexts/KeyboardContext.js | 31 ++++++- src/contexts/MapDataContext.js | 16 +++- src/contexts/MapInteractionContext.js | 12 ++- src/contexts/MapLoadingContext.js | 10 ++- src/contexts/MapStageContext.js | 10 ++- src/contexts/PartyContext.js | 10 ++- src/contexts/PlayerContext.js | 24 +++++- src/contexts/SettingsContext.js | 10 ++- src/contexts/TokenDataContext.js | 16 +++- src/helpers/Size.js | 65 ++++++++++++++ src/helpers/Vector2.js | 11 ++- src/helpers/drawing.js | 52 ++++++++---- src/helpers/grid.js | 82 +++++++++--------- src/hooks/useKeyboard.js | 30 ------- src/hooks/useSetting.js | 6 +- src/modals/AuthModal.js | 8 +- src/modals/EditMapModal.js | 6 +- src/modals/EditTokenModal.js | 6 +- src/modals/ImportExportModal.js | 6 +- src/modals/SelectMapModal.js | 21 +++-- src/modals/SelectTokensModal.js | 12 +-- src/modals/SettingsModal.js | 10 +-- src/modals/StartModal.js | 6 +- src/network/NetworkedMapAndTokens.js | 30 +++---- src/network/NetworkedMapPointer.js | 9 +- src/network/NetworkedParty.js | 12 +-- src/routes/Game.js | 14 ++- src/routes/Home.js | 6 +- 60 files changed, 672 insertions(+), 460 deletions(-) create mode 100644 src/contexts/GridContext.js create mode 100644 src/helpers/Size.js delete mode 100644 src/hooks/useKeyboard.js diff --git a/src/components/Grid.js b/src/components/Grid.js index 9dda566..9eded3d 100644 --- a/src/components/Grid.js +++ b/src/components/Grid.js @@ -1,50 +1,43 @@ import React from "react"; import { Line, Group, RegularPolygon } from "react-konva"; -import { getStrokeWidth } from "../helpers/drawing"; import { - getCellSize, getCellLocation, gridClipFunction, shouldClipCell, } from "../helpers/grid"; -function Grid({ grid, strokeWidth, width, height, stroke }) { +import { useGrid } from "../contexts/GridContext"; + +function Grid({ strokeWidth, stroke }) { + const { + grid, + gridStrokeWidth, + gridPixelSize, + gridOffset, + gridCellPixelSize, + } = useGrid(); + if (!grid?.size.x || !grid?.size.y) { return null; } - const gridSizeNormalized = { - x: (grid.inset.bottomRight.x - grid.inset.topLeft.x) / grid.size.x, - y: (grid.inset.bottomRight.y - grid.inset.topLeft.y) / grid.size.y, - }; - - const insetWidth = (grid.inset.bottomRight.x - grid.inset.topLeft.x) * width; - const insetHeight = - (grid.inset.bottomRight.y - grid.inset.topLeft.y) * height; - - const offsetX = grid.inset.topLeft.x * width * -1; - const offsetY = grid.inset.topLeft.y * height * -1; - - const cellSize = getCellSize(grid, insetWidth, insetHeight); - const shapes = []; if (grid.type === "square") { for (let x = 1; x < grid.size.x; x++) { shapes.push( ); } @@ -52,17 +45,16 @@ function Grid({ grid, strokeWidth, width, height, stroke }) { shapes.push( ); } @@ -70,29 +62,24 @@ function Grid({ grid, strokeWidth, width, height, stroke }) { // Start at -1 to overshoot the bounds of the grid to ensure all lines are drawn for (let x = -1; x < grid.size.x; x++) { for (let y = -1; y < grid.size.y; y++) { - const cellLocation = getCellLocation(grid, x, y, cellSize); + const cellLocation = getCellLocation(grid, x, y, gridCellPixelSize); shapes.push( gridClipFunction(context, grid, x, y, cellSize)) + ((context) => + gridClipFunction(context, grid, x, y, gridCellPixelSize)) } x={cellLocation.x} y={cellLocation.y} - offsetX={offsetX} - offsetY={offsetY} + offset={gridOffset} > diff --git a/src/components/dice/DiceTrayOverlay.js b/src/components/dice/DiceTrayOverlay.js index ce78dce..62028a3 100644 --- a/src/components/dice/DiceTrayOverlay.js +++ b/src/components/dice/DiceTrayOverlay.js @@ -1,10 +1,4 @@ -import React, { - useRef, - useCallback, - useEffect, - useContext, - useState, -} from "react"; +import React, { useRef, useCallback, useEffect, useState } from "react"; import { Vector3 } from "@babylonjs/core/Maths/math"; import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator"; @@ -21,7 +15,7 @@ import DiceResults from "./DiceResults"; import DiceTray from "../../dice/diceTray/DiceTray"; -import DiceLoadingContext from "../../contexts/DiceLoadingContext"; +import { useDiceLoading } from "../../contexts/DiceLoadingContext"; import { getDiceRoll } from "../../helpers/dice"; import useSetting from "../../hooks/useSetting"; @@ -43,9 +37,7 @@ function DiceTrayOverlay({ const diceTrayRef = useRef(); const [diceTraySize, setDiceTraySize] = useState("single"); - const { assetLoadStart, assetLoadFinish, isLoading } = useContext( - DiceLoadingContext - ); + const { assetLoadStart, assetLoadFinish, isLoading } = useDiceLoading(); const [fullScreen] = useSetting("map.fullScreen"); function handleAssetLoadStart() { diff --git a/src/components/map/Map.js b/src/components/map/Map.js index edae871..6c06d5d 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -1,4 +1,4 @@ -import React, { useState, useContext } from "react"; +import React, { useState } from "react"; import { Group } from "react-konva"; import MapControls from "./MapControls"; @@ -11,8 +11,8 @@ import MapMeasure from "./MapMeasure"; import NetworkedMapPointer from "../../network/NetworkedMapPointer"; import MapNotes from "./MapNotes"; -import TokenDataContext from "../../contexts/TokenDataContext"; -import SettingsContext from "../../contexts/SettingsContext"; +import { useTokenData } from "../../contexts/TokenDataContext"; +import { useSettings } from "../../contexts/SettingsContext"; import TokenMenu from "../token/TokenMenu"; import TokenDragOverlay from "../token/TokenDragOverlay"; @@ -49,19 +49,10 @@ function Map({ disabledTokens, session, }) { - const { tokensById } = useContext(TokenDataContext); - - const gridX = map && map.grid.size.x; - const gridY = map && map.grid.size.y; - const inset = map && map.grid.inset; - const gridSizeNormalized = { - x: gridX ? (inset.bottomRight.x - inset.topLeft.x) / gridX : 0, - y: gridY ? (inset.bottomRight.y - inset.topLeft.y) / gridY : 0, - }; - const tokenSizePercent = gridSizeNormalized.x; + const { tokensById } = useTokenData(); const [selectedToolId, setSelectedToolId] = useState("pan"); - const { settings, setSettings } = useContext(SettingsContext); + const { settings, setSettings } = useSettings(); function handleToolSettingChange(tool, change) { setSettings((prevSettings) => ({ @@ -243,7 +234,6 @@ function Map({ key={tokenState.id} token={tokensById[tokenState.tokenId]} tokenState={tokenState} - tokenSizePercent={tokenSizePercent} onTokenStateChange={onMapTokenStateChange} onTokenMenuOpen={handleTokenMenuOpen} onTokenDragStart={(e) => @@ -306,7 +296,6 @@ function Map({ onShapesRemove={handleMapShapesRemove} active={selectedToolId === "drawing"} toolSettings={settings.drawing} - gridSize={gridSizeNormalized} /> ); @@ -320,7 +309,6 @@ function Map({ onShapesEdit={handleFogShapesEdit} active={selectedToolId === "fog"} toolSettings={settings.fog} - gridSize={gridSizeNormalized} editable={allowFogDrawing && !settings.fog.preview} /> ); @@ -331,7 +319,6 @@ function Map({ ); @@ -339,7 +326,6 @@ function Map({ const mapPointer = ( ); @@ -377,7 +363,6 @@ function Map({ ); @@ -239,12 +237,7 @@ function MapDrawing({ (acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight], [] )} - strokeWidth={getStrokeWidth( - shape.strokeWidth, - gridSize, - mapWidth, - mapHeight - )} + strokeWidth={gridStrokeWidth * shape.strokeWidth} stroke={colors[shape.color] || shape.color} lineCap="round" {...defaultProps} diff --git a/src/components/map/MapEditor.js b/src/components/map/MapEditor.js index 5744a78..134d55f 100644 --- a/src/components/map/MapEditor.js +++ b/src/components/map/MapEditor.js @@ -13,6 +13,7 @@ import { getGridDefaultInset, getGridMaxZoom } from "../../helpers/grid"; import { MapInteractionProvider } from "../../contexts/MapInteractionContext"; import KeyboardContext from "../../contexts/KeyboardContext"; +import { GridProvider } from "../../contexts/GridContext"; import ResetMapIcon from "../../icons/ResetMapIcon"; import GridOnIcon from "../../icons/GridOnIcon"; @@ -130,10 +131,14 @@ function MapEditor({ map, onSettingsChange }) { {showGridControls && canEditGrid && ( - <> + - + )} diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index ae7209b..c66d7c7 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -1,30 +1,24 @@ -import React, { - useContext, - useState, - useEffect, - useCallback, - useRef, -} from "react"; +import React, { useState, useEffect, useCallback, useRef } from "react"; import shortid from "shortid"; import { Group, Rect } from "react-konva"; import useImage from "use-image"; import diagonalPattern from "../../images/DiagonalPattern.png"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; -import MapStageContext from "../../contexts/MapStageContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useMapStage } from "../../contexts/MapStageContext"; +import { useGrid } from "../../contexts/GridContext"; +import { useKeyboard } from "../../contexts/KeyboardContext"; import Vector2 from "../../helpers/Vector2"; import { getFogBrushPosition, simplifyPoints, - getStrokeWidth, mergeShapes, } from "../../helpers/drawing"; import colors from "../../helpers/colors"; import { HoleyLine, Tick } from "../../helpers/konva"; -import useKeyboard from "../../hooks/useKeyboard"; import useDebounce from "../../hooks/useDebounce"; function MapFog({ @@ -36,13 +30,17 @@ function MapFog({ onShapesEdit, active, toolSettings, - gridSize, editable, }) { - const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext( - MapInteractionContext - ); - const mapStageRef = useContext(MapStageContext); + const { + stageScale, + mapWidth, + mapHeight, + interactionEmitter, + } = useMapInteraction(); + const { gridCellNormalizedSize, gridStrokeWidth } = useGrid(); + const mapStageRef = useMapStage(); + const [drawingShape, setDrawingShape] = useState(null); const [isBrushDown, setIsBrushDown] = useState(false); const [editingShapes, setEditingShapes] = useState([]); @@ -70,7 +68,7 @@ function MapFog({ map, mapStage, useGridSnapping, - gridSize, + gridCellNormalizedSize, toolSettings.useEdgeSnapping, shapes ); @@ -114,7 +112,7 @@ function MapFog({ map, mapStage, useGridSnapping, - gridSize, + gridCellNormalizedSize, toolSettings.useEdgeSnapping, shapes ); @@ -144,7 +142,7 @@ function MapFog({ map, mapStage, useGridSnapping, - gridSize, + gridCellNormalizedSize, toolSettings.useEdgeSnapping, shapes, prevPoints @@ -185,7 +183,7 @@ function MapFog({ ...drawingShape.data, points: simplifyPoints( drawingShape.data.points, - gridSize, + gridCellNormalizedSize, // Downscale fog as smoothing doesn't currently work with edge snapping stageScale / 2 ), @@ -211,7 +209,7 @@ function MapFog({ map, mapStage, useGridSnapping, - gridSize, + gridCellNormalizedSize, toolSettings.useEdgeSnapping, shapes ); @@ -247,7 +245,7 @@ function MapFog({ map, mapStage, useGridSnapping, - gridSize, + gridCellNormalizedSize, toolSettings.useEdgeSnapping, shapes ); @@ -378,12 +376,7 @@ function MapFog({ closed lineCap="round" lineJoin="round" - strokeWidth={getStrokeWidth( - shape.strokeWidth, - gridSize, - mapWidth, - mapHeight - )} + strokeWidth={gridStrokeWidth * shape.strokeWidth} opacity={editable ? 0.5 : 1} fillPatternImage={patternImage} fillPriority={active && !shape.visible ? "pattern" : "color"} diff --git a/src/components/map/MapGrid.js b/src/components/map/MapGrid.js index 4e7a2b1..119eb6e 100644 --- a/src/components/map/MapGrid.js +++ b/src/components/map/MapGrid.js @@ -1,8 +1,6 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import useImage from "use-image"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; - import useDataSource from "../../hooks/useDataSource"; import { mapSources as defaultMapSources } from "../../maps"; @@ -11,8 +9,6 @@ import { getImageLightness } from "../../helpers/image"; import Grid from "../Grid"; function MapGrid({ map, strokeWidth }) { - const { mapWidth, mapHeight } = useContext(MapInteractionContext); - let mapSourceMap = map; // Use lowest resolution for grid lightness if (map && map.type === "file" && map.resolutions) { @@ -34,13 +30,7 @@ function MapGrid({ map, strokeWidth }) { }, [mapImage, mapLoadingStatus]); return ( - + ); } diff --git a/src/components/map/MapGridEditor.js b/src/components/map/MapGridEditor.js index 6a2e689..a138442 100644 --- a/src/components/map/MapGridEditor.js +++ b/src/components/map/MapGridEditor.js @@ -1,19 +1,18 @@ -import React, { useContext, useRef } from "react"; +import React, { useRef } from "react"; import { Group, Circle, Rect } from "react-konva"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useKeyboard } from "../../contexts/KeyboardContext"; import Vector2 from "../../helpers/Vector2"; -import useKeyboard from "../../hooks/useKeyboard"; - function MapGridEditor({ map, onGridChange }) { const { mapWidth, mapHeight, stageScale, setPreventMapInteraction, - } = useContext(MapInteractionContext); + } = useMapInteraction(); const mapSize = { x: mapWidth, y: mapHeight }; diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js index d6b865d..ce2fd31 100644 --- a/src/components/map/MapInteraction.js +++ b/src/components/map/MapInteraction.js @@ -6,19 +6,18 @@ import { EventEmitter } from "events"; import useMapImage from "../../hooks/useMapImage"; import usePreventOverscroll from "../../hooks/usePreventOverscroll"; -import useKeyboard from "../../hooks/useKeyboard"; import useStageInteraction from "../../hooks/useStageInteraction"; import useImageCenter from "../../hooks/useImageCenter"; import { getGridMaxZoom } from "../../helpers/grid"; import { MapInteractionProvider } from "../../contexts/MapInteractionContext"; -import MapStageContext, { - MapStageProvider, -} from "../../contexts/MapStageContext"; -import AuthContext from "../../contexts/AuthContext"; -import SettingsContext from "../../contexts/SettingsContext"; +import { MapStageProvider, useMapStage } from "../../contexts/MapStageContext"; +import AuthContext, { useAuth } from "../../contexts/AuthContext"; +import SettingsContext, { useSettings } from "../../contexts/SettingsContext"; import KeyboardContext from "../../contexts/KeyboardContext"; +import { GridProvider } from "../../contexts/GridContext"; +import { useKeyboard } from "../../contexts/KeyboardContext"; function MapInteraction({ map, @@ -53,7 +52,7 @@ function MapInteraction({ // Avoid state udpates when panning the map by using a ref and updating the konva element directly const stageTranslateRef = useRef({ x: 0, y: 0 }); - const mapStageRef = useContext(MapStageContext); + const mapStageRef = useMapStage(); const mapLayerRef = useRef(); const mapImageRef = useRef(); @@ -177,8 +176,8 @@ function MapInteraction({ } } - const auth = useContext(AuthContext); - const settings = useContext(SettingsContext); + const auth = useAuth(); + const settings = useSettings(); const mapInteraction = { stageScale, @@ -223,9 +222,15 @@ function MapInteraction({ - - {mapLoaded && children} - + + + {mapLoaded && children} + + @@ -234,7 +239,9 @@ function MapInteraction({ - {controls} + + {controls} + ); diff --git a/src/components/map/MapLoadingOverlay.js b/src/components/map/MapLoadingOverlay.js index da9661a..e3b77b6 100644 --- a/src/components/map/MapLoadingOverlay.js +++ b/src/components/map/MapLoadingOverlay.js @@ -1,12 +1,12 @@ -import React, { useContext } from "react"; +import React from "react"; import { Box } from "theme-ui"; -import MapLoadingContext from "../../contexts/MapLoadingContext"; +import { useMapLoading } from "../../contexts/MapLoadingContext"; import LoadingBar from "../LoadingBar"; function MapLoadingOverlay() { - const { isLoading, loadingProgressRef } = useContext(MapLoadingContext); + const { isLoading, loadingProgressRef } = useMapLoading(); return ( isLoading && ( diff --git a/src/components/map/MapMeasure.js b/src/components/map/MapMeasure.js index 49e8d7e..169e91f 100644 --- a/src/components/map/MapMeasure.js +++ b/src/components/map/MapMeasure.js @@ -1,22 +1,26 @@ -import React, { useContext, useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { Group, Line, Text, Label, Tag } from "react-konva"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; -import MapStageContext from "../../contexts/MapStageContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useMapStage } from "../../contexts/MapStageContext"; +import { useGrid } from "../../contexts/GridContext"; import { getBrushPosition, getDefaultShapeData, getUpdatedShapeData, - getStrokeWidth, } from "../../helpers/drawing"; import Vector2 from "../../helpers/Vector2"; -function MapMeasure({ map, selectedToolSettings, active, gridSize }) { - const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext( - MapInteractionContext - ); - const mapStageRef = useContext(MapStageContext); +function MapMeasure({ map, selectedToolSettings, active }) { + const { + stageScale, + mapWidth, + mapHeight, + interactionEmitter, + } = useMapInteraction(); + const { gridCellNormalizedSize, gridStrokeWidth } = useGrid(); + const mapStageRef = useMapStage(); const [drawingShapeData, setDrawingShapeData] = useState(null); const [isBrushDown, setIsBrushDown] = useState(false); @@ -52,7 +56,7 @@ function MapMeasure({ map, selectedToolSettings, active, gridSize }) { map, mapStage, map.snapToGrid, - gridSize + gridCellNormalizedSize ); const { points } = getDefaultShapeData("line", brushPosition); const length = 0; @@ -65,20 +69,26 @@ function MapMeasure({ map, selectedToolSettings, active, gridSize }) { map, mapStage, map.snapToGrid, - gridSize + gridCellNormalizedSize ); if (isBrushDown && drawingShapeData) { const { points } = getUpdatedShapeData( "line", drawingShapeData, brushPosition, - gridSize + gridCellNormalizedSize ); // Round the grid positions to the nearest 0.1 to aviod floating point issues const precision = { x: 0.1, y: 0.1 }; const length = Vector2.distance( - Vector2.roundTo(Vector2.divide(points[0], gridSize), precision), - Vector2.roundTo(Vector2.divide(points[1], gridSize), precision), + Vector2.roundTo( + Vector2.divide(points[0], gridCellNormalizedSize), + precision + ), + Vector2.roundTo( + Vector2.divide(points[1], gridCellNormalizedSize), + precision + ), selectedToolSettings.type ); setDrawingShapeData({ @@ -119,13 +129,13 @@ function MapMeasure({ map, selectedToolSettings, active, gridSize }) { diff --git a/src/components/map/MapNotes.js b/src/components/map/MapNotes.js index 82fb68d..4c60e13 100644 --- a/src/components/map/MapNotes.js +++ b/src/components/map/MapNotes.js @@ -1,10 +1,11 @@ -import React, { useContext, useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef } from "react"; import shortid from "shortid"; import { Group } from "react-konva"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; -import MapStageContext from "../../contexts/MapStageContext"; -import AuthContext from "../../contexts/AuthContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useMapStage } from "../../contexts/MapStageContext"; +import { useAuth } from "../../contexts/AuthContext"; +import { useGrid } from "../../contexts/GridContext"; import { getBrushPosition } from "../../helpers/drawing"; @@ -15,7 +16,6 @@ const defaultNoteSize = 2; function MapNotes({ map, active, - gridSize, onNoteAdd, onNoteChange, notes, @@ -25,9 +25,10 @@ function MapNotes({ onNoteDragEnd, fadeOnHover, }) { - const { interactionEmitter } = useContext(MapInteractionContext); - const { userId } = useContext(AuthContext); - const mapStageRef = useContext(MapStageContext); + const { interactionEmitter } = useMapInteraction(); + const { userId } = useAuth(); + const { gridCellNormalizedSize } = useGrid(); + const mapStageRef = useMapStage(); const [isBrushDown, setIsBrushDown] = useState(false); const [noteData, setNoteData] = useState(null); @@ -44,7 +45,7 @@ function MapNotes({ map, mapStage, map.snapToGrid, - gridSize + gridCellNormalizedSize ); setNoteData({ x: brushPosition.x, @@ -68,7 +69,7 @@ function MapNotes({ map, mapStage, map.snapToGrid, - gridSize + gridCellNormalizedSize ); setNoteData((prev) => ({ ...prev, diff --git a/src/components/map/MapPointer.js b/src/components/map/MapPointer.js index 6069f42..952ff11 100644 --- a/src/components/map/MapPointer.js +++ b/src/components/map/MapPointer.js @@ -1,10 +1,10 @@ -import React, { useContext, useEffect } from "react"; +import React, { useEffect } from "react"; import { Group } from "react-konva"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; -import MapStageContext from "../../contexts/MapStageContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useMapStage } from "../../contexts/MapStageContext"; +import { useGrid } from "../../contexts/GridContext"; -import { getStrokeWidth } from "../../helpers/drawing"; import { getRelativePointerPositionNormalized, Trail, @@ -14,7 +14,6 @@ import Vector2 from "../../helpers/Vector2"; import colors from "../../helpers/colors"; function MapPointer({ - gridSize, active, position, onPointerDown, @@ -23,10 +22,9 @@ function MapPointer({ visible, color, }) { - const { mapWidth, mapHeight, interactionEmitter } = useContext( - MapInteractionContext - ); - const mapStageRef = useContext(MapStageContext); + const { mapWidth, mapHeight, interactionEmitter } = useMapInteraction(); + const { gridStrokeWidth } = useGrid(); + const mapStageRef = useMapStage(); useEffect(() => { if (!active) { @@ -63,7 +61,7 @@ function MapPointer({ }; }); - const size = getStrokeWidth(2, gridSize, mapWidth, mapHeight); + const size = 2 * gridStrokeWidth; return ( diff --git a/src/components/map/MapTiles.js b/src/components/map/MapTiles.js index 3aec65b..3b86589 100644 --- a/src/components/map/MapTiles.js +++ b/src/components/map/MapTiles.js @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React from "react"; import { Flex, Box, Text, IconButton, Close, Label } from "theme-ui"; import SimpleBar from "simplebar-react"; import Case from "case"; @@ -11,7 +11,7 @@ import MapTile from "./MapTile"; import Link from "../Link"; import FilterBar from "../FilterBar"; -import DatabaseContext from "../../contexts/DatabaseContext"; +import { useDatabase } from "../../contexts/DatabaseContext"; import useResponsiveLayout from "../../hooks/useResponsiveLayout"; @@ -32,7 +32,7 @@ function MapTiles({ onSearchChange, onMapsGroup, }) { - const { databaseStatus } = useContext(DatabaseContext); + const { databaseStatus } = useDatabase(); const layout = useResponsiveLayout(); let hasMapState = false; diff --git a/src/components/map/MapToken.js b/src/components/map/MapToken.js index eccc1a1..c3e3dfd 100644 --- a/src/components/map/MapToken.js +++ b/src/components/map/MapToken.js @@ -1,4 +1,4 @@ -import React, { useContext, useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { Image as KonvaImage, Group } from "react-konva"; import { useSpring, animated } from "react-spring/konva"; import useImage from "use-image"; @@ -10,8 +10,9 @@ import usePrevious from "../../hooks/usePrevious"; import { snapNodeToGrid } from "../../helpers/grid"; -import AuthContext from "../../contexts/AuthContext"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; +import { useAuth } from "../../contexts/AuthContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useGrid } from "../../contexts/GridContext"; import TokenStatus from "../token/TokenStatus"; import TokenLabel from "../token/TokenLabel"; @@ -23,7 +24,6 @@ const snappingThreshold = 1 / 7; function MapToken({ token, tokenState, - tokenSizePercent, onTokenStateChange, onTokenMenuOpen, onTokenDragStart, @@ -33,13 +33,14 @@ function MapToken({ fadeOnHover, map, }) { - const { userId } = useContext(AuthContext); + const { userId } = useAuth(); const { setPreventMapInteraction, mapWidth, mapHeight, stageScale, - } = useContext(MapInteractionContext); + } = useMapInteraction(); + const { gridCellPixelSize } = useGrid(); const tokenSource = useDataSource(token, tokenSources, unknownSource); const [tokenSourceImage, tokenSourceStatus] = useImage(tokenSource); @@ -182,9 +183,9 @@ function MapToken({ } } - const tokenWidth = tokenSizePercent * mapWidth * tokenState.size; + const tokenWidth = gridCellPixelSize.width * tokenState.size; const tokenHeight = - tokenSizePercent * (mapWidth / tokenAspectRatio) * tokenState.size; + (gridCellPixelSize.width / tokenAspectRatio) * tokenState.size; const debouncedStageScale = useDebounce(stageScale, 50); const imageRef = useRef(); diff --git a/src/components/map/SelectMapButton.js b/src/components/map/SelectMapButton.js index 7bf63b8..8484ee9 100644 --- a/src/components/map/SelectMapButton.js +++ b/src/components/map/SelectMapButton.js @@ -1,11 +1,11 @@ -import React, { useState, useContext } from "react"; +import React, { useState } from "react"; import { IconButton } from "theme-ui"; import SelectMapModal from "../../modals/SelectMapModal"; import SelectMapIcon from "../../icons/SelectMapIcon"; -import MapDataContext from "../../contexts/MapDataContext"; -import AuthContext from "../../contexts/AuthContext"; +import { useMapData } from "../../contexts/MapDataContext"; +import { useAuth } from "../../contexts/AuthContext"; function SelectMapButton({ onMapChange, @@ -16,8 +16,8 @@ function SelectMapButton({ }) { const [isModalOpen, setIsModalOpen] = useState(false); - const { updateMapState } = useContext(MapDataContext); - const { userId } = useContext(AuthContext); + const { updateMapState } = useMapData(); + const { userId } = useAuth(); function openModal() { if (currentMapState && currentMap && currentMap.owner === userId) { updateMapState(currentMapState.mapId, currentMapState); diff --git a/src/components/map/controls/DrawingToolSettings.js b/src/components/map/controls/DrawingToolSettings.js index 4c79b81..429766b 100644 --- a/src/components/map/controls/DrawingToolSettings.js +++ b/src/components/map/controls/DrawingToolSettings.js @@ -22,7 +22,7 @@ import RedoButton from "./RedoButton"; import Divider from "../../Divider"; -import useKeyboard from "../../../hooks/useKeyboard"; +import { useKeyboard } from "../../../contexts/KeyboardContext"; function DrawingToolSettings({ settings, diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index dc67012..acdf51f 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -20,7 +20,7 @@ import ToolSection from "./ToolSection"; import Divider from "../../Divider"; -import useKeyboard from "../../../hooks/useKeyboard"; +import { useKeyboard } from "../../../contexts/KeyboardContext"; function BrushToolSettings({ settings, diff --git a/src/components/map/controls/MeasureToolSettings.js b/src/components/map/controls/MeasureToolSettings.js index cd24d2b..e1a7a18 100644 --- a/src/components/map/controls/MeasureToolSettings.js +++ b/src/components/map/controls/MeasureToolSettings.js @@ -9,7 +9,7 @@ import MeasureAlternatingIcon from "../../../icons/MeasureAlternatingIcon"; import Divider from "../../Divider"; -import useKeyboard from "../../../hooks/useKeyboard"; +import { useKeyboard } from "../../../contexts/KeyboardContext"; function MeasureToolSettings({ settings, onSettingChange }) { // Keyboard shortcuts diff --git a/src/components/note/Note.js b/src/components/note/Note.js index d096275..1005286 100644 --- a/src/components/note/Note.js +++ b/src/components/note/Note.js @@ -1,9 +1,10 @@ -import React, { useContext, useEffect, useState, useRef } from "react"; +import React, { useEffect, useState, useRef } from "react"; import { Rect, Text } from "react-konva"; import { useSpring, animated } from "react-spring/konva"; -import AuthContext from "../../contexts/AuthContext"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; +import { useAuth } from "../../contexts/AuthContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; +import { useGrid } from "../../contexts/GridContext"; import { snapNodeToGrid } from "../../helpers/grid"; import colors from "../../helpers/colors"; @@ -22,12 +23,11 @@ function Note({ onNoteDragEnd, fadeOnHover, }) { - const { userId } = useContext(AuthContext); - const { mapWidth, mapHeight, setPreventMapInteraction } = useContext( - MapInteractionContext - ); + const { userId } = useAuth(); + const { mapWidth, mapHeight, setPreventMapInteraction } = useMapInteraction(); + const { gridCellPixelSize } = useGrid(); - const noteWidth = map && (mapWidth / map.grid.size.x) * note.size; + const noteWidth = gridCellPixelSize.width * note.size; const noteHeight = noteWidth; const notePadding = noteWidth / 10; diff --git a/src/components/note/NoteMenu.js b/src/components/note/NoteMenu.js index 7d5bc38..9d30af0 100644 --- a/src/components/note/NoteMenu.js +++ b/src/components/note/NoteMenu.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useContext } from "react"; +import React, { useEffect, useState } from "react"; import { Box, Flex, Text, IconButton, Textarea } from "theme-ui"; import Slider from "../Slider"; @@ -16,7 +16,7 @@ import HideIcon from "../../icons/TokenHideIcon"; import NoteIcon from "../../icons/NoteToolIcon"; import TextIcon from "../../icons/NoteTextIcon"; -import AuthContext from "../../contexts/AuthContext"; +import { useAuth } from "../../contexts/AuthContext"; const defaultNoteMaxSize = 6; @@ -28,7 +28,7 @@ function NoteMenu({ onNoteChange, map, }) { - const { userId } = useContext(AuthContext); + const { userId } = useAuth(); const wasOpen = usePrevious(isOpen); diff --git a/src/components/party/Party.js b/src/components/party/Party.js index c81edd2..8e58feb 100644 --- a/src/components/party/Party.js +++ b/src/components/party/Party.js @@ -1,4 +1,4 @@ -import React, { useContext, useEffect } from "react"; +import React, { useEffect } from "react"; import { Flex, Box, Text } from "theme-ui"; import SimpleBar from "simplebar-react"; @@ -13,16 +13,13 @@ import DiceTrayButton from "./DiceTrayButton"; import useSetting from "../../hooks/useSetting"; -import PartyContext from "../../contexts/PartyContext"; -import { - PlayerUpdaterContext, - PlayerStateContext, -} from "../../contexts/PlayerContext"; +import { useParty } from "../../contexts/PartyContext"; +import { usePlayerState, usePlayerUpdater } from "../../contexts/PlayerContext"; function Party({ gameId, stream, partyStreams, onStreamStart, onStreamEnd }) { - const setPlayerState = useContext(PlayerUpdaterContext); - const playerState = useContext(PlayerStateContext); - const partyState = useContext(PartyContext); + const setPlayerState = usePlayerUpdater(); + const playerState = usePlayerState(); + const partyState = useParty(); const [fullScreen] = useSetting("map.fullScreen"); const [shareDice, setShareDice] = useSetting("dice.shareDice"); diff --git a/src/components/token/ProxyToken.js b/src/components/token/ProxyToken.js index 0ce751a..989e8fe 100644 --- a/src/components/token/ProxyToken.js +++ b/src/components/token/ProxyToken.js @@ -1,11 +1,11 @@ -import React, { useEffect, useRef, useState, useContext } from "react"; +import React, { useEffect, useRef, useState } from "react"; import ReactDOM from "react-dom"; import { Image, Box } from "theme-ui"; import interact from "interactjs"; import usePortal from "../../hooks/usePortal"; -import MapStageContext from "../../contexts/MapStageContext"; +import { useMapStage } from "../../contexts/MapStageContext"; /** * @callback onProxyDragEnd @@ -34,7 +34,7 @@ function ProxyToken({ tokenClassName, onProxyDragEnd, tokens }) { }, [tokens]); const proxyOnMap = useRef(false); - const mapStageRef = useContext(MapStageContext); + const mapStageRef = useMapStage(); useEffect(() => { interact(`.${tokenClassName}`).draggable({ diff --git a/src/components/token/TokenDragOverlay.js b/src/components/token/TokenDragOverlay.js index 4d90e9a..467967f 100644 --- a/src/components/token/TokenDragOverlay.js +++ b/src/components/token/TokenDragOverlay.js @@ -1,7 +1,7 @@ -import React, { useContext } from "react"; +import React from "react"; -import AuthContext from "../../contexts/AuthContext"; -import MapInteractionContext from "../../contexts/MapInteractionContext"; +import { useAuth } from "../../contexts/AuthContext"; +import { useMapInteraction } from "../../contexts/MapInteractionContext"; import DragOverlay from "../DragOverlay"; @@ -14,8 +14,8 @@ function TokenDragOverlay({ dragging, mapState, }) { - const { userId } = useContext(AuthContext); - const { mapWidth, mapHeight } = useContext(MapInteractionContext); + const { userId } = useAuth(); + const { mapWidth, mapHeight } = useMapInteraction(); function handleTokenRemove() { // Handle other tokens when a vehicle gets deleted diff --git a/src/components/token/TokenMenu.js b/src/components/token/TokenMenu.js index d34f360..c9ff828 100644 --- a/src/components/token/TokenMenu.js +++ b/src/components/token/TokenMenu.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useContext } from "react"; +import React, { useEffect, useState } from "react"; import { Box, Input, Flex, Text, IconButton } from "theme-ui"; import Slider from "../Slider"; @@ -14,7 +14,7 @@ import UnlockIcon from "../../icons/TokenUnlockIcon"; import ShowIcon from "../../icons/TokenShowIcon"; import HideIcon from "../../icons/TokenHideIcon"; -import AuthContext from "../../contexts/AuthContext"; +import { useAuth } from "../../contexts/AuthContext"; const defaultTokenMaxSize = 6; function TokenMenu({ @@ -25,7 +25,7 @@ function TokenMenu({ onTokenStateChange, map, }) { - const { userId } = useContext(AuthContext); + const { userId } = useAuth(); const wasOpen = usePrevious(isOpen); diff --git a/src/components/token/TokenPreview.js b/src/components/token/TokenPreview.js index 10b097f..e13b044 100644 --- a/src/components/token/TokenPreview.js +++ b/src/components/token/TokenPreview.js @@ -10,6 +10,8 @@ import useDataSource from "../../hooks/useDataSource"; import useImageCenter from "../../hooks/useImageCenter"; import useResponsiveLayout from "../../hooks/useResponsiveLayout"; +import { GridProvider } from "../../contexts/GridContext"; + import GridOnIcon from "../../icons/GridOnIcon"; import GridOffIcon from "../../icons/GridOffIcon"; @@ -110,7 +112,7 @@ function TokenPreview({ token }) { /> {showGridPreview && ( - + > + + {children}; } +export function useAuth() { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error("useAuth must be used within a AuthProvider"); + } + return context; +} + export default AuthContext; diff --git a/src/contexts/DatabaseContext.js b/src/contexts/DatabaseContext.js index 323b49a..3eb32dd 100644 --- a/src/contexts/DatabaseContext.js +++ b/src/contexts/DatabaseContext.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { Box, Text } from "theme-ui"; import Banner from "../components/Banner"; @@ -79,4 +79,12 @@ export function DatabaseProvider({ children }) { ); } +export function useDatabase() { + const context = useContext(DatabaseContext); + if (context === undefined) { + throw new Error("useDatabase must be used within a DatabaseProvider"); + } + return context; +} + export default DatabaseContext; diff --git a/src/contexts/DiceLoadingContext.js b/src/contexts/DiceLoadingContext.js index 4d75d40..8bc9ed3 100644 --- a/src/contexts/DiceLoadingContext.js +++ b/src/contexts/DiceLoadingContext.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useContext } from "react"; const DiceLoadingContext = React.createContext(); @@ -28,4 +28,12 @@ export function DiceLoadingProvider({ children }) { ); } +export function useDiceLoading() { + const context = useContext(DiceLoadingContext); + if (context === undefined) { + throw new Error("useDiceLoading must be used within a DiceLoadingProvider"); + } + return context; +} + export default DiceLoadingContext; diff --git a/src/contexts/GridContext.js b/src/contexts/GridContext.js new file mode 100644 index 0000000..215c231 --- /dev/null +++ b/src/contexts/GridContext.js @@ -0,0 +1,85 @@ +import React, { useContext } from "react"; + +import Vector2 from "../helpers/Vector2"; +import Size from "../helpers/Size"; +import { getGridPixelSize, getCellPixelSize, Grid } from "../helpers/grid"; + +/** + * @typedef GridContextValue + * @property {Grid} grid Base grid value + * @property {Size} gridPixelSize Size of the grid in pixels + * @property {Size} gridCellPixelSize Size of each cell in pixels + * @property {Size} gridCellNormalizedSize Size of each cell normalized to the grid + * @property {Vector2} gridOffset Offset of the grid from the top left in pixels + * @property {number} gridStrokeWidth Stroke width of the grid in pixels + */ + +/** + * @type {GridContextValue} + */ +const defaultValue = { + grid: { + size: { x: 0, y: 0 }, + inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } }, + type: "square", + }, + gridPixelSize: new Size(0, 0), + gridCellPixelSize: new Size(0, 0, 0), + gridCellNormalizedSize: new Size(0, 0, 0), + gridOffset: { x: 0, y: 0 }, + gridStrokeWidth: 0, +}; + +const GridContext = React.createContext(defaultValue); + +const defaultStrokeWidth = 1 / 10; + +export function GridProvider({ grid, width, height, children }) { + if (!grid?.size.x || !grid?.size.y) { + return ( + + {children} + + ); + } + + const gridPixelSize = getGridPixelSize(grid, width, height); + const gridCellPixelSize = getCellPixelSize( + grid, + gridPixelSize.width, + gridPixelSize.height + ); + const gridCellNormalizedSize = new Size( + gridCellPixelSize.width / gridPixelSize.width, + gridCellPixelSize.height / gridPixelSize.height + ); + const gridOffset = { + x: grid.inset.topLeft.x * width * -1, + y: grid.inset.topLeft.y * height * -1, + }; + const gridStrokeWidth = + (gridCellPixelSize.width < gridCellPixelSize.height + ? gridCellPixelSize.width + : gridCellPixelSize.height) * defaultStrokeWidth; + + const value = { + grid, + gridPixelSize, + gridCellPixelSize, + gridCellNormalizedSize, + gridOffset, + gridStrokeWidth, + }; + + return {children}; +} + +export function useGrid() { + const context = useContext(GridContext); + if (context === undefined) { + throw new Error("useGrid must be used within a GridProvider"); + } + return context; +} + +export default GridContext; diff --git a/src/contexts/KeyboardContext.js b/src/contexts/KeyboardContext.js index 8736f46..ec47dd9 100644 --- a/src/contexts/KeyboardContext.js +++ b/src/contexts/KeyboardContext.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useContext } from "react"; import { EventEmitter } from "events"; const KeyboardContext = React.createContext({ keyEmitter: new EventEmitter() }); @@ -45,4 +45,33 @@ export function KeyboardProvider({ children }) { ); } +/** + * @param {KeyboardEvent} onKeyDown + * @param {KeyboardEvent} onKeyUp + */ +export function useKeyboard(onKeyDown, onKeyUp) { + const context = useContext(KeyboardContext); + if (context === undefined) { + throw new Error("useKeyboard must be used within a KeyboardProvider"); + } + const { keyEmitter } = context; + useEffect(() => { + if (onKeyDown) { + keyEmitter.on("keyDown", onKeyDown); + } + if (onKeyUp) { + keyEmitter.on("keyUp", onKeyUp); + } + + return () => { + if (onKeyDown) { + keyEmitter.off("keyDown", onKeyDown); + } + if (onKeyUp) { + keyEmitter.off("keyUp", onKeyUp); + } + }; + }); +} + export default KeyboardContext; diff --git a/src/contexts/MapDataContext.js b/src/contexts/MapDataContext.js index 5783adb..f94d375 100644 --- a/src/contexts/MapDataContext.js +++ b/src/contexts/MapDataContext.js @@ -8,8 +8,8 @@ import React, { import * as Comlink from "comlink"; import { decode } from "@msgpack/msgpack"; -import AuthContext from "./AuthContext"; -import DatabaseContext from "./DatabaseContext"; +import { useAuth } from "./AuthContext"; +import { useDatabase } from "./DatabaseContext"; import DatabaseWorker from "worker-loader!../workers/DatabaseWorker"; // eslint-disable-line import/no-webpack-loader-syntax @@ -32,8 +32,8 @@ const defaultMapState = { const worker = Comlink.wrap(new DatabaseWorker()); export function MapDataProvider({ children }) { - const { database, databaseStatus } = useContext(DatabaseContext); - const { userId } = useContext(AuthContext); + const { database, databaseStatus } = useDatabase(); + const { userId } = useAuth(); const [maps, setMaps] = useState([]); const [mapStates, setMapStates] = useState([]); @@ -303,4 +303,12 @@ export function MapDataProvider({ children }) { ); } +export function useMapData() { + const context = useContext(MapDataContext); + if (context === undefined) { + throw new Error("useMapData must be used within a MapDataProvider"); + } + return context; +} + export default MapDataContext; diff --git a/src/contexts/MapInteractionContext.js b/src/contexts/MapInteractionContext.js index cc8397b..905b1fe 100644 --- a/src/contexts/MapInteractionContext.js +++ b/src/contexts/MapInteractionContext.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useContext } from "react"; const MapInteractionContext = React.createContext({ stageScale: 1, @@ -11,4 +11,14 @@ const MapInteractionContext = React.createContext({ }); export const MapInteractionProvider = MapInteractionContext.Provider; +export function useMapInteraction() { + const context = useContext(MapInteractionContext); + if (context === undefined) { + throw new Error( + "useMapInteraction must be used within a MapInteractionProvider" + ); + } + return context; +} + export default MapInteractionContext; diff --git a/src/contexts/MapLoadingContext.js b/src/contexts/MapLoadingContext.js index e413a09..94cb5b6 100644 --- a/src/contexts/MapLoadingContext.js +++ b/src/contexts/MapLoadingContext.js @@ -1,4 +1,4 @@ -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useContext } from "react"; import { omit, isEmpty } from "../helpers/shared"; const MapLoadingContext = React.createContext(); @@ -53,4 +53,12 @@ export function MapLoadingProvider({ children }) { ); } +export function useMapLoading() { + const context = useContext(MapLoadingContext); + if (context === undefined) { + throw new Error("useMapLoading must be used within a MapLoadingProvider"); + } + return context; +} + export default MapLoadingContext; diff --git a/src/contexts/MapStageContext.js b/src/contexts/MapStageContext.js index e8f61d8..c5c3952 100644 --- a/src/contexts/MapStageContext.js +++ b/src/contexts/MapStageContext.js @@ -1,8 +1,16 @@ -import React from "react"; +import React, { useContext } from "react"; const MapStageContext = React.createContext({ mapStageRef: { current: null }, }); export const MapStageProvider = MapStageContext.Provider; +export function useMapStage() { + const context = useContext(MapStageContext); + if (context === undefined) { + throw new Error("useMapStage must be used within a MapStageProvider"); + } + return context; +} + export default MapStageContext; diff --git a/src/contexts/PartyContext.js b/src/contexts/PartyContext.js index 1b90af7..905112e 100644 --- a/src/contexts/PartyContext.js +++ b/src/contexts/PartyContext.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; const PartyContext = React.createContext(); @@ -27,4 +27,12 @@ export function PartyProvider({ session, children }) { ); } +export function useParty() { + const context = useContext(PartyContext); + if (context === undefined) { + throw new Error("useParty must be used within a PartyProvider"); + } + return context; +} + export default PartyContext; diff --git a/src/contexts/PlayerContext.js b/src/contexts/PlayerContext.js index 8764365..6a3af8f 100644 --- a/src/contexts/PlayerContext.js +++ b/src/contexts/PlayerContext.js @@ -1,7 +1,7 @@ import React, { useEffect, useContext } from "react"; -import DatabaseContext from "./DatabaseContext"; -import AuthContext from "./AuthContext"; +import { useDatabase } from "./DatabaseContext"; +import { useAuth } from "./AuthContext"; import { getRandomMonster } from "../helpers/monsters"; @@ -11,8 +11,8 @@ export const PlayerStateContext = React.createContext(); export const PlayerUpdaterContext = React.createContext(() => {}); export function PlayerProvider({ session, children }) { - const { userId } = useContext(AuthContext); - const { database, databaseStatus } = useContext(DatabaseContext); + const { userId } = useAuth(); + const { database, databaseStatus } = useDatabase(); const [playerState, setPlayerState] = useNetworkedState( { @@ -93,3 +93,19 @@ export function PlayerProvider({ session, children }) { ); } + +export function usePlayerState() { + const context = useContext(PlayerStateContext); + if (context === undefined) { + throw new Error("usePlayerState must be used within a PlayerProvider"); + } + return context; +} + +export function usePlayerUpdater() { + const context = useContext(PlayerUpdaterContext); + if (context === undefined) { + throw new Error("usePlayerUpdater must be used within a PlayerProvider"); + } + return context; +} diff --git a/src/contexts/SettingsContext.js b/src/contexts/SettingsContext.js index b396577..c9df1e7 100644 --- a/src/contexts/SettingsContext.js +++ b/src/contexts/SettingsContext.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { getSettings } from "../settings"; @@ -28,4 +28,12 @@ export function SettingsProvider({ children }) { ); } +export function useSettings() { + const context = useContext(SettingsContext); + if (context === undefined) { + throw new Error("useSettings must be used within a SettingsProvider"); + } + return context; +} + export default SettingsContext; diff --git a/src/contexts/TokenDataContext.js b/src/contexts/TokenDataContext.js index 53a3940..ae81e02 100644 --- a/src/contexts/TokenDataContext.js +++ b/src/contexts/TokenDataContext.js @@ -8,8 +8,8 @@ import React, { import * as Comlink from "comlink"; import { decode } from "@msgpack/msgpack"; -import AuthContext from "./AuthContext"; -import DatabaseContext from "./DatabaseContext"; +import { useAuth } from "./AuthContext"; +import { useDatabase } from "./DatabaseContext"; import DatabaseWorker from "worker-loader!../workers/DatabaseWorker"; // eslint-disable-line import/no-webpack-loader-syntax @@ -22,8 +22,8 @@ const cachedTokenMax = 100; const worker = Comlink.wrap(new DatabaseWorker()); export function TokenDataProvider({ children }) { - const { database, databaseStatus } = useContext(DatabaseContext); - const { userId } = useContext(AuthContext); + const { database, databaseStatus } = useDatabase(); + const { userId } = useAuth(); const [tokens, setTokens] = useState([]); const [tokensLoading, setTokensLoading] = useState(true); @@ -224,4 +224,12 @@ export function TokenDataProvider({ children }) { ); } +export function useTokenData() { + const context = useContext(TokenDataContext); + if (context === undefined) { + throw new Error("useTokenData must be used within a TokenDataProvider"); + } + return context; +} + export default TokenDataContext; diff --git a/src/helpers/Size.js b/src/helpers/Size.js new file mode 100644 index 0000000..8330c45 --- /dev/null +++ b/src/helpers/Size.js @@ -0,0 +1,65 @@ +import Vector2 from "./Vector2"; + +/** + * Wrapper for Vector2 that provides width, height and radius properties + */ +class Size extends Vector2 { + _radius; + /** + * @param {number} width + * @param {number} height + * @param {number=} radius Used to represent hexagon sizes + */ + constructor(width, height, radius) { + super(width, height); + this._radius = radius; + } + + /** + * @returns {number} + */ + get width() { + return this.x; + } + + /** + * @param {number} width + */ + set width(width) { + this.x = width; + } + + /** + * @returns {number} + */ + get height() { + return this.y; + } + + /** + * @param {number} height + */ + set height(height) { + this.y = height; + } + + /** + * @returns {number} + */ + get radius() { + if (this._radius) { + return this._radius; + } else { + return Math.min(this.x, this.y) / 2; + } + } + + /** + * @param {number} radius + */ + set radius(radius) { + this._radius = radius; + } +} + +export default Size; diff --git a/src/helpers/Vector2.js b/src/helpers/Vector2.js index 823d64b..01dce79 100644 --- a/src/helpers/Vector2.js +++ b/src/helpers/Vector2.js @@ -17,6 +17,15 @@ class Vector2 { */ y; + /** + * @param {number} x + * @param {number} y + */ + constructor(x, y) { + this.x = x; + this.y = y; + } + /** * @param {Vector2} p * @returns {number} Length squared of `p` @@ -385,7 +394,7 @@ class Vector2 { * Returns the distance between two vectors * @param {Vector2} a * @param {Vector2} b - * @param {string?} type - `chebyshev | euclidean | manhattan | alternating` + * @param {string=} type - `chebyshev | euclidean | manhattan | alternating` */ static distance(a, b, type = "euclidean") { switch (type) { diff --git a/src/helpers/drawing.js b/src/helpers/drawing.js index bdd61b6..fdbd3a5 100644 --- a/src/helpers/drawing.js +++ b/src/helpers/drawing.js @@ -7,7 +7,12 @@ import { getRelativePointerPositionNormalized } from "./konva"; import { logError } from "./logging"; const snappingThreshold = 1 / 5; -export function getBrushPosition(map, mapStage, useGridSnappning, gridSize) { +export function getBrushPosition( + map, + mapStage, + useGridSnappning, + gridCellNormalizedSize +) { const mapImage = mapStage.findOne("#mapImage"); let position = getRelativePointerPositionNormalized(mapImage); if (useGridSnappning) { @@ -15,18 +20,21 @@ export function getBrushPosition(map, mapStage, useGridSnappning, gridSize) { // Subtract offset to transform into offset space then add it back transform back const offset = map.grid.inset.topLeft; const gridSnap = Vector2.add( - Vector2.roundTo(Vector2.subtract(position, offset), gridSize), + Vector2.roundTo( + Vector2.subtract(position, offset), + gridCellNormalizedSize + ), offset ); const gridDistance = Vector2.length(Vector2.subtract(gridSnap, position)); // Snap to center of grid // Subtract offset and half size to transform it into offset half space then transform it back - const halfSize = Vector2.multiply(gridSize, 0.5); + const halfSize = Vector2.multiply(gridCellNormalizedSize, 0.5); const centerSnap = Vector2.add( Vector2.add( Vector2.roundTo( Vector2.subtract(Vector2.subtract(position, offset), halfSize), - gridSize + gridCellNormalizedSize ), halfSize ), @@ -35,7 +43,7 @@ export function getBrushPosition(map, mapStage, useGridSnappning, gridSize) { const centerDistance = Vector2.length( Vector2.subtract(centerSnap, position) ); - const minGrid = Vector2.min(gridSize); + const minGrid = Vector2.min(gridCellNormalizedSize); if (gridDistance < minGrid * snappingThreshold) { position = gridSnap; } else if (centerDistance < minGrid * snappingThreshold) { @@ -49,14 +57,19 @@ export function getFogBrushPosition( map, mapStage, useGridSnappning, - gridSize, + gridCellNormalizedSize, useEdgeSnapping, fogShapes, rectPoints ) { - let position = getBrushPosition(map, mapStage, useGridSnappning, gridSize); + let position = getBrushPosition( + map, + mapStage, + useGridSnappning, + gridCellNormalizedSize + ); if (useEdgeSnapping) { - const minGrid = Vector2.min(gridSize); + const minGrid = Vector2.min(gridCellNormalizedSize); let closestDistance = Number.MAX_VALUE; let closestPosition = position; // Find the closest point on all fog shapes @@ -139,18 +152,23 @@ export function getDefaultShapeData(type, brushPosition) { } } -export function getGridScale(gridSize) { - if (gridSize.x < gridSize.y) { - return { x: gridSize.y / gridSize.x, y: 1 }; - } else if (gridSize.y < gridSize.x) { - return { x: 1, y: gridSize.x / gridSize.y }; +export function getGridScale(cellSize) { + if (cellSize.x < cellSize.y) { + return { x: cellSize.y / cellSize.x, y: 1 }; + } else if (cellSize.y < cellSize.x) { + return { x: 1, y: cellSize.x / cellSize.y }; } else { return { x: 1, y: 1 }; } } -export function getUpdatedShapeData(type, data, brushPosition, gridSize) { - const gridScale = getGridScale(gridSize); +export function getUpdatedShapeData( + type, + data, + brushPosition, + gridCellNormalizedSize +) { + const gridScale = getGridScale(gridCellNormalizedSize); if (type === "line") { return { points: [data.points[0], { x: brushPosition.x, y: brushPosition.y }], @@ -210,10 +228,10 @@ export function getStrokeWidth(multiplier, gridSize, mapWidth, mapHeight) { } const defaultSimplifySize = 1 / 100; -export function simplifyPoints(points, gridSize, scale) { +export function simplifyPoints(points, gridCellNormalizedSize, scale) { return simplify( points, - (Vector2.min(gridSize) * defaultSimplifySize) / scale + (Vector2.min(gridCellNormalizedSize) * defaultSimplifySize) / scale ); } diff --git a/src/helpers/grid.js b/src/helpers/grid.js index 554e2cc..29d5a44 100644 --- a/src/helpers/grid.js +++ b/src/helpers/grid.js @@ -1,5 +1,6 @@ import GridSizeModel from "../ml/gridSize/GridSizeModel"; import Vector2 from "./Vector2"; +import Size from "./Size"; import { logError } from "./logging"; @@ -8,52 +9,47 @@ const GRID_TYPE_NOT_IMPLEMENTED = new Error("Grid type not implemented"); /** * @typedef GridInset - * @property {Vector2} topLeft - * @property {Vector2} bottomRight + * @property {Vector2} topLeft Top left position of the inset + * @property {Vector2} bottomRight Bottom right position of the inset */ /** * @typedef Grid - * @property {GridInset} inset - * @property {Vector2} size + * @property {GridInset} inset The inset of the grid from the map + * @property {Vector2} size The number of columns and rows of the grid as `x` and `y` * @property {("square"|"hexVertical"|"hexHorizontal")} type */ /** - * @typedef CellSize - * @property {number} width - * @property {number} height - * @property {number?} radius - Used for hex cell sizes + * Gets the size of a grid in pixels taking into account the inset + * @param {Grid} grid + * @param {number} baseWidth Width of the grid in pixels before inset + * @param {number} baseHeight Height of the grid in pixels before inset + * @returns {Size} */ +export function getGridPixelSize(grid, baseWidth, baseHeight) { + const width = (grid.inset.bottomRight.x - grid.inset.topLeft.x) * baseWidth; + const height = (grid.inset.bottomRight.y - grid.inset.topLeft.y) * baseHeight; + return new Size(width, height); +} /** - * Gets the cell size for a grid taking into account inset and grid type + * Gets the cell size for a grid in pixels for each grid type * @param {Grid} grid - * @param {number} gridWidth - * @param {number} gridHeight - * @returns {CellSize} + * @param {number} gridWidth Width of the grid in pixels after inset + * @param {number} gridHeight Height of the grid in pixels after inset + * @returns {Size} */ -export function getCellSize(grid, gridWidth, gridHeight) { +export function getCellPixelSize(grid, gridWidth, gridHeight) { switch (grid.type) { case "square": - return { - width: gridWidth / grid.size.x, - height: gridHeight / grid.size.y, - }; + return new Size(gridWidth / grid.size.x, gridHeight / grid.size.y); case "hexVertical": const radiusVert = gridWidth / grid.size.x / SQRT3; - return { - width: radiusVert * SQRT3, - height: radiusVert * 2, - radius: radiusVert, - }; + return new Size(radiusVert * SQRT3, radiusVert * 2, radiusVert); case "hexHorizontal": const radiusHorz = gridHeight / grid.size.y / SQRT3; - return { - width: radiusHorz * 2, - height: radiusHorz * SQRT3, - radius: radiusHorz, - }; + return new Size(radiusHorz * 2, radiusHorz * SQRT3, radiusHorz); default: throw GRID_TYPE_NOT_IMPLEMENTED; } @@ -64,7 +60,7 @@ export function getCellSize(grid, gridWidth, gridHeight) { * @param {Grid} grid * @param {number} x X-axis location of the cell * @param {number} y Y-axis location of the cell - * @param {CellSize} cellSize + * @param {Size} cellSize Cell size in pixels * @returns {Vector2} */ export function getCellLocation(grid, x, y, cellSize) { @@ -111,11 +107,11 @@ export function shouldClipCell(grid, x, y) { /** * Canvas clip function for culling hex cells that overshoot/undershoot the grid - * @param {CanvasRenderingContext2D} context + * @param {CanvasRenderingContext2D} context The canvas context of the clip function * @param {Grid} grid - * @param {number} x - * @param {number} y - * @param {CellSize} cellSize + * @param {number} x X-axis location of the cell + * @param {number} y Y-axis location of the cell + * @param {Size} cellSize Cell size in pixels */ export function gridClipFunction(context, grid, x, y, cellSize) { // Clip the undershooting cells unless they are needed to fill out a specific grid type @@ -138,7 +134,7 @@ export function gridClipFunction(context, grid, x, y, cellSize) { /** * Get the height of a grid based off of its width * @param {Grid} grid - * @param {number} gridWidth + * @param {number} gridWidth Width of the grid in pixels after inset */ function getGridHeightFromWidth(grid, gridWidth) { switch (grid.type) { @@ -157,22 +153,22 @@ function getGridHeightFromWidth(grid, gridWidth) { /** * Get the default inset for a grid - * @param {Grid} grid - * @param {number} gridWidth - * @param {number} gridHeight + * @param {Grid} grid Grid with no inset property set + * @param {number} mapWidth Width of the map in pixels before inset + * @param {number} mapHeight Height of the map in pixels before inset * @returns {GridInset} */ -export function getGridDefaultInset(grid, gridWidth, gridHeight) { +export function getGridDefaultInset(grid, mapWidth, mapHeight) { // Max the width of the inset and figure out the resulting height value - const insetHeightNorm = getGridHeightFromWidth(grid, gridWidth) / gridHeight; + const insetHeightNorm = getGridHeightFromWidth(grid, mapWidth) / mapHeight; return { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: insetHeightNorm } }; } /** * Get an updated inset for a grid when its size changes - * @param {Grid} grid - * @param {number} mapWidth - * @param {number} mapHeight + * @param {Grid} grid Grid with an inset property set + * @param {number} mapWidth Width of the map in pixels before inset + * @param {number} mapHeight Height of the map in pixels before inset * @returns {GridInset} */ export function getGridUpdatedInset(grid, mapWidth, mapHeight) { @@ -348,7 +344,7 @@ async function gridSizeML(image, candidates) { * @param {Image} image * @returns {Vector2} */ -export async function getGridSize(image) { +export async function getGridSizeFromImage(image) { const candidates = dividers(image.width, image.height); let prediction; @@ -387,7 +383,7 @@ export function getGridMaxZoom(grid) { * @param {Grid} grid * @param {number} mapWidth * @param {number} mapHeight - * @param {Konva.node} node + * @param {Konva.Node} node * @param {number} snappingThreshold 1 = Always snap, 0 = never snap */ export function snapNodeToGrid( diff --git a/src/hooks/useKeyboard.js b/src/hooks/useKeyboard.js deleted file mode 100644 index 5f20a20..0000000 --- a/src/hooks/useKeyboard.js +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect, useContext } from "react"; - -import KeyboardContext from "../contexts/KeyboardContext"; - -/** - * @param {KeyboardEvent} onKeyDown - * @param {KeyboardEvent} onKeyUp - */ -function useKeyboard(onKeyDown, onKeyUp) { - const { keyEmitter } = useContext(KeyboardContext); - useEffect(() => { - if (onKeyDown) { - keyEmitter.on("keyDown", onKeyDown); - } - if (onKeyUp) { - keyEmitter.on("keyUp", onKeyUp); - } - - return () => { - if (onKeyDown) { - keyEmitter.off("keyDown", onKeyDown); - } - if (onKeyUp) { - keyEmitter.off("keyUp", onKeyUp); - } - }; - }); -} - -export default useKeyboard; diff --git a/src/hooks/useSetting.js b/src/hooks/useSetting.js index ca38152..f8553e7 100644 --- a/src/hooks/useSetting.js +++ b/src/hooks/useSetting.js @@ -1,16 +1,14 @@ -import { useContext } from "react"; - import get from "lodash.get"; import set from "lodash.set"; -import SettingsContext from "../contexts/SettingsContext"; +import { useSettings } from "../contexts/SettingsContext"; /** * Helper to get and set nested settings that are saved in local storage * @param {String} path The path to the setting within the Settings object provided by the SettingsContext */ function useSetting(path) { - const { settings, setSettings } = useContext(SettingsContext); + const { settings, setSettings } = useSettings(); const setting = get(settings, path); diff --git a/src/modals/AuthModal.js b/src/modals/AuthModal.js index e97b0a8..4e2dfdf 100644 --- a/src/modals/AuthModal.js +++ b/src/modals/AuthModal.js @@ -1,14 +1,12 @@ -import React, { useState, useContext, useRef } from "react"; +import React, { useState, useRef } from "react"; import { Box, Input, Button, Label, Flex } from "theme-ui"; -import AuthContext from "../contexts/AuthContext"; +import { useAuth } from "../contexts/AuthContext"; import Modal from "../components/Modal"; function AuthModal({ isOpen }) { - const { password, setPassword, setAuthenticationStatus } = useContext( - AuthContext - ); + const { password, setPassword, setAuthenticationStatus } = useAuth(); const [tmpPassword, setTempPassword] = useState(password); function handleChange(event) { diff --git a/src/modals/EditMapModal.js b/src/modals/EditMapModal.js index 466ba5b..dcfc4ed 100644 --- a/src/modals/EditMapModal.js +++ b/src/modals/EditMapModal.js @@ -1,11 +1,11 @@ -import React, { useState, useContext } from "react"; +import React, { useState } from "react"; import { Button, Flex, Label } from "theme-ui"; import Modal from "../components/Modal"; import MapSettings from "../components/map/MapSettings"; import MapEditor from "../components/map/MapEditor"; -import MapDataContext from "../contexts/MapDataContext"; +import { useMapData } from "../contexts/MapDataContext"; import { isEmpty } from "../helpers/shared"; import { getGridDefaultInset } from "../helpers/grid"; @@ -13,7 +13,7 @@ import { getGridDefaultInset } from "../helpers/grid"; import useResponsiveLayout from "../hooks/useResponsiveLayout"; function EditMapModal({ isOpen, onDone, map, mapState }) { - const { updateMap, updateMapState } = useContext(MapDataContext); + const { updateMap, updateMapState } = useMapData(); function handleClose() { setMapSettingChanges({}); diff --git a/src/modals/EditTokenModal.js b/src/modals/EditTokenModal.js index 860241f..345f44e 100644 --- a/src/modals/EditTokenModal.js +++ b/src/modals/EditTokenModal.js @@ -1,18 +1,18 @@ -import React, { useState, useContext } from "react"; +import React, { useState } from "react"; import { Button, Flex, Label } from "theme-ui"; import Modal from "../components/Modal"; import TokenSettings from "../components/token/TokenSettings"; import TokenPreview from "../components/token/TokenPreview"; -import TokenDataContext from "../contexts/TokenDataContext"; +import { useTokenData } from "../contexts/TokenDataContext"; import { isEmpty } from "../helpers/shared"; import useResponsiveLayout from "../hooks/useResponsiveLayout"; function EditTokenModal({ isOpen, onDone, token }) { - const { updateToken } = useContext(TokenDataContext); + const { updateToken } = useTokenData(); function handleClose() { setTokenSettingChanges({}); diff --git a/src/modals/ImportExportModal.js b/src/modals/ImportExportModal.js index 5b91474..d719c46 100644 --- a/src/modals/ImportExportModal.js +++ b/src/modals/ImportExportModal.js @@ -1,4 +1,4 @@ -import React, { useRef, useState, useContext, useEffect } from "react"; +import React, { useRef, useState, useEffect } from "react"; import { Box, Label, Text, Button, Flex } from "theme-ui"; import streamSaver from "streamsaver"; import * as Comlink from "comlink"; @@ -7,14 +7,14 @@ import Modal from "../components/Modal"; import LoadingOverlay from "../components/LoadingOverlay"; import LoadingBar from "../components/LoadingBar"; -import DatabaseContext from "../contexts/DatabaseContext"; +import { useDatabase } from "../contexts/DatabaseContext"; import DatabaseWorker from "worker-loader!../workers/DatabaseWorker"; // eslint-disable-line import/no-webpack-loader-syntax const worker = Comlink.wrap(new DatabaseWorker()); function ImportDatabaseModal({ isOpen, onRequestClose }) { - const { database } = useContext(DatabaseContext); + const { database } = useDatabase(); const [isLoading, setIsLoading] = useState(false); const backgroundTaskRunningRef = useRef(false); diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index 9882426..7cf1c74 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -1,4 +1,4 @@ -import React, { useRef, useState, useContext, useEffect } from "react"; +import React, { useRef, useState, useEffect } from "react"; import { Button, Flex, Label } from "theme-ui"; import shortid from "shortid"; import Case from "case"; @@ -17,16 +17,16 @@ import { resizeImage } from "../helpers/image"; import { useSearch, useGroup, handleItemSelect } from "../helpers/select"; import { getGridDefaultInset, - getGridSize, + getGridSizeFromImage, gridSizeVaild, } from "../helpers/grid"; import Vector2 from "../helpers/Vector2"; -import useKeyboard from "../hooks/useKeyboard"; import useResponsiveLayout from "../hooks/useResponsiveLayout"; -import MapDataContext from "../contexts/MapDataContext"; -import AuthContext from "../contexts/AuthContext"; +import { useMapData } from "../contexts/MapDataContext"; +import { useAuth } from "../contexts/AuthContext"; +import { useKeyboard } from "../contexts/KeyboardContext"; const defaultMapProps = { showGrid: false, @@ -54,7 +54,7 @@ function SelectMapModal({ // The map currently being view in the map screen currentMap, }) { - const { userId } = useContext(AuthContext); + const { userId } = useAuth(); const { ownedMaps, mapStates, @@ -64,7 +64,7 @@ function SelectMapModal({ updateMap, updateMaps, mapsLoading, - } = useContext(MapDataContext); + } = useMapData(); /** * Search @@ -152,7 +152,7 @@ function SelectMapModal({ } if (!gridSize) { - gridSize = await getGridSize(image); + gridSize = await getGridSizeFromImage(image); } // Remove file extension @@ -207,10 +207,9 @@ function SelectMapModal({ grid: { size: gridSize, inset: getGridDefaultInset( + { size: { x: gridSize.x, y: gridSize.y }, type: "square" }, image.width, - image.height, - gridSize.x, - gridSize.y + image.height ), type: "square", }, diff --git a/src/modals/SelectTokensModal.js b/src/modals/SelectTokensModal.js index ca920ce..7221a56 100644 --- a/src/modals/SelectTokensModal.js +++ b/src/modals/SelectTokensModal.js @@ -1,4 +1,4 @@ -import React, { useRef, useContext, useState, useEffect } from "react"; +import React, { useRef, useState, useEffect } from "react"; import { Flex, Label, Button } from "theme-ui"; import shortid from "shortid"; import Case from "case"; @@ -15,21 +15,21 @@ import LoadingOverlay from "../components/LoadingOverlay"; import blobToBuffer from "../helpers/blobToBuffer"; import { useSearch, useGroup, handleItemSelect } from "../helpers/select"; -import useKeyboard from "../hooks/useKeyboard"; import useResponsiveLayout from "../hooks/useResponsiveLayout"; -import TokenDataContext from "../contexts/TokenDataContext"; -import AuthContext from "../contexts/AuthContext"; +import { useTokenData } from "../contexts/TokenDataContext"; +import { useAuth } from "../contexts/AuthContext"; +import { useKeyboard } from "../contexts/KeyboardContext"; function SelectTokensModal({ isOpen, onRequestClose }) { - const { userId } = useContext(AuthContext); + const { userId } = useAuth(); const { ownedTokens, addToken, removeTokens, updateTokens, tokensLoading, - } = useContext(TokenDataContext); + } = useTokenData(); /** * Search diff --git a/src/modals/SettingsModal.js b/src/modals/SettingsModal.js index 6cf3901..e579922 100644 --- a/src/modals/SettingsModal.js +++ b/src/modals/SettingsModal.js @@ -1,4 +1,4 @@ -import React, { useState, useContext, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { Label, Flex, @@ -13,8 +13,8 @@ import prettyBytes from "pretty-bytes"; import Modal from "../components/Modal"; import Slider from "../components/Slider"; -import AuthContext from "../contexts/AuthContext"; -import DatabaseContext from "../contexts/DatabaseContext"; +import { useAuth } from "../contexts/AuthContext"; +import { useDatabase } from "../contexts/DatabaseContext"; import useSetting from "../hooks/useSetting"; @@ -22,8 +22,8 @@ import ConfirmModal from "./ConfirmModal"; import ImportExportModal from "./ImportExportModal"; function SettingsModal({ isOpen, onRequestClose }) { - const { database, databaseStatus } = useContext(DatabaseContext); - const { userId } = useContext(AuthContext); + const { database, databaseStatus } = useDatabase(); + const { userId } = useAuth(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [labelSize, setLabelSize] = useSetting("map.labelSize"); const [storageEstimate, setStorageEstimate] = useState(); diff --git a/src/modals/StartModal.js b/src/modals/StartModal.js index 87e1093..7aa23fd 100644 --- a/src/modals/StartModal.js +++ b/src/modals/StartModal.js @@ -1,9 +1,9 @@ -import React, { useContext, useRef } from "react"; +import React, { useRef } from "react"; import { Box, Label, Input, Button, Flex, Checkbox } from "theme-ui"; import { useHistory } from "react-router-dom"; import shortid from "shortid"; -import AuthContext from "../contexts/AuthContext"; +import { useAuth } from "../contexts/AuthContext"; import useSetting from "../hooks/useSetting"; @@ -11,7 +11,7 @@ import Modal from "../components/Modal"; function StartModal({ isOpen, onRequestClose }) { let history = useHistory(); - const { password, setPassword } = useContext(AuthContext); + const { password, setPassword } = useAuth(); function handlePasswordChange(event) { setPassword(event.target.value); diff --git a/src/network/NetworkedMapAndTokens.js b/src/network/NetworkedMapAndTokens.js index f54b7c1..538ba18 100644 --- a/src/network/NetworkedMapAndTokens.js +++ b/src/network/NetworkedMapAndTokens.js @@ -1,11 +1,11 @@ -import React, { useState, useContext, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef } from "react"; -import TokenDataContext from "../contexts/TokenDataContext"; -import MapDataContext from "../contexts/MapDataContext"; -import MapLoadingContext from "../contexts/MapLoadingContext"; -import AuthContext from "../contexts/AuthContext"; -import DatabaseContext from "../contexts/DatabaseContext"; -import PartyContext from "../contexts/PartyContext"; +import { useTokenData } from "../contexts/TokenDataContext"; +import { useMapData } from "../contexts/MapDataContext"; +import { useMapLoading } from "../contexts/MapLoadingContext"; +import { useAuth } from "../contexts/AuthContext"; +import { useDatabase } from "../contexts/DatabaseContext"; +import { useParty } from "../contexts/PartyContext"; import { omit } from "../helpers/shared"; @@ -35,21 +35,17 @@ const defaultMapActions = { * @param {NetworkedMapProps} props */ function NetworkedMapAndTokens({ session }) { - const { userId } = useContext(AuthContext); - const partyState = useContext(PartyContext); + const { userId } = useAuth(); + const partyState = useParty(); const { assetLoadStart, assetLoadFinish, assetProgressUpdate, isLoading, - } = useContext(MapLoadingContext); + } = useMapLoading(); - const { putToken, updateToken, getTokenFromDB } = useContext( - TokenDataContext - ); - const { putMap, updateMap, getMapFromDB, updateMapState } = useContext( - MapDataContext - ); + const { putToken, updateToken, getTokenFromDB } = useTokenData(); + const { putMap, updateMap, getMapFromDB, updateMapState } = useMapData(); const [currentMap, setCurrentMap] = useState(null); const [currentMapState, setCurrentMapState] = useNetworkedState( @@ -193,7 +189,7 @@ function NetworkedMapAndTokens({ session }) { * Map state */ - const { database } = useContext(DatabaseContext); + const { database } = useDatabase(); // Sync the map state to the database after 500ms of inactivity const debouncedMapState = useDebounce(currentMapState, 500); useEffect(() => { diff --git a/src/network/NetworkedMapPointer.js b/src/network/NetworkedMapPointer.js index d953239..6398eb1 100644 --- a/src/network/NetworkedMapPointer.js +++ b/src/network/NetworkedMapPointer.js @@ -1,7 +1,7 @@ -import React, { useState, useContext, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { Group } from "react-konva"; -import AuthContext from "../contexts/AuthContext"; +import { useAuth } from "../contexts/AuthContext"; import MapPointer from "../components/map/MapPointer"; import { isEmpty } from "../helpers/shared"; @@ -12,8 +12,8 @@ import useSetting from "../hooks/useSetting"; // Send pointer updates every 50ms (20fps) const sendTickRate = 50; -function NetworkedMapPointer({ session, active, gridSize }) { - const { userId } = useContext(AuthContext); +function NetworkedMapPointer({ session, active }) { + const { userId } = useAuth(); const [localPointerState, setLocalPointerState] = useState({}); const [pointerColor] = useSetting("pointer.color"); @@ -194,7 +194,6 @@ function NetworkedMapPointer({ session, active, gridSize }) { {Object.values(localPointerState).map((pointer) => ( { setPassword(""); }, [setPassword]);