Typescript
This commit is contained in:
parent
97734a2f55
commit
2a053f4854
@ -58,7 +58,7 @@
|
|||||||
"react-toast-notifications": "^2.4.3",
|
"react-toast-notifications": "^2.4.3",
|
||||||
"react-use-gesture": "^9.1.3",
|
"react-use-gesture": "^9.1.3",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"simple-peer": "feross/simple-peer#694/head",
|
"simple-peer": "^9.11.0",
|
||||||
"simplebar-react": "^2.1.0",
|
"simplebar-react": "^2.1.0",
|
||||||
"simplify-js": "^1.2.4",
|
"simplify-js": "^1.2.4",
|
||||||
"socket.io-client": "^4.1.2",
|
"socket.io-client": "^4.1.2",
|
||||||
@ -110,7 +110,7 @@
|
|||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"@types/react-select": "^4.0.17",
|
"@types/react-select": "^4.0.17",
|
||||||
"@types/shortid": "^0.0.29",
|
"@types/shortid": "^0.0.29",
|
||||||
"@types/simple-peer": "^9.6.3",
|
"@types/simple-peer": "^9.11.1",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.2.4",
|
||||||
"worker-loader": "^3.0.8"
|
"worker-loader": "^3.0.8"
|
||||||
|
@ -47,8 +47,8 @@ import { TokenDraggingOptions, TokenMenuOptions } from "../../types/Token";
|
|||||||
import { Note, NoteDraggingOptions, NoteMenuOptions } from "../../types/Note";
|
import { Note, NoteDraggingOptions, NoteMenuOptions } from "../../types/Note";
|
||||||
|
|
||||||
type MapProps = {
|
type MapProps = {
|
||||||
map: Map;
|
map: Map | null;
|
||||||
mapState: MapState;
|
mapState: MapState | null;
|
||||||
mapActions: MapActions;
|
mapActions: MapActions;
|
||||||
onMapTokenStateChange: TokenStateChangeEventHandler;
|
onMapTokenStateChange: TokenStateChangeEventHandler;
|
||||||
onMapTokenStateRemove: MapTokenStateRemoveHandler;
|
onMapTokenStateRemove: MapTokenStateRemoveHandler;
|
||||||
@ -66,7 +66,7 @@ type MapProps = {
|
|||||||
allowFogDrawing: boolean;
|
allowFogDrawing: boolean;
|
||||||
allowMapChange: boolean;
|
allowMapChange: boolean;
|
||||||
allowNoteEditing: boolean;
|
allowNoteEditing: boolean;
|
||||||
disabledTokens: string[];
|
disabledTokens: Record<string, boolean>;
|
||||||
session: Session;
|
session: Session;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -244,9 +244,7 @@ function Map({
|
|||||||
onRequestClose={() => setIsTokenMenuOpen(false)}
|
onRequestClose={() => setIsTokenMenuOpen(false)}
|
||||||
onTokenStateChange={onMapTokenStateChange}
|
onTokenStateChange={onMapTokenStateChange}
|
||||||
tokenState={
|
tokenState={
|
||||||
tokenMenuOptions &&
|
tokenMenuOptions && mapState?.tokens[tokenMenuOptions.tokenStateId]
|
||||||
mapState &&
|
|
||||||
mapState.tokens[tokenMenuOptions.tokenStateId]
|
|
||||||
}
|
}
|
||||||
tokenImage={tokenMenuOptions && tokenMenuOptions.tokenImage}
|
tokenImage={tokenMenuOptions && tokenMenuOptions.tokenImage}
|
||||||
map={map}
|
map={map}
|
||||||
@ -372,9 +370,7 @@ function Map({
|
|||||||
isOpen={isNoteMenuOpen}
|
isOpen={isNoteMenuOpen}
|
||||||
onRequestClose={() => setIsNoteMenuOpen(false)}
|
onRequestClose={() => setIsNoteMenuOpen(false)}
|
||||||
onNoteChange={onMapNoteChange}
|
onNoteChange={onMapNoteChange}
|
||||||
note={
|
note={noteMenuOptions && mapState?.notes[noteMenuOptions.noteId]}
|
||||||
noteMenuOptions && mapState && mapState.notes[noteMenuOptions.noteId]
|
|
||||||
}
|
|
||||||
noteNode={noteMenuOptions?.noteNode}
|
noteNode={noteMenuOptions?.noteNode}
|
||||||
map={map}
|
map={map}
|
||||||
/>
|
/>
|
||||||
|
@ -32,8 +32,8 @@ import { Settings } from "../../types/Settings";
|
|||||||
type MapControlsProps = {
|
type MapControlsProps = {
|
||||||
onMapChange: MapChangeEventHandler;
|
onMapChange: MapChangeEventHandler;
|
||||||
onMapReset: MapResetEventHandler;
|
onMapReset: MapResetEventHandler;
|
||||||
currentMap?: Map;
|
currentMap: Map | null;
|
||||||
currentMapState?: MapState;
|
currentMapState: MapState | null;
|
||||||
selectedToolId: MapToolId;
|
selectedToolId: MapToolId;
|
||||||
onSelectedToolChange: (toolId: MapToolId) => void;
|
onSelectedToolChange: (toolId: MapToolId) => void;
|
||||||
toolSettings: Settings;
|
toolSettings: Settings;
|
||||||
|
@ -37,7 +37,7 @@ export type DrawingAddEventHanlder = (drawing: Drawing) => void;
|
|||||||
export type DrawingsRemoveEventHandler = (drawingIds: string[]) => void;
|
export type DrawingsRemoveEventHandler = (drawingIds: string[]) => void;
|
||||||
|
|
||||||
type MapDrawingProps = {
|
type MapDrawingProps = {
|
||||||
map: Map;
|
map: Map | null;
|
||||||
drawings: Drawing[];
|
drawings: Drawing[];
|
||||||
onDrawingAdd: DrawingAddEventHanlder;
|
onDrawingAdd: DrawingAddEventHanlder;
|
||||||
onDrawingsRemove: DrawingsRemoveEventHandler;
|
onDrawingsRemove: DrawingsRemoveEventHandler;
|
||||||
@ -85,7 +85,7 @@ function MapDrawing({
|
|||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
function getBrushPosition() {
|
||||||
if (!mapStage) {
|
if (!mapStage || !map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
const mapImage = mapStage.findOne("#mapImage");
|
||||||
|
@ -20,7 +20,7 @@ import {
|
|||||||
} from "../../types/Events";
|
} from "../../types/Events";
|
||||||
|
|
||||||
type MapEditBarProps = {
|
type MapEditBarProps = {
|
||||||
currentMap?: Map;
|
currentMap: Map | null;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
onMapChange: MapChangeEventHandler;
|
onMapChange: MapChangeEventHandler;
|
||||||
onMapReset: MapResetEventHandler;
|
onMapReset: MapResetEventHandler;
|
||||||
@ -79,7 +79,7 @@ function MapEditBar({
|
|||||||
await removeMaps(selectedMapIds);
|
await removeMaps(selectedMapIds);
|
||||||
// Removed the map from the map screen if needed
|
// Removed the map from the map screen if needed
|
||||||
if (currentMap && selectedMapIds.includes(currentMap.id)) {
|
if (currentMap && selectedMapIds.includes(currentMap.id)) {
|
||||||
onMapChange(undefined, undefined);
|
onMapChange(null, null);
|
||||||
}
|
}
|
||||||
onLoad(false);
|
onLoad(false);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ type FogEditEventHandler = (edit: Partial<Fog>[]) => void;
|
|||||||
type FogErrorEventHandler = (message: string) => void;
|
type FogErrorEventHandler = (message: string) => void;
|
||||||
|
|
||||||
type MapFogProps = {
|
type MapFogProps = {
|
||||||
map: Map;
|
map: Map | null;
|
||||||
shapes: Fog[];
|
shapes: Fog[];
|
||||||
onShapesAdd: FogAddEventHandler;
|
onShapesAdd: FogAddEventHandler;
|
||||||
onShapesCut: FogCutEventHandler;
|
onShapesCut: FogCutEventHandler;
|
||||||
@ -354,7 +354,7 @@ function MapFog({
|
|||||||
x: mapWidth,
|
x: mapWidth,
|
||||||
y: mapHeight,
|
y: mapHeight,
|
||||||
});
|
});
|
||||||
if (map.snapToGrid) {
|
if (map?.snapToGrid) {
|
||||||
guides.push(
|
guides.push(
|
||||||
...getGuidesFromGridCell(
|
...getGuidesFromGridCell(
|
||||||
absoluteBrushPosition,
|
absoluteBrushPosition,
|
||||||
|
@ -25,8 +25,8 @@ import { MapState } from "../../types/MapState";
|
|||||||
type SelectedToolChangeEventHanlder = (tool: MapToolId) => void;
|
type SelectedToolChangeEventHanlder = (tool: MapToolId) => void;
|
||||||
|
|
||||||
type MapInteractionProps = {
|
type MapInteractionProps = {
|
||||||
map: Map;
|
map: Map | null;
|
||||||
mapState: MapState;
|
mapState: MapState | null;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
controls: React.ReactNode;
|
controls: React.ReactNode;
|
||||||
selectedToolId: MapToolId;
|
selectedToolId: MapToolId;
|
||||||
|
@ -29,7 +29,7 @@ import { Map } from "../../types/Map";
|
|||||||
import { PointsData } from "../../types/Drawing";
|
import { PointsData } from "../../types/Drawing";
|
||||||
|
|
||||||
type MapMeasureProps = {
|
type MapMeasureProps = {
|
||||||
map: Map;
|
map: Map | null;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ function MapMeasure({ map, active }: MapMeasureProps) {
|
|||||||
if (!position) {
|
if (!position) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (map.snapToGrid) {
|
if (map?.snapToGrid) {
|
||||||
position = snapPositionToGrid(position);
|
position = snapPositionToGrid(position);
|
||||||
}
|
}
|
||||||
return Vector2.divide(position, {
|
return Vector2.divide(position, {
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
const defaultNoteSize = 2;
|
const defaultNoteSize = 2;
|
||||||
|
|
||||||
type MapNoteProps = {
|
type MapNoteProps = {
|
||||||
map: Map;
|
map: Map | null;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
onNoteAdd: NoteAddEventHander;
|
onNoteAdd: NoteAddEventHander;
|
||||||
onNoteChange: NoteChangeEventHandler;
|
onNoteChange: NoteChangeEventHandler;
|
||||||
@ -75,7 +75,7 @@ function MapNotes({
|
|||||||
if (!position) {
|
if (!position) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (map.snapToGrid) {
|
if (map?.snapToGrid) {
|
||||||
position = snapPositionToGrid(position);
|
position = snapPositionToGrid(position);
|
||||||
}
|
}
|
||||||
return Vector2.divide(position, {
|
return Vector2.divide(position, {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
import MapTile from "./MapTile";
|
import MapTile from "./MapTile";
|
||||||
import MapTileGroup from "./MapTileGroup";
|
import MapTileGroup from "./MapTileGroup";
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ type MapTokensProps = {
|
|||||||
onMapTokenStateChange: TokenStateChangeEventHandler;
|
onMapTokenStateChange: TokenStateChangeEventHandler;
|
||||||
onTokenMenuOpen: TokenMenuOpenChangeEventHandler;
|
onTokenMenuOpen: TokenMenuOpenChangeEventHandler;
|
||||||
selectedToolId: MapToolId;
|
selectedToolId: MapToolId;
|
||||||
disabledTokens: string[];
|
disabledTokens: Record<string, boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function MapTokens({
|
function MapTokens({
|
||||||
|
@ -16,8 +16,8 @@ import { MapState } from "../../types/MapState";
|
|||||||
type SelectMapButtonProps = {
|
type SelectMapButtonProps = {
|
||||||
onMapChange: MapChangeEventHandler;
|
onMapChange: MapChangeEventHandler;
|
||||||
onMapReset: MapResetEventHandler;
|
onMapReset: MapResetEventHandler;
|
||||||
currentMap?: Map;
|
currentMap: Map | null;
|
||||||
currentMapState?: MapState;
|
currentMapState: MapState | null;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ const defaultFontSize = 16;
|
|||||||
|
|
||||||
type NoteProps = {
|
type NoteProps = {
|
||||||
note: NoteType;
|
note: NoteType;
|
||||||
map: Map;
|
map: Map | null;
|
||||||
onNoteChange?: NoteChangeEventHandler;
|
onNoteChange?: NoteChangeEventHandler;
|
||||||
onNoteMenuOpen?: NoteMenuOpenEventHandler;
|
onNoteMenuOpen?: NoteMenuOpenEventHandler;
|
||||||
draggable: boolean;
|
draggable: boolean;
|
||||||
@ -72,20 +72,22 @@ function Note({
|
|||||||
function handleDragMove(event: Konva.KonvaEventObject<DragEvent>) {
|
function handleDragMove(event: Konva.KonvaEventObject<DragEvent>) {
|
||||||
const noteGroup = event.target;
|
const noteGroup = event.target;
|
||||||
// Snap to corners of grid
|
// Snap to corners of grid
|
||||||
if (map.snapToGrid) {
|
if (map?.snapToGrid) {
|
||||||
noteGroup.position(snapPositionToGrid(noteGroup.position()));
|
noteGroup.position(snapPositionToGrid(noteGroup.position()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDragEnd(event: Konva.KonvaEventObject<DragEvent>) {
|
function handleDragEnd(event: Konva.KonvaEventObject<DragEvent>) {
|
||||||
const noteGroup = event.target;
|
const noteGroup = event.target;
|
||||||
onNoteChange?.({
|
if (userId) {
|
||||||
...note,
|
onNoteChange?.({
|
||||||
x: noteGroup.x() / mapWidth,
|
...note,
|
||||||
y: noteGroup.y() / mapHeight,
|
x: noteGroup.x() / mapWidth,
|
||||||
lastModifiedBy: userId,
|
y: noteGroup.y() / mapHeight,
|
||||||
lastModified: Date.now(),
|
lastModifiedBy: userId,
|
||||||
});
|
lastModified: Date.now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
onNoteDragEnd?.(event, note.id);
|
onNoteDragEnd?.(event, note.id);
|
||||||
setPreventMapInteraction(false);
|
setPreventMapInteraction(false);
|
||||||
}
|
}
|
||||||
@ -103,7 +105,7 @@ function Note({
|
|||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(true);
|
setPreventMapInteraction(true);
|
||||||
}
|
}
|
||||||
if (note.locked && map.owner === userId) {
|
if (note.locked && map?.owner === userId) {
|
||||||
notePointerDownTimeRef.current = event.evt.timeStamp;
|
notePointerDownTimeRef.current = event.evt.timeStamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,7 +116,7 @@ function Note({
|
|||||||
}
|
}
|
||||||
// Check note click when locked and we are the map owner
|
// Check note click when locked and we are the map owner
|
||||||
// We can't use onClick because that doesn't check pointer distance
|
// We can't use onClick because that doesn't check pointer distance
|
||||||
if (note.locked && map.owner === userId) {
|
if (note.locked && map?.owner === userId) {
|
||||||
// If down and up time is small trigger a click
|
// If down and up time is small trigger a click
|
||||||
const delta = event.evt.timeStamp - notePointerDownTimeRef.current;
|
const delta = event.evt.timeStamp - notePointerDownTimeRef.current;
|
||||||
if (delta < 300) {
|
if (delta < 300) {
|
||||||
|
@ -35,7 +35,7 @@ type NoteMenuProps = {
|
|||||||
note?: Note;
|
note?: Note;
|
||||||
noteNode?: Konva.Node;
|
noteNode?: Konva.Node;
|
||||||
onNoteChange: NoteChangeEventHandler;
|
onNoteChange: NoteChangeEventHandler;
|
||||||
map: Map;
|
map: Map | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function NoteMenu({
|
function NoteMenu({
|
||||||
|
@ -10,12 +10,6 @@ import SettingsButton from "../SettingsButton";
|
|||||||
import StartTimerButton from "./StartTimerButton";
|
import StartTimerButton from "./StartTimerButton";
|
||||||
import Timer from "./Timer";
|
import Timer from "./Timer";
|
||||||
import DiceTrayButton from "./DiceTrayButton";
|
import DiceTrayButton from "./DiceTrayButton";
|
||||||
import {
|
|
||||||
PartyState,
|
|
||||||
PlayerDice,
|
|
||||||
PlayerInfo,
|
|
||||||
Timer as PartyTimer,
|
|
||||||
} from "./PartyState";
|
|
||||||
|
|
||||||
import useSetting from "../../hooks/useSetting";
|
import useSetting from "../../hooks/useSetting";
|
||||||
|
|
||||||
@ -36,10 +30,10 @@ function Party({
|
|||||||
onStreamEnd;
|
onStreamEnd;
|
||||||
}) {
|
}) {
|
||||||
const setPlayerState = usePlayerUpdater();
|
const setPlayerState = usePlayerUpdater();
|
||||||
const playerState: PlayerInfo = usePlayerState();
|
const playerState = usePlayerState();
|
||||||
const partyState: PartyState = useParty();
|
const partyState = useParty();
|
||||||
|
|
||||||
const [fullScreen] = useSetting("map.fullScreen");
|
const [fullScreen] = useSetting<boolean>("map.fullScreen");
|
||||||
const [shareDice, setShareDice] = useSetting("dice.shareDice");
|
const [shareDice, setShareDice] = useSetting("dice.shareDice");
|
||||||
|
|
||||||
function handleTimerStart(newTimer: PartyTimer) {
|
function handleTimerStart(newTimer: PartyTimer) {
|
||||||
|
@ -6,7 +6,7 @@ import Slider from "../Slider";
|
|||||||
|
|
||||||
import MapMenu from "../map/MapMenu";
|
import MapMenu from "../map/MapMenu";
|
||||||
|
|
||||||
import colors, { colorOptions } from "../../helpers/colors";
|
import colors, { Color, colorOptions } from "../../helpers/colors";
|
||||||
|
|
||||||
import usePrevious from "../../hooks/usePrevious";
|
import usePrevious from "../../hooks/usePrevious";
|
||||||
|
|
||||||
@ -27,10 +27,10 @@ import { Map } from "../../types/Map";
|
|||||||
type TokenMenuProps = {
|
type TokenMenuProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onRequestClose: RequestCloseEventHandler;
|
onRequestClose: RequestCloseEventHandler;
|
||||||
tokenState: TokenState;
|
tokenState?: TokenState;
|
||||||
tokenImage: Konva.Node;
|
tokenImage?: Konva.Node;
|
||||||
onTokenStateChange: TokenStateChangeEventHandler;
|
onTokenStateChange: TokenStateChangeEventHandler;
|
||||||
map: Map;
|
map: Map | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultTokenMaxSize = 6;
|
const defaultTokenMaxSize = 6;
|
||||||
@ -74,7 +74,7 @@ function TokenMenu({
|
|||||||
tokenState && onTokenStateChange({ [tokenState.id]: { label: label } });
|
tokenState && onTokenStateChange({ [tokenState.id]: { label: label } });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleStatusChange(status: string) {
|
function handleStatusChange(status: Color) {
|
||||||
if (!tokenState) {
|
if (!tokenState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,8 +209,8 @@ export function AssetURLsProvider({ children }: { children: React.ReactNode }) {
|
|||||||
* Helper function to load either file or default asset into a URL
|
* Helper function to load either file or default asset into a URL
|
||||||
*/
|
*/
|
||||||
export function useAssetURL(
|
export function useAssetURL(
|
||||||
assetId: string,
|
assetId: string | null,
|
||||||
type: "file" | "default",
|
type: "file" | "default" | null,
|
||||||
defaultSources: Record<string, string>,
|
defaultSources: Record<string, string>,
|
||||||
unknownSource?: string
|
unknownSource?: string
|
||||||
) {
|
) {
|
||||||
@ -227,9 +227,8 @@ export function useAssetURL(
|
|||||||
if (!assetId || type !== "file") {
|
if (!assetId || type !== "file") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const updateAssetURL = () => {
|
||||||
function updateAssetURL() {
|
const increaseReferences = (prevURLs: AssetURLs): AssetURLs => {
|
||||||
function increaseReferences(prevURLs: AssetURLs): AssetURLs {
|
|
||||||
return {
|
return {
|
||||||
...prevURLs,
|
...prevURLs,
|
||||||
[assetId]: {
|
[assetId]: {
|
||||||
@ -237,14 +236,14 @@ export function useAssetURL(
|
|||||||
references: prevURLs[assetId].references + 1,
|
references: prevURLs[assetId].references + 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
function createReference(prevURLs: AssetURLs): AssetURLs {
|
const createReference = (prevURLs: AssetURLs): AssetURLs => {
|
||||||
return {
|
return {
|
||||||
...prevURLs,
|
...prevURLs,
|
||||||
[assetId]: { url: null, id: assetId, references: 1 },
|
[assetId]: { url: null, id: assetId, references: 1 },
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
setAssetURLs?.((prevURLs) => {
|
setAssetURLs?.((prevURLs) => {
|
||||||
if (assetId in prevURLs) {
|
if (assetId in prevURLs) {
|
||||||
// Check if the asset url is already added and increase references
|
// Check if the asset url is already added and increase references
|
||||||
@ -253,7 +252,7 @@ export function useAssetURL(
|
|||||||
return createReference(prevURLs);
|
return createReference(prevURLs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
updateAssetURL();
|
updateAssetURL();
|
||||||
|
|
||||||
@ -307,22 +306,22 @@ type DefaultData = {
|
|||||||
* Load a map or token into a URL taking into account a thumbnail and multiple resolutions
|
* Load a map or token into a URL taking into account a thumbnail and multiple resolutions
|
||||||
*/
|
*/
|
||||||
export function useDataURL(
|
export function useDataURL(
|
||||||
data: FileData | DefaultData,
|
data: FileData | DefaultData | null,
|
||||||
defaultSources: Record<string, string>,
|
defaultSources: Record<string, string>,
|
||||||
unknownSource: string | undefined = undefined,
|
unknownSource: string | undefined = undefined,
|
||||||
thumbnail = false
|
thumbnail = false
|
||||||
) {
|
) {
|
||||||
const [assetId, setAssetId] = useState<string>();
|
const [assetId, setAssetId] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function loadAssetId() {
|
function loadAssetId() {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (data.type === "default") {
|
if (data.type === "default") {
|
||||||
setAssetId(data.key);
|
setAssetId(data.key);
|
||||||
} else {
|
} else {
|
||||||
if (thumbnail) {
|
if (thumbnail && data.thumbnail) {
|
||||||
setAssetId(data.thumbnail);
|
setAssetId(data.thumbnail);
|
||||||
} else if (
|
} else if (
|
||||||
data.resolutions &&
|
data.resolutions &&
|
||||||
@ -340,8 +339,8 @@ export function useDataURL(
|
|||||||
}, [data, thumbnail]);
|
}, [data, thumbnail]);
|
||||||
|
|
||||||
const assetURL = useAssetURL(
|
const assetURL = useAssetURL(
|
||||||
assetId || "",
|
assetId || null,
|
||||||
data?.type,
|
data?.type || null,
|
||||||
defaultSources,
|
defaultSources,
|
||||||
unknownSource
|
unknownSource
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ import { getRandomMonster } from "../helpers/monsters";
|
|||||||
import useNetworkedState, {
|
import useNetworkedState, {
|
||||||
SetNetworkedState,
|
SetNetworkedState,
|
||||||
} from "../hooks/useNetworkedState";
|
} from "../hooks/useNetworkedState";
|
||||||
import Session 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 =
|
||||||
@ -103,7 +103,7 @@ export function PlayerProvider({ session, children }: PlayerProviderProps) {
|
|||||||
updateSessionId();
|
updateSessionId();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSocketStatus(status: string) {
|
function handleSocketStatus(status: SessionStatus) {
|
||||||
if (status === "joined") {
|
if (status === "joined") {
|
||||||
updateSessionId();
|
updateSessionId();
|
||||||
}
|
}
|
||||||
|
@ -245,10 +245,10 @@ export function getGridUpdatedInset(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the max zoom for a grid
|
* Get the max zoom for a grid
|
||||||
* @param {Grid} grid
|
* @param {Grid=} grid
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
export function getGridMaxZoom(grid: Grid): number {
|
export function getGridMaxZoom(grid?: Grid): number {
|
||||||
if (!grid) {
|
if (!grid) {
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ type ImageData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function useImageCenter(
|
function useImageCenter(
|
||||||
data: ImageData,
|
data: ImageData | null,
|
||||||
stageRef: MapStage,
|
stageRef: MapStage,
|
||||||
stageWidth: number,
|
stageWidth: number,
|
||||||
stageHeight: number,
|
stageHeight: number,
|
||||||
|
@ -8,7 +8,7 @@ import { mapSources as defaultMapSources } from "../maps";
|
|||||||
import { Map } from "../types/Map";
|
import { Map } from "../types/Map";
|
||||||
|
|
||||||
function useMapImage(
|
function useMapImage(
|
||||||
map: Map
|
map: Map | null
|
||||||
): [HTMLImageElement | undefined, "loaded" | "loading" | "failed"] {
|
): [HTMLImageElement | undefined, "loaded" | "loading" | "failed"] {
|
||||||
const mapURL = useDataURL(map, defaultMapSources);
|
const mapURL = useDataURL(map, defaultMapSources);
|
||||||
const [mapImage, mapImageStatus] = useImage(mapURL || "");
|
const [mapImage, mapImageStatus] = useImage(mapURL || "");
|
||||||
|
@ -42,7 +42,7 @@ type SelectMapProps = {
|
|||||||
onDone: RequestCloseEventHandler;
|
onDone: RequestCloseEventHandler;
|
||||||
onMapChange: MapChangeEventHandler;
|
onMapChange: MapChangeEventHandler;
|
||||||
onMapReset: MapResetEventHandler;
|
onMapReset: MapResetEventHandler;
|
||||||
currentMap?: Map;
|
currentMap: Map | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function SelectMapModal({
|
function SelectMapModal({
|
||||||
@ -175,12 +175,12 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
if (mapId) {
|
if (mapId) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const map = await getMap(mapId);
|
const map = (await getMap(mapId)) || null;
|
||||||
const mapState = await getMapState(mapId);
|
const mapState = (await getMapState(mapId)) || null;
|
||||||
onMapChange(map, mapState);
|
onMapChange(map, mapState);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} else {
|
} else {
|
||||||
onMapChange(undefined, undefined);
|
onMapChange(null, null);
|
||||||
}
|
}
|
||||||
onDone();
|
onDone();
|
||||||
}
|
}
|
||||||
|
@ -8,21 +8,37 @@ import blobToBuffer from "../helpers/blobToBuffer";
|
|||||||
// http://viblast.com/blog/2015/2/5/webrtc-data-channel-message-size/
|
// http://viblast.com/blog/2015/2/5/webrtc-data-channel-message-size/
|
||||||
const MAX_BUFFER_SIZE = 16000;
|
const MAX_BUFFER_SIZE = 16000;
|
||||||
|
|
||||||
class Connection extends SimplePeer {
|
type NetworkChunk = {
|
||||||
currentChunks;
|
__chunked: boolean;
|
||||||
dataChannels;
|
data: Uint8Array;
|
||||||
|
id: string;
|
||||||
|
index: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
constructor(props) {
|
type LocalChunk = {
|
||||||
|
data: Uint8Array[];
|
||||||
|
count: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DataProgressEvent = {
|
||||||
|
id: string;
|
||||||
|
count: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Connection extends SimplePeer {
|
||||||
|
currentChunks: Record<string, LocalChunk>;
|
||||||
|
constructor(props: SimplePeer.Options) {
|
||||||
super(props);
|
super(props);
|
||||||
this.currentChunks = {};
|
this.currentChunks = {};
|
||||||
this.dataChannels = {};
|
|
||||||
this.on("data", this.handleData);
|
this.on("data", this.handleData);
|
||||||
this.on("datachannel", this.handleDataChannel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intercept the data event with decoding and chunking support
|
// Intercept the data event with decoding and chunking support
|
||||||
handleData(packed) {
|
handleData(packed: Uint8Array) {
|
||||||
const unpacked = decode(packed);
|
const unpacked = decode(packed) as NetworkChunk;
|
||||||
// If the special property __chunked is set and true
|
// If the special property __chunked is set and true
|
||||||
// The data is a partial chunk of the a larger file
|
// The data is a partial chunk of the a larger file
|
||||||
// So wait until all chunks are collected and assembled
|
// So wait until all chunks are collected and assembled
|
||||||
@ -46,7 +62,6 @@ class Connection extends SimplePeer {
|
|||||||
// All chunks have been loaded
|
// All chunks have been loaded
|
||||||
if (chunk.count === chunk.total) {
|
if (chunk.count === chunk.total) {
|
||||||
// Merge chunks with a blob
|
// Merge chunks with a blob
|
||||||
// TODO: Look at a more efficient way to recombine buffer data
|
|
||||||
const merged = new Blob(chunk.data);
|
const merged = new Blob(chunk.data);
|
||||||
blobToBuffer(merged).then((buffer) => {
|
blobToBuffer(merged).then((buffer) => {
|
||||||
this.emit("dataComplete", decode(buffer));
|
this.emit("dataComplete", decode(buffer));
|
||||||
@ -62,52 +77,23 @@ class Connection extends SimplePeer {
|
|||||||
* Custom send function with encoding, chunking and data channel support
|
* Custom send function with encoding, chunking and data channel support
|
||||||
* Uses `write` to send the data to allow for buffer / backpressure handling
|
* Uses `write` to send the data to allow for buffer / backpressure handling
|
||||||
* @param {any} object
|
* @param {any} object
|
||||||
* @param {string=} channel
|
|
||||||
* @param {string=} chunkId Optional ID to use for chunking
|
* @param {string=} chunkId Optional ID to use for chunking
|
||||||
*/
|
*/
|
||||||
sendObject(object, channel?: string, chunkId?: string) {
|
sendObject(object: any, chunkId?: string) {
|
||||||
try {
|
try {
|
||||||
const packedData = encode(object);
|
const packedData = encode(object);
|
||||||
const chunks = this.chunk(packedData, chunkId);
|
const chunks = this.chunk(packedData, chunkId);
|
||||||
for (let chunk of chunks) {
|
for (let chunk of chunks) {
|
||||||
if (this.dataChannels[channel]) {
|
this.write(encode(chunk));
|
||||||
this.dataChannels[channel].write(encode(chunk));
|
|
||||||
} else {
|
|
||||||
this.write(encode(chunk));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the create data channel function to store our own named reference to it
|
|
||||||
// and to use our custom data handler
|
|
||||||
createDataChannel(channelName: string, channelConfig, opts) {
|
|
||||||
// TODO: resolve createDataChannel
|
|
||||||
// @ts-ignore
|
|
||||||
const channel = super.createDataChannel(channelName, channelConfig, opts);
|
|
||||||
this.handleDataChannel(channel);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDataChannel(channel) {
|
|
||||||
const channelName = channel.channelName;
|
|
||||||
this.dataChannels[channelName] = channel;
|
|
||||||
channel.on("data", this.handleData.bind(this));
|
|
||||||
channel.on("error", (error) => {
|
|
||||||
this.emit("error", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converted from https://github.com/peers/peerjs/
|
// Converted from https://github.com/peers/peerjs/
|
||||||
/**
|
/** Chunk byte array */
|
||||||
* Chunk byte array
|
chunk(data: Uint8Array, chunkId?: string): NetworkChunk[] {
|
||||||
* @param {Uint8Array} data
|
|
||||||
* @param {string=} chunkId
|
|
||||||
* @returns {Uint8Array[]}
|
|
||||||
*/
|
|
||||||
chunk(data: Uint8Array, chunkId?: string) {
|
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
const size = data.byteLength;
|
const size = data.byteLength;
|
||||||
const total = Math.ceil(size / MAX_BUFFER_SIZE);
|
const total = Math.ceil(size / MAX_BUFFER_SIZE);
|
||||||
|
@ -14,7 +14,7 @@ import useDebounce from "../hooks/useDebounce";
|
|||||||
import useNetworkedState from "../hooks/useNetworkedState";
|
import useNetworkedState from "../hooks/useNetworkedState";
|
||||||
|
|
||||||
// Load session for auto complete
|
// Load session for auto complete
|
||||||
import Session from "./Session";
|
import Session, { PeerDataEvent, PeerDataProgressEvent } from "./Session";
|
||||||
|
|
||||||
import Action from "../actions/Action";
|
import Action from "../actions/Action";
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ import {
|
|||||||
import { TokenState } from "../types/TokenState";
|
import { TokenState } from "../types/TokenState";
|
||||||
import { DrawingState } from "../types/Drawing";
|
import { DrawingState } from "../types/Drawing";
|
||||||
import { FogState } from "../types/Fog";
|
import { FogState } from "../types/Fog";
|
||||||
|
import { Note } from "../types/Note";
|
||||||
|
|
||||||
const defaultMapActions: MapActions = {
|
const defaultMapActions: MapActions = {
|
||||||
mapDrawActions: [],
|
mapDrawActions: [],
|
||||||
@ -204,7 +205,10 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
}
|
}
|
||||||
}, [currentMap, debouncedMapState, userId, database, updateMapState]);
|
}, [currentMap, debouncedMapState, userId, database, updateMapState]);
|
||||||
|
|
||||||
async function handleMapChange(newMap, newMapState) {
|
async function handleMapChange(
|
||||||
|
newMap: MapType | null,
|
||||||
|
newMapState: MapState | null
|
||||||
|
) {
|
||||||
// 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);
|
||||||
@ -222,7 +226,7 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
await loadAssetManifestFromMap(newMap, newMapState);
|
await loadAssetManifestFromMap(newMap, newMapState);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapReset(newMapState) {
|
function handleMapReset(newMapState: MapState) {
|
||||||
setCurrentMapState(newMapState, true, true);
|
setCurrentMapState(newMapState, true, true);
|
||||||
setMapActions(defaultMapActions);
|
setMapActions(defaultMapActions);
|
||||||
}
|
}
|
||||||
@ -264,7 +268,7 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateActionIndex(
|
function updateActionIndex(
|
||||||
change,
|
change: number,
|
||||||
indexKey: MapActionsIndexKey,
|
indexKey: MapActionsIndexKey,
|
||||||
actionsKey: MapActionsKey,
|
actionsKey: MapActionsKey,
|
||||||
shapesKey: "drawShapes" | "fogShapes"
|
shapesKey: "drawShapes" | "fogShapes"
|
||||||
@ -288,19 +292,21 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
// Redo
|
// Redo
|
||||||
for (let i = prevIndex + 1; i < newIndex + 1; i++) {
|
for (let i = prevIndex + 1; i < newIndex + 1; i++) {
|
||||||
let action = mapActions[actionsKey][i];
|
let action = mapActions[actionsKey][i];
|
||||||
shapes = action.execute(shapes);
|
shapes = action.execute(shapes as any);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Undo
|
// Undo
|
||||||
for (let i = prevIndex; i > newIndex; i--) {
|
for (let i = prevIndex; i > newIndex; i--) {
|
||||||
let action = mapActions[actionsKey][i];
|
let action = mapActions[actionsKey][i];
|
||||||
shapes = action.undo(shapes);
|
shapes = action.undo(shapes as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...prevMapState,
|
...prevMapState,
|
||||||
[shapesKey]: shapes,
|
[shapesKey]: shapes,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return prevMapState;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -342,7 +348,7 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If map changes clear map actions
|
// If map changes clear map actions
|
||||||
const previousMapIdRef = useRef();
|
const previousMapIdRef = useRef<string>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentMap && currentMap?.id !== previousMapIdRef.current) {
|
if (currentMap && currentMap?.id !== previousMapIdRef.current) {
|
||||||
setMapActions(defaultMapActions);
|
setMapActions(defaultMapActions);
|
||||||
@ -350,21 +356,31 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
}
|
}
|
||||||
}, [currentMap]);
|
}, [currentMap]);
|
||||||
|
|
||||||
function handleNoteChange(note) {
|
function handleNoteChange(note: Note) {
|
||||||
setCurrentMapState((prevMapState) => ({
|
setCurrentMapState((prevMapState) => {
|
||||||
...prevMapState,
|
if (!prevMapState) {
|
||||||
notes: {
|
return prevMapState;
|
||||||
...prevMapState.notes,
|
}
|
||||||
[note.id]: note,
|
return {
|
||||||
},
|
...prevMapState,
|
||||||
}));
|
notes: {
|
||||||
|
...prevMapState.notes,
|
||||||
|
[note.id]: note,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNoteRemove(noteId: string) {
|
function handleNoteRemove(noteId: string) {
|
||||||
setCurrentMapState((prevMapState) => ({
|
setCurrentMapState((prevMapState) => {
|
||||||
...prevMapState,
|
if (!prevMapState) {
|
||||||
notes: omit(prevMapState.notes, [noteId]),
|
return prevMapState;
|
||||||
}));
|
}
|
||||||
|
return {
|
||||||
|
...prevMapState,
|
||||||
|
notes: omit(prevMapState.notes, [noteId]),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -387,6 +403,9 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCurrentMapState((prevMapState) => {
|
setCurrentMapState((prevMapState) => {
|
||||||
|
if (!prevMapState) {
|
||||||
|
return prevMapState;
|
||||||
|
}
|
||||||
let newMapTokens = { ...prevMapState.tokens };
|
let newMapTokens = { ...prevMapState.tokens };
|
||||||
for (let tokenState of tokenStates) {
|
for (let tokenState of tokenStates) {
|
||||||
newMapTokens[tokenState.id] = tokenState;
|
newMapTokens[tokenState.id] = tokenState;
|
||||||
@ -395,15 +414,20 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapTokenStateChange(change) {
|
function handleMapTokenStateChange(
|
||||||
|
change: Record<string, Partial<TokenState>>
|
||||||
|
) {
|
||||||
if (!currentMapState) {
|
if (!currentMapState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setCurrentMapState((prevMapState) => {
|
setCurrentMapState((prevMapState) => {
|
||||||
|
if (!prevMapState) {
|
||||||
|
return prevMapState;
|
||||||
|
}
|
||||||
let tokens = { ...prevMapState.tokens };
|
let tokens = { ...prevMapState.tokens };
|
||||||
for (let id in change) {
|
for (let id in change) {
|
||||||
if (id in tokens) {
|
if (id in tokens) {
|
||||||
tokens[id] = { ...tokens[id], ...change[id] };
|
tokens[id] = { ...tokens[id], ...change[id] } as TokenState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,29 +438,24 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapTokenStateRemove(tokenState) {
|
function handleMapTokenStateRemove(tokenState: TokenState) {
|
||||||
setCurrentMapState((prevMapState) => {
|
setCurrentMapState((prevMapState) => {
|
||||||
|
if (!prevMapState) {
|
||||||
|
return prevMapState;
|
||||||
|
}
|
||||||
const { [tokenState.id]: old, ...rest } = prevMapState.tokens;
|
const { [tokenState.id]: old, ...rest } = prevMapState.tokens;
|
||||||
return { ...prevMapState, tokens: rest };
|
return { ...prevMapState, tokens: rest };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function handlePeerData({
|
async function handlePeerData({ id, data, reply }: PeerDataEvent) {
|
||||||
id,
|
|
||||||
data,
|
|
||||||
reply,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
data;
|
|
||||||
reply;
|
|
||||||
}) {
|
|
||||||
if (id === "assetRequest") {
|
if (id === "assetRequest") {
|
||||||
const asset = await getAsset(data.id);
|
const asset = await getAsset(data.id);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
reply("assetResponseSuccess", asset, undefined, data.id);
|
reply("assetResponseSuccess", asset, data.id);
|
||||||
} else {
|
} else {
|
||||||
reply("assetResponseFail", data.id, undefined, data.id);
|
reply("assetResponseFail", data.id, data.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,15 +475,11 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
id,
|
id,
|
||||||
total,
|
total,
|
||||||
count,
|
count,
|
||||||
}: {
|
}: PeerDataProgressEvent) {
|
||||||
id: string;
|
|
||||||
total: number;
|
|
||||||
count: number;
|
|
||||||
}) {
|
|
||||||
assetProgressUpdate({ id, total, count });
|
assetProgressUpdate({ id, total, count });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSocketMap(map) {
|
async function handleSocketMap(map?: MapType) {
|
||||||
if (map) {
|
if (map) {
|
||||||
setCurrentMap(map);
|
setCurrentMap(map);
|
||||||
} else {
|
} else {
|
||||||
@ -502,7 +517,7 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
(currentMapState.editFlags.includes("notes") ||
|
(currentMapState.editFlags.includes("notes") ||
|
||||||
currentMap?.owner === userId);
|
currentMap?.owner === userId);
|
||||||
|
|
||||||
const disabledMapTokens = {};
|
const disabledMapTokens: Record<string, boolean> = {};
|
||||||
// If we have a map and state and have the token permission disabled
|
// If we have a map and state and have the token permission disabled
|
||||||
// and are not the map owner
|
// and are not the map owner
|
||||||
if (
|
if (
|
||||||
@ -539,10 +554,10 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
|||||||
onFogDrawRedo={handleFogDrawRedo}
|
onFogDrawRedo={handleFogDrawRedo}
|
||||||
onMapNoteChange={handleNoteChange}
|
onMapNoteChange={handleNoteChange}
|
||||||
onMapNoteRemove={handleNoteRemove}
|
onMapNoteRemove={handleNoteRemove}
|
||||||
allowMapDrawing={canEditMapDrawing}
|
allowMapDrawing={!!canEditMapDrawing}
|
||||||
allowFogDrawing={canEditFogDrawing}
|
allowFogDrawing={!!canEditFogDrawing}
|
||||||
allowMapChange={canChangeMap}
|
allowMapChange={canChangeMap}
|
||||||
allowNoteEditing={canEditNotes}
|
allowNoteEditing={!!canEditNotes}
|
||||||
disabledTokens={disabledMapTokens}
|
disabledTokens={disabledMapTokens}
|
||||||
session={session}
|
session={session}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
|
|
||||||
import { useUserId } from "../contexts/UserIdContext";
|
import { useUserId } from "../contexts/UserIdContext";
|
||||||
@ -9,20 +9,31 @@ import Vector2 from "../helpers/Vector2";
|
|||||||
|
|
||||||
import useSetting from "../hooks/useSetting";
|
import useSetting from "../hooks/useSetting";
|
||||||
import Session from "./Session";
|
import Session from "./Session";
|
||||||
|
import { Color } from "../helpers/colors";
|
||||||
|
import { PointerState } from "../types/Pointer";
|
||||||
|
|
||||||
// Send pointer updates every 50ms (20fps)
|
// Send pointer updates every 50ms (20fps)
|
||||||
const sendTickRate = 50;
|
const sendTickRate = 50;
|
||||||
|
|
||||||
function NetworkedMapPointer({
|
type InterpolatedPointerState = PointerState & { time: number };
|
||||||
session,
|
|
||||||
active,
|
type PointerInterpolation = {
|
||||||
}: {
|
id: string;
|
||||||
|
from: InterpolatedPointerState | null;
|
||||||
|
to: InterpolatedPointerState;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NetworkedMapPointerProps = {
|
||||||
session: Session;
|
session: Session;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
}) {
|
};
|
||||||
|
|
||||||
|
function NetworkedMapPointer({ session, active }: NetworkedMapPointerProps) {
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
const [localPointerState, setLocalPointerState] = useState({});
|
const [localPointerState, setLocalPointerState] = useState<
|
||||||
const [pointerColor] = useSetting("pointer.color");
|
Record<string, PointerState>
|
||||||
|
>({});
|
||||||
|
const [pointerColor] = useSetting<Color>("pointer.color");
|
||||||
|
|
||||||
const sessionRef = useRef(session);
|
const sessionRef = useRef(session);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -45,14 +56,12 @@ function NetworkedMapPointer({
|
|||||||
// Send pointer updates every sendTickRate to peers to save on bandwidth
|
// Send pointer updates every sendTickRate to peers to save on bandwidth
|
||||||
// We use requestAnimationFrame as setInterval was being blocked during
|
// We use requestAnimationFrame as setInterval was being blocked during
|
||||||
// re-renders on Chrome with Windows
|
// re-renders on Chrome with Windows
|
||||||
const ownPointerUpdateRef: React.MutableRefObject<
|
const ownPointerUpdateRef = useRef<PointerState | null>(null);
|
||||||
{ position; visible: boolean; id; color } | undefined | null
|
|
||||||
> = useRef();
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let prevTime = performance.now();
|
let prevTime = performance.now();
|
||||||
let request = requestAnimationFrame(update);
|
let request = requestAnimationFrame(update);
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
function update(time) {
|
function update(time: number) {
|
||||||
request = requestAnimationFrame(update);
|
request = requestAnimationFrame(update);
|
||||||
const deltaTime = time - prevTime;
|
const deltaTime = time - prevTime;
|
||||||
counter += deltaTime;
|
counter += deltaTime;
|
||||||
@ -79,7 +88,10 @@ function NetworkedMapPointer({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function updateOwnPointerState(position, visible: boolean) {
|
function updateOwnPointerState(position: Vector2, visible: boolean) {
|
||||||
|
if (!userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLocalPointerState((prev) => ({
|
setLocalPointerState((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[userId]: { position, visible, id: userId, color: pointerColor },
|
[userId]: { position, visible, id: userId, color: pointerColor },
|
||||||
@ -92,23 +104,23 @@ function NetworkedMapPointer({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOwnPointerDown(position) {
|
function handleOwnPointerDown(position: Vector2) {
|
||||||
updateOwnPointerState(position, true);
|
updateOwnPointerState(position, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOwnPointerMove(position) {
|
function handleOwnPointerMove(position: Vector2) {
|
||||||
updateOwnPointerState(position, true);
|
updateOwnPointerState(position, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOwnPointerUp(position) {
|
function handleOwnPointerUp(position: Vector2) {
|
||||||
updateOwnPointerState(position, false);
|
updateOwnPointerState(position, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pointer data receive
|
// Handle pointer data receive
|
||||||
const interpolationsRef: React.MutableRefObject = useRef({});
|
const interpolationsRef = useRef<Record<string, PointerInterpolation>>({});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO: Handle player disconnect while pointer visible
|
// TODO: Handle player disconnect while pointer visible
|
||||||
function handleSocketPlayerPointer(pointer) {
|
function handleSocketPlayerPointer(pointer: InterpolatedPointerState) {
|
||||||
const interpolations = interpolationsRef.current;
|
const interpolations = interpolationsRef.current;
|
||||||
const id = pointer.id;
|
const id = pointer.id;
|
||||||
if (!(id in interpolations)) {
|
if (!(id in interpolations)) {
|
||||||
@ -154,7 +166,7 @@ function NetworkedMapPointer({
|
|||||||
function animate() {
|
function animate() {
|
||||||
request = requestAnimationFrame(animate);
|
request = requestAnimationFrame(animate);
|
||||||
const time = performance.now();
|
const time = performance.now();
|
||||||
let interpolatedPointerState = {};
|
let interpolatedPointerState: Record<string, PointerState> = {};
|
||||||
for (let interp of Object.values(interpolationsRef.current)) {
|
for (let interp of Object.values(interpolationsRef.current)) {
|
||||||
if (!interp.from || !interp.to) {
|
if (!interp.from || !interp.to) {
|
||||||
continue;
|
continue;
|
||||||
@ -206,9 +218,13 @@ function NetworkedMapPointer({
|
|||||||
active={pointer.id === userId ? active : false}
|
active={pointer.id === userId ? active : false}
|
||||||
position={pointer.position}
|
position={pointer.position}
|
||||||
visible={pointer.visible}
|
visible={pointer.visible}
|
||||||
onPointerDown={pointer.id === userId && handleOwnPointerDown}
|
onPointerDown={
|
||||||
onPointerMove={pointer.id === userId && handleOwnPointerMove}
|
pointer.id === userId ? handleOwnPointerDown : undefined
|
||||||
onPointerUp={pointer.id === userId && handleOwnPointerUp}
|
}
|
||||||
|
onPointerMove={
|
||||||
|
pointer.id === userId ? handleOwnPointerMove : undefined
|
||||||
|
}
|
||||||
|
onPointerUp={pointer.id === userId ? handleOwnPointerUp : undefined}
|
||||||
color={pointer.color}
|
color={pointer.color}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from "react";
|
import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import { useToasts } from "react-toast-notifications";
|
import { useToasts } from "react-toast-notifications";
|
||||||
|
|
||||||
// Load session for auto complete
|
import Session, { PeerTrackAddedEvent, PeerTrackRemovedEvent } from "./Session";
|
||||||
import Session, { SessionPeer } from "./Session";
|
|
||||||
import { isStreamStopped, omit } from "../helpers/shared";
|
import { isStreamStopped, omit } from "../helpers/shared";
|
||||||
|
|
||||||
import { useParty } from "../contexts/PartyContext";
|
import { useParty } from "../contexts/PartyContext";
|
||||||
|
|
||||||
import Party from "../components/party/Party";
|
import Party from "../components/party/Party";
|
||||||
import { PartyState } from "../components/party/PartyState";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} NetworkedPartyProps
|
* @typedef {object} NetworkedPartyProps
|
||||||
@ -16,13 +14,13 @@ import { PartyState } from "../components/party/PartyState";
|
|||||||
* @property {Session} session
|
* @property {Session} session
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type NetworkedPartyProps = { gameId: string, session: Session }
|
type NetworkedPartyProps = { gameId: string; session: Session };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {NetworkedPartyProps} props
|
* @param {NetworkedPartyProps} props
|
||||||
*/
|
*/
|
||||||
function NetworkedParty(props: NetworkedPartyProps) {
|
function NetworkedParty({ gameId, session }: NetworkedPartyProps) {
|
||||||
const partyState: PartyState = useParty();
|
const partyState = useParty();
|
||||||
const [stream, setStream] = useState<MediaStream | null>(null);
|
const [stream, setStream] = useState<MediaStream | null>(null);
|
||||||
const [partyStreams, setPartyStreams] = useState({});
|
const [partyStreams, setPartyStreams] = useState({});
|
||||||
|
|
||||||
@ -35,7 +33,9 @@ function NetworkedParty(props: NetworkedPartyProps) {
|
|||||||
// Only add the audio track of the stream to the remote peer
|
// Only add the audio track of the stream to the remote peer
|
||||||
if (track.kind === "audio") {
|
if (track.kind === "audio") {
|
||||||
for (let player of Object.values(partyState)) {
|
for (let player of Object.values(partyState)) {
|
||||||
props.session.startStreamTo(player.sessionId, track, localStream);
|
if (player.sessionId) {
|
||||||
|
session.startStreamTo(player.sessionId, track, localStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,12 +50,14 @@ function NetworkedParty(props: NetworkedPartyProps) {
|
|||||||
// Only sending audio so only remove the audio track
|
// Only sending audio so only remove the audio track
|
||||||
if (track.kind === "audio") {
|
if (track.kind === "audio") {
|
||||||
for (let player of Object.values(partyState)) {
|
for (let player of Object.values(partyState)) {
|
||||||
props.session.endStreamTo(player.sessionId, track, localStream);
|
if (player.sessionId) {
|
||||||
|
session.endStreamTo(player.sessionId, track, localStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[props.session, partyState]
|
[session, partyState]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Keep a reference to players who have just joined to show the joined notification
|
// Keep a reference to players who have just joined to show the joined notification
|
||||||
@ -77,7 +79,7 @@ function NetworkedParty(props: NetworkedPartyProps) {
|
|||||||
const tracks = stream.getTracks();
|
const tracks = stream.getTracks();
|
||||||
for (let track of tracks) {
|
for (let track of tracks) {
|
||||||
if (track.kind === "audio") {
|
if (track.kind === "audio") {
|
||||||
props.session.startStreamTo(sessionId, track, stream);
|
session.startStreamTo(sessionId, track, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,14 +94,20 @@ function NetworkedParty(props: NetworkedPartyProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePeerTrackAdded({ peer, stream: remoteStream }: { peer: SessionPeer, stream: MediaStream}) {
|
function handlePeerTrackAdded({
|
||||||
|
peer,
|
||||||
|
stream: remoteStream,
|
||||||
|
}: PeerTrackAddedEvent) {
|
||||||
setPartyStreams((prevStreams) => ({
|
setPartyStreams((prevStreams) => ({
|
||||||
...prevStreams,
|
...prevStreams,
|
||||||
[peer.id]: remoteStream,
|
[peer.id]: remoteStream,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePeerTrackRemoved({ peer, stream: remoteStream }: { peer: SessionPeer, stream: MediaStream }) {
|
function handlePeerTrackRemoved({
|
||||||
|
peer,
|
||||||
|
stream: remoteStream,
|
||||||
|
}: PeerTrackRemovedEvent) {
|
||||||
if (isStreamStopped(remoteStream)) {
|
if (isStreamStopped(remoteStream)) {
|
||||||
setPartyStreams((prevStreams) => omit(prevStreams, [peer.id]));
|
setPartyStreams((prevStreams) => omit(prevStreams, [peer.id]));
|
||||||
} else {
|
} else {
|
||||||
@ -110,16 +118,16 @@ function NetworkedParty(props: NetworkedPartyProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
props.session.on("playerJoined", handlePlayerJoined);
|
session.on("playerJoined", handlePlayerJoined);
|
||||||
props.session.on("playerLeft", handlePlayerLeft);
|
session.on("playerLeft", handlePlayerLeft);
|
||||||
props.session.on("peerTrackAdded", handlePeerTrackAdded);
|
session.on("peerTrackAdded", handlePeerTrackAdded);
|
||||||
props.session.on("peerTrackRemoved", handlePeerTrackRemoved);
|
session.on("peerTrackRemoved", handlePeerTrackRemoved);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
props.session.off("playerJoined", handlePlayerJoined);
|
session.off("playerJoined", handlePlayerJoined);
|
||||||
props.session.off("playerLeft", handlePlayerLeft);
|
session.off("playerLeft", handlePlayerLeft);
|
||||||
props.session.off("peerTrackAdded", handlePeerTrackAdded);
|
session.off("peerTrackAdded", handlePeerTrackAdded);
|
||||||
props.session.off("peerTrackRemoved", handlePeerTrackRemoved);
|
session.off("peerTrackRemoved", handlePeerTrackRemoved);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -142,7 +150,7 @@ function NetworkedParty(props: NetworkedPartyProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Party
|
<Party
|
||||||
gameId={props.gameId}
|
gameId={gameId}
|
||||||
onStreamStart={handleStreamStart}
|
onStreamStart={handleStreamStart}
|
||||||
onStreamEnd={handleStreamEnd}
|
onStreamEnd={handleStreamEnd}
|
||||||
stream={stream}
|
stream={stream}
|
||||||
|
@ -2,14 +2,13 @@ import io, { Socket } 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";
|
||||||
|
|
||||||
import Connection from "./Connection";
|
import Connection, { DataProgressEvent } from "./Connection";
|
||||||
|
|
||||||
import { omit } from "../helpers/shared";
|
import { omit } from "../helpers/shared";
|
||||||
import { logError } from "../helpers/logging";
|
import { logError } from "../helpers/logging";
|
||||||
import { SimplePeerData } from "simple-peer";
|
import { SignalData } from "simple-peer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} SessionPeer
|
|
||||||
* @property {string} id - The socket id of the peer
|
* @property {string} id - The socket id of the peer
|
||||||
* @property {Connection} connection - The actual peer connection
|
* @property {Connection} connection - The actual peer connection
|
||||||
* @property {boolean} initiator - Is this peer the initiator of the connection
|
* @property {boolean} initiator - Is this peer the initiator of the connection
|
||||||
@ -22,37 +21,13 @@ export type SessionPeer = {
|
|||||||
ready: boolean;
|
ready: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export type PeerData = any;
|
||||||
* @callback peerReply
|
|
||||||
* @param {string} id - The id of the event
|
|
||||||
* @param {object} data - The data to send
|
|
||||||
* @param {string=} channel - The channel to send to
|
|
||||||
* @param {string=} chunkId
|
|
||||||
*/
|
|
||||||
|
|
||||||
type peerReply = (id: string, data: SimplePeerData, channel: string) => void;
|
export type PeerReply = (id: string, data: PeerData, chunkId?: string) => void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Session Status Event - Status of the session has changed
|
|
||||||
*
|
|
||||||
* @event Session#status
|
|
||||||
* @property {"ready"|"joining"|"joined"|"offline"|"reconnecting"|"auth"|"needs_update"} status
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Handles connections to multiple peers
|
* Handles connections to multiple peers
|
||||||
*
|
|
||||||
* @fires Session#peerConnect
|
|
||||||
* @fires Session#peerData
|
|
||||||
* @fires Session#peerTrackAdded
|
|
||||||
* @fires Session#peerTrackRemoved
|
|
||||||
* @fires Session#peerDisconnect
|
|
||||||
* @fires Session#peerError
|
|
||||||
* @fires Session#status
|
|
||||||
* @fires Session#playerJoined
|
|
||||||
* @fires Session#playerLeft
|
|
||||||
* @fires Session#gameExpired
|
|
||||||
*/
|
*/
|
||||||
class Session extends EventEmitter {
|
class Session extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
@ -73,7 +48,7 @@ class Session extends EventEmitter {
|
|||||||
return this.socket && this.socket.id;
|
return this.socket && this.socket.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
_iceServers: string[] = [];
|
_iceServers: RTCIceServer[] = [];
|
||||||
|
|
||||||
// Store party id and password for reconnect
|
// Store party id and password for reconnect
|
||||||
_gameId: string = "";
|
_gameId: string = "";
|
||||||
@ -133,19 +108,10 @@ class Session extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Send data to a single peer
|
* Send data to a single peer
|
||||||
*
|
*
|
||||||
* @param {string} sessionId - The socket id of the player to send to
|
* @param sessionId - The socket id of the player to send to
|
||||||
* @param {string} eventId - The id of the event to send
|
* @param eventId - The id of the event to send
|
||||||
* @param {object} data
|
|
||||||
* @param {string=} channel
|
|
||||||
* @param {string=} chunkId
|
|
||||||
*/
|
*/
|
||||||
sendTo(
|
sendTo(sessionId: string, eventId: string, data: PeerData, chunkId?: string) {
|
||||||
sessionId: string,
|
|
||||||
eventId: string,
|
|
||||||
data,
|
|
||||||
channel?: string,
|
|
||||||
chunkId?: string
|
|
||||||
) {
|
|
||||||
if (!(sessionId in this.peers)) {
|
if (!(sessionId in this.peers)) {
|
||||||
if (!this._addPeer(sessionId, true)) {
|
if (!this._addPeer(sessionId, true)) {
|
||||||
return;
|
return;
|
||||||
@ -156,14 +122,13 @@ class Session extends EventEmitter {
|
|||||||
this.peers[sessionId].connection.once("connect", () => {
|
this.peers[sessionId].connection.once("connect", () => {
|
||||||
this.peers[sessionId].connection.sendObject(
|
this.peers[sessionId].connection.sendObject(
|
||||||
{ id: eventId, data },
|
{ id: eventId, data },
|
||||||
channel,
|
|
||||||
chunkId
|
chunkId
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.peers[sessionId].connection.sendObject(
|
this.peers[sessionId].connection.sendObject(
|
||||||
{ id: eventId, data },
|
{ id: eventId, data },
|
||||||
channel
|
chunkId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,7 +206,7 @@ class Session extends EventEmitter {
|
|||||||
* @param {boolean} initiator
|
* @param {boolean} initiator
|
||||||
* @returns {boolean} True if peer was added successfully
|
* @returns {boolean} True if peer was added successfully
|
||||||
*/
|
*/
|
||||||
_addPeer(id: string, initiator: boolean) {
|
_addPeer(id: string, initiator: boolean): boolean {
|
||||||
try {
|
try {
|
||||||
const connection = new Connection({
|
const connection = new Connection({
|
||||||
initiator,
|
initiator,
|
||||||
@ -254,11 +219,11 @@ class Session extends EventEmitter {
|
|||||||
|
|
||||||
const peer = { id, connection, initiator, ready: false };
|
const peer = { id, connection, initiator, ready: false };
|
||||||
|
|
||||||
function reply(id: string, data, channel?: string, chunkId?: string) {
|
const reply: PeerReply = (id, data, chunkId) => {
|
||||||
peer.connection.sendObject({ id, data }, channel, chunkId);
|
peer.connection.sendObject({ id, data }, chunkId);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleSignal = (signal) => {
|
const handleSignal = (signal: SignalData) => {
|
||||||
this.socket.emit("signal", JSON.stringify({ to: peer.id, signal }));
|
this.socket.emit("signal", JSON.stringify({ to: peer.id, signal }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -266,105 +231,54 @@ class Session extends EventEmitter {
|
|||||||
if (peer.id in this.peers) {
|
if (peer.id in this.peers) {
|
||||||
this.peers[peer.id].ready = true;
|
this.peers[peer.id].ready = true;
|
||||||
}
|
}
|
||||||
/**
|
const peerConnectEvent: PeerConnectEvent = {
|
||||||
* Peer Connect Event - A peer has connected
|
peer,
|
||||||
*
|
reply,
|
||||||
* @event Session#peerConnect
|
};
|
||||||
* @type {object}
|
this.emit("peerConnect", peerConnectEvent);
|
||||||
* @property {SessionPeer} peer
|
|
||||||
* @property {peerReply} reply
|
|
||||||
*/
|
|
||||||
this.emit("peerConnect", { peer, reply });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDataComplete = (data) => {
|
const handleDataComplete = (data: any) => {
|
||||||
/**
|
const peerDataEvent: PeerDataEvent = {
|
||||||
* Peer Data Event - Data received by a peer
|
|
||||||
*
|
|
||||||
* @event Session#peerData
|
|
||||||
* @type {object}
|
|
||||||
* @property {SessionPeer} peer
|
|
||||||
* @property {string} id
|
|
||||||
* @property {object} data
|
|
||||||
* @property {peerReply} reply
|
|
||||||
*/
|
|
||||||
let peerDataEvent: {
|
|
||||||
peer: SessionPeer;
|
|
||||||
id: string;
|
|
||||||
data;
|
|
||||||
reply: peerReply;
|
|
||||||
} = {
|
|
||||||
peer,
|
peer,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
data: data.data,
|
data: data.data,
|
||||||
reply: reply,
|
reply: reply,
|
||||||
};
|
};
|
||||||
console.log(`Data: ${JSON.stringify(data)}`);
|
|
||||||
this.emit("peerData", peerDataEvent);
|
this.emit("peerData", peerDataEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDataProgress = ({
|
const handleDataProgress = ({ id, count, total }: DataProgressEvent) => {
|
||||||
id,
|
const peerDataProgressEvent: PeerDataProgressEvent = {
|
||||||
count,
|
|
||||||
total,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
count: number;
|
|
||||||
total: number;
|
|
||||||
}) => {
|
|
||||||
this.emit("peerDataProgress", {
|
|
||||||
peer,
|
peer,
|
||||||
id,
|
id,
|
||||||
count,
|
count,
|
||||||
total,
|
total,
|
||||||
reply,
|
reply,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
this.emit("peerDataProgress", peerDataProgressEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTrack = (track: MediaStreamTrack, stream: MediaStream) => {
|
const handleTrack = (track: MediaStreamTrack, stream: MediaStream) => {
|
||||||
/**
|
const peerTrackAddedEvent: PeerTrackAddedEvent = {
|
||||||
* Peer Track Added Event - A `MediaStreamTrack` was added by a peer
|
peer,
|
||||||
*
|
track,
|
||||||
* @event Session#peerTrackAdded
|
stream,
|
||||||
* @type {object}
|
};
|
||||||
* @property {SessionPeer} peer
|
|
||||||
* @property {MediaStreamTrack} track
|
|
||||||
* @property {MediaStream} stream
|
|
||||||
*/
|
|
||||||
let peerTrackAddedEvent: {
|
|
||||||
peer: SessionPeer;
|
|
||||||
track: MediaStreamTrack;
|
|
||||||
stream: MediaStream;
|
|
||||||
} = { peer, track, stream };
|
|
||||||
this.emit("peerTrackAdded", peerTrackAddedEvent);
|
this.emit("peerTrackAdded", peerTrackAddedEvent);
|
||||||
track.addEventListener("mute", () => {
|
track.addEventListener("mute", () => {
|
||||||
/**
|
const peerTrackRemovedEvent: PeerTrackRemovedEvent = {
|
||||||
* Peer Track Removed Event - A `MediaStreamTrack` was removed by a peer
|
peer,
|
||||||
*
|
track,
|
||||||
* @event Session#peerTrackRemoved
|
stream,
|
||||||
* @type {object}
|
};
|
||||||
* @property {SessionPeer} peer
|
|
||||||
* @property {MediaStreamTrack} track
|
|
||||||
* @property {MediaStream} stream
|
|
||||||
*/
|
|
||||||
let peerTrackRemovedEvent: {
|
|
||||||
peer: SessionPeer;
|
|
||||||
track: MediaStreamTrack;
|
|
||||||
stream: MediaStream;
|
|
||||||
} = { peer, track, stream };
|
|
||||||
this.emit("peerTrackRemoved", peerTrackRemovedEvent);
|
this.emit("peerTrackRemoved", peerTrackRemovedEvent);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
/**
|
const peerDisconnectEvent: PeerDisconnectEvent = { peer };
|
||||||
* Peer Disconnect Event - A peer has disconnected
|
|
||||||
*
|
|
||||||
* @event Session#peerDisconnect
|
|
||||||
* @type {object}
|
|
||||||
* @property {SessionPeer} peer
|
|
||||||
*/
|
|
||||||
let peerDisconnectEvent: { peer: SessionPeer } = { peer };
|
|
||||||
this.emit("peerDisconnect", peerDisconnectEvent);
|
this.emit("peerDisconnect", peerDisconnectEvent);
|
||||||
if (peer.id in this.peers) {
|
if (peer.id in this.peers) {
|
||||||
peer.connection.destroy();
|
peer.connection.destroy();
|
||||||
@ -372,16 +286,8 @@ class Session extends EventEmitter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (error: Error) => {
|
const handleError = (error: PeerError) => {
|
||||||
/**
|
const peerErrorEvent: PeerErrorEvent = {
|
||||||
* Peer Error Event - An error occured with a peer connection
|
|
||||||
*
|
|
||||||
* @event Session#peerError
|
|
||||||
* @type {object}
|
|
||||||
* @property {SessionPeer} peer
|
|
||||||
* @property {Error} error
|
|
||||||
*/
|
|
||||||
let peerErrorEvent: { peer: SessionPeer; error: Error } = {
|
|
||||||
peer,
|
peer,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
@ -418,31 +324,14 @@ class Session extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleGameExpired() {
|
_handleGameExpired() {
|
||||||
/**
|
|
||||||
* Game Expired Event - A joining game has expired
|
|
||||||
*
|
|
||||||
* @event Session#gameExpired
|
|
||||||
*/
|
|
||||||
this.emit("gameExpired");
|
this.emit("gameExpired");
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePlayerJoined(id: string) {
|
_handlePlayerJoined(id: string) {
|
||||||
/**
|
|
||||||
* Player Joined Event - A player has joined the game
|
|
||||||
*
|
|
||||||
* @event Session#playerJoined
|
|
||||||
* @property {string} id
|
|
||||||
*/
|
|
||||||
this.emit("playerJoined", id);
|
this.emit("playerJoined", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePlayerLeft(id: string) {
|
_handlePlayerLeft(id: string) {
|
||||||
/**
|
|
||||||
* Player Left Event - A player has left the game
|
|
||||||
*
|
|
||||||
* @event Session#playerLeft
|
|
||||||
* @property {string} id
|
|
||||||
*/
|
|
||||||
this.emit("playerLeft", id);
|
this.emit("playerLeft", id);
|
||||||
if (id in this.peers) {
|
if (id in this.peers) {
|
||||||
this.peers[id].connection.destroy();
|
this.peers[id].connection.destroy();
|
||||||
@ -450,7 +339,7 @@ class Session extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleSignal(data) {
|
_handleSignal(data: { from: string; signal: SignalData }) {
|
||||||
const { from, signal } = data;
|
const { from, signal } = data;
|
||||||
if (!(from in this.peers)) {
|
if (!(from in this.peers)) {
|
||||||
if (!this._addPeer(from, false)) {
|
if (!this._addPeer(from, false)) {
|
||||||
@ -484,14 +373,96 @@ class Session extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleForceUpdate() {
|
_handleForceUpdate() {
|
||||||
/**
|
|
||||||
* Force Update Event - An update has been released
|
|
||||||
*
|
|
||||||
* @event Session#forceUpdate
|
|
||||||
*/
|
|
||||||
this.socket.disconnect();
|
this.socket.disconnect();
|
||||||
this.emit("status", "needs_update");
|
this.emit("status", "needs_update");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PeerConnectEvent = {
|
||||||
|
peer: SessionPeer;
|
||||||
|
reply: PeerReply;
|
||||||
|
};
|
||||||
|
export type PeerConnectEventHandler = (event: PeerConnectEvent) => void;
|
||||||
|
|
||||||
|
export type PeerDataEvent = {
|
||||||
|
peer: SessionPeer;
|
||||||
|
id: string;
|
||||||
|
data: PeerData;
|
||||||
|
reply: PeerReply;
|
||||||
|
};
|
||||||
|
export type PeerDataEventHandler = (event: PeerDataEvent) => void;
|
||||||
|
|
||||||
|
export type PeerDataProgressEvent = {
|
||||||
|
peer: SessionPeer;
|
||||||
|
id: string;
|
||||||
|
count: number;
|
||||||
|
total: number;
|
||||||
|
reply: PeerReply;
|
||||||
|
};
|
||||||
|
export type PeerDataProgressEventHandler = (
|
||||||
|
event: PeerDataProgressEvent
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export type PeerTrackAddedEvent = {
|
||||||
|
peer: SessionPeer;
|
||||||
|
track: MediaStreamTrack;
|
||||||
|
stream: MediaStream;
|
||||||
|
};
|
||||||
|
export type PeerTrackAddedEventHandler = (event: PeerTrackAddedEvent) => void;
|
||||||
|
|
||||||
|
export type PeerTrackRemovedEvent = {
|
||||||
|
peer: SessionPeer;
|
||||||
|
track: MediaStreamTrack;
|
||||||
|
stream: MediaStream;
|
||||||
|
};
|
||||||
|
export type PeerTrackRemovedEventHandler = (
|
||||||
|
event: PeerTrackRemovedEvent
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export type PeerDisconnectEvent = { peer: SessionPeer };
|
||||||
|
export type PeerDisconnectEventHandler = (event: PeerDisconnectEvent) => void;
|
||||||
|
|
||||||
|
export type PeerError = Error & { code: string };
|
||||||
|
export type PeerErrorEvent = { peer: SessionPeer; error: PeerError };
|
||||||
|
export type PeerErrorEventHandler = (event: PeerErrorEvent) => void;
|
||||||
|
|
||||||
|
export type SessionStatus =
|
||||||
|
| "ready"
|
||||||
|
| "joining"
|
||||||
|
| "joined"
|
||||||
|
| "offline"
|
||||||
|
| "reconnecting"
|
||||||
|
| "auth"
|
||||||
|
| "needs_update";
|
||||||
|
export type SessionStatusHandler = (status: SessionStatus) => void;
|
||||||
|
|
||||||
|
export type PlayerJoinedHandler = (id: string) => void;
|
||||||
|
export type PlayerLeftHandler = (id: string) => void;
|
||||||
|
export type GameExpiredHandler = () => void;
|
||||||
|
|
||||||
|
declare interface Session {
|
||||||
|
/** Peer Connect Event - A peer has connected */
|
||||||
|
on(event: "peerConnect", listener: PeerConnectEventHandler): this;
|
||||||
|
/** Peer Data Event - Data received by a peer */
|
||||||
|
on(event: "peerData", listener: PeerDataEventHandler): this;
|
||||||
|
/** Peer Data Progress Event - Part of some data received by a peer */
|
||||||
|
on(event: "peerDataProgress", listener: PeerDataProgressEventHandler): this;
|
||||||
|
/** Peer Track Added Event - A `MediaStreamTrack` was added by a peer */
|
||||||
|
on(event: "peerTrackAdded", listener: PeerTrackAddedEventHandler): this;
|
||||||
|
/** Peer Track Removed Event - A `MediaStreamTrack` was removed by a peer */
|
||||||
|
on(event: "peerTrackRemoved", listener: PeerTrackRemovedEventHandler): this;
|
||||||
|
/** Peer Disconnect Event - A peer has disconnected */
|
||||||
|
on(event: "peerDisconnect", listener: PeerDisconnectEventHandler): this;
|
||||||
|
/** Peer Error Event - An error occured with a peer connection */
|
||||||
|
on(event: "peerError", listener: PeerErrorEventHandler): this;
|
||||||
|
/** Session Status Event - Status of the session has changed */
|
||||||
|
on(event: "status", listener: SessionStatusHandler): this;
|
||||||
|
/** Player Joined Event - A player has joined the game */
|
||||||
|
on(event: "playerJoined", listener: PlayerJoinedHandler): this;
|
||||||
|
/** Player Left Event - A player has left the game */
|
||||||
|
on(event: "playerLeft", listener: PlayerLeftHandler): this;
|
||||||
|
/** Game Expired Event - A joining game has expired */
|
||||||
|
on(event: "gameExpired", listener: GameExpiredHandler): this;
|
||||||
|
}
|
||||||
|
|
||||||
export default Session;
|
export default Session;
|
||||||
|
@ -28,7 +28,7 @@ import { MapLoadingProvider } from "../contexts/MapLoadingContext";
|
|||||||
import NetworkedMapAndTokens from "../network/NetworkedMapAndTokens";
|
import NetworkedMapAndTokens from "../network/NetworkedMapAndTokens";
|
||||||
import NetworkedParty from "../network/NetworkedParty";
|
import NetworkedParty from "../network/NetworkedParty";
|
||||||
|
|
||||||
import Session from "../network/Session";
|
import Session, { PeerErrorEvent, SessionStatus } from "../network/Session";
|
||||||
|
|
||||||
function Game() {
|
function Game() {
|
||||||
const { id: gameId }: { id: string } = useParams();
|
const { id: gameId }: { id: string } = useParams();
|
||||||
@ -36,7 +36,7 @@ function Game() {
|
|||||||
const { databaseStatus } = useDatabase();
|
const { databaseStatus } = useDatabase();
|
||||||
|
|
||||||
const [session] = useState(new Session());
|
const [session] = useState(new Session());
|
||||||
const [sessionStatus, setSessionStatus] = useState();
|
const [sessionStatus, setSessionStatus] = useState<SessionStatus>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function connect() {
|
async function connect() {
|
||||||
@ -50,9 +50,9 @@ function Game() {
|
|||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
// Handle session errors
|
// Handle session errors
|
||||||
const [peerError, setPeerError] = useState(null);
|
const [peerError, setPeerError] = useState<string | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handlePeerError({ error }) {
|
function handlePeerError({ error }: PeerErrorEvent) {
|
||||||
if (error.code === "ERR_WEBRTC_SUPPORT") {
|
if (error.code === "ERR_WEBRTC_SUPPORT") {
|
||||||
setPeerError("WebRTC not supported.");
|
setPeerError("WebRTC not supported.");
|
||||||
} else if (error.code === "ERR_CREATE_OFFER") {
|
} else if (error.code === "ERR_CREATE_OFFER") {
|
||||||
@ -66,7 +66,7 @@ function Game() {
|
|||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleStatus(status: any) {
|
function handleStatus(status: SessionStatus) {
|
||||||
setSessionStatus(status);
|
setSessionStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Flex, Button, Image, Text, IconButton, Link } from "theme-ui";
|
import { Flex, Button, Image, Text, IconButton, Link } from "theme-ui";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
@ -23,9 +23,8 @@ import owlington from "../images/Owlington.png";
|
|||||||
function Home() {
|
function Home() {
|
||||||
const [isStartModalOpen, setIsStartModalOpen] = useState(false);
|
const [isStartModalOpen, setIsStartModalOpen] = useState(false);
|
||||||
const [isJoinModalOpen, setIsJoinModalOpen] = useState(false);
|
const [isJoinModalOpen, setIsJoinModalOpen] = useState(false);
|
||||||
const [isGettingStartedModalOpen, setIsGettingStartedModalOpen] = useState(
|
const [isGettingStartedModalOpen, setIsGettingStartedModalOpen] =
|
||||||
false
|
useState(false);
|
||||||
);
|
|
||||||
|
|
||||||
// Reset password on visiting home
|
// Reset password on visiting home
|
||||||
const { setPassword } = useAuth();
|
const { setPassword } = useAuth();
|
||||||
@ -80,6 +79,7 @@ function Home() {
|
|||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
as="a"
|
as="a"
|
||||||
|
// @ts-ignore
|
||||||
href="https://patreon.com/owlbearrodeo"
|
href="https://patreon.com/owlbearrodeo"
|
||||||
mt={4}
|
mt={4}
|
||||||
mx={2}
|
mx={2}
|
||||||
@ -94,6 +94,7 @@ function Home() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as="a"
|
as="a"
|
||||||
|
// @ts-ignore
|
||||||
href="/donate"
|
href="/donate"
|
||||||
mt={2}
|
mt={2}
|
||||||
mb={4}
|
mb={4}
|
@ -6,7 +6,10 @@ import { Note } from "./Note";
|
|||||||
import { Token } from "./Token";
|
import { Token } from "./Token";
|
||||||
import { TokenState } from "./TokenState";
|
import { TokenState } from "./TokenState";
|
||||||
|
|
||||||
export type MapChangeEventHandler = (map?: Map, mapState?: MapState) => void;
|
export type MapChangeEventHandler = (
|
||||||
|
map: Map | null,
|
||||||
|
mapState: MapState | null
|
||||||
|
) => void;
|
||||||
export type MapResetEventHandler = (newState: MapState) => void;
|
export type MapResetEventHandler = (newState: MapState) => void;
|
||||||
export type MapSettingsChangeEventHandler = (change: Partial<Map>) => void;
|
export type MapSettingsChangeEventHandler = (change: Partial<Map>) => void;
|
||||||
export type MapStateSettingsChangeEventHandler = (
|
export type MapStateSettingsChangeEventHandler = (
|
||||||
@ -31,7 +34,7 @@ export type TokenSettingsChangeEventHandler = (change: Partial<Token>) => void;
|
|||||||
|
|
||||||
export type NoteAddEventHander = (note: Note) => void;
|
export type NoteAddEventHander = (note: Note) => void;
|
||||||
export type NoteRemoveEventHander = (noteId: string) => void;
|
export type NoteRemoveEventHander = (noteId: string) => void;
|
||||||
export type NoteChangeEventHandler = (change: Partial<Note>) => void;
|
export type NoteChangeEventHandler = (note: Note) => void;
|
||||||
export type NoteMenuOpenEventHandler = (
|
export type NoteMenuOpenEventHandler = (
|
||||||
noteId: string,
|
noteId: string,
|
||||||
noteNode: Konva.Node
|
noteNode: Konva.Node
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Action from "../actions/Action";
|
import Action from "../actions/Action";
|
||||||
import { Drawing } from "./Drawing";
|
import { DrawingState } from "./Drawing";
|
||||||
import { Fog } from "./Fog";
|
import { FogState } from "./Fog";
|
||||||
import { Grid } from "./Grid";
|
import { Grid } from "./Grid";
|
||||||
|
|
||||||
export type MapToolId =
|
export type MapToolId =
|
||||||
@ -59,9 +59,9 @@ export type FileMap = BaseMap & {
|
|||||||
export type Map = DefaultMap | FileMap;
|
export type Map = DefaultMap | FileMap;
|
||||||
|
|
||||||
export type MapActions = {
|
export type MapActions = {
|
||||||
mapDrawActions: Action<Drawing>[];
|
mapDrawActions: Action<DrawingState>[];
|
||||||
mapDrawActionIndex: number;
|
mapDrawActionIndex: number;
|
||||||
fogDrawActions: Action<Fog>[];
|
fogDrawActions: Action<FogState>[];
|
||||||
fogDrawActionIndex: number;
|
fogDrawActionIndex: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Drawing } from "./Drawing";
|
import { DrawingState } from "./Drawing";
|
||||||
import { FogState } from "./Fog";
|
import { FogState } from "./Fog";
|
||||||
import { Note } from "./Note";
|
import { Note } from "./Note";
|
||||||
import { TokenState } from "./TokenState";
|
import { TokenState } from "./TokenState";
|
||||||
@ -7,7 +7,7 @@ export type EditFlag = "drawing" | "tokens" | "notes" | "fog";
|
|||||||
|
|
||||||
export type MapState = {
|
export type MapState = {
|
||||||
tokens: Record<string, TokenState>;
|
tokens: Record<string, TokenState>;
|
||||||
drawShapes: Record<string, Drawing>;
|
drawShapes: DrawingState;
|
||||||
fogShapes: FogState;
|
fogShapes: FogState;
|
||||||
editFlags: Array<EditFlag>;
|
editFlags: Array<EditFlag>;
|
||||||
notes: Record<string, Note>;
|
notes: Record<string, Note>;
|
||||||
|
74
yarn.lock
74
yarn.lock
@ -3244,10 +3244,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/shortid/-/shortid-0.0.29.tgz#8093ee0416a6e2bf2aa6338109114b3fbffa0e9b"
|
resolved "https://registry.yarnpkg.com/@types/shortid/-/shortid-0.0.29.tgz#8093ee0416a6e2bf2aa6338109114b3fbffa0e9b"
|
||||||
integrity sha1-gJPuBBam4r8qpjOBCRFLP7/6Dps=
|
integrity sha1-gJPuBBam4r8qpjOBCRFLP7/6Dps=
|
||||||
|
|
||||||
"@types/simple-peer@^9.6.3":
|
"@types/simple-peer@^9.11.1":
|
||||||
version "9.6.3"
|
version "9.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.6.3.tgz#aa118a57e036f4ce2059a7e25367526a4764206d"
|
resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.11.1.tgz#bef6ff1e75178d83438e33aa6a4df2fd98fded1d"
|
||||||
integrity sha512-zrXEBch9tF4NgkZDsGR3c1D0kq99M1bBCjzEyL0PVfEWzCIXrK64TuxRz3XKOx1B0KoEQ9kTs+AhMDuQaHy5RQ==
|
integrity sha512-Pzqbau/WlivSXdRC0He2Wz/ANj2wbi4gzJrtysZz93jvOyI2jo/ibMjUe6AvPllFl/UO6QXT/A0Rcp44bDQB5A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
@ -4219,7 +4219,7 @@ base64-arraybuffer@0.1.4:
|
|||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
|
||||||
integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=
|
integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=
|
||||||
|
|
||||||
base64-js@^1.0.2:
|
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
@ -4499,6 +4499,14 @@ buffer@^4.3.0:
|
|||||||
ieee754 "^1.1.4"
|
ieee754 "^1.1.4"
|
||||||
isarray "^1.0.0"
|
isarray "^1.0.0"
|
||||||
|
|
||||||
|
buffer@^6.0.3:
|
||||||
|
version "6.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||||
|
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.2.1"
|
||||||
|
|
||||||
builtin-modules@^3.1.0:
|
builtin-modules@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
|
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
|
||||||
@ -5535,6 +5543,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
debug@^4.3.1:
|
||||||
|
version "4.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||||
|
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
debug@~4.3.1:
|
debug@~4.3.1:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||||
@ -6044,11 +6059,6 @@ entities@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
||||||
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
||||||
|
|
||||||
err-code@^2.0.3:
|
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
|
|
||||||
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
|
|
||||||
|
|
||||||
err-code@^3.0.1:
|
err-code@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920"
|
resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920"
|
||||||
@ -6969,7 +6979,7 @@ gensync@^1.0.0-beta.1:
|
|||||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||||
|
|
||||||
get-browser-rtc@^1.0.0, get-browser-rtc@substack/get-browser-rtc#4/head:
|
get-browser-rtc@^1.1.0, get-browser-rtc@substack/get-browser-rtc#4/head:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://codeload.github.com/substack/get-browser-rtc/tar.gz/8c8b8e086026bc68b24b872ecd8f6c3e83885097"
|
resolved "https://codeload.github.com/substack/get-browser-rtc/tar.gz/8c8b8e086026bc68b24b872ecd8f6c3e83885097"
|
||||||
|
|
||||||
@ -7492,7 +7502,7 @@ identity-obj-proxy@3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
harmony-reflect "^1.4.6"
|
harmony-reflect "^1.4.6"
|
||||||
|
|
||||||
ieee754@^1.1.4:
|
ieee754@^1.1.4, ieee754@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
@ -11090,10 +11100,10 @@ querystringify@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||||
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||||
|
|
||||||
queue-microtask@^1.1.0:
|
queue-microtask@^1.2.3:
|
||||||
version "1.2.2"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3"
|
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||||
integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==
|
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||||
|
|
||||||
raf@^3.4.1:
|
raf@^3.4.1:
|
||||||
version "3.4.1"
|
version "3.4.1"
|
||||||
@ -11107,7 +11117,7 @@ ramda@^0.27.1:
|
|||||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9"
|
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9"
|
||||||
integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==
|
integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==
|
||||||
|
|
||||||
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.3, randombytes@^2.0.5, randombytes@^2.1.0:
|
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||||
@ -11509,7 +11519,7 @@ read-pkg@^5.2.0:
|
|||||||
string_decoder "~1.1.1"
|
string_decoder "~1.1.1"
|
||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||||
@ -12043,14 +12053,6 @@ scheduler@^0.20.2:
|
|||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
scheduler@^0.20.2:
|
|
||||||
version "0.20.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
|
||||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
|
|
||||||
schema-utils@^1.0.0:
|
schema-utils@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
|
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
|
||||||
@ -12268,16 +12270,18 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||||
|
|
||||||
simple-peer@feross/simple-peer#694/head:
|
simple-peer@^9.11.0:
|
||||||
version "9.7.2"
|
version "9.11.0"
|
||||||
resolved "https://codeload.github.com/feross/simple-peer/tar.gz/0d08d07b83ff3b8c60401688d80642d24dfeffe2"
|
resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.11.0.tgz#e8d27609c7a610c3ddd75767da868e8daab67571"
|
||||||
|
integrity sha512-qvdNu/dGMHBm2uQ7oLhQBMhYlrOZC1ywXNCH/i8I4etxR1vrjCnU6ZSQBptndB1gcakjo2+w4OHo7Sjza1SHxg==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.0.1"
|
buffer "^6.0.3"
|
||||||
err-code "^2.0.3"
|
debug "^4.3.1"
|
||||||
get-browser-rtc "^1.0.0"
|
err-code "^3.0.1"
|
||||||
queue-microtask "^1.1.0"
|
get-browser-rtc "^1.1.0"
|
||||||
randombytes "^2.0.3"
|
queue-microtask "^1.2.3"
|
||||||
readable-stream "^3.4.0"
|
randombytes "^2.1.0"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
simple-swizzle@^0.2.2:
|
simple-swizzle@^0.2.2:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
|
Loading…
Reference in New Issue
Block a user