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