Typescript

This commit is contained in:
Mitchell McCaffrey 2021-07-13 08:59:28 +10:00
parent 49b8caa2d7
commit 569ed696fc
26 changed files with 298 additions and 138 deletions

View File

@ -100,6 +100,7 @@
"@types/jest": "^26.0.23",
"@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.get": "^4.4.6",
"@types/lodash.set": "^4.3.6",
"@types/node": "^15.6.0",
"@types/react": "^17.0.6",
"@types/react-dom": "^17.0.5",

View File

@ -5,12 +5,13 @@ import DiceTile from "./DiceTile";
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
import { DefaultDice } from "../../types/Dice";
import { DiceSelectEventHandler } from "../../types/Events";
type DiceTileProps = {
dice: DefaultDice[];
onDiceSelect: (dice: DefaultDice) => void;
onDiceSelect: DiceSelectEventHandler;
selectedDice: DefaultDice;
onDone: (dice: DefaultDice) => void;
onDone: DiceSelectEventHandler;
};
function DiceTiles({

View File

@ -1,26 +1,38 @@
import React, { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { Box, IconButton } from "theme-ui";
import { Node } from "konva/types/Node";
import RemoveTokenIcon from "../../icons/RemoveTokenIcon";
function DragOverlay({ dragging, node, onRemove }) {
type DragOverlayProps = {
dragging: boolean;
node: Node;
onRemove: () => void;
};
function DragOverlay({ dragging, node, onRemove }: DragOverlayProps) {
const [isRemoveHovered, setIsRemoveHovered] = useState(false);
const removeTokenRef = useRef();
const removeTokenRef = useRef<HTMLDivElement>(null);
// Detect token hover on remove icon manually to support touch devices
useEffect(() => {
const map = document.querySelector(".map");
const mapRect = map.getBoundingClientRect();
function detectRemoveHover() {
if (!node || !dragging || !removeTokenRef.current) {
return;
}
const map = document.querySelector(".map");
if (!map) {
return;
}
const mapRect = map.getBoundingClientRect();
const stage = node.getStage();
if (!stage) {
return;
}
const pointerPosition = stage.getPointerPosition();
if (!pointerPosition) {
return;
}
const screenSpacePointerPosition = {
x: pointerPosition.x + mapRect.left,
y: pointerPosition.y + mapRect.top,
@ -41,7 +53,7 @@ function DragOverlay({ dragging, node, onRemove }) {
}
}
let handler;
let handler: NodeJS.Timeout;
if (node && dragging) {
handler = setInterval(detectRemoveHover, 100);
}

View File

@ -21,6 +21,22 @@ import FullScreenExitIcon from "../../icons/FullScreenExitIcon";
import NoteToolIcon from "../../icons/NoteToolIcon";
import useSetting from "../../hooks/useSetting";
import { Map } from "../../types/Map";
import { MapState } from "../../types/MapState";
type MapControlsProps = {
onMapChange: () => void;
onMapReset: () => void;
currentMap?: Map;
currentMapState?: MapState;
selectedToolId: string;
onSelectedToolChange: () => void;
toolSettings: any;
onToolSettingChange: () => void;
onToolAction: () => void;
disabledControls: string[];
disabledSettings: string[];
};
function MapContols({
onMapChange,
@ -34,7 +50,7 @@ function MapContols({
onToolAction,
disabledControls,
disabledSettings,
}) {
}: MapControlsProps) {
const [isExpanded, setIsExpanded] = useState(true);
const [fullScreen, setFullScreen] = useSetting("map.fullScreen");

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { Flex, Close, IconButton } from "theme-ui";
import { groupsFromIds, itemsFromGroups } from "../../helpers/group";
@ -13,8 +13,27 @@ import { useMapData } from "../../contexts/MapDataContext";
import { useKeyboard } from "../../contexts/KeyboardContext";
import shortcuts from "../../shortcuts";
import { Map } from "../../types/Map";
import {
MapChangeEventHandler,
MapResetEventHandler,
} from "../../types/Events";
function MapEditBar({ currentMap, disabled, onMapChange, onMapReset, onLoad }) {
type MapEditBarProps = {
currentMap?: Map;
disabled: boolean;
onMapChange: MapChangeEventHandler;
onMapReset: MapResetEventHandler;
onLoad: (loading: boolean) => void;
};
function MapEditBar({
currentMap,
disabled,
onMapChange,
onMapReset,
onLoad,
}: MapEditBarProps) {
const [hasMapState, setHasMapState] = useState(false);
const { maps, mapStates, removeMaps, resetMap } = useMapData();
@ -56,11 +75,11 @@ function MapEditBar({ currentMap, disabled, onMapChange, onMapReset, onLoad }) {
setIsMapsRemoveModalOpen(false);
const selectedMaps = getSelectedMaps();
const selectedMapIds = selectedMaps.map((map) => map.id);
onGroupSelect();
onGroupSelect(undefined);
await removeMaps(selectedMapIds);
// Removed the map from the map screen if needed
if (currentMap && selectedMapIds.includes(currentMap.id)) {
onMapChange(null, null);
onMapChange(undefined, undefined);
}
onLoad(false);
}
@ -84,7 +103,7 @@ function MapEditBar({ currentMap, disabled, onMapChange, onMapReset, onLoad }) {
/**
* Shortcuts
*/
function handleKeyDown(event) {
function handleKeyDown(event: KeyboardEvent) {
if (disabled) {
return;
}
@ -117,7 +136,7 @@ function MapEditBar({ currentMap, disabled, onMapChange, onMapReset, onLoad }) {
<Close
title="Clear Selection"
aria-label="Clear Selection"
onClick={() => onGroupSelect()}
onClick={() => onGroupSelect(undefined)}
/>
<Flex>
<IconButton

View File

@ -11,37 +11,58 @@ import { useDatabase } from "./DatabaseContext";
import { Map } from "../types/Map";
import { MapState } from "../types/MapState";
import { Note } from "../types/Note";
import { removeGroupsItems } from "../helpers/group";
// TODO: fix differences in types between default maps and imported maps
import { Group } from "../types/Group";
export type AddMapEventHandler = (map: Map) => Promise<void>;
export type RemoveMapsEventHandler = (ids: string[]) => Promise<void>;
export type ResetMapEventHandler = (id: string) => Promise<MapState>;
export type UpdateMapEventHanlder = (
id: string,
update: Partial<Map>
) => Promise<void>;
export type UpdateMapStateEventHandler = (
id: string,
update: Partial<MapState>
) => Promise<void>;
export type GetMapStateEventHandler = (
id: string
) => Promise<MapState | undefined>;
export type GetMapEventHandler = (id: string) => Promise<Map | undefined>;
export type UpdateMapGroupsEventHandler = (groups: Group[]) => Promise<void>;
type MapDataContext = {
maps: Array<Map>;
mapStates: MapState[];
addMap: (map: Map) => void;
removeMaps: (ids: string[]) => void;
resetMap: (id: string) => void;
updateMap: (id: string, update: Partial<Map>) => void;
updateMapState: (id: string, update: Partial<MapState>) => void;
getMapState: (id: string) => Promise<MapState>;
getMap: (id: string) => Promise<Map | undefined>;
/** Adds a map to the database, also adds an assosiated state and group for that map */
addMap: AddMapEventHandler;
removeMaps: RemoveMapsEventHandler;
resetMap: ResetMapEventHandler;
updateMap: UpdateMapEventHanlder;
updateMapState: UpdateMapStateEventHandler;
getMapState: GetMapStateEventHandler;
getMap: GetMapEventHandler;
mapsLoading: boolean;
updateMapGroups: (groups: any) => void;
updateMapGroups: UpdateMapGroupsEventHandler;
mapsById: Record<string, Map>;
mapGroups: any[];
mapGroups: Group[];
};
const MapDataContext =
React.createContext<MapDataContext | undefined>(undefined);
const defaultMapState = {
const defaultMapState: Pick<
MapState,
"tokens" | "drawShapes" | "fogShapes" | "editFlags" | "notes"
> = {
tokens: {},
drawShapes: {},
fogShapes: {},
// Flags to determine what other people can edit
editFlags: ["drawing", "tokens", "notes", "fog"],
notes: {} as Note[],
notes: {},
};
export function MapDataProvider({ children }: { children: React.ReactNode }) {
@ -68,7 +89,7 @@ export function MapDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const [mapGroups, setMapGroups] = useState([]);
const [mapGroups, setMapGroups] = useState<Group[]>([]);
useEffect(() => {
async function updateMapGroups() {
const group = await database?.table("groups").get("maps");
@ -79,27 +100,23 @@ export function MapDataProvider({ children }: { children: React.ReactNode }) {
}
}, [mapGroupQuery, database]);
const getMap = useCallback(
async (mapId: string) => {
let map = (await database?.table("maps").get(mapId)) as Map;
const getMap = useCallback<GetMapEventHandler>(
async (mapId) => {
let map = await database?.table("maps").get(mapId);
return map;
},
[database]
);
const getMapState = useCallback(
const getMapState = useCallback<GetMapStateEventHandler>(
async (mapId) => {
let mapState = (await database?.table("states").get(mapId)) as MapState;
let mapState = await database?.table("states").get(mapId);
return mapState;
},
[database]
);
/**
* Adds a map to the database, also adds an assosiated state and group for that map
* @param {Object} map map to add
*/
const addMap = useCallback(
const addMap = useCallback<AddMapEventHandler>(
async (map) => {
if (database) {
// Just update map database as react state will be updated with an Observable
@ -115,7 +132,7 @@ export function MapDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const removeMaps = useCallback(
const removeMaps = useCallback<RemoveMapsEventHandler>(
async (ids) => {
if (database) {
const maps = await database.table("maps").bulkGet(ids);
@ -143,30 +160,30 @@ export function MapDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const resetMap = useCallback(
const resetMap = useCallback<ResetMapEventHandler>(
async (id) => {
const state = { ...defaultMapState, mapId: id };
const state: MapState = { ...defaultMapState, mapId: id };
await database?.table("states").put(state);
return state;
},
[database]
);
const updateMap = useCallback(
const updateMap = useCallback<UpdateMapEventHanlder>(
async (id, update) => {
await database?.table("maps").update(id, update);
},
[database]
);
const updateMapState = useCallback(
const updateMapState = useCallback<UpdateMapStateEventHandler>(
async (id, update) => {
await database?.table("states").update(id, update);
},
[database]
);
const updateMapGroups = useCallback(
const updateMapGroups = useCallback<UpdateMapGroupsEventHandler>(
async (groups) => {
// Update group state immediately to avoid animation delay
setMapGroups(groups);

View File

@ -2,21 +2,26 @@ import React, { useState, useEffect, useContext } from "react";
import { getSettings } from "../settings";
const SettingsContext = React.createContext({
settings: {},
setSettings: () => {},
});
import { Settings } from "../types/Settings";
type SettingsContext = {
settings: Settings;
setSettings: React.Dispatch<React.SetStateAction<Settings>>;
};
const SettingsContext =
React.createContext<SettingsContext | undefined>(undefined);
const settingsProvider = getSettings();
export function SettingsProvider({ children }: { children: any }) {
const [settings, setSettings] = useState(settingsProvider.getAll());
const [settings, setSettings] = useState<Settings>(settingsProvider.getAll());
useEffect(() => {
settingsProvider.setAll(settings);
}, [settings]);
const value: { settings: any, setSettings: any} = {
const value = {
settings,
setSettings,
};

View File

@ -9,20 +9,37 @@ import { useLiveQuery } from "dexie-react-hooks";
import { useDatabase } from "./DatabaseContext";
import { Token } from "../tokens";
import { removeGroupsItems } from "../helpers/group";
import { Token } from "../types/Token";
import { Group } from "../types/Group";
export type AddTokenEventHandler = (token: Token) => Promise<void>;
export type RemoveTokensEventHandler = (ids: string[]) => Promise<void>;
export type UpdateTokenEventHandler = (
id: string,
update: Partial<Token>
) => Promise<void>;
export type GetTokenEventHandler = (
tokenId: string
) => Promise<Token | undefined>;
export type UpdateTokenGroupsEventHandler = (groups: any[]) => Promise<void>;
export type UpdateTokensHiddenEventHandler = (
ids: string[],
hideInSidebar: boolean
) => Promise<void>;
type TokenDataContext = {
tokens: Token[];
addToken: (token: Token) => Promise<void>;
tokenGroups: any[];
removeTokens: (ids: string[]) => Promise<void>;
updateToken: (id: string, update: Partial<Token>) => Promise<void>;
getToken: (tokenId: string) => Promise<Token | undefined>;
addToken: AddTokenEventHandler;
tokenGroups: Group[];
removeTokens: RemoveTokensEventHandler;
updateToken: UpdateTokenEventHandler;
getToken: GetTokenEventHandler;
tokensById: Record<string, Token>;
tokensLoading: boolean;
updateTokenGroups: (groups: any[]) => void;
updateTokensHidden: (ids: string[], hideInSidebar: boolean) => void;
updateTokenGroups: UpdateTokenGroupsEventHandler;
updateTokensHidden: UpdateTokensHiddenEventHandler;
};
const TokenDataContext =
@ -44,7 +61,7 @@ export function TokenDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const [tokenGroups, setTokenGroups] = useState([]);
const [tokenGroups, setTokenGroups] = useState<Group[]>([]);
useEffect(() => {
async function updateTokenGroups() {
const group = await database?.table("groups").get("tokens");
@ -55,7 +72,7 @@ export function TokenDataProvider({ children }: { children: React.ReactNode }) {
}
}, [tokenGroupQuery, database]);
const getToken = useCallback(
const getToken = useCallback<GetTokenEventHandler>(
async (tokenId) => {
let token = await database?.table("tokens").get(tokenId);
return token;
@ -64,7 +81,7 @@ export function TokenDataProvider({ children }: { children: React.ReactNode }) {
);
// Add token and add it to the token group
const addToken = useCallback(
const addToken = useCallback<AddTokenEventHandler>(
async (token) => {
if (database) {
await database.table("tokens").add(token);
@ -77,7 +94,7 @@ export function TokenDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const removeTokens = useCallback(
const removeTokens = useCallback<RemoveTokensEventHandler>(
async (ids) => {
if (database) {
const tokens = await database.table("tokens").bulkGet(ids);
@ -100,14 +117,14 @@ export function TokenDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const updateToken = useCallback(
const updateToken = useCallback<UpdateTokenEventHandler>(
async (id, update) => {
await database?.table("tokens").update(id, update);
},
[database]
);
const updateTokensHidden = useCallback(
const updateTokensHidden = useCallback<UpdateTokensHiddenEventHandler>(
async (ids: string[], hideInSidebar: boolean) => {
await Promise.all(
ids.map((id) => database?.table("tokens").update(id, { hideInSidebar }))
@ -116,8 +133,8 @@ export function TokenDataProvider({ children }: { children: React.ReactNode }) {
[database]
);
const updateTokenGroups = useCallback(
async (groups) => {
const updateTokenGroups = useCallback<UpdateTokenGroupsEventHandler>(
async (groups: Group[]) => {
// Update group state immediately to avoid animation delay
setTokenGroups(groups);
await database?.table("groups").update("tokens", { items: groups });

View File

@ -5,14 +5,14 @@ import { useSettings } from "../contexts/SettingsContext";
/**
* Helper to get and set nested settings that are saved in local storage
* @param {String} path The path to the setting within the Settings object provided by the SettingsContext
* @param {string} path The path to the setting within the Settings object provided by the SettingsContext
*/
function useSetting(path) {
function useSetting<Type>(path: string): [Type, (value: Type) => void] {
const { settings, setSettings } = useSettings();
const setting = get(settings, path);
const setting = get(settings, path) as Type;
const setSetting = (value) =>
const setSetting = (value: Type) =>
setSettings((prev) => {
const updated = set({ ...prev }, path, value);
return updated;

View File

@ -9,13 +9,16 @@ import TokenPreview from "../components/token/TokenPreview";
import { isEmpty } from "../helpers/shared";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
import { Token } from "../types/Token";
import { UpdateTokenEventHandler } from "../contexts/TokenDataContext";
type EditModalProps = {
isOpen: boolean;
onDone: () => void;
token: Token;
onUpdateToken: (id: string, update: Partial<Token>) => void;
onUpdateToken: UpdateTokenEventHandler;
};
function EditTokenModal({

View File

@ -2,9 +2,11 @@ import { Box, Label, Flex, Button, Text } from "theme-ui";
import Modal from "../components/Modal";
import { RequestCloseEventHandler } from "../types/Events";
type GameExpiredModalProps = {
isOpen: boolean;
onRequestClose: () => void;
onRequestClose: RequestCloseEventHandler;
};
function GameExpiredModal({ isOpen, onRequestClose }: GameExpiredModalProps) {

View File

@ -5,11 +5,13 @@ import Modal from "../components/Modal";
import Markdown from "../components/Markdown";
import Link from "../components/Link";
import { RequestCloseEventHandler } from "../types/Events";
const gettingStarted = raw("../docs/howTo/gettingStarted.md");
type GettingStartedModalProps = {
isOpen: boolean;
onRequestClose: () => void;
onRequestClose: RequestCloseEventHandler;
};
function GettingStartedModal({

View File

@ -3,11 +3,15 @@ import { Box, Input, Button, Label, Flex } from "theme-ui";
import Modal from "../components/Modal";
import { RequestCloseEventHandler } from "../types/Events";
export type GroupNameEventHandler = (name: string) => void;
type GroupNameModalProps = {
isOpen: boolean;
onRequestClose: () => void;
onRequestClose: RequestCloseEventHandler;
name: string;
onSubmit: (name: string) => void;
onSubmit: GroupNameEventHandler;
};
function GroupNameModal({

View File

@ -21,6 +21,7 @@ import { Map } from "../types/Map";
import { MapState } from "../types/MapState";
import { Token } from "../types/Token";
import { Group } from "../types/Group";
import { RequestCloseEventHandler } from "../types/Events";
const importDBName = "OwlbearRodeoImportDB";
@ -36,7 +37,7 @@ function ImportExportModal({
onRequestClose,
}: {
isOpen: boolean;
onRequestClose: () => void;
onRequestClose: RequestCloseEventHandler;
}) {
const { worker } = useDatabase();
const userId = useUserId();

View File

@ -4,9 +4,11 @@ import { useHistory } from "react-router-dom";
import Modal from "../components/Modal";
import { RequestCloseEventHandler } from "../types/Events";
type JoinModalProps = {
isOpen: boolean;
onRequestClose: () => void;
onRequestClose: RequestCloseEventHandler;
};
function JoinModal({ isOpen, onRequestClose }: JoinModalProps) {

View File

@ -12,21 +12,7 @@ import { Map } from "../types/Map";
import { Group, GroupContainer } from "../types/Group";
import { MapState } from "../types/MapState";
import { Token } from "../types/Token";
type SelectDataProps = {
isOpen: boolean;
onRequestClose: () => void;
onConfirm: (
checkedMaps: SelectData[],
checkedTokens: SelectData[],
checkedMapGroups: Group[],
checkedTokenGroups: Group[]
) => void;
confirmText: string;
label: string;
databaseName: string;
filter: (table: string, data: Map | MapState | Token, id: string) => boolean;
};
import { RequestCloseEventHandler } from "../types/Events";
export type SelectData = {
name: string;
@ -35,6 +21,23 @@ export type SelectData = {
checked: boolean;
};
export type ConfirmDataEventHandler = (
checkedMaps: SelectData[],
checkedTokens: SelectData[],
checkedMapGroups: Group[],
checkedTokenGroups: Group[]
) => void;
type SelectDataProps = {
isOpen: boolean;
onRequestClose: RequestCloseEventHandler;
onConfirm: ConfirmDataEventHandler;
confirmText: string;
label: string;
databaseName: string;
filter: (table: string, data: Map | MapState | Token, id: string) => boolean;
};
type DataRecord = Record<string, SelectData>;
function SelectDataModal({

View File

@ -8,12 +8,16 @@ import { dice } from "../dice";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
import {
DiceSelectEventHandler,
RequestCloseEventHandler,
} from "../types/Events";
import { DefaultDice } from "../types/Dice";
type SelectDiceProps = {
isOpen: boolean;
onRequestClose: () => void;
onDone: (dice: DefaultDice) => void;
onRequestClose: RequestCloseEventHandler;
onDone: DiceSelectEventHandler;
defaultDice: DefaultDice;
};

View File

@ -31,13 +31,17 @@ import { GroupProvider } from "../contexts/GroupContext";
import { TileDragProvider } from "../contexts/TileDragContext";
import { Map } from "../types/Map";
import { MapState } from "../types/MapState";
import {
MapChangeEventHandler,
MapResetEventHandler,
RequestCloseEventHandler,
} from "../types/Events";
type SelectMapProps = {
isOpen: boolean;
onDone: () => void;
onMapChange: (map?: Map, mapState?: MapState) => void;
onMapReset: (newState: MapState) => void;
onDone: RequestCloseEventHandler;
onMapChange: MapChangeEventHandler;
onMapReset: MapResetEventHandler;
currentMap?: Map;
};
@ -294,7 +298,7 @@ function SelectMapModal({
</TileDragProvider>
<MapEditBar
currentMap={currentMap}
disabled={isLoading || editingMapId}
disabled={isLoading || !!editingMapId}
onMapChange={onMapChange}
onMapReset={onMapReset}
onLoad={setIsLoading}

View File

@ -36,11 +36,15 @@ import { TileDragProvider } from "../contexts/TileDragContext";
import { useMapStage } from "../contexts/MapStageContext";
import { TokenState } from "../types/TokenState";
import {
MapTokensStateCreateHandler,
RequestCloseEventHandler,
} from "../types/Events";
type SelectTokensModalProps = {
isOpen: boolean;
onRequestClose: () => void;
onMapTokensStateCreate: (states: TokenState[]) => void;
onRequestClose: RequestCloseEventHandler;
onMapTokensStateCreate: MapTokensStateCreateHandler;
};
function SelectTokensModal({

View File

@ -21,24 +21,27 @@ import useSetting from "../hooks/useSetting";
import ConfirmModal from "./ConfirmModal";
import ImportExportModal from "./ImportExportModal";
import { MapState } from "../components/map/Map";
import { MapState } from "../types/MapState";
import { RequestCloseEventHandler } from "../types/Events";
function SettingsModal({
isOpen,
onRequestClose,
}: {
isOpen: boolean;
onRequestClose: () => void;
onRequestClose: RequestCloseEventHandler;
}) {
const { database, databaseStatus } = useDatabase();
const userId = useUserId();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [labelSize, setLabelSize] = useSetting("map.labelSize");
const [gridSnappingSensitivity, setGridSnappingSensitivity] = useSetting(
"map.gridSnappingSensitivity"
);
const [showFogGuides, setShowFogGuides] = useSetting("fog.showGuides");
const [fogEditOpacity, setFogEditOpacity] = useSetting("fog.editOpacity");
const [labelSize, setLabelSize] = useSetting<number>("map.labelSize");
const [gridSnappingSensitivity, setGridSnappingSensitivity] =
useSetting<number>("map.gridSnappingSensitivity");
const [showFogGuides, setShowFogGuides] =
useSetting<boolean>("fog.showGuides");
const [fogEditOpacity, setFogEditOpacity] =
useSetting<number>("fog.editOpacity");
const [storageEstimate, setStorageEstimate] = useState<StorageEstimate>();
const [isImportExportModalOpen, setIsImportExportModalOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);

View File

@ -9,7 +9,15 @@ import useSetting from "../hooks/useSetting";
import Modal from "../components/Modal";
function StartModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: () => void}) {
import { RequestCloseEventHandler } from "../types/Events";
function StartModal({
isOpen,
onRequestClose,
}: {
isOpen: boolean;
onRequestClose: RequestCloseEventHandler;
}) {
let history = useHistory();
const { password, setPassword } = useAuth();
@ -17,7 +25,7 @@ function StartModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClos
setPassword(event.target.value);
}
const [usePassword, setUsePassword] = useSetting("game.usePassword");
const [usePassword, setUsePassword] = useSetting<boolean>("game.usePassword");
function handleUsePasswordChange(event: ChangeEvent<HTMLInputElement>) {
setUsePassword(event.target.checked);
}

View File

@ -2,17 +2,22 @@ import { Box, Text, Button, Label, Flex } from "theme-ui";
import Modal from "../components/Modal";
import { RequestCloseEventHandler } from "../types/Events";
export type StreamStartEventHandler = () => void;
export type StreamEndEventHandler = (stream: MediaStream) => void;
type StartStreamProps = {
isOpen: boolean,
onRequestClose: () => void,
isSupported: boolean,
unavailableMessage: JSX.Element,
stream: MediaStream,
noAudioTrack: boolean,
noAudioMessage: JSX.Element,
onStreamStart: any,
onStreamEnd: any,
}
isOpen: boolean;
onRequestClose: RequestCloseEventHandler;
isSupported: boolean;
unavailableMessage: JSX.Element;
stream: MediaStream;
noAudioTrack: boolean;
noAudioMessage: JSX.Element;
onStreamStart: StreamStartEventHandler;
onStreamEnd: StreamEndEventHandler;
};
function StartStreamModal({
isOpen,

View File

@ -7,13 +7,19 @@ import { getHMSDuration, getDurationHMS } from "../helpers/timer";
import useSetting from "../hooks/useSetting";
import { RequestCloseEventHandler } from "../types/Events";
import { Timer } from "../types/Timer";
export type TimerStartEventHandler = (event: Timer) => void;
export type TimerStopEventHandler = () => void;
type StartTimerProps = {
isOpen: boolean,
onRequestClose: () => void,
onTimerStart: any,
onTimerStop: any,
timer: any,
}
isOpen: boolean;
onRequestClose: RequestCloseEventHandler;
onTimerStart: TimerStartEventHandler;
onTimerStop: TimerStopEventHandler;
timer?: Timer;
};
function StartTimerModal({
isOpen,
@ -27,9 +33,9 @@ function StartTimerModal({
inputRef.current && inputRef.current.focus();
}
const [hour, setHour] = useSetting("timer.hour");
const [minute, setMinute] = useSetting("timer.minute");
const [second, setSecond] = useSetting("timer.second");
const [hour, setHour] = useSetting<number>("timer.hour");
const [minute, setMinute] = useSetting<number>("timer.minute");
const [second, setSecond] = useSetting<number>("timer.second");
function handleSubmit(event: ChangeEvent<HTMLInputElement>) {
event.preventDefault();
@ -85,10 +91,10 @@ function StartTimerModal({
</Text>
<Input
sx={inputStyle}
value={`${timer ? timerHMS.hour : hour}`}
value={`${timerHMS ? timerHMS.hour : hour}`}
onChange={(e) => setHour(parseValue(e.target.value, 24))}
type="number"
disabled={timer}
disabled={!!timer}
min={0}
max={24}
/>
@ -97,11 +103,11 @@ function StartTimerModal({
</Text>
<Input
sx={inputStyle}
value={`${timer ? timerHMS.minute : minute}`}
value={`${timerHMS ? timerHMS.minute : minute}`}
onChange={(e) => setMinute(parseValue(e.target.value, 59))}
type="number"
ref={inputRef}
disabled={timer}
disabled={!!timer}
min={0}
max={59}
/>
@ -110,10 +116,10 @@ function StartTimerModal({
</Text>
<Input
sx={inputStyle}
value={`${timer ? timerHMS.second : second}`}
value={`${timerHMS ? timerHMS.second : second}`}
onChange={(e) => setSecond(parseValue(e.target.value, 59))}
type="number"
disabled={timer}
disabled={!!timer}
min={0}
max={59}
/>

14
src/types/Events.ts Normal file
View File

@ -0,0 +1,14 @@
import { DefaultDice } from "./Dice";
import { Map } from "./Map";
import { MapState } from "./MapState";
import { TokenState } from "./TokenState";
export type MapChangeEventHandler = (map?: Map, mapState?: MapState) => void;
export type MapResetEventHandler = (newState: MapState) => void;
export type DiceSelectEventHandler = (dice: DefaultDice) => void;
export type RequestCloseEventHandler = () => void;
export type MapTokensStateCreateHandler = (states: TokenState[]) => void;

View File

@ -2998,6 +2998,13 @@
dependencies:
"@types/lodash" "*"
"@types/lodash.set@^4.3.6":
version "4.3.6"
resolved "https://registry.yarnpkg.com/@types/lodash.set/-/lodash.set-4.3.6.tgz#33e635c2323f855359225df6a5c8c6f1f1908264"
integrity sha512-ZeGDDlnRYTvS31Laij0RsSaguIUSBTYIlJFKL3vm3T2OAZAQj2YpSvVWJc0WiG4jqg9fGX6PAPGvDqBcHfSgFg==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.170"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"