Added shared grid context and moved away from calling useContext directly

This commit is contained in:
Mitchell McCaffrey 2021-02-06 13:32:38 +11:00
parent 8991be923e
commit f20173de35
60 changed files with 672 additions and 460 deletions

View File

@ -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(
<Line
key={`grid_x_${x}`}
points={[x * cellSize.width, 0, x * cellSize.width, insetHeight]}
points={[
x * gridCellPixelSize.width,
0,
x * gridCellPixelSize.width,
gridPixelSize.height,
]}
stroke={stroke}
strokeWidth={getStrokeWidth(
strokeWidth,
gridSizeNormalized,
width,
height
)}
strokeWidth={gridStrokeWidth * strokeWidth}
opacity={0.5}
offsetX={offsetX}
offsetY={offsetY}
offset={gridOffset}
/>
);
}
@ -52,17 +45,16 @@ function Grid({ grid, strokeWidth, width, height, stroke }) {
shapes.push(
<Line
key={`grid_y_${y}`}
points={[0, y * cellSize.height, insetWidth, y * cellSize.height]}
points={[
0,
y * gridCellPixelSize.height,
gridPixelSize.width,
y * gridCellPixelSize.height,
]}
stroke={stroke}
strokeWidth={getStrokeWidth(
strokeWidth,
gridSizeNormalized,
width,
height
)}
strokeWidth={gridStrokeWidth * strokeWidth}
opacity={0.5}
offsetX={offsetX}
offsetY={offsetY}
offset={gridOffset}
/>
);
}
@ -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(
<Group
key={`grid_${x}_${y}`}
clipFunc={
shouldClipCell(grid, x, y) &&
((context) => 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}
>
<RegularPolygon
sides={6}
radius={cellSize.radius}
radius={gridCellPixelSize.radius}
stroke={stroke}
strokeWidth={getStrokeWidth(
strokeWidth,
gridSizeNormalized,
width,
height
)}
strokeWidth={gridStrokeWidth * strokeWidth}
opacity={0.5}
rotation={grid.type === "hexVertical" ? 0 : 90}
/>

View File

@ -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() {

View File

@ -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({
<MapMeasure
map={map}
active={selectedToolId === "measure"}
gridSize={gridSizeNormalized}
selectedToolSettings={settings[selectedToolId]}
/>
);
@ -339,7 +326,6 @@ function Map({
const mapPointer = (
<NetworkedMapPointer
active={selectedToolId === "pointer"}
gridSize={gridSizeNormalized}
session={session}
/>
);
@ -377,7 +363,6 @@ function Map({
<MapNotes
map={map}
active={selectedToolId === "note"}
gridSize={gridSizeNormalized}
selectedToolSettings={settings[selectedToolId]}
onNoteAdd={onMapNoteChange}
onNoteChange={onMapNoteChange}

View File

@ -1,9 +1,10 @@
import React, { useContext, useState, useEffect } from "react";
import React, { useState, useEffect } from "react";
import shortid from "shortid";
import { Group, Line, Rect, Circle } 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 Vector2 from "../../helpers/Vector2";
import {
@ -11,7 +12,6 @@ import {
getDefaultShapeData,
getUpdatedShapeData,
simplifyPoints,
getStrokeWidth,
} from "../../helpers/drawing";
import colors from "../../helpers/colors";
@ -23,12 +23,15 @@ function MapDrawing({
onShapesRemove,
active,
toolSettings,
gridSize,
}) {
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 [erasingShapes, setErasingShapes] = useState([]);
@ -53,7 +56,7 @@ function MapDrawing({
map,
mapStage,
map.snapToGrid && isShape,
gridSize
gridCellNormalizedSize
);
const commonShapeData = {
color: toolSettings.color,
@ -85,7 +88,7 @@ function MapDrawing({
map,
mapStage,
map.snapToGrid && isShape,
gridSize
gridCellNormalizedSize
);
if (isBrushDown && drawingShape) {
if (isBrush) {
@ -102,7 +105,7 @@ function MapDrawing({
}
const simplified = simplifyPoints(
[...prevPoints, brushPosition],
gridSize,
gridCellNormalizedSize,
stageScale
);
return {
@ -117,7 +120,7 @@ function MapDrawing({
prevShape.shapeType,
prevShape.data,
brushPosition,
gridSize
gridCellNormalizedSize
),
}));
}
@ -191,12 +194,7 @@ function MapDrawing({
fillEnabled={shape.pathType === "fill"}
lineCap="round"
lineJoin="round"
strokeWidth={getStrokeWidth(
shape.strokeWidth,
gridSize,
mapWidth,
mapHeight
)}
strokeWidth={gridStrokeWidth * shape.strokeWidth}
{...defaultProps}
/>
);
@ -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}

View File

@ -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 }) {
<KeyboardContext.Provider value={keyboardValue}>
<MapInteractionProvider value={mapInteraction}>
{showGridControls && canEditGrid && (
<>
<GridProvider
grid={map.grid}
width={mapWidth}
height={mapHeight}
>
<MapGrid map={map} strokeWidth={0.5} />
<MapGridEditor map={map} onGridChange={handleGridChange} />
</>
</GridProvider>
)}
</MapInteractionProvider>
</KeyboardContext.Provider>

View File

@ -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"}

View File

@ -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 (
<Grid
grid={map?.grid}
strokeWidth={strokeWidth}
width={mapWidth}
height={mapHeight}
stroke={isImageLight ? "black" : "white"}
/>
<Grid strokeWidth={strokeWidth} stroke={isImageLight ? "black" : "white"} />
);
}

View File

@ -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 };

View File

@ -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({
<SettingsContext.Provider value={settings}>
<KeyboardContext.Provider value={keyboardValue}>
<MapInteractionProvider value={mapInteraction}>
<MapStageProvider value={mapStageRef}>
{mapLoaded && children}
</MapStageProvider>
<GridProvider
grid={map?.grid}
width={mapWidth}
height={mapHeight}
>
<MapStageProvider value={mapStageRef}>
{mapLoaded && children}
</MapStageProvider>
</GridProvider>
</MapInteractionProvider>
</KeyboardContext.Provider>
</SettingsContext.Provider>
@ -234,7 +239,9 @@ function MapInteraction({
</Stage>
</ReactResizeDetector>
<MapInteractionProvider value={mapInteraction}>
{controls}
<GridProvider grid={map?.grid} width={mapWidth} height={mapHeight}>
{controls}
</GridProvider>
</MapInteractionProvider>
</Box>
);

View File

@ -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 && (

View File

@ -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 }) {
<Group>
<Line
points={linePoints}
strokeWidth={getStrokeWidth(1.5, gridSize, mapWidth, mapHeight)}
strokeWidth={1.5 * gridStrokeWidth}
stroke="hsla(230, 25%, 18%, 0.8)"
lineCap="round"
/>
<Line
points={linePoints}
strokeWidth={getStrokeWidth(0.25, gridSize, mapWidth, mapHeight)}
strokeWidth={0.25 * gridStrokeWidth}
stroke="white"
lineCap="round"
/>

View File

@ -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,

View File

@ -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 (
<Group>

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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({

View File

@ -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

View File

@ -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);

View File

@ -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 && (
<Group offsetY={gridHeight - tokenHeight}>
<Grid
<GridProvider
grid={{
size: { x: gridX, y: gridY },
inset: {
@ -121,7 +123,9 @@ function TokenPreview({ token }) {
}}
width={gridWidth}
height={gridHeight}
/>
>
<Grid />
</GridProvider>
<Rect
width={gridWidth}
height={gridHeight}

View File

@ -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";
@ -12,7 +12,7 @@ import TokenTile from "./TokenTile";
import Link from "../Link";
import FilterBar from "../FilterBar";
import DatabaseContext from "../../contexts/DatabaseContext";
import { useDatabase } from "../../contexts/DatabaseContext";
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
@ -31,7 +31,7 @@ function TokenTiles({
onTokensGroup,
onTokensHide,
}) {
const { databaseStatus } = useContext(DatabaseContext);
const { databaseStatus } = useDatabase();
const layout = useResponsiveLayout();
let hasSelectedDefaultToken = selectedTokens.some(

View File

@ -1,4 +1,4 @@
import React, { useContext } from "react";
import React from "react";
import { Box, Flex } from "theme-ui";
import shortid from "shortid";
import SimpleBar from "simplebar-react";
@ -12,14 +12,14 @@ import { fromEntries } from "../../helpers/shared";
import useSetting from "../../hooks/useSetting";
import AuthContext from "../../contexts/AuthContext";
import TokenDataContext from "../../contexts/TokenDataContext";
import { useAuth } from "../../contexts/AuthContext";
import { useTokenData } from "../../contexts/TokenDataContext";
const listTokenClassName = "list-token";
function Tokens({ onMapTokenStateCreate }) {
const { userId } = useContext(AuthContext);
const { ownedTokens, tokens, updateToken } = useContext(TokenDataContext);
const { userId } = useAuth();
const { ownedTokens, tokens, updateToken } = useTokenData();
const [fullScreen] = useSetting("map.fullScreen");
function handleProxyDragEnd(isOnMap, token) {

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useContext } from "react";
import shortid from "shortid";
import DatabaseContext from "./DatabaseContext";
import { useDatabase } from "./DatabaseContext";
import FakeStorage from "../helpers/FakeStorage";
@ -18,7 +18,7 @@ try {
}
export function AuthProvider({ children }) {
const { database, databaseStatus } = useContext(DatabaseContext);
const { database, databaseStatus } = useDatabase();
const [password, setPassword] = useState(storage.getItem("auth") || "");
@ -57,4 +57,12 @@ export function AuthProvider({ children }) {
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
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;

View File

@ -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;

View File

@ -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;

View File

@ -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 (
<GridContext.Provider value={defaultValue}>
{children}
</GridContext.Provider>
);
}
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 <GridContext.Provider value={value}>{children}</GridContext.Provider>;
}
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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 }) {
</PlayerStateContext.Provider>
);
}
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;
}

View File

@ -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;

View File

@ -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;

65
src/helpers/Size.js Normal file
View File

@ -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;

View File

@ -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) {

View File

@ -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
);
}

View File

@ -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(

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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({});

View File

@ -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({});

View File

@ -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);

View File

@ -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",
},

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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(() => {

View File

@ -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) => (
<MapPointer
key={pointer.id}
gridSize={gridSize}
active={pointer.id === userId ? active : false}
position={pointer.position}
visible={pointer.visible}

View File

@ -1,10 +1,4 @@
import React, {
useContext,
useState,
useEffect,
useCallback,
useRef,
} from "react";
import React, { useState, useEffect, useCallback, useRef } from "react";
import { useToasts } from "react-toast-notifications";
// Load session for auto complete
@ -12,7 +6,7 @@ import { useToasts } from "react-toast-notifications";
import Session from "./Session";
import { isStreamStopped, omit } from "../helpers/shared";
import PartyContext from "../contexts/PartyContext";
import { useParty } from "../contexts/PartyContext";
import Party from "../components/party/Party";
@ -26,7 +20,7 @@ import Party from "../components/party/Party";
* @param {NetworkedPartyProps} props
*/
function NetworkedParty({ gameId, session }) {
const partyState = useContext(PartyContext);
const partyState = useParty();
const [stream, setStream] = useState(null);
const [partyStreams, setPartyStreams] = useState({});

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useContext, useRef } from "react";
import React, { useState, useEffect, useRef } from "react";
import { Flex, Box, Text } from "theme-ui";
import { useParams } from "react-router-dom";
@ -9,9 +9,9 @@ import MapLoadingOverlay from "../components/map/MapLoadingOverlay";
import AuthModal from "../modals/AuthModal";
import AuthContext from "../contexts/AuthContext";
import { useAuth } from "../contexts/AuthContext";
import { MapStageProvider } from "../contexts/MapStageContext";
import DatabaseContext from "../contexts/DatabaseContext";
import { useDatabase } from "../contexts/DatabaseContext";
import { PlayerProvider } from "../contexts/PlayerContext";
import { PartyProvider } from "../contexts/PartyContext";
@ -22,12 +22,8 @@ import Session from "../network/Session";
function Game() {
const { id: gameId } = useParams();
const {
authenticationStatus,
password,
setAuthenticationStatus,
} = useContext(AuthContext);
const { databaseStatus } = useContext(DatabaseContext);
const { authenticationStatus, password, setAuthenticationStatus } = useAuth();
const { databaseStatus } = useDatabase();
const [session] = useState(new Session());
const [offline, setOffline] = useState();

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useContext } from "react";
import React, { useState, useEffect } from "react";
import { Flex, Button, Image, Text, IconButton, Link } from "theme-ui";
import { useHistory } from "react-router-dom";
@ -10,7 +10,7 @@ import GettingStartedModal from "../modals/GettingStartedModal";
import HelpIcon from "../icons/HelpIcon";
import AuthContext from "../contexts/AuthContext";
import { useAuth } from "../contexts/AuthContext";
import RedditIcon from "../icons/SocialRedditIcon";
import TwitterIcon from "../icons/SocialTwitterIcon";
@ -28,7 +28,7 @@ function Home() {
);
// Reset password on visiting home
const { setPassword } = useContext(AuthContext);
const { setPassword } = useAuth();
useEffect(() => {
setPassword("");
}, [setPassword]);