commit
2a7817006f
@ -1,5 +1,5 @@
|
|||||||
REACT_APP_BROKER_URL=https://prod.owlbear.rodeo
|
REACT_APP_BROKER_URL=https://stage.owlbear.rodeo
|
||||||
REACT_APP_ICE_SERVERS_URL=https://prod.owlbear.rodeo/iceservers
|
REACT_APP_ICE_SERVERS_URL=https://stage.owlbear.rodeo/iceservers
|
||||||
REACT_APP_STRIPE_API_KEY=pk_live_MJjzi5djj524Y7h3fL5PNh4e00a852XD51
|
REACT_APP_STRIPE_API_KEY=pk_live_MJjzi5djj524Y7h3fL5PNh4e00a852XD51
|
||||||
REACT_APP_STRIPE_URL=https://payment.owlbear.rodeo
|
REACT_APP_STRIPE_URL=https://payment.owlbear.rodeo
|
||||||
REACT_APP_VERSION=$npm_package_version
|
REACT_APP_VERSION=$npm_package_version
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "owlbear-rodeo",
|
"name": "owlbear-rodeo",
|
||||||
"version": "1.10.0.2",
|
"version": "1.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babylonjs/core": "^4.2.0",
|
"@babylonjs/core": "^4.2.0",
|
||||||
@ -67,6 +67,7 @@
|
|||||||
"socket.io-msgpack-parser": "^3.0.1",
|
"socket.io-msgpack-parser": "^3.0.1",
|
||||||
"source-map-explorer": "^2.5.2",
|
"source-map-explorer": "^2.5.2",
|
||||||
"theme-ui": "^0.10.0",
|
"theme-ui": "^0.10.0",
|
||||||
|
"tiny-typed-emitter": "^2.1.0",
|
||||||
"use-image": "^1.0.8",
|
"use-image": "^1.0.8",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"webrtc-adapter": "^8.1.0"
|
"webrtc-adapter": "^8.1.0"
|
||||||
|
47
src/components/konva/BlankNote.tsx
Normal file
47
src/components/konva/BlankNote.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Rect } from "react-konva";
|
||||||
|
|
||||||
|
import {
|
||||||
|
useMapWidth,
|
||||||
|
useMapHeight,
|
||||||
|
} from "../../contexts/MapInteractionContext";
|
||||||
|
import { useGridCellPixelSize } from "../../contexts/GridContext";
|
||||||
|
|
||||||
|
import colors from "../../helpers/colors";
|
||||||
|
|
||||||
|
import { Note as NoteType } from "../../types/Note";
|
||||||
|
|
||||||
|
type NoteProps = {
|
||||||
|
note: NoteType;
|
||||||
|
};
|
||||||
|
|
||||||
|
function BlankNote({ note }: NoteProps) {
|
||||||
|
const mapWidth = useMapWidth();
|
||||||
|
const mapHeight = useMapHeight();
|
||||||
|
|
||||||
|
const gridCellPixelSize = useGridCellPixelSize();
|
||||||
|
|
||||||
|
const minCellSize = Math.min(
|
||||||
|
gridCellPixelSize.width,
|
||||||
|
gridCellPixelSize.height
|
||||||
|
);
|
||||||
|
const noteWidth = minCellSize * note.size;
|
||||||
|
const noteHeight = noteWidth;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Rect
|
||||||
|
x={note.x * mapWidth}
|
||||||
|
y={note.y * mapHeight}
|
||||||
|
width={noteWidth}
|
||||||
|
height={noteHeight}
|
||||||
|
offsetX={noteWidth / 2}
|
||||||
|
offsetY={noteHeight / 2}
|
||||||
|
shadowColor="rgba(0, 0, 0, 0.16)"
|
||||||
|
shadowOffset={{ x: 3, y: 6 }}
|
||||||
|
shadowBlur={6}
|
||||||
|
cornerRadius={0.25}
|
||||||
|
fill={colors[note.color]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlankNote;
|
@ -8,6 +8,7 @@ import {
|
|||||||
useSetPreventMapInteraction,
|
useSetPreventMapInteraction,
|
||||||
useMapWidth,
|
useMapWidth,
|
||||||
useMapHeight,
|
useMapHeight,
|
||||||
|
leftMouseButton,
|
||||||
} from "../../contexts/MapInteractionContext";
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useGridCellPixelSize } from "../../contexts/GridContext";
|
import { useGridCellPixelSize } from "../../contexts/GridContext";
|
||||||
|
|
||||||
@ -102,6 +103,9 @@ function Note({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleClick(event: Konva.KonvaEventObject<MouseEvent>) {
|
function handleClick(event: Konva.KonvaEventObject<MouseEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
const noteNode = event.target;
|
const noteNode = event.target;
|
||||||
onNoteMenuOpen && onNoteMenuOpen(note.id, noteNode, true);
|
onNoteMenuOpen && onNoteMenuOpen(note.id, noteNode, true);
|
||||||
@ -111,6 +115,9 @@ function Note({
|
|||||||
// Store note pointer down time to check for a click when note is locked
|
// Store note pointer down time to check for a click when note is locked
|
||||||
const notePointerDownTimeRef = useRef<number>(0);
|
const notePointerDownTimeRef = useRef<number>(0);
|
||||||
function handlePointerDown(event: Konva.KonvaEventObject<PointerEvent>) {
|
function handlePointerDown(event: Konva.KonvaEventObject<PointerEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(true);
|
setPreventMapInteraction(true);
|
||||||
}
|
}
|
||||||
@ -120,6 +127,9 @@ function Note({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handlePointerUp(event: Konva.KonvaEventObject<PointerEvent>) {
|
function handlePointerUp(event: Konva.KonvaEventObject<PointerEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(false);
|
setPreventMapInteraction(false);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
useSetPreventMapInteraction,
|
useSetPreventMapInteraction,
|
||||||
useMapWidth,
|
useMapWidth,
|
||||||
useMapHeight,
|
useMapHeight,
|
||||||
|
leftMouseButton,
|
||||||
} from "../../contexts/MapInteractionContext";
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useGridCellPixelSize } from "../../contexts/GridContext";
|
import { useGridCellPixelSize } from "../../contexts/GridContext";
|
||||||
import { useDataURL } from "../../contexts/AssetsContext";
|
import { useDataURL } from "../../contexts/AssetsContext";
|
||||||
@ -197,7 +198,10 @@ function Token({
|
|||||||
setAttachmentOverCharacter(false);
|
setAttachmentOverCharacter(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick(event: Konva.KonvaEventObject<MouseEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectable && draggable && transformRootRef.current) {
|
if (selectable && draggable && transformRootRef.current) {
|
||||||
onTokenMenuOpen(tokenState.id, transformRootRef.current, true);
|
onTokenMenuOpen(tokenState.id, transformRootRef.current, true);
|
||||||
}
|
}
|
||||||
@ -207,6 +211,9 @@ function Token({
|
|||||||
// Store token pointer down time to check for a click when token is locked
|
// Store token pointer down time to check for a click when token is locked
|
||||||
const tokenPointerDownTimeRef = useRef<number>(0);
|
const tokenPointerDownTimeRef = useRef<number>(0);
|
||||||
function handlePointerDown(event: Konva.KonvaEventObject<PointerEvent>) {
|
function handlePointerDown(event: Konva.KonvaEventObject<PointerEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(true);
|
setPreventMapInteraction(true);
|
||||||
}
|
}
|
||||||
@ -216,6 +223,9 @@ function Token({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handlePointerUp(event: Konva.KonvaEventObject<PointerEvent>) {
|
function handlePointerUp(event: Konva.KonvaEventObject<PointerEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(false);
|
setPreventMapInteraction(false);
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,20 @@ import { Box } from "theme-ui";
|
|||||||
import ReactResizeDetector from "react-resize-detector";
|
import ReactResizeDetector from "react-resize-detector";
|
||||||
import { Stage, Layer, Image, Group } from "react-konva";
|
import { Stage, Layer, Image, Group } from "react-konva";
|
||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import { EventEmitter } from "events";
|
|
||||||
|
|
||||||
import useMapImage from "../../hooks/useMapImage";
|
import useMapImage from "../../hooks/useMapImage";
|
||||||
import usePreventOverscroll from "../../hooks/usePreventOverscroll";
|
import usePreventOverscroll from "../../hooks/usePreventOverscroll";
|
||||||
import useStageInteraction from "../../hooks/useStageInteraction";
|
import useStageInteraction from "../../hooks/useStageInteraction";
|
||||||
import useImageCenter from "../../hooks/useImageCenter";
|
import useImageCenter from "../../hooks/useImageCenter";
|
||||||
|
import usePreventContextMenu from "../../hooks/usePreventContextMenu";
|
||||||
|
|
||||||
import { getGridMaxZoom } from "../../helpers/grid";
|
import { getGridMaxZoom } from "../../helpers/grid";
|
||||||
import KonvaBridge from "../../helpers/KonvaBridge";
|
import KonvaBridge from "../../helpers/KonvaBridge";
|
||||||
|
|
||||||
import { MapInteractionProvider } from "../../contexts/MapInteractionContext";
|
import {
|
||||||
|
MapInteractionEmitter,
|
||||||
|
MapInteractionProvider,
|
||||||
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
import { GridProvider } from "../../contexts/GridContext";
|
import { GridProvider } from "../../contexts/GridContext";
|
||||||
import { useKeyboard } from "../../contexts/KeyboardContext";
|
import { useKeyboard } from "../../contexts/KeyboardContext";
|
||||||
@ -72,6 +75,7 @@ function MapInteraction({
|
|||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
usePreventOverscroll(containerRef);
|
usePreventOverscroll(containerRef);
|
||||||
|
usePreventContextMenu(containerRef);
|
||||||
|
|
||||||
const [mapWidth, mapHeight] = useImageCenter(
|
const [mapWidth, mapHeight] = useImageCenter(
|
||||||
map,
|
map,
|
||||||
@ -85,8 +89,9 @@ function MapInteraction({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const previousSelectedToolRef = useRef(selectedToolId);
|
const previousSelectedToolRef = useRef(selectedToolId);
|
||||||
|
const [currentMouseButtons, setCurentMouseButtons] = useState(0);
|
||||||
|
|
||||||
const [interactionEmitter] = useState(new EventEmitter());
|
const [interactionEmitter] = useState(new MapInteractionEmitter());
|
||||||
|
|
||||||
useStageInteraction(
|
useStageInteraction(
|
||||||
mapStageRef,
|
mapStageRef,
|
||||||
@ -106,13 +111,17 @@ function MapInteraction({
|
|||||||
onPinchEnd: () => {
|
onPinchEnd: () => {
|
||||||
onSelectedToolChange(previousSelectedToolRef.current);
|
onSelectedToolChange(previousSelectedToolRef.current);
|
||||||
},
|
},
|
||||||
onDrag: ({ first, last }) => {
|
onDrag: (props) => {
|
||||||
|
const { first, last, buttons } = props;
|
||||||
|
if (buttons !== currentMouseButtons) {
|
||||||
|
setCurentMouseButtons(buttons);
|
||||||
|
}
|
||||||
if (first) {
|
if (first) {
|
||||||
interactionEmitter.emit("dragStart");
|
interactionEmitter.emit("dragStart", props);
|
||||||
} else if (last) {
|
} else if (last) {
|
||||||
interactionEmitter.emit("dragEnd");
|
interactionEmitter.emit("dragEnd", props);
|
||||||
} else {
|
} else {
|
||||||
interactionEmitter.emit("drag");
|
interactionEmitter.emit("drag", props);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -143,6 +152,11 @@ function MapInteraction({
|
|||||||
useKeyboard(handleKeyDown, handleKeyUp);
|
useKeyboard(handleKeyDown, handleKeyUp);
|
||||||
|
|
||||||
function getCursorForTool(tool: MapToolId) {
|
function getCursorForTool(tool: MapToolId) {
|
||||||
|
if (currentMouseButtons === 2) {
|
||||||
|
return "crosshair";
|
||||||
|
} else if (currentMouseButtons > 2) {
|
||||||
|
return "move";
|
||||||
|
}
|
||||||
switch (tool) {
|
switch (tool) {
|
||||||
case "move":
|
case "move":
|
||||||
return "move";
|
return "move";
|
||||||
|
@ -7,6 +7,8 @@ import {
|
|||||||
useMapWidth,
|
useMapWidth,
|
||||||
useMapHeight,
|
useMapHeight,
|
||||||
useInteractionEmitter,
|
useInteractionEmitter,
|
||||||
|
leftMouseButton,
|
||||||
|
MapDragEvent,
|
||||||
} from "../../contexts/MapInteractionContext";
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
import {
|
import {
|
||||||
@ -103,7 +105,10 @@ function DrawingTool({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition) {
|
if (!brushPosition) {
|
||||||
return;
|
return;
|
||||||
@ -135,7 +140,10 @@ function DrawingTool({
|
|||||||
setIsBrushDown(true);
|
setIsBrushDown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition) {
|
if (!brushPosition) {
|
||||||
return;
|
return;
|
||||||
@ -186,7 +194,10 @@ function DrawingTool({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isBrush && drawing && drawing.type === "path") {
|
if (isBrush && drawing && drawing.type === "path") {
|
||||||
if (drawing.data.points.length > 1) {
|
if (drawing.data.points.length > 1) {
|
||||||
onShapeAdd(drawing);
|
onShapeAdd(drawing);
|
||||||
|
@ -3,6 +3,7 @@ import shortid from "shortid";
|
|||||||
import { Group, Line } from "react-konva";
|
import { Group, Line } from "react-konva";
|
||||||
import useImage from "use-image";
|
import useImage from "use-image";
|
||||||
import Color from "color";
|
import Color from "color";
|
||||||
|
import Konva from "konva";
|
||||||
|
|
||||||
import diagonalPattern from "../../images/DiagonalPattern.png";
|
import diagonalPattern from "../../images/DiagonalPattern.png";
|
||||||
|
|
||||||
@ -11,6 +12,8 @@ import {
|
|||||||
useMapWidth,
|
useMapWidth,
|
||||||
useMapHeight,
|
useMapHeight,
|
||||||
useInteractionEmitter,
|
useInteractionEmitter,
|
||||||
|
MapDragEvent,
|
||||||
|
leftMouseButton,
|
||||||
} from "../../contexts/MapInteractionContext";
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
import {
|
import {
|
||||||
@ -104,7 +107,7 @@ function FogTool({
|
|||||||
|
|
||||||
const [drawingShape, setDrawingShape] = useState<Fog | null>(null);
|
const [drawingShape, setDrawingShape] = useState<Fog | null>(null);
|
||||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||||
const [editingShapes, setEditingShapes] = useState<Fog[]>([]);
|
const [hoveredShapes, setHoveredShapes] = useState<Fog[]>([]);
|
||||||
|
|
||||||
// Shapes that have been merged for fog
|
// Shapes that have been merged for fog
|
||||||
const [fogShapes, setFogShapes] = useState(shapes);
|
const [fogShapes, setFogShapes] = useState(shapes);
|
||||||
@ -160,7 +163,10 @@ function FogTool({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (toolSettings.type === "brush") {
|
if (toolSettings.type === "brush") {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition) {
|
if (!brushPosition) {
|
||||||
@ -203,7 +209,10 @@ function FogTool({
|
|||||||
setIsBrushDown(true);
|
setIsBrushDown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
|
if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition) {
|
if (!brushPosition) {
|
||||||
@ -258,7 +267,10 @@ function FogTool({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(toolSettings.type === "brush" || toolSettings.type === "rectangle") &&
|
(toolSettings.type === "brush" || toolSettings.type === "rectangle") &&
|
||||||
drawingShape
|
drawingShape
|
||||||
@ -318,7 +330,10 @@ function FogTool({
|
|||||||
setIsBrushDown(false);
|
setIsBrushDown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePointerClick() {
|
function handlePointerClick(event: Konva.KonvaEventObject<MouseEvent>) {
|
||||||
|
if (!leftMouseButton(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (toolSettings.type === "polygon") {
|
if (toolSettings.type === "polygon") {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (brushPosition) {
|
if (brushPosition) {
|
||||||
@ -553,25 +568,25 @@ function FogTool({
|
|||||||
|
|
||||||
function eraseHoveredShapes() {
|
function eraseHoveredShapes() {
|
||||||
// Erase
|
// Erase
|
||||||
if (editingShapes.length > 0) {
|
if (hoveredShapes.length > 0) {
|
||||||
if (toolSettings.type === "remove") {
|
if (toolSettings.type === "remove") {
|
||||||
onShapesRemove(editingShapes.map((shape) => shape.id));
|
onShapesRemove(hoveredShapes.map((shape) => shape.id));
|
||||||
} else if (toolSettings.type === "toggle") {
|
} else if (toolSettings.type === "toggle") {
|
||||||
onShapesEdit(
|
onShapesEdit(
|
||||||
editingShapes.map((shape) => ({
|
hoveredShapes.map((shape) => ({
|
||||||
id: shape.id,
|
id: shape.id,
|
||||||
visible: !shape.visible,
|
visible: !shape.visible,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
setEditingShapes([]);
|
setHoveredShapes([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShapeOver(shape: Fog, isDown: boolean) {
|
function handleShapeOver(shape: Fog, isDown: boolean) {
|
||||||
if (shouldHover && isDown) {
|
if (shouldHover && isDown) {
|
||||||
if (editingShapes.findIndex((s) => s.id === shape.id) === -1) {
|
if (hoveredShapes.findIndex((s) => s.id === shape.id) === -1) {
|
||||||
setEditingShapes((prevShapes) => [...prevShapes, shape]);
|
setHoveredShapes((prevShapes) => [...prevShapes, shape]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -609,7 +624,7 @@ function FogTool({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEditingShape(shape: Fog) {
|
function renderHoveredShape(shape: Fog) {
|
||||||
const editingShape: Fog = {
|
const editingShape: Fog = {
|
||||||
...shape,
|
...shape,
|
||||||
color: "primary",
|
color: "primary",
|
||||||
@ -617,6 +632,27 @@ function FogTool({
|
|||||||
return renderShape(editingShape);
|
return renderShape(editingShape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderDrawingShape(shape: Fog) {
|
||||||
|
const opacity = editable ? editOpacity : 1;
|
||||||
|
const stroke =
|
||||||
|
editable && active
|
||||||
|
? colors.lightGray
|
||||||
|
: colors[shape.color] || shape.color;
|
||||||
|
const fill = new Color(colors[shape.color] || shape.color)
|
||||||
|
.alpha(opacity)
|
||||||
|
.string();
|
||||||
|
return (
|
||||||
|
<FogShape
|
||||||
|
fog={shape}
|
||||||
|
fill={fill}
|
||||||
|
stroke={stroke}
|
||||||
|
opacity={opacity}
|
||||||
|
strokeWidth={gridStrokeWidth * shape.strokeWidth}
|
||||||
|
hitFunc={() => {}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function renderPolygonAcceptTick(shape: Fog) {
|
function renderPolygonAcceptTick(shape: Fog) {
|
||||||
if (shape.data.points.length === 0) {
|
if (shape.data.points.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
@ -681,12 +717,12 @@ function FogTool({
|
|||||||
<Group>
|
<Group>
|
||||||
<Group>{fogShapes.map(renderShape)}</Group>
|
<Group>{fogShapes.map(renderShape)}</Group>
|
||||||
{shouldRenderGuides && renderGuides()}
|
{shouldRenderGuides && renderGuides()}
|
||||||
{drawingShape && renderShape(drawingShape)}
|
{drawingShape && renderDrawingShape(drawingShape)}
|
||||||
{drawingShape &&
|
{drawingShape &&
|
||||||
toolSettings &&
|
toolSettings &&
|
||||||
toolSettings.type === "polygon" &&
|
toolSettings.type === "polygon" &&
|
||||||
renderPolygonAcceptTick(drawingShape)}
|
renderPolygonAcceptTick(drawingShape)}
|
||||||
{editingShapes.length > 0 && editingShapes.map(renderEditingShape)}
|
{hoveredShapes.length > 0 && hoveredShapes.map(renderHoveredShape)}
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
|
|
||||||
import { useInteractionEmitter } from "../../contexts/MapInteractionContext";
|
import {
|
||||||
|
useInteractionEmitter,
|
||||||
|
MapDragEvent,
|
||||||
|
leftMouseButton,
|
||||||
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
import {
|
import {
|
||||||
useGrid,
|
useGrid,
|
||||||
@ -40,8 +44,9 @@ function MeasureTool({ map, active }: MapMeasureProps) {
|
|||||||
const gridOffset = useGridOffset();
|
const gridOffset = useGridOffset();
|
||||||
|
|
||||||
const mapStageRef = useMapStage();
|
const mapStageRef = useMapStage();
|
||||||
const [drawingShapeData, setDrawingShapeData] =
|
const [drawingShapeData, setDrawingShapeData] = useState<MeasureData | null>(
|
||||||
useState<MeasureData | null>(null);
|
null
|
||||||
|
);
|
||||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||||
|
|
||||||
const gridScale = parseGridScale(active ? grid.measurement.scale : null);
|
const gridScale = parseGridScale(active ? grid.measurement.scale : null);
|
||||||
@ -75,7 +80,10 @@ function MeasureTool({ map, active }: MapMeasureProps) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition) {
|
if (!brushPosition) {
|
||||||
return;
|
return;
|
||||||
@ -89,7 +97,10 @@ function MeasureTool({ map, active }: MapMeasureProps) {
|
|||||||
setIsBrushDown(true);
|
setIsBrushDown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (isBrushDown && drawingShapeData && brushPosition && mapImage) {
|
if (isBrushDown && drawingShapeData && brushPosition && mapImage) {
|
||||||
const { points } = getUpdatedShapeData(
|
const { points } = getUpdatedShapeData(
|
||||||
@ -123,7 +134,10 @@ function MeasureTool({ map, active }: MapMeasureProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setDrawingShapeData(null);
|
setDrawingShapeData(null);
|
||||||
setIsBrushDown(false);
|
setIsBrushDown(false);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,11 @@ import shortid from "shortid";
|
|||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
|
|
||||||
import { useInteractionEmitter } from "../../contexts/MapInteractionContext";
|
import {
|
||||||
|
useInteractionEmitter,
|
||||||
|
MapDragEvent,
|
||||||
|
leftMouseButton,
|
||||||
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
import { useUserId } from "../../contexts/UserIdContext";
|
import { useUserId } from "../../contexts/UserIdContext";
|
||||||
|
|
||||||
@ -12,7 +16,7 @@ import { getRelativePointerPosition } from "../../helpers/konva";
|
|||||||
|
|
||||||
import useGridSnapping from "../../hooks/useGridSnapping";
|
import useGridSnapping from "../../hooks/useGridSnapping";
|
||||||
|
|
||||||
import Note from "../konva/Note";
|
import BlankNote from "../konva/BlankNote";
|
||||||
|
|
||||||
import { Map } from "../../types/Map";
|
import { Map } from "../../types/Map";
|
||||||
import { Note as NoteType } from "../../types/Note";
|
import { Note as NoteType } from "../../types/Note";
|
||||||
@ -28,20 +32,12 @@ type MapNoteProps = {
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
onNoteCreate: NoteCreateEventHander;
|
onNoteCreate: NoteCreateEventHander;
|
||||||
onNoteMenuOpen: NoteMenuOpenEventHandler;
|
onNoteMenuOpen: NoteMenuOpenEventHandler;
|
||||||
children: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function NoteTool({
|
function NoteTool({ map, active, onNoteCreate, onNoteMenuOpen }: MapNoteProps) {
|
||||||
map,
|
|
||||||
active,
|
|
||||||
onNoteCreate,
|
|
||||||
onNoteMenuOpen,
|
|
||||||
children,
|
|
||||||
}: MapNoteProps) {
|
|
||||||
const interactionEmitter = useInteractionEmitter();
|
const interactionEmitter = useInteractionEmitter();
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
const mapStageRef = useMapStage();
|
const mapStageRef = useMapStage();
|
||||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
|
||||||
const [noteData, setNoteData] = useState<NoteType | null>(null);
|
const [noteData, setNoteData] = useState<NoteType | null>(null);
|
||||||
|
|
||||||
const creatingNoteRef = useRef<Konva.Group>(null);
|
const creatingNoteRef = useRef<Konva.Group>(null);
|
||||||
@ -72,7 +68,10 @@ function NoteTool({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition || !userId) {
|
if (!brushPosition || !userId) {
|
||||||
return;
|
return;
|
||||||
@ -91,10 +90,15 @@ function NoteTool({
|
|||||||
textOnly: false,
|
textOnly: false,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
});
|
});
|
||||||
setIsBrushDown(true);
|
if (creatingNoteRef.current) {
|
||||||
|
creatingNoteRef.current.visible(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (noteData) {
|
if (noteData) {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition) {
|
if (!brushPosition) {
|
||||||
@ -110,17 +114,20 @@ function NoteTool({
|
|||||||
y: brushPosition.y,
|
y: brushPosition.y,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
setIsBrushDown(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (noteData && creatingNoteRef.current) {
|
if (noteData && creatingNoteRef.current) {
|
||||||
onNoteCreate([noteData]);
|
onNoteCreate([noteData]);
|
||||||
onNoteMenuOpen(noteData.id, creatingNoteRef.current, true);
|
onNoteMenuOpen(noteData.id, creatingNoteRef.current, true);
|
||||||
|
// Hide creating note tool here as settings noteData to null
|
||||||
|
// was causing performance issues in FireFox
|
||||||
|
creatingNoteRef.current.visible(false);
|
||||||
}
|
}
|
||||||
setNoteData(null);
|
|
||||||
setIsBrushDown(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interactionEmitter?.on("dragStart", handleBrushDown);
|
interactionEmitter?.on("dragStart", handleBrushDown);
|
||||||
@ -135,13 +142,8 @@ function NoteTool({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group id="notes">
|
<Group ref={creatingNoteRef}>
|
||||||
{children}
|
{noteData && <BlankNote note={noteData} />}
|
||||||
<Group ref={creatingNoteRef}>
|
|
||||||
{isBrushDown && noteData && (
|
|
||||||
<Note note={noteData} map={map} selected={false} />
|
|
||||||
)}
|
|
||||||
</Group>
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useMapWidth,
|
useMapWidth,
|
||||||
useMapHeight,
|
useMapHeight,
|
||||||
useInteractionEmitter,
|
useInteractionEmitter,
|
||||||
|
MapDragEvent,
|
||||||
|
leftMouseButton,
|
||||||
|
rightMouseButton,
|
||||||
} from "../../contexts/MapInteractionContext";
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
import { useGridStrokeWidth } from "../../contexts/GridContext";
|
import { useGridStrokeWidth } from "../../contexts/GridContext";
|
||||||
@ -41,11 +44,9 @@ function PointerTool({
|
|||||||
const gridStrokeWidth = useGridStrokeWidth();
|
const gridStrokeWidth = useGridStrokeWidth();
|
||||||
const mapStageRef = useMapStage();
|
const mapStageRef = useMapStage();
|
||||||
|
|
||||||
useEffect(() => {
|
const brushDownRef = useRef(false);
|
||||||
if (!active) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
function getBrushPosition() {
|
||||||
@ -56,19 +57,27 @@ function PointerTool({
|
|||||||
return getRelativePointerPositionNormalized(mapImage);
|
return getRelativePointerPositionNormalized(mapImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown(props: MapDragEvent) {
|
||||||
const brushPosition = getBrushPosition();
|
if ((leftMouseButton(props) && active) || rightMouseButton(props)) {
|
||||||
brushPosition && onPointerDown?.(brushPosition);
|
const brushPosition = getBrushPosition();
|
||||||
|
brushPosition && onPointerDown?.(brushPosition);
|
||||||
|
brushDownRef.current = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
const brushPosition = getBrushPosition();
|
if (brushDownRef.current) {
|
||||||
brushPosition && visible && onPointerMove?.(brushPosition);
|
const brushPosition = getBrushPosition();
|
||||||
|
brushPosition && visible && onPointerMove?.(brushPosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp() {
|
||||||
const brushPosition = getBrushPosition();
|
if (brushDownRef.current) {
|
||||||
brushPosition && onPointerUp?.(brushPosition);
|
const brushPosition = getBrushPosition();
|
||||||
|
brushPosition && onPointerUp?.(brushPosition);
|
||||||
|
brushDownRef.current = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interactionEmitter?.on("dragStart", handleBrushDown);
|
interactionEmitter?.on("dragStart", handleBrushDown);
|
||||||
|
@ -7,6 +7,8 @@ import {
|
|||||||
useMapWidth,
|
useMapWidth,
|
||||||
useMapHeight,
|
useMapHeight,
|
||||||
useInteractionEmitter,
|
useInteractionEmitter,
|
||||||
|
MapDragEvent,
|
||||||
|
leftMouseButton,
|
||||||
} from "../../contexts/MapInteractionContext";
|
} from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
|
|
||||||
@ -96,7 +98,10 @@ function SelectTool({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition || preventSelectionRef.current) {
|
if (!brushPosition || preventSelectionRef.current) {
|
||||||
return;
|
return;
|
||||||
@ -121,7 +126,10 @@ function SelectTool({
|
|||||||
setIsBrushDown(true);
|
setIsBrushDown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (!brushPosition || preventSelectionRef.current) {
|
if (!brushPosition || preventSelectionRef.current) {
|
||||||
return;
|
return;
|
||||||
@ -172,7 +180,10 @@ function SelectTool({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp(props: MapDragEvent) {
|
||||||
|
if (!leftMouseButton(props)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (preventSelectionRef.current) {
|
if (preventSelectionRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,22 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { EventEmitter } from "stream";
|
import { FullGestureState } from "react-use-gesture/dist/types";
|
||||||
import useDebounce from "../hooks/useDebounce";
|
import useDebounce from "../hooks/useDebounce";
|
||||||
|
import { TypedEmitter } from "tiny-typed-emitter";
|
||||||
|
import Konva from "konva";
|
||||||
|
|
||||||
|
export type MapDragEvent = Omit<FullGestureState<"drag">, "event"> & {
|
||||||
|
event: React.PointerEvent<Element> | PointerEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MapDragEventHandler = (props: MapDragEvent) => void;
|
||||||
|
|
||||||
|
export interface MapInteractionEvents {
|
||||||
|
dragStart: MapDragEventHandler;
|
||||||
|
drag: MapDragEventHandler;
|
||||||
|
dragEnd: MapDragEventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MapInteractionEmitter extends TypedEmitter<MapInteractionEvents> {}
|
||||||
|
|
||||||
type MapInteraction = {
|
type MapInteraction = {
|
||||||
stageScale: number;
|
stageScale: number;
|
||||||
@ -9,29 +25,33 @@ type MapInteraction = {
|
|||||||
setPreventMapInteraction: React.Dispatch<React.SetStateAction<boolean>>;
|
setPreventMapInteraction: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
mapWidth: number;
|
mapWidth: number;
|
||||||
mapHeight: number;
|
mapHeight: number;
|
||||||
interactionEmitter: EventEmitter | null;
|
interactionEmitter: MapInteractionEmitter | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StageScaleContext =
|
export const StageScaleContext = React.createContext<
|
||||||
React.createContext<MapInteraction["stageScale"] | undefined>(undefined);
|
MapInteraction["stageScale"] | undefined
|
||||||
export const DebouncedStageScaleContext =
|
>(undefined);
|
||||||
React.createContext<MapInteraction["stageScale"] | undefined>(undefined);
|
export const DebouncedStageScaleContext = React.createContext<
|
||||||
export const StageWidthContext =
|
MapInteraction["stageScale"] | undefined
|
||||||
React.createContext<MapInteraction["stageWidth"] | undefined>(undefined);
|
>(undefined);
|
||||||
export const StageHeightContext =
|
export const StageWidthContext = React.createContext<
|
||||||
React.createContext<MapInteraction["stageHeight"] | undefined>(undefined);
|
MapInteraction["stageWidth"] | undefined
|
||||||
export const SetPreventMapInteractionContext =
|
>(undefined);
|
||||||
React.createContext<MapInteraction["setPreventMapInteraction"] | undefined>(
|
export const StageHeightContext = React.createContext<
|
||||||
undefined
|
MapInteraction["stageHeight"] | undefined
|
||||||
);
|
>(undefined);
|
||||||
export const MapWidthContext =
|
export const SetPreventMapInteractionContext = React.createContext<
|
||||||
React.createContext<MapInteraction["mapWidth"] | undefined>(undefined);
|
MapInteraction["setPreventMapInteraction"] | undefined
|
||||||
export const MapHeightContext =
|
>(undefined);
|
||||||
React.createContext<MapInteraction["mapHeight"] | undefined>(undefined);
|
export const MapWidthContext = React.createContext<
|
||||||
export const InteractionEmitterContext =
|
MapInteraction["mapWidth"] | undefined
|
||||||
React.createContext<MapInteraction["interactionEmitter"] | undefined>(
|
>(undefined);
|
||||||
undefined
|
export const MapHeightContext = React.createContext<
|
||||||
);
|
MapInteraction["mapHeight"] | undefined
|
||||||
|
>(undefined);
|
||||||
|
export const InteractionEmitterContext = React.createContext<
|
||||||
|
MapInteraction["interactionEmitter"] | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
export function MapInteractionProvider({
|
export function MapInteractionProvider({
|
||||||
value,
|
value,
|
||||||
@ -152,3 +172,30 @@ export function useDebouncedStageScale() {
|
|||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function leftMouseButton(event: MapDragEvent): boolean;
|
||||||
|
export function leftMouseButton(
|
||||||
|
event: Konva.KonvaEventObject<PointerEvent>
|
||||||
|
): boolean;
|
||||||
|
export function leftMouseButton(
|
||||||
|
event: Konva.KonvaEventObject<MouseEvent>
|
||||||
|
): boolean;
|
||||||
|
|
||||||
|
export function leftMouseButton(event: any) {
|
||||||
|
if (event.evt) {
|
||||||
|
// Konva events
|
||||||
|
// Check for undefined (touch) and mouse left click (0)
|
||||||
|
return event.evt.button === undefined || event.evt.button === 0;
|
||||||
|
} else {
|
||||||
|
// Drag event
|
||||||
|
return event.buttons <= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function middleMouseButton(event: MapDragEvent) {
|
||||||
|
return event.buttons === 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rightMouseButton(event: MapDragEvent) {
|
||||||
|
return event.buttons === 2;
|
||||||
|
}
|
||||||
|
@ -23,10 +23,10 @@ export function PartyProvider({ session, children }: PartyProviderProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.socket?.on("party_state", handleSocketPartyState);
|
session.socket.on("party_state", handleSocketPartyState);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
session.socket?.off("party_state", handleSocketPartyState);
|
session.socket.off("party_state", handleSocketPartyState);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,10 +9,12 @@ import useNetworkedState, {
|
|||||||
import Session, { SessionStatus } from "../network/Session";
|
import Session, { SessionStatus } from "../network/Session";
|
||||||
import { PlayerState } from "../types/PlayerState";
|
import { PlayerState } from "../types/PlayerState";
|
||||||
|
|
||||||
export const PlayerStateContext =
|
export const PlayerStateContext = React.createContext<PlayerState | undefined>(
|
||||||
React.createContext<PlayerState | undefined>(undefined);
|
undefined
|
||||||
export const PlayerUpdaterContext =
|
);
|
||||||
React.createContext<SetNetworkedState<PlayerState> | undefined>(undefined);
|
export const PlayerUpdaterContext = React.createContext<
|
||||||
|
SetNetworkedState<PlayerState> | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
type PlayerProviderProps = {
|
type PlayerProviderProps = {
|
||||||
session: Session;
|
session: Session;
|
||||||
@ -104,13 +106,13 @@ export function PlayerProvider({ session, children }: PlayerProviderProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
session.on("status", handleSocketStatus);
|
session.on("status", handleSocketStatus);
|
||||||
session.socket?.on("connect", handleSocketConnect);
|
session.socket.on("connect", handleSocketConnect);
|
||||||
session.socket?.io.on("reconnect", handleSocketConnect);
|
session.socket.io.on("reconnect", handleSocketConnect);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
session.off("status", handleSocketStatus);
|
session.off("status", handleSocketStatus);
|
||||||
session.socket?.off("connect", handleSocketConnect);
|
session.socket.off("connect", handleSocketConnect);
|
||||||
session.socket?.io.off("reconnect", handleSocketConnect);
|
session.socket.io.off("reconnect", handleSocketConnect);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
## General
|
## General
|
||||||
|
|
||||||
| Shortcut | Description |
|
| Shortcut | Description |
|
||||||
| ---------------- | ----------------------- |
|
| ------------------- | ----------------------- |
|
||||||
| W | Move Tool |
|
| W | Move Tool |
|
||||||
| Space Bar (Hold) | Move Tool |
|
| Space Bar (Hold) | Move Tool |
|
||||||
| F | Fog Tool |
|
| Middle Mouse Button | Move Tool |
|
||||||
| D | Drawing Tool |
|
| F | Fog Tool |
|
||||||
| M | Measure Tool |
|
| D | Drawing Tool |
|
||||||
| Q | Pointer Tool |
|
| M | Measure Tool |
|
||||||
| N | Note Tool |
|
| Q | Pointer Tool |
|
||||||
| + | Zoom In |
|
| Right Mouse Button | Pointer Tool |
|
||||||
| - | Zoom Out |
|
| N | Note Tool |
|
||||||
| Shift + Zoom | Precision Zoom |
|
| + | Zoom In |
|
||||||
| Ctrl + Z | Undo |
|
| - | Zoom Out |
|
||||||
| Ctrl + Shift + Z | Redo |
|
| Shift + Zoom | Precision Zoom |
|
||||||
| Ctrl (Hold) | Disabled Grid Snapping |
|
| Ctrl + Z | Undo |
|
||||||
| Alt + Drag | Duplicate Token or Note |
|
| Ctrl + Shift + Z | Redo |
|
||||||
|
| Ctrl (Hold) | Disabled Grid Snapping |
|
||||||
|
| Alt + Drag | Duplicate Token or Note |
|
||||||
|
|
||||||
## Select Tool
|
## Select Tool
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
![embed:](https://www.youtube.com/embed/Er_grVmqpk0)
|
|
||||||
|
|
||||||
Owlbear Rodeo supports a physically simulated 3D dice tray and dice. To access these features click the Show Dice Tray icon in the top left of the map view.
|
Owlbear Rodeo supports a physically simulated 3D dice tray and dice. To access these features click the Show Dice Tray icon in the top left of the map view.
|
||||||
|
|
||||||
![Open Dice Tray](openDiceTray)
|
![Open Dice Tray](openDiceTray)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
![embed:](https://www.youtube.com/embed/2e07DtB-Xrc)
|
|
||||||
|
|
||||||
The Drawing Tool allows you to draw on top of a map. To access the Drawing Tool click the Drawing Tool button in the top right of the map view.
|
The Drawing Tool allows you to draw on top of a map. To access the Drawing Tool click the Drawing Tool button in the top right of the map view.
|
||||||
|
|
||||||
![Using Drawing](usingDrawing)
|
![Using Drawing](usingDrawing)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
![embed:](https://www.youtube.com/embed/MCOzpQ4auqs)
|
||||||
|
|
||||||
Once you have a map shared between a party all players can drag tokens from the Token List on the right hand side of the screen. Tokens can then be used to represent players, monsters or any other object that needs to be moved around the map.
|
Once you have a map shared between a party all players can drag tokens from the Token List on the right hand side of the screen. Tokens can then be used to represent players, monsters or any other object that needs to be moved around the map.
|
||||||
|
|
||||||
## Default Tokens
|
## Default Tokens
|
||||||
|
20
src/docs/releaseNotes/v1.10.1.md
Normal file
20
src/docs/releaseNotes/v1.10.1.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
## Minor Changes
|
||||||
|
|
||||||
|
This is a small release fixing a few bugs and adding some QoL improvements.
|
||||||
|
|
||||||
|
- Updated alternating diagonals measurement to fix inaccuracy with diagonal distances.
|
||||||
|
- Added support for middle mouse button move shortcut.
|
||||||
|
- Added support for right mouse button pointer shortcut.
|
||||||
|
- Optimised note creation to reduce lag when creating a note on FireFox.
|
||||||
|
- Fixed a bug with using the polygon fog tool on touch devices.
|
||||||
|
- Fixed a bug that would cause phantom players to be shown in the party view on a server error.
|
||||||
|
|
||||||
|
We are currently working on some big changes that will take some time so expect smaller releases like this one until that is ready. If you'd like to keep up with the development of these changes check out our [Patreon](https://patreon.com/owlbearrodeo) where we are sharing previews of what's to come.
|
||||||
|
|
||||||
|
[Reddit]()
|
||||||
|
[Twitter]()
|
||||||
|
[Patreon]()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
October 21 2021
|
@ -325,7 +325,7 @@ export function gridDistance(
|
|||||||
const delta = Vector2.abs(Vector2.subtract(aCoord, bCoord));
|
const delta = Vector2.abs(Vector2.subtract(aCoord, bCoord));
|
||||||
const max = Vector2.componentMax(delta);
|
const max = Vector2.componentMax(delta);
|
||||||
const min = Vector2.componentMin(delta);
|
const min = Vector2.componentMin(delta);
|
||||||
return max - min + Math.floor(1.5 * min);
|
return max + Math.floor(0.5 * min);
|
||||||
} else if (grid.measurement.type === "euclidean") {
|
} else if (grid.measurement.type === "euclidean") {
|
||||||
return Vector2.magnitude(
|
return Vector2.magnitude(
|
||||||
Vector2.divide(Vector2.subtract(a, b), cellSize)
|
Vector2.divide(Vector2.subtract(a, b), cellSize)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
|
import { Group } from "react-konva";
|
||||||
import { KonvaEventObject } from "konva/lib/Node";
|
import { KonvaEventObject } from "konva/lib/Node";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
@ -95,12 +96,7 @@ function useMapNotes(
|
|||||||
useBlur(handleBlur);
|
useBlur(handleBlur);
|
||||||
|
|
||||||
const notes = (
|
const notes = (
|
||||||
<NoteTool
|
<Group id="notes">
|
||||||
map={map}
|
|
||||||
active={selectedToolId === "note"}
|
|
||||||
onNoteCreate={onNoteCreate}
|
|
||||||
onNoteMenuOpen={handleNoteMenuOpen}
|
|
||||||
>
|
|
||||||
{(mapState
|
{(mapState
|
||||||
? Object.values(mapState.notes).sort((a, b) =>
|
? Object.values(mapState.notes).sort((a, b) =>
|
||||||
sortNotes(a, b, noteDraggingOptions)
|
sortNotes(a, b, noteDraggingOptions)
|
||||||
@ -129,7 +125,13 @@ function useMapNotes(
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</NoteTool>
|
<NoteTool
|
||||||
|
map={map}
|
||||||
|
active={selectedToolId === "note"}
|
||||||
|
onNoteCreate={onNoteCreate}
|
||||||
|
onNoteMenuOpen={handleNoteMenuOpen}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
);
|
);
|
||||||
|
|
||||||
const noteMenu = (
|
const noteMenu = (
|
||||||
|
@ -60,7 +60,7 @@ function useNetworkedState<S extends { readonly [x: string]: any } | null>(
|
|||||||
const debouncedState = useDebounce(state, debounceRate);
|
const debouncedState = useDebounce(state, debounceRate);
|
||||||
const lastSyncedStateRef = useRef<S>();
|
const lastSyncedStateRef = useRef<S>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session.socket && dirtyRef.current) {
|
if (dirtyRef.current) {
|
||||||
// If partial updates enabled, send just the changes to the socket
|
// If partial updates enabled, send just the changes to the socket
|
||||||
if (
|
if (
|
||||||
lastSyncedStateRef.current &&
|
lastSyncedStateRef.current &&
|
||||||
@ -112,11 +112,11 @@ function useNetworkedState<S extends { readonly [x: string]: any } | null>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
session.socket?.on(eventName, handleSocketEvent);
|
session.socket.on(eventName, handleSocketEvent);
|
||||||
session.socket?.on(`${eventName}_update`, handleSocketUpdateEvent);
|
session.socket.on(`${eventName}_update`, handleSocketUpdateEvent);
|
||||||
return () => {
|
return () => {
|
||||||
session.socket?.off(eventName, handleSocketEvent);
|
session.socket.off(eventName, handleSocketEvent);
|
||||||
session.socket?.off(`${eventName}_update`, handleSocketUpdateEvent);
|
session.socket.off(`${eventName}_update`, handleSocketUpdateEvent);
|
||||||
};
|
};
|
||||||
}, [session.socket, eventName, partialUpdatesKey]);
|
}, [session.socket, eventName, partialUpdatesKey]);
|
||||||
|
|
||||||
|
25
src/hooks/usePreventContextMenu.ts
Normal file
25
src/hooks/usePreventContextMenu.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
|
function usePreventContextMenu(elementRef: React.RefObject<HTMLElement>) {
|
||||||
|
useEffect(() => {
|
||||||
|
// Stop conext menu i.e. right click dialog
|
||||||
|
function preventContextMenu(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const element = elementRef.current;
|
||||||
|
if (element) {
|
||||||
|
element.addEventListener("contextmenu", preventContextMenu, {
|
||||||
|
passive: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (element) {
|
||||||
|
element.removeEventListener("contextmenu", preventContextMenu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [elementRef]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default usePreventContextMenu;
|
@ -67,6 +67,7 @@ function useStageInteraction(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { event, last } = props;
|
const { event, last } = props;
|
||||||
|
// Prevent double zoom on wheel end
|
||||||
if (!last) {
|
if (!last) {
|
||||||
const { pixelY } = normalizeWheel(event);
|
const { pixelY } = normalizeWheel(event);
|
||||||
|
|
||||||
@ -178,7 +179,7 @@ function useStageInteraction(
|
|||||||
gesture.onDragStart && gesture.onDragStart(props);
|
gesture.onDragStart && gesture.onDragStart(props);
|
||||||
},
|
},
|
||||||
onDrag: (props) => {
|
onDrag: (props) => {
|
||||||
const { delta, pinching } = props;
|
const { delta, pinching, buttons } = props;
|
||||||
const stage = stageRef.current;
|
const stage = stageRef.current;
|
||||||
if (
|
if (
|
||||||
preventInteraction ||
|
preventInteraction ||
|
||||||
@ -191,7 +192,8 @@ function useStageInteraction(
|
|||||||
|
|
||||||
const [dx, dy] = delta;
|
const [dx, dy] = delta;
|
||||||
const stageTranslate = stageTranslateRef.current;
|
const stageTranslate = stageTranslateRef.current;
|
||||||
if (tool === "move") {
|
// Move with move tool and left click or any mouse button but right click
|
||||||
|
if ((tool === "move" && buttons < 2) || buttons > 2) {
|
||||||
const newTranslate = {
|
const newTranslate = {
|
||||||
x: stageTranslate.x + dx,
|
x: stageTranslate.x + dx,
|
||||||
y: stageTranslate.y + dy,
|
y: stageTranslate.y + dy,
|
||||||
|
@ -202,12 +202,12 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
) {
|
) {
|
||||||
// Clear map before sending new one
|
// Clear map before sending new one
|
||||||
setCurrentMap(null);
|
setCurrentMap(null);
|
||||||
session.socket?.emit("map", null);
|
session.socket.emit("map", null);
|
||||||
|
|
||||||
setCurrentMapState(newMapState, true, true);
|
setCurrentMapState(newMapState, true, true);
|
||||||
setCurrentMap(newMap);
|
setCurrentMap(newMap);
|
||||||
|
|
||||||
session.socket?.emit("map", newMap);
|
session.socket.emit("map", newMap);
|
||||||
|
|
||||||
if (!newMap || !newMapState) {
|
if (!newMap || !newMapState) {
|
||||||
setAssetManifest(null, true, true);
|
setAssetManifest(null, true, true);
|
||||||
@ -395,12 +395,12 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
|
|
||||||
session.on("peerData", handlePeerData);
|
session.on("peerData", handlePeerData);
|
||||||
session.on("peerDataProgress", handlePeerDataProgress);
|
session.on("peerDataProgress", handlePeerDataProgress);
|
||||||
session.socket?.on("map", handleSocketMap);
|
session.socket.on("map", handleSocketMap);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
session.off("peerData", handlePeerData);
|
session.off("peerData", handlePeerData);
|
||||||
session.off("peerDataProgress", handlePeerDataProgress);
|
session.off("peerDataProgress", handlePeerDataProgress);
|
||||||
session.socket?.off("map", handleSocketMap);
|
session.socket.off("map", handleSocketMap);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ function NetworkedMapPointer({ session, active }: NetworkedMapPointerProps) {
|
|||||||
sessionRef.current &&
|
sessionRef.current &&
|
||||||
sessionRef.current.socket
|
sessionRef.current.socket
|
||||||
) {
|
) {
|
||||||
sessionRef.current.socket.emit(
|
sessionRef.current.socket.volatile.emit(
|
||||||
"player_pointer",
|
"player_pointer",
|
||||||
ownPointerUpdateRef.current
|
ownPointerUpdateRef.current
|
||||||
);
|
);
|
||||||
@ -152,10 +152,10 @@ function NetworkedMapPointer({ session, active }: NetworkedMapPointerProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.socket?.on("player_pointer", handleSocketPlayerPointer);
|
session.socket.on("player_pointer", handleSocketPlayerPointer);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
session.socket?.off("player_pointer", handleSocketPlayerPointer);
|
session.socket.off("player_pointer", handleSocketPlayerPointer);
|
||||||
};
|
};
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import io, { Socket } from "socket.io-client";
|
import io from "socket.io-client";
|
||||||
import msgParser from "socket.io-msgpack-parser";
|
import msgParser from "socket.io-msgpack-parser";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
@ -32,10 +32,11 @@ export type PeerReply = (id: string, data: PeerData, chunkId?: string) => void;
|
|||||||
class Session extends EventEmitter {
|
class Session extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* The socket io connection
|
* The socket io connection
|
||||||
*
|
|
||||||
* @type {io.Socket}
|
|
||||||
*/
|
*/
|
||||||
socket?: Socket;
|
socket = io(process.env.REACT_APP_BROKER_URL!, {
|
||||||
|
withCredentials: true,
|
||||||
|
parser: msgParser,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of socket ids to session peers
|
* A mapping of socket ids to session peers
|
||||||
@ -45,7 +46,7 @@ class Session extends EventEmitter {
|
|||||||
peers: Record<string, SessionPeer>;
|
peers: Record<string, SessionPeer>;
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this.socket?.id || "";
|
return this.socket.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
_iceServers: RTCIceServer[] = [];
|
_iceServers: RTCIceServer[] = [];
|
||||||
@ -76,14 +77,6 @@ class Session extends EventEmitter {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this._iceServers = data.iceServers;
|
this._iceServers = data.iceServers;
|
||||||
|
|
||||||
if (!process.env.REACT_APP_BROKER_URL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.socket = io(process.env.REACT_APP_BROKER_URL, {
|
|
||||||
withCredentials: true,
|
|
||||||
parser: msgParser,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("player_joined", this._handlePlayerJoined.bind(this));
|
this.socket.on("player_joined", this._handlePlayerJoined.bind(this));
|
||||||
this.socket.on("player_left", this._handlePlayerLeft.bind(this));
|
this.socket.on("player_left", this._handlePlayerLeft.bind(this));
|
||||||
this.socket.on("joined_game", this._handleJoinedGame.bind(this));
|
this.socket.on("joined_game", this._handleJoinedGame.bind(this));
|
||||||
@ -95,14 +88,14 @@ class Session extends EventEmitter {
|
|||||||
this.socket.on("force_update", this._handleForceUpdate.bind(this));
|
this.socket.on("force_update", this._handleForceUpdate.bind(this));
|
||||||
|
|
||||||
this.emit("status", "ready");
|
this.emit("status", "ready");
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
logError(error);
|
logError(error);
|
||||||
this.emit("status", "offline");
|
this.emit("status", "offline");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this.socket?.disconnect();
|
this.socket.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,7 +184,7 @@ class Session extends EventEmitter {
|
|||||||
|
|
||||||
this._gameId = gameId;
|
this._gameId = gameId;
|
||||||
this._password = password;
|
this._password = password;
|
||||||
this.socket?.emit(
|
this.socket.emit(
|
||||||
"join_game",
|
"join_game",
|
||||||
gameId,
|
gameId,
|
||||||
password,
|
password,
|
||||||
@ -224,7 +217,7 @@ class Session extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSignal = (signal: SignalData) => {
|
const handleSignal = (signal: SignalData) => {
|
||||||
this.socket?.emit("signal", JSON.stringify({ to: peer.id, signal }));
|
this.socket.emit("signal", JSON.stringify({ to: peer.id, signal }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConnect = () => {
|
const handleConnect = () => {
|
||||||
@ -309,7 +302,7 @@ class Session extends EventEmitter {
|
|||||||
this.peers[id] = peer;
|
this.peers[id] = peer;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
logError(error);
|
logError(error);
|
||||||
this.emit("peerError", { error });
|
this.emit("peerError", { error });
|
||||||
for (let peer of Object.values(this.peers)) {
|
for (let peer of Object.values(this.peers)) {
|
||||||
@ -367,13 +360,14 @@ class Session extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleSocketReconnect() {
|
_handleSocketReconnect() {
|
||||||
|
this.socket.sendBuffer = [];
|
||||||
if (this._gameId) {
|
if (this._gameId) {
|
||||||
this.joinGame(this._gameId, this._password);
|
this.joinGame(this._gameId, this._password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleForceUpdate() {
|
_handleForceUpdate() {
|
||||||
this.socket?.disconnect();
|
this.socket.disconnect();
|
||||||
this.emit("status", "needs_update");
|
this.emit("status", "needs_update");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ const v180 = raw("../docs/releaseNotes/v1.8.0.md");
|
|||||||
const v181 = raw("../docs/releaseNotes/v1.8.1.md");
|
const v181 = raw("../docs/releaseNotes/v1.8.1.md");
|
||||||
const v190 = raw("../docs/releaseNotes/v1.9.0.md");
|
const v190 = raw("../docs/releaseNotes/v1.9.0.md");
|
||||||
const v1100 = raw("../docs/releaseNotes/v1.10.0.md");
|
const v1100 = raw("../docs/releaseNotes/v1.10.0.md");
|
||||||
|
const v1101 = raw("../docs/releaseNotes/v1.10.1.md");
|
||||||
|
|
||||||
function ReleaseNotes() {
|
function ReleaseNotes() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -51,6 +52,11 @@ function ReleaseNotes() {
|
|||||||
<Text mb={2} variant="heading" as="h1" sx={{ fontSize: 5 }}>
|
<Text mb={2} variant="heading" as="h1" sx={{ fontSize: 5 }}>
|
||||||
Release Notes
|
Release Notes
|
||||||
</Text>
|
</Text>
|
||||||
|
<div id="v1101">
|
||||||
|
<Accordion heading="v1.10.1" defaultOpen>
|
||||||
|
<Markdown source={v1101} />
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
<div id="v1100">
|
<div id="v1100">
|
||||||
<Accordion heading="v1.10.0" defaultOpen>
|
<Accordion heading="v1.10.0" defaultOpen>
|
||||||
<Markdown source={v1100} />
|
<Markdown source={v1100} />
|
||||||
|
@ -13181,6 +13181,11 @@ tiny-invariant@^1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||||
|
|
||||||
|
tiny-typed-emitter@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5"
|
||||||
|
integrity sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==
|
||||||
|
|
||||||
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||||
|
Loading…
Reference in New Issue
Block a user