Typescript
This commit is contained in:
parent
2a053f4854
commit
a4363e542c
@ -99,6 +99,7 @@
|
||||
"@types/deep-diff": "^1.0.0",
|
||||
"@types/file-saver": "^2.0.2",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/lodash.chunk": "^4.2.6",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@types/lodash.set": "^4.3.6",
|
||||
|
@ -18,11 +18,13 @@ import SelectDiceButton from "./SelectDiceButton";
|
||||
|
||||
import Divider from "../Divider";
|
||||
|
||||
import Dice from "../../dice/Dice";
|
||||
|
||||
import { dice } from "../../dice";
|
||||
import useSetting from "../../hooks/useSetting";
|
||||
|
||||
import { DefaultDice, DiceRoll, DiceType } from "../../types/Dice";
|
||||
import Dice from "../../dice/Dice";
|
||||
import { DiceShareChangeEventHandler } from "../../types/Events";
|
||||
|
||||
type DiceButtonsProps = {
|
||||
diceRolls: DiceRoll[];
|
||||
@ -31,7 +33,7 @@ type DiceButtonsProps = {
|
||||
diceTraySize: "single" | "double";
|
||||
onDiceTraySizeChange: (newSize: "single" | "double") => void;
|
||||
shareDice: boolean;
|
||||
onShareDiceChange: (value: boolean) => void;
|
||||
onShareDiceChange: DiceShareChangeEventHandler;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { Vector3 } from "@babylonjs/core/Maths/math";
|
||||
import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight";
|
||||
import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator";
|
||||
import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture";
|
||||
import { Scene } from "@babylonjs/core";
|
||||
import { Box } from "theme-ui";
|
||||
|
||||
// @ts-ignore
|
||||
@ -19,16 +20,21 @@ import DiceTray from "../../dice/diceTray/DiceTray";
|
||||
import { useDiceLoading } from "../../contexts/DiceLoadingContext";
|
||||
|
||||
import { getDiceRoll } from "../../helpers/dice";
|
||||
|
||||
import useSetting from "../../hooks/useSetting";
|
||||
|
||||
import { DefaultDice, DiceMesh, DiceRoll, DiceType } from "../../types/Dice";
|
||||
import { Scene } from "@babylonjs/core";
|
||||
import {
|
||||
DiceRollsChangeEventHandler,
|
||||
DiceShareChangeEventHandler,
|
||||
} from "../../types/Events";
|
||||
|
||||
type DiceTrayOverlayProps = {
|
||||
isOpen: boolean;
|
||||
shareDice: boolean;
|
||||
onShareDiceChange: () => void;
|
||||
onShareDiceChange: DiceShareChangeEventHandler;
|
||||
diceRolls: DiceRoll[];
|
||||
onDiceRollsChange: (newRolls: DiceRoll[]) => void;
|
||||
onDiceRollsChange: DiceRollsChangeEventHandler;
|
||||
};
|
||||
|
||||
function DiceTrayOverlay({
|
||||
|
@ -12,7 +12,6 @@ import MapMeasure from "./MapMeasure";
|
||||
import NetworkedMapPointer from "../../network/NetworkedMapPointer";
|
||||
import MapNotes from "./MapNotes";
|
||||
|
||||
import { useTokenData } from "../../contexts/TokenDataContext";
|
||||
import { useSettings } from "../../contexts/SettingsContext";
|
||||
|
||||
import TokenMenu from "../token/TokenMenu";
|
||||
@ -29,13 +28,12 @@ import {
|
||||
import Session from "../../network/Session";
|
||||
import { Drawing, DrawingState } from "../../types/Drawing";
|
||||
import { Fog, FogState } from "../../types/Fog";
|
||||
import { Map, MapActions, MapToolId } from "../../types/Map";
|
||||
import { Map as MapType, MapActions, MapToolId } from "../../types/Map";
|
||||
import { MapState } from "../../types/MapState";
|
||||
import { Settings } from "../../types/Settings";
|
||||
import {
|
||||
MapChangeEventHandler,
|
||||
MapResetEventHandler,
|
||||
MapTokensStateCreateHandler,
|
||||
MapTokenStateRemoveHandler,
|
||||
NoteChangeEventHandler,
|
||||
NoteRemoveEventHander,
|
||||
@ -47,7 +45,7 @@ import { TokenDraggingOptions, TokenMenuOptions } from "../../types/Token";
|
||||
import { Note, NoteDraggingOptions, NoteMenuOptions } from "../../types/Note";
|
||||
|
||||
type MapProps = {
|
||||
map: Map | null;
|
||||
map: MapType | null;
|
||||
mapState: MapState | null;
|
||||
mapActions: MapActions;
|
||||
onMapTokenStateChange: TokenStateChangeEventHandler;
|
||||
@ -95,8 +93,6 @@ function Map({
|
||||
}: MapProps) {
|
||||
const { addToast } = useToasts();
|
||||
|
||||
const { tokensById } = useTokenData();
|
||||
|
||||
const [selectedToolId, setSelectedToolId] = useState<MapToolId>("move");
|
||||
const { settings, setSettings } = useSettings();
|
||||
|
||||
|
@ -193,7 +193,11 @@ function MapInteraction({
|
||||
|
||||
return (
|
||||
<MapInteractionProvider value={mapInteraction}>
|
||||
<GridProvider grid={map?.grid} width={mapWidth} height={mapHeight}>
|
||||
<GridProvider
|
||||
grid={map?.grid || null}
|
||||
width={mapWidth}
|
||||
height={mapHeight}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
|
@ -52,7 +52,7 @@ function MapMeasure({ map, active }: MapMeasureProps) {
|
||||
useState<MeasureData | null>(null);
|
||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||
|
||||
const gridScale = parseGridScale(active && grid.measurement.scale);
|
||||
const gridScale = parseGridScale(active ? grid.measurement.scale : null);
|
||||
|
||||
const snapPositionToGrid = useGridSnapping(
|
||||
grid.measurement.type === "euclidean" ? 0 : 1,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import { Grid } from "theme-ui";
|
||||
|
||||
import Tile from "../tile/Tile";
|
||||
|
@ -9,15 +9,14 @@ type MapTileImageProps = {
|
||||
} & ImageProps;
|
||||
|
||||
function MapTileImage({ map, ...props }: MapTileImageProps) {
|
||||
const mapURL = useDataURL(
|
||||
map,
|
||||
defaultMapSources,
|
||||
undefined,
|
||||
map.type === "file"
|
||||
);
|
||||
const mapURL = useDataURL(
|
||||
map,
|
||||
defaultMapSources,
|
||||
undefined,
|
||||
map.type === "file"
|
||||
);
|
||||
|
||||
return <Image src={mapURL} {...props} />;
|
||||
}
|
||||
);
|
||||
return <Image src={mapURL} {...props} />;
|
||||
}
|
||||
|
||||
export default MapTileImage;
|
||||
|
@ -26,11 +26,14 @@ import { useKeyboard } from "../../../contexts/KeyboardContext";
|
||||
|
||||
import shortcuts from "../../../shortcuts";
|
||||
|
||||
import { DrawingToolSettings, DrawingToolType } from "../../../types/Drawing";
|
||||
import {
|
||||
DrawingToolSettings as DrawingToolSettingsType,
|
||||
DrawingToolType,
|
||||
} from "../../../types/Drawing";
|
||||
|
||||
type DrawingToolSettingsProps = {
|
||||
settings: DrawingToolSettings;
|
||||
onSettingChange: (change: Partial<DrawingToolSettings>) => void;
|
||||
settings: DrawingToolSettingsType;
|
||||
onSettingChange: (change: Partial<DrawingToolSettingsType>) => void;
|
||||
onToolAction: (action: string) => void;
|
||||
disabledActions: string[];
|
||||
};
|
||||
|
@ -23,11 +23,14 @@ import { useKeyboard } from "../../../contexts/KeyboardContext";
|
||||
|
||||
import shortcuts from "../../../shortcuts";
|
||||
|
||||
import { FogToolSettings, FogToolType } from "../../../types/Fog";
|
||||
import {
|
||||
FogToolSettings as FogToolSettingsType,
|
||||
FogToolType,
|
||||
} from "../../../types/Fog";
|
||||
|
||||
type FogToolSettingsProps = {
|
||||
settings: FogToolSettings;
|
||||
onSettingChange: (change: Partial<FogToolSettings>) => void;
|
||||
settings: FogToolSettingsType;
|
||||
onSettingChange: (change: Partial<FogToolSettingsType>) => void;
|
||||
onToolAction: (action: string) => void;
|
||||
disabledActions: string[];
|
||||
};
|
||||
|
@ -2,11 +2,11 @@ import { Flex } from "theme-ui";
|
||||
|
||||
import ColorControl from "./ColorControl";
|
||||
|
||||
import { PointerToolSettings } from "../../../types/Pointer";
|
||||
import { PointerToolSettings as PointerToolSettingsType } from "../../../types/Pointer";
|
||||
|
||||
type PointerToolSettingsProps = {
|
||||
settings: PointerToolSettings;
|
||||
onSettingChange: (change: Partial<PointerToolSettings>) => void;
|
||||
settings: PointerToolSettingsType;
|
||||
onSettingChange: (change: Partial<PointerToolSettingsType>) => void;
|
||||
};
|
||||
|
||||
function PointerToolSettings({
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { IconButton } from "theme-ui";
|
||||
|
||||
import AddPartyMemberModal from "../../modals/AddPartyMemberModal";
|
||||
|
@ -1,16 +1,18 @@
|
||||
import React, { useState, useEffect, ChangeEvent } from "react";
|
||||
import { useState, useEffect, ChangeEvent, FormEvent } from "react";
|
||||
import { IconButton } from "theme-ui";
|
||||
|
||||
import ChangeNicknameModal from "../../modals/ChangeNicknameModal";
|
||||
import ChangeNicknameIcon from "../../icons/ChangeNicknameIcon";
|
||||
|
||||
type ChangeNicknameButtonProps = {
|
||||
nickname: string;
|
||||
onChange: (nickname: string) => void;
|
||||
};
|
||||
|
||||
function ChangeNicknameButton({
|
||||
nickname,
|
||||
onChange,
|
||||
}: {
|
||||
nickname: string;
|
||||
onChange;
|
||||
}) {
|
||||
}: ChangeNicknameButtonProps) {
|
||||
const [isChangeModalOpen, setIsChangeModalOpen] = useState(false);
|
||||
function openModal() {
|
||||
setIsChangeModalOpen(true);
|
||||
@ -25,7 +27,7 @@ function ChangeNicknameButton({
|
||||
setChangedNickname(nickname);
|
||||
}, [nickname]);
|
||||
|
||||
function handleChangeSubmit(event: Event) {
|
||||
function handleChangeSubmit(event: FormEvent) {
|
||||
event.preventDefault();
|
||||
onChange(changedNickname);
|
||||
closeModal();
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { Flex, Box, Text } from "theme-ui";
|
||||
import { DiceRoll as DiceRollType, DiceType } from "../../types/Dice";
|
||||
|
||||
function DiceRoll({
|
||||
rolls,
|
||||
type,
|
||||
children,
|
||||
}: {
|
||||
rolls;
|
||||
type: string;
|
||||
children;
|
||||
}) {
|
||||
type DiceRollProps = {
|
||||
rolls: DiceRollType[];
|
||||
type: DiceType;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
function DiceRoll({ rolls, type, children }: DiceRollProps) {
|
||||
return (
|
||||
<Flex sx={{ flexWrap: "wrap" }}>
|
||||
<Box sx={{ transform: "scale(0.8)" }}>{children}</Box>
|
||||
|
@ -13,8 +13,9 @@ import D100Icon from "../../icons/D100Icon";
|
||||
import DiceRoll from "./DiceRoll";
|
||||
|
||||
import { getDiceRollTotal } from "../../helpers/dice";
|
||||
import { DiceRoll as DiceRollType, DiceType } from "../../types/Dice";
|
||||
|
||||
const diceIcons = [
|
||||
const diceIcons: { type: DiceType; Icon: React.ElementType }[] = [
|
||||
{ type: "d20", Icon: D20Icon },
|
||||
{ type: "d12", Icon: D12Icon },
|
||||
{ type: "d10", Icon: D10Icon },
|
||||
@ -24,7 +25,11 @@ const diceIcons = [
|
||||
{ type: "d100", Icon: D100Icon },
|
||||
];
|
||||
|
||||
function DiceRolls({ rolls }: { rolls }) {
|
||||
type DiceRollsProps = {
|
||||
rolls: DiceRollType[];
|
||||
};
|
||||
|
||||
function DiceRolls({ rolls }: DiceRollsProps) {
|
||||
const total = getDiceRollTotal(rolls);
|
||||
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
|
@ -9,19 +9,27 @@ import useSetting from "../../hooks/useSetting";
|
||||
|
||||
import LoadingOverlay from "../LoadingOverlay";
|
||||
|
||||
import {
|
||||
DiceShareChangeEventHandler,
|
||||
DiceRollsChangeEventHandler,
|
||||
} from "../../types/Events";
|
||||
import { DiceRoll } from "../../types/Dice";
|
||||
|
||||
const DiceTrayOverlay = React.lazy(() => import("../dice/DiceTrayOverlay"));
|
||||
|
||||
type DiceTrayButtonProps = {
|
||||
shareDice: boolean;
|
||||
onShareDiceChange: DiceShareChangeEventHandler;
|
||||
diceRolls: DiceRoll[];
|
||||
onDiceRollsChange: DiceRollsChangeEventHandler;
|
||||
};
|
||||
|
||||
function DiceTrayButton({
|
||||
shareDice,
|
||||
onShareDiceChange,
|
||||
diceRolls,
|
||||
onDiceRollsChange,
|
||||
}: {
|
||||
shareDice: boolean;
|
||||
onShareDiceChange;
|
||||
diceRolls: [];
|
||||
onDiceRollsChange;
|
||||
}) {
|
||||
}: DiceTrayButtonProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [fullScreen] = useSetting("map.fullScreen");
|
||||
|
||||
|
@ -2,17 +2,15 @@ import { Text, Flex } from "theme-ui";
|
||||
|
||||
import Stream from "./Stream";
|
||||
import DiceRolls from "./DiceRolls";
|
||||
import { DiceRoll } from "../../types/Dice";
|
||||
|
||||
// TODO: check if stream is a required or optional param
|
||||
function Nickname({
|
||||
nickname,
|
||||
stream,
|
||||
diceRolls,
|
||||
}: {
|
||||
type NicknameProps = {
|
||||
nickname: string;
|
||||
stream?;
|
||||
diceRolls;
|
||||
}) {
|
||||
stream?: MediaStream;
|
||||
diceRolls?: DiceRoll[];
|
||||
};
|
||||
|
||||
function Nickname({ nickname, stream, diceRolls }: NicknameProps) {
|
||||
return (
|
||||
<Flex sx={{ flexDirection: "column" }}>
|
||||
<Text
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { Flex, Box, Text } from "theme-ui";
|
||||
import SimpleBar from "simplebar-react";
|
||||
|
||||
@ -15,6 +15,20 @@ import useSetting from "../../hooks/useSetting";
|
||||
|
||||
import { useParty } from "../../contexts/PartyContext";
|
||||
import { usePlayerState, usePlayerUpdater } from "../../contexts/PlayerContext";
|
||||
import { DiceRoll } from "../../types/Dice";
|
||||
import {
|
||||
StreamEndEventHandler,
|
||||
StreamStartEventHandler,
|
||||
} from "../../types/Events";
|
||||
import { Timer as TimerType } from "../../types/Timer";
|
||||
|
||||
type PartyProps = {
|
||||
gameId: string;
|
||||
stream: MediaStream | null;
|
||||
partyStreams: Record<string, MediaStream>;
|
||||
onStreamStart: StreamStartEventHandler;
|
||||
onStreamEnd: StreamEndEventHandler;
|
||||
};
|
||||
|
||||
function Party({
|
||||
gameId,
|
||||
@ -22,33 +36,27 @@ function Party({
|
||||
partyStreams,
|
||||
onStreamStart,
|
||||
onStreamEnd,
|
||||
}: {
|
||||
gameId: string;
|
||||
stream;
|
||||
partyStreams;
|
||||
onStreamStart;
|
||||
onStreamEnd;
|
||||
}) {
|
||||
}: PartyProps) {
|
||||
const setPlayerState = usePlayerUpdater();
|
||||
const playerState = usePlayerState();
|
||||
const partyState = useParty();
|
||||
|
||||
const [fullScreen] = useSetting<boolean>("map.fullScreen");
|
||||
const [shareDice, setShareDice] = useSetting("dice.shareDice");
|
||||
const [shareDice, setShareDice] = useSetting<boolean>("dice.shareDice");
|
||||
|
||||
function handleTimerStart(newTimer: PartyTimer) {
|
||||
function handleTimerStart(newTimer: TimerType) {
|
||||
setPlayerState((prevState) => ({ ...prevState, timer: newTimer }));
|
||||
}
|
||||
|
||||
function handleTimerStop() {
|
||||
setPlayerState((prevState) => ({ ...prevState, timer: null }));
|
||||
setPlayerState((prevState) => ({ ...prevState, timer: undefined }));
|
||||
}
|
||||
|
||||
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;
|
||||
prevTime = time;
|
||||
@ -57,12 +65,12 @@ function Party({
|
||||
counter += deltaTime;
|
||||
// Update timer every second
|
||||
if (counter > 1000) {
|
||||
const newTimer: PartyTimer = {
|
||||
const newTimer: TimerType = {
|
||||
...playerState.timer,
|
||||
current: playerState.timer.current - counter,
|
||||
};
|
||||
if (newTimer.current < 0) {
|
||||
setPlayerState((prevState) => ({ ...prevState, timer: null }));
|
||||
setPlayerState((prevState) => ({ ...prevState, timer: undefined }));
|
||||
} else {
|
||||
setPlayerState((prevState) => ({ ...prevState, timer: newTimer }));
|
||||
}
|
||||
@ -79,9 +87,9 @@ function Party({
|
||||
setPlayerState((prevState) => ({ ...prevState, nickname: newNickname }));
|
||||
}
|
||||
|
||||
function handleDiceRollsChange(newDiceRolls: number[]) {
|
||||
function handleDiceRollsChange(newDiceRolls: DiceRoll[]) {
|
||||
setPlayerState(
|
||||
(prevState: PlayerDice) => ({
|
||||
(prevState) => ({
|
||||
...prevState,
|
||||
dice: { share: shareDice, rolls: newDiceRolls },
|
||||
}),
|
||||
@ -91,7 +99,7 @@ function Party({
|
||||
|
||||
function handleShareDiceChange(newShareDice: boolean) {
|
||||
setShareDice(newShareDice);
|
||||
setPlayerState((prevState: PlayerInfo) => ({
|
||||
setPlayerState((prevState) => ({
|
||||
...prevState,
|
||||
dice: { ...prevState.dice, share: newShareDice },
|
||||
}));
|
||||
@ -134,17 +142,16 @@ function Party({
|
||||
height: "calc(100% - 232px)",
|
||||
}}
|
||||
>
|
||||
{/* TODO: check if stream is required here */}
|
||||
<Nickname
|
||||
nickname={`${playerState.nickname} (you)`}
|
||||
diceRolls={shareDice && playerState.dice.rolls}
|
||||
diceRolls={shareDice ? playerState.dice.rolls : undefined}
|
||||
/>
|
||||
{Object.entries(partyState).map(([id, { nickname, dice }]) => (
|
||||
<Nickname
|
||||
nickname={nickname}
|
||||
key={id}
|
||||
stream={partyStreams[id]}
|
||||
diceRolls={dice.share && dice.rolls}
|
||||
diceRolls={dice.share ? dice.rolls : undefined}
|
||||
/>
|
||||
))}
|
||||
{playerState.timer && <Timer timer={playerState.timer} index={0} />}
|
||||
|
@ -1,20 +1,26 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { IconButton, Box, Text } from "theme-ui";
|
||||
import adapter from "webrtc-adapter";
|
||||
|
||||
import Link from "../Link";
|
||||
|
||||
import StartStreamModal from "../../modals/StartStreamModal";
|
||||
import {
|
||||
StreamEndEventHandler,
|
||||
StreamStartEventHandler,
|
||||
} from "../../types/Events";
|
||||
|
||||
type StartStreamProps = {
|
||||
onStreamStart: StreamStartEventHandler;
|
||||
onStreamEnd: StreamEndEventHandler;
|
||||
stream: MediaStream | null;
|
||||
};
|
||||
|
||||
function StartStreamButton({
|
||||
onStreamStart,
|
||||
onStreamEnd,
|
||||
stream,
|
||||
}: {
|
||||
onStreamStart;
|
||||
onStreamEnd;
|
||||
stream;
|
||||
}) {
|
||||
}: StartStreamProps) {
|
||||
const [isStreamModalOpoen, setIsStreamModalOpen] = useState(false);
|
||||
function openModal() {
|
||||
setIsStreamModalOpen(true);
|
||||
@ -53,7 +59,7 @@ function StartStreamButton({
|
||||
|
||||
function handleStreamStart() {
|
||||
// Must be defined this way in typescript due to open issue - https://github.com/microsoft/TypeScript/issues/33232
|
||||
const mediaDevices = navigator.mediaDevices;
|
||||
const mediaDevices: any = navigator.mediaDevices;
|
||||
mediaDevices
|
||||
.getDisplayMedia({
|
||||
video: true,
|
||||
@ -63,7 +69,7 @@ function StartStreamButton({
|
||||
echoCancellation: false,
|
||||
},
|
||||
})
|
||||
.then((localStream: { getTracks }) => {
|
||||
.then((localStream: MediaStream) => {
|
||||
const tracks = localStream.getTracks();
|
||||
|
||||
const hasAudio = tracks.some(
|
||||
|
@ -1,18 +1,25 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { IconButton } from "theme-ui";
|
||||
|
||||
import StartTimerModal from "../../modals/StartTimerModal";
|
||||
import StartTimerIcon from "../../icons/StartTimerIcon";
|
||||
import {
|
||||
TimerStartEventHandler,
|
||||
TimerStopEventHandler,
|
||||
} from "../../types/Events";
|
||||
import { Timer } from "../../types/Timer";
|
||||
|
||||
type StartTimerButtonProps = {
|
||||
onTimerStart: TimerStartEventHandler;
|
||||
onTimerStop: TimerStopEventHandler;
|
||||
timer?: Timer;
|
||||
};
|
||||
|
||||
function StartTimerButton({
|
||||
onTimerStart,
|
||||
onTimerStop,
|
||||
timer,
|
||||
}: {
|
||||
onTimerStart;
|
||||
onTimerStop;
|
||||
timer;
|
||||
}) {
|
||||
}: StartTimerButtonProps) {
|
||||
const [isTimerModalOpen, setIsTimerModalOpen] = useState(false);
|
||||
|
||||
function openModal() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef, useEffect, ChangeEvent } from "react";
|
||||
import { useState, useRef, useEffect, ChangeEvent } from "react";
|
||||
import { Text, IconButton, Box, Flex } from "theme-ui";
|
||||
|
||||
import StreamMuteIcon from "../../icons/StreamMuteIcon";
|
||||
@ -6,18 +6,17 @@ import StreamMuteIcon from "../../icons/StreamMuteIcon";
|
||||
import Banner from "../banner/Banner";
|
||||
import Slider from "../Slider";
|
||||
|
||||
function Stream({
|
||||
stream,
|
||||
nickname,
|
||||
}: {
|
||||
type StreamProps = {
|
||||
stream: MediaStream;
|
||||
nickname: string;
|
||||
}) {
|
||||
};
|
||||
|
||||
function Stream({ stream, nickname }: StreamProps) {
|
||||
const [streamVolume, setStreamVolume] = useState(1);
|
||||
const [showStreamInteractBanner, setShowStreamInteractBanner] =
|
||||
useState(false);
|
||||
const [streamMuted, setStreamMuted] = useState(false);
|
||||
const audioRef = useRef();
|
||||
const audioRef = useRef<HTMLAudioElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (audioRef.current) {
|
||||
@ -59,17 +58,21 @@ function Stream({
|
||||
const [isVolumeControlAvailable, setIsVolumeControlAvailable] =
|
||||
useState(true);
|
||||
useEffect(() => {
|
||||
let audio = audioRef.current;
|
||||
function checkVolumeControlAvailable() {
|
||||
const audio = audioRef.current;
|
||||
if (!audio) {
|
||||
return;
|
||||
}
|
||||
const checkVolumeControlAvailable = () => {
|
||||
const prevVolume = audio.volume;
|
||||
// Set volume to 0.5, then check if the value actually stuck 100ms later
|
||||
audio.volume = 0.5;
|
||||
setTimeout(() => {
|
||||
setIsVolumeControlAvailable(audio.volume === 0.5);
|
||||
audio.volume = prevVolume;
|
||||
// TODO: check if this supposed to be a number or number[]
|
||||
if (audio) {
|
||||
setIsVolumeControlAvailable(audio.volume === 0.5);
|
||||
audio.volume = prevVolume;
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
audio.addEventListener("playing", checkVolumeControlAvailable);
|
||||
|
||||
@ -79,7 +82,7 @@ function Stream({
|
||||
}, []);
|
||||
|
||||
// Use an audio context gain node to control volume to go past 100%
|
||||
const audioGainRef = useRef();
|
||||
const audioGainRef = useRef<GainNode>();
|
||||
useEffect(() => {
|
||||
let audioContext: AudioContext;
|
||||
if (stream && !streamMuted && isVolumeControlAvailable && audioGainRef) {
|
||||
|
@ -1,11 +1,17 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { Box, Progress } from "theme-ui";
|
||||
|
||||
import usePortal from "../../hooks/usePortal";
|
||||
import { Timer as TimerType } from "../../types/Timer";
|
||||
|
||||
function Timer({ timer, index }: { timer; index: number }) {
|
||||
const progressBarRef = useRef();
|
||||
type TimerProps = {
|
||||
timer?: TimerType;
|
||||
index: number;
|
||||
};
|
||||
|
||||
function Timer({ timer, index }: TimerProps) {
|
||||
const progressBarRef = useRef<HTMLProgressElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (progressBarRef.current && timer) {
|
||||
@ -16,7 +22,7 @@ function Timer({ timer, index }: { timer; index: number }) {
|
||||
useEffect(() => {
|
||||
let request = requestAnimationFrame(animate);
|
||||
let previousTime = performance.now();
|
||||
function animate(time) {
|
||||
function animate(time: number) {
|
||||
request = requestAnimationFrame(animate);
|
||||
const deltaTime = time - previousTime;
|
||||
previousTime = time;
|
||||
|
@ -23,7 +23,6 @@ export type CustomDragEndEvent = DragEndWithOverlayEvent & DragEndEvent;
|
||||
|
||||
type CustomDragProps = {
|
||||
onDragEnd?: (event: CustomDragEndEvent) => void;
|
||||
;
|
||||
};
|
||||
|
||||
function DragPositionMonitor({ onDragEnd }: CustomDragProps) {
|
||||
|
@ -66,21 +66,25 @@ export const GridCellPixelOffsetContext = React.createContext(
|
||||
|
||||
const defaultStrokeWidth = 1 / 10;
|
||||
|
||||
type GridProviderProps = {
|
||||
grid: Grid | null;
|
||||
width: number;
|
||||
height: number;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export function GridProvider({
|
||||
grid: inputGrid,
|
||||
width,
|
||||
height,
|
||||
children,
|
||||
}: {
|
||||
grid: Grid;
|
||||
width: number;
|
||||
height: number;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
let grid = inputGrid;
|
||||
}: GridProviderProps) {
|
||||
let grid: Grid;
|
||||
|
||||
if (!grid.size.x || !grid.size.y) {
|
||||
if (!inputGrid || !inputGrid.size.x || !inputGrid.size.y) {
|
||||
grid = defaultValue.grid;
|
||||
} else {
|
||||
grid = inputGrid;
|
||||
}
|
||||
|
||||
const [gridPixelSize, setGridPixelSize] = useState(
|
||||
|
@ -359,24 +359,22 @@ export function gridDistance(
|
||||
* @returns {GridScale}
|
||||
*/
|
||||
|
||||
export function parseGridScale(scale: string): GridScale {
|
||||
export function parseGridScale(scale: string | null): GridScale {
|
||||
if (typeof scale === "string") {
|
||||
const match = scale.match(/(\d*)(\.\d*)?([a-zA-Z]*)/);
|
||||
// TODO: handle case where match is not found
|
||||
if (!match) {
|
||||
throw Error;
|
||||
}
|
||||
const integer = parseFloat(match[1]);
|
||||
const fractional = parseFloat(match[2]);
|
||||
const unit = match[3] || "";
|
||||
if (!isNaN(integer) && !isNaN(fractional)) {
|
||||
return {
|
||||
multiplier: integer + fractional,
|
||||
unit: unit,
|
||||
digits: match[2].length - 1,
|
||||
};
|
||||
} else if (!isNaN(integer) && isNaN(fractional)) {
|
||||
return { multiplier: integer, unit: unit, digits: 0 };
|
||||
if (match) {
|
||||
const integer = parseFloat(match[1]);
|
||||
const fractional = parseFloat(match[2]);
|
||||
const unit = match[3] || "";
|
||||
if (!isNaN(integer) && !isNaN(fractional)) {
|
||||
return {
|
||||
multiplier: integer + fractional,
|
||||
unit: unit,
|
||||
digits: match[2].length - 1,
|
||||
};
|
||||
} else if (!isNaN(integer) && isNaN(fractional)) {
|
||||
return { multiplier: integer, unit: unit, digits: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
return { multiplier: 1, unit: "", digits: 0 };
|
||||
|
@ -2,20 +2,22 @@ import { Box, Text, Button, Label, Flex } from "theme-ui";
|
||||
|
||||
import Modal from "../components/Modal";
|
||||
|
||||
import { RequestCloseEventHandler } from "../types/Events";
|
||||
import {
|
||||
RequestCloseEventHandler,
|
||||
StreamEndEventHandler,
|
||||
} from "../types/Events";
|
||||
|
||||
export type StreamStartEventHandler = () => void;
|
||||
export type StreamEndEventHandler = (stream: MediaStream) => void;
|
||||
export type StreamOpenAndStartEventHandler = () => void;
|
||||
|
||||
type StartStreamProps = {
|
||||
isOpen: boolean;
|
||||
onRequestClose: RequestCloseEventHandler;
|
||||
isSupported: boolean;
|
||||
unavailableMessage: JSX.Element;
|
||||
stream: MediaStream;
|
||||
stream: MediaStream | null;
|
||||
noAudioTrack: boolean;
|
||||
noAudioMessage: JSX.Element;
|
||||
onStreamStart: StreamStartEventHandler;
|
||||
onStreamStart: StreamOpenAndStartEventHandler;
|
||||
onStreamEnd: StreamEndEventHandler;
|
||||
};
|
||||
|
||||
|
@ -7,12 +7,13 @@ import { getHMSDuration, getDurationHMS } from "../helpers/timer";
|
||||
|
||||
import useSetting from "../hooks/useSetting";
|
||||
|
||||
import { RequestCloseEventHandler } from "../types/Events";
|
||||
import {
|
||||
RequestCloseEventHandler,
|
||||
TimerStartEventHandler,
|
||||
TimerStopEventHandler,
|
||||
} from "../types/Events";
|
||||
import { Timer } from "../types/Timer";
|
||||
|
||||
export type TimerStartEventHandler = (event: Timer) => void;
|
||||
export type TimerStopEventHandler = () => void;
|
||||
|
||||
type StartTimerProps = {
|
||||
isOpen: boolean;
|
||||
onRequestClose: RequestCloseEventHandler;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Konva from "konva";
|
||||
import { DefaultDice } from "./Dice";
|
||||
import { DefaultDice, DiceRoll } from "./Dice";
|
||||
import { Map } from "./Map";
|
||||
import { MapState } from "./MapState";
|
||||
import { Note } from "./Note";
|
||||
import { Timer } from "./Timer";
|
||||
import { Token } from "./Token";
|
||||
import { TokenState } from "./TokenState";
|
||||
|
||||
@ -43,3 +44,12 @@ export type NoteDragEventHandler = (
|
||||
event: Konva.KonvaEventObject<DragEvent>,
|
||||
noteId: string
|
||||
) => void;
|
||||
|
||||
export type DiceShareChangeEventHandler = (share: boolean) => void;
|
||||
export type DiceRollsChangeEventHandler = (newRolls: DiceRoll[]) => void;
|
||||
|
||||
export type StreamStartEventHandler = (stream: MediaStream) => void;
|
||||
export type StreamEndEventHandler = (stream: MediaStream) => void;
|
||||
|
||||
export type TimerStartEventHandler = (event: Timer) => void;
|
||||
export type TimerStopEventHandler = () => void;
|
||||
|
@ -22,7 +22,10 @@ import { Group, GroupContainer } from "./types/Group";
|
||||
|
||||
export type UpgradeEventHandler = (versionNumber: number) => void;
|
||||
|
||||
type VersionCallback = (version: Version, onUpgrade?: UpgradeEventHandler);
|
||||
type VersionCallback = (
|
||||
version: Version,
|
||||
onUpgrade?: UpgradeEventHandler
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Mapping of version number to their upgrade function
|
||||
@ -285,7 +288,7 @@ export const versions: Record<number, VersionCallback> = {
|
||||
v.stores({}).upgrade(async (tx) => {
|
||||
onUpgrade?.(15);
|
||||
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
|
||||
let tokenSizes: Record<string, {width: number, height: number}> = {};
|
||||
let tokenSizes: Record<string, { width: number; height: number }> = {};
|
||||
for (let token of tokens) {
|
||||
const url = URL.createObjectURL(new Blob([token.file]));
|
||||
let image = new Image();
|
||||
@ -775,7 +778,7 @@ export const versions: Record<number, VersionCallback> = {
|
||||
34(v, onUpgrade) {
|
||||
v.stores({ groups: "id" }).upgrade(async (tx) => {
|
||||
onUpgrade?.(34);
|
||||
function groupItems(items: {id: string, group: string}[]) {
|
||||
function groupItems(items: { id: string; group: string }[]) {
|
||||
let groups: Group[] = [];
|
||||
let subGroups: Record<string, GroupContainer> = {};
|
||||
for (let item of items) {
|
||||
@ -841,7 +844,11 @@ export const latestVersion = 36;
|
||||
* @param {number=} upTo version number to load up to, latest version if undefined
|
||||
* @param {UpgradeEventHandler=} onUpgrade
|
||||
*/
|
||||
export function loadVersions(db: Dexie, upTo: number | undefined = latestVersion, onUpgrade: UpgradeEventHandler | undefined) {
|
||||
export function loadVersions(
|
||||
db: Dexie,
|
||||
upTo: number | undefined = latestVersion,
|
||||
onUpgrade: UpgradeEventHandler | undefined
|
||||
) {
|
||||
for (let versionNumber = 1; versionNumber <= upTo; versionNumber++) {
|
||||
versions[versionNumber](db.version(versionNumber), onUpgrade);
|
||||
}
|
||||
@ -883,12 +890,12 @@ function convertOldActionsToShapes(actions: any[], actionIndex: number) {
|
||||
}
|
||||
|
||||
// Helper to create a thumbnail for a file in a db
|
||||
async function createDataThumbnail(data: any) : Promise<{
|
||||
file: Uint8Array,
|
||||
width: number,
|
||||
height: number,
|
||||
type: "file",
|
||||
id: "thumbnail",
|
||||
async function createDataThumbnail(data: any): Promise<{
|
||||
file: Uint8Array;
|
||||
width: number;
|
||||
height: number;
|
||||
type: "file";
|
||||
id: "thumbnail";
|
||||
}> {
|
||||
let url: string;
|
||||
if (data?.resolutions?.low?.file) {
|
||||
@ -917,7 +924,9 @@ async function createDataThumbnail(data: any) : Promise<{
|
||||
);
|
||||
}
|
||||
|
||||
async function createDataOutline(data: any) : Promise<{id: string, outline: Outline}> {
|
||||
async function createDataOutline(
|
||||
data: any
|
||||
): Promise<{ id: string; outline: Outline }> {
|
||||
const url = URL.createObjectURL(new Blob([data.file]));
|
||||
return await Dexie.waitFor(
|
||||
new Promise((resolve) => {
|
||||
|
@ -3069,6 +3069,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||
|
||||
"@types/lodash.chunk@^4.2.6":
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.chunk/-/lodash.chunk-4.2.6.tgz#9d35f05360b0298715d7f3d9efb34dd4f77e5d2a"
|
||||
integrity sha512-SPlusB7jxXyGcTXYcUdWr7WmhArO/rmTq54VN88iKMxGUhyg79I4Q8n4riGn3kjaTjOJrVlHhxgX/d7woak5BQ==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash.clonedeep@^4.5.6":
|
||||
version "4.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz#3b6c40a0affe0799a2ce823b440a6cf33571d32b"
|
||||
|
Loading…
Reference in New Issue
Block a user