diff --git a/package.json b/package.json index cc32910..a9d8f31 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/dice/DiceTiles.tsx b/src/components/dice/DiceTiles.tsx index f6a5cb0..33a9086 100644 --- a/src/components/dice/DiceTiles.tsx +++ b/src/components/dice/DiceTiles.tsx @@ -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({ diff --git a/src/components/map/DragOverlay.js b/src/components/map/DragOverlay.tsx similarity index 80% rename from src/components/map/DragOverlay.js rename to src/components/map/DragOverlay.tsx index 57f140e..641fb43 100644 --- a/src/components/map/DragOverlay.js +++ b/src/components/map/DragOverlay.tsx @@ -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(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); } diff --git a/src/components/map/MapControls.js b/src/components/map/MapControls.tsx similarity index 92% rename from src/components/map/MapControls.js rename to src/components/map/MapControls.tsx index 44b3f77..92a32a8 100644 --- a/src/components/map/MapControls.js +++ b/src/components/map/MapControls.tsx @@ -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"); diff --git a/src/components/map/MapEditBar.js b/src/components/map/MapEditBar.tsx similarity index 87% rename from src/components/map/MapEditBar.js rename to src/components/map/MapEditBar.tsx index c1ec6a4..d944e05 100644 --- a/src/components/map/MapEditBar.js +++ b/src/components/map/MapEditBar.tsx @@ -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 }) { onGroupSelect()} + onClick={() => onGroupSelect(undefined)} /> Promise; +export type RemoveMapsEventHandler = (ids: string[]) => Promise; +export type ResetMapEventHandler = (id: string) => Promise; +export type UpdateMapEventHanlder = ( + id: string, + update: Partial +) => Promise; +export type UpdateMapStateEventHandler = ( + id: string, + update: Partial +) => Promise; +export type GetMapStateEventHandler = ( + id: string +) => Promise; +export type GetMapEventHandler = (id: string) => Promise; +export type UpdateMapGroupsEventHandler = (groups: Group[]) => Promise; + type MapDataContext = { maps: Array; mapStates: MapState[]; - addMap: (map: Map) => void; - removeMaps: (ids: string[]) => void; - resetMap: (id: string) => void; - updateMap: (id: string, update: Partial) => void; - updateMapState: (id: string, update: Partial) => void; - getMapState: (id: string) => Promise; - getMap: (id: string) => Promise; + /** 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; - mapGroups: any[]; + mapGroups: Group[]; }; const MapDataContext = React.createContext(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([]); 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( + async (mapId) => { + let map = await database?.table("maps").get(mapId); return map; }, [database] ); - const getMapState = useCallback( + const getMapState = useCallback( 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( 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( 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( 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( async (id, update) => { await database?.table("maps").update(id, update); }, [database] ); - const updateMapState = useCallback( + const updateMapState = useCallback( async (id, update) => { await database?.table("states").update(id, update); }, [database] ); - const updateMapGroups = useCallback( + const updateMapGroups = useCallback( async (groups) => { // Update group state immediately to avoid animation delay setMapGroups(groups); diff --git a/src/contexts/SettingsContext.tsx b/src/contexts/SettingsContext.tsx index 1e17934..097c005 100644 --- a/src/contexts/SettingsContext.tsx +++ b/src/contexts/SettingsContext.tsx @@ -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>; +}; + +const SettingsContext = + React.createContext(undefined); const settingsProvider = getSettings(); export function SettingsProvider({ children }: { children: any }) { - const [settings, setSettings] = useState(settingsProvider.getAll()); + const [settings, setSettings] = useState(settingsProvider.getAll()); useEffect(() => { settingsProvider.setAll(settings); }, [settings]); - const value: { settings: any, setSettings: any} = { + const value = { settings, setSettings, }; diff --git a/src/contexts/TokenDataContext.tsx b/src/contexts/TokenDataContext.tsx index 3da939d..fadc712 100644 --- a/src/contexts/TokenDataContext.tsx +++ b/src/contexts/TokenDataContext.tsx @@ -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; +export type RemoveTokensEventHandler = (ids: string[]) => Promise; +export type UpdateTokenEventHandler = ( + id: string, + update: Partial +) => Promise; +export type GetTokenEventHandler = ( + tokenId: string +) => Promise; +export type UpdateTokenGroupsEventHandler = (groups: any[]) => Promise; +export type UpdateTokensHiddenEventHandler = ( + ids: string[], + hideInSidebar: boolean +) => Promise; + type TokenDataContext = { tokens: Token[]; - addToken: (token: Token) => Promise; - tokenGroups: any[]; - removeTokens: (ids: string[]) => Promise; - updateToken: (id: string, update: Partial) => Promise; - getToken: (tokenId: string) => Promise; + addToken: AddTokenEventHandler; + tokenGroups: Group[]; + removeTokens: RemoveTokensEventHandler; + updateToken: UpdateTokenEventHandler; + getToken: GetTokenEventHandler; tokensById: Record; 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([]); 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( 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( 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( 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( async (id, update) => { await database?.table("tokens").update(id, update); }, [database] ); - const updateTokensHidden = useCallback( + const updateTokensHidden = useCallback( 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( + async (groups: Group[]) => { // Update group state immediately to avoid animation delay setTokenGroups(groups); await database?.table("groups").update("tokens", { items: groups }); diff --git a/src/hooks/useSetting.js b/src/hooks/useSetting.ts similarity index 68% rename from src/hooks/useSetting.js rename to src/hooks/useSetting.ts index f8553e7..293dc90 100644 --- a/src/hooks/useSetting.js +++ b/src/hooks/useSetting.ts @@ -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(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; diff --git a/src/modals/EditTokenModal.tsx b/src/modals/EditTokenModal.tsx index e830322..655c7fe 100644 --- a/src/modals/EditTokenModal.tsx +++ b/src/modals/EditTokenModal.tsx @@ -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) => void; + onUpdateToken: UpdateTokenEventHandler; }; function EditTokenModal({ diff --git a/src/modals/GameExpiredModal.tsx b/src/modals/GameExpiredModal.tsx index b71ee90..11d8228 100644 --- a/src/modals/GameExpiredModal.tsx +++ b/src/modals/GameExpiredModal.tsx @@ -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) { diff --git a/src/modals/GettingStartedModal.tsx b/src/modals/GettingStartedModal.tsx index a24015a..019195e 100644 --- a/src/modals/GettingStartedModal.tsx +++ b/src/modals/GettingStartedModal.tsx @@ -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({ diff --git a/src/modals/GroupNameModal.tsx b/src/modals/GroupNameModal.tsx index 11b4ff4..556325d 100644 --- a/src/modals/GroupNameModal.tsx +++ b/src/modals/GroupNameModal.tsx @@ -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({ diff --git a/src/modals/ImportExportModal.tsx b/src/modals/ImportExportModal.tsx index 3c6a543..06d8b90 100644 --- a/src/modals/ImportExportModal.tsx +++ b/src/modals/ImportExportModal.tsx @@ -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(); diff --git a/src/modals/JoinModal.tsx b/src/modals/JoinModal.tsx index 0e79ec6..7ec0118 100644 --- a/src/modals/JoinModal.tsx +++ b/src/modals/JoinModal.tsx @@ -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) { diff --git a/src/modals/SelectDataModal.tsx b/src/modals/SelectDataModal.tsx index f5a99e5..ce3306f 100644 --- a/src/modals/SelectDataModal.tsx +++ b/src/modals/SelectDataModal.tsx @@ -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; function SelectDataModal({ diff --git a/src/modals/SelectDiceModal.tsx b/src/modals/SelectDiceModal.tsx index 08f21bc..ce11c86 100644 --- a/src/modals/SelectDiceModal.tsx +++ b/src/modals/SelectDiceModal.tsx @@ -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; }; diff --git a/src/modals/SelectMapModal.tsx b/src/modals/SelectMapModal.tsx index 85ff501..b21a4f3 100644 --- a/src/modals/SelectMapModal.tsx +++ b/src/modals/SelectMapModal.tsx @@ -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({ void; - onMapTokensStateCreate: (states: TokenState[]) => void; + onRequestClose: RequestCloseEventHandler; + onMapTokensStateCreate: MapTokensStateCreateHandler; }; function SelectTokensModal({ diff --git a/src/modals/SettingsModal.tsx b/src/modals/SettingsModal.tsx index ca026a4..394507a 100644 --- a/src/modals/SettingsModal.tsx +++ b/src/modals/SettingsModal.tsx @@ -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("map.labelSize"); + const [gridSnappingSensitivity, setGridSnappingSensitivity] = + useSetting("map.gridSnappingSensitivity"); + const [showFogGuides, setShowFogGuides] = + useSetting("fog.showGuides"); + const [fogEditOpacity, setFogEditOpacity] = + useSetting("fog.editOpacity"); const [storageEstimate, setStorageEstimate] = useState(); const [isImportExportModalOpen, setIsImportExportModalOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); diff --git a/src/modals/StartModal.tsx b/src/modals/StartModal.tsx index 58f0c70..e747810 100644 --- a/src/modals/StartModal.tsx +++ b/src/modals/StartModal.tsx @@ -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("game.usePassword"); function handleUsePasswordChange(event: ChangeEvent) { setUsePassword(event.target.checked); } diff --git a/src/modals/StartStreamModal.tsx b/src/modals/StartStreamModal.tsx index 4ab6cb3..4a2b6e6 100644 --- a/src/modals/StartStreamModal.tsx +++ b/src/modals/StartStreamModal.tsx @@ -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, diff --git a/src/modals/StartTimerModal.tsx b/src/modals/StartTimerModal.tsx index d2a021f..78c09b7 100644 --- a/src/modals/StartTimerModal.tsx +++ b/src/modals/StartTimerModal.tsx @@ -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("timer.hour"); + const [minute, setMinute] = useSetting("timer.minute"); + const [second, setSecond] = useSetting("timer.second"); function handleSubmit(event: ChangeEvent) { event.preventDefault(); @@ -85,10 +91,10 @@ function StartTimerModal({ setHour(parseValue(e.target.value, 24))} type="number" - disabled={timer} + disabled={!!timer} min={0} max={24} /> @@ -97,11 +103,11 @@ function StartTimerModal({ setMinute(parseValue(e.target.value, 59))} type="number" ref={inputRef} - disabled={timer} + disabled={!!timer} min={0} max={59} /> @@ -110,10 +116,10 @@ function StartTimerModal({ setSecond(parseValue(e.target.value, 59))} type="number" - disabled={timer} + disabled={!!timer} min={0} max={59} /> diff --git a/src/types/Events.ts b/src/types/Events.ts new file mode 100644 index 0000000..ee99c72 --- /dev/null +++ b/src/types/Events.ts @@ -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; diff --git a/yarn.lock b/yarn.lock index f275668..ecef399 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"