Converted /modals to typescript
This commit is contained in:
parent
32f6e1fb23
commit
ecc4f67f37
@ -1,9 +1,8 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box } from "theme-ui";
|
import { Box } from "theme-ui";
|
||||||
|
|
||||||
import Spinner from "./Spinner";
|
import Spinner from "./Spinner";
|
||||||
|
|
||||||
function LoadingOverlay({ bg }) {
|
function LoadingOverlay({ bg }: any ) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
@ -1,7 +1,11 @@
|
|||||||
import React from "react";
|
import React, { ReactChild } from "react";
|
||||||
import Modal from "react-modal";
|
import Modal, { Props } from "react-modal";
|
||||||
import { useThemeUI, Close } from "theme-ui";
|
import { useThemeUI, Close } from "theme-ui";
|
||||||
|
|
||||||
|
type ModalProps = Props & {
|
||||||
|
children: ReactChild | ReactChild[],
|
||||||
|
allowClose: boolean
|
||||||
|
}
|
||||||
function StyledModal({
|
function StyledModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
@ -9,7 +13,7 @@ function StyledModal({
|
|||||||
allowClose,
|
allowClose,
|
||||||
style,
|
style,
|
||||||
...props
|
...props
|
||||||
}) {
|
}: ModalProps ) {
|
||||||
const { theme } = useThemeUI();
|
const { theme } = useThemeUI();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -19,7 +23,7 @@ function StyledModal({
|
|||||||
style={{
|
style={{
|
||||||
overlay: { backgroundColor: "rgba(0, 0, 0, 0.73)", zIndex: 100 },
|
overlay: { backgroundColor: "rgba(0, 0, 0, 0.73)", zIndex: 100 },
|
||||||
content: {
|
content: {
|
||||||
backgroundColor: theme.colors.background,
|
backgroundColor: theme.colors?.background,
|
||||||
top: "50%",
|
top: "50%",
|
||||||
left: "50%",
|
left: "50%",
|
||||||
right: "auto",
|
right: "auto",
|
||||||
@ -28,7 +32,7 @@ function StyledModal({
|
|||||||
transform: "translate(-50%, -50%)",
|
transform: "translate(-50%, -50%)",
|
||||||
maxHeight: "100%",
|
maxHeight: "100%",
|
||||||
...style,
|
...style,
|
||||||
},
|
} as React.CSSProperties,
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@ -46,7 +50,7 @@ function StyledModal({
|
|||||||
|
|
||||||
StyledModal.defaultProps = {
|
StyledModal.defaultProps = {
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
style: {},
|
style: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StyledModal;
|
export default StyledModal;
|
@ -1,10 +1,19 @@
|
|||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Box, Slider as ThemeSlider } from "theme-ui";
|
import { Box, Slider as ThemeSlider, SliderProps } from "theme-ui";
|
||||||
|
|
||||||
function Slider({ min, max, value, ml, mr, labelFunc, ...rest }) {
|
type SliderModalProps = SliderProps & {
|
||||||
|
min: number,
|
||||||
|
max: number,
|
||||||
|
value: number,
|
||||||
|
ml: any,
|
||||||
|
mr: any,
|
||||||
|
labelFunc: any
|
||||||
|
}
|
||||||
|
|
||||||
|
function Slider({ min, max, value, ml, mr, labelFunc, ...rest }: SliderModalProps ) {
|
||||||
const percentValue = ((value - min) * 100) / (max - min);
|
const percentValue = ((value - min) * 100) / (max - min);
|
||||||
|
|
||||||
const [labelVisible, setLabelVisible] = useState(false);
|
const [labelVisible, setLabelVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ position: "relative" }} ml={ml} mr={mr}>
|
<Box sx={{ position: "relative" }} ml={ml} mr={mr}>
|
||||||
@ -63,7 +72,7 @@ Slider.defaultProps = {
|
|||||||
value: 0,
|
value: 0,
|
||||||
ml: 0,
|
ml: 0,
|
||||||
mr: 0,
|
mr: 0,
|
||||||
labelFunc: (value) => value,
|
labelFunc: (value: any) => value,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Slider;
|
export default Slider;
|
@ -24,6 +24,78 @@ import {
|
|||||||
EditShapeAction,
|
EditShapeAction,
|
||||||
RemoveShapeAction,
|
RemoveShapeAction,
|
||||||
} from "../../actions";
|
} from "../../actions";
|
||||||
|
import { Fog, Path, Shape } from "../../helpers/drawing";
|
||||||
|
import Session from "../../network/Session";
|
||||||
|
import { Grid } from "../../helpers/grid";
|
||||||
|
import { ImageFile } from "../../helpers/image";
|
||||||
|
|
||||||
|
type Resolutions = Record<string, ImageFile>
|
||||||
|
|
||||||
|
export type Map = {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
owner: string,
|
||||||
|
file: Uint8Array,
|
||||||
|
quality: string,
|
||||||
|
resolutions: Resolutions,
|
||||||
|
grid: Grid,
|
||||||
|
group: string,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
type: string,
|
||||||
|
lastUsed: number,
|
||||||
|
lastModified: number,
|
||||||
|
created: number,
|
||||||
|
showGrid: boolean,
|
||||||
|
snapToGrid: boolean,
|
||||||
|
thumbnail: ImageFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Note = {
|
||||||
|
id: string,
|
||||||
|
color: string,
|
||||||
|
lastModified: number,
|
||||||
|
lastModifiedBy: string,
|
||||||
|
locked: boolean,
|
||||||
|
size: number,
|
||||||
|
text: string,
|
||||||
|
textOnly: boolean,
|
||||||
|
visible: boolean,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TokenState = {
|
||||||
|
id: string,
|
||||||
|
tokenId: string,
|
||||||
|
owner: string,
|
||||||
|
size: number,
|
||||||
|
label: string,
|
||||||
|
status: string[],
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
lastModifiedBy: string,
|
||||||
|
lastModified: number,
|
||||||
|
rotation: number,
|
||||||
|
locked: boolean,
|
||||||
|
visible: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PathId extends Path {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShapeId extends Shape {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
export type MapState = {
|
||||||
|
tokens: Record<string, TokenState>,
|
||||||
|
drawShapes: PathId | ShapeId,
|
||||||
|
fogShapes: Fog[],
|
||||||
|
editFlags: string[],
|
||||||
|
notes: Note[],
|
||||||
|
mapId: string,
|
||||||
|
}
|
||||||
|
|
||||||
function Map({
|
function Map({
|
||||||
map,
|
map,
|
||||||
@ -47,14 +119,36 @@ function Map({
|
|||||||
allowNoteEditing,
|
allowNoteEditing,
|
||||||
disabledTokens,
|
disabledTokens,
|
||||||
session,
|
session,
|
||||||
|
}: {
|
||||||
|
map: any
|
||||||
|
mapState: MapState
|
||||||
|
mapActions: any,
|
||||||
|
onMapTokenStateChange: any,
|
||||||
|
onMapTokenStateRemove: any,
|
||||||
|
onMapChange: any,
|
||||||
|
onMapReset: any,
|
||||||
|
onMapDraw: any,
|
||||||
|
onMapDrawUndo: any,
|
||||||
|
onMapDrawRedo: any,
|
||||||
|
onFogDraw: any,
|
||||||
|
onFogDrawUndo: any,
|
||||||
|
onFogDrawRedo: any,
|
||||||
|
onMapNoteChange: any,
|
||||||
|
onMapNoteRemove: any,
|
||||||
|
allowMapDrawing: boolean,
|
||||||
|
allowFogDrawing: boolean,
|
||||||
|
allowMapChange: boolean,
|
||||||
|
allowNoteEditing: boolean,
|
||||||
|
disabledTokens: any,
|
||||||
|
session: Session
|
||||||
}) {
|
}) {
|
||||||
const { tokensById } = useTokenData();
|
const { tokensById } = useTokenData();
|
||||||
|
|
||||||
const [selectedToolId, setSelectedToolId] = useState("move");
|
const [selectedToolId, setSelectedToolId] = useState("move");
|
||||||
const { settings, setSettings } = useSettings();
|
const { settings, setSettings }: { settings: any, setSettings: any} = useSettings();
|
||||||
|
|
||||||
function handleToolSettingChange(tool, change) {
|
function handleToolSettingChange(tool: any, change: any) {
|
||||||
setSettings((prevSettings) => ({
|
setSettings((prevSettings: any) => ({
|
||||||
...prevSettings,
|
...prevSettings,
|
||||||
[tool]: {
|
[tool]: {
|
||||||
...prevSettings[tool],
|
...prevSettings[tool],
|
||||||
@ -66,7 +160,7 @@ function Map({
|
|||||||
const drawShapes = Object.values(mapState?.drawShapes || {});
|
const drawShapes = Object.values(mapState?.drawShapes || {});
|
||||||
const fogShapes = Object.values(mapState?.fogShapes || {});
|
const fogShapes = Object.values(mapState?.fogShapes || {});
|
||||||
|
|
||||||
function handleToolAction(action) {
|
function handleToolAction(action: string) {
|
||||||
if (action === "eraseAll") {
|
if (action === "eraseAll") {
|
||||||
onMapDraw(new RemoveShapeAction(drawShapes.map((s) => s.id)));
|
onMapDraw(new RemoveShapeAction(drawShapes.map((s) => s.id)));
|
||||||
}
|
}
|
||||||
@ -84,27 +178,27 @@ function Map({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapShapeAdd(shape) {
|
function handleMapShapeAdd(shape: Shape) {
|
||||||
onMapDraw(new AddShapeAction([shape]));
|
onMapDraw(new AddShapeAction([shape]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapShapesRemove(shapeIds) {
|
function handleMapShapesRemove(shapeIds: string[]) {
|
||||||
onMapDraw(new RemoveShapeAction(shapeIds));
|
onMapDraw(new RemoveShapeAction(shapeIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapesAdd(shapes) {
|
function handleFogShapesAdd(shapes: Shape[]) {
|
||||||
onFogDraw(new AddShapeAction(shapes));
|
onFogDraw(new AddShapeAction(shapes));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapesCut(shapes) {
|
function handleFogShapesCut(shapes: Shape[]) {
|
||||||
onFogDraw(new CutShapeAction(shapes));
|
onFogDraw(new CutShapeAction(shapes));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapesRemove(shapeIds) {
|
function handleFogShapesRemove(shapeIds: string[]) {
|
||||||
onFogDraw(new RemoveShapeAction(shapeIds));
|
onFogDraw(new RemoveShapeAction(shapeIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapesEdit(shapes) {
|
function handleFogShapesEdit(shapes: Shape[]) {
|
||||||
onFogDraw(new EditShapeAction(shapes));
|
onFogDraw(new EditShapeAction(shapes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +221,7 @@ function Map({
|
|||||||
disabledControls.push("note");
|
disabledControls.push("note");
|
||||||
}
|
}
|
||||||
|
|
||||||
const disabledSettings = { fog: [], drawing: [] };
|
const disabledSettings: { fog: any[], drawing: any[]} = { fog: [], drawing: [] };
|
||||||
if (drawShapes.length === 0) {
|
if (drawShapes.length === 0) {
|
||||||
disabledSettings.drawing.push("erase");
|
disabledSettings.drawing.push("erase");
|
||||||
}
|
}
|
||||||
@ -166,10 +260,10 @@ function Map({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isTokenMenuOpen, setIsTokenMenuOpen] = useState(false);
|
const [isTokenMenuOpen, setIsTokenMenuOpen]: [ isTokenMenuOpen: boolean, setIsTokenMenuOpen: React.Dispatch<React.SetStateAction<boolean>>] = useState<boolean>(false);
|
||||||
const [tokenMenuOptions, setTokenMenuOptions] = useState({});
|
const [tokenMenuOptions, setTokenMenuOptions]: [ tokenMenuOptions: any, setTokenMenuOptions: any ] = useState({});
|
||||||
const [tokenDraggingOptions, setTokenDraggingOptions] = useState();
|
const [tokenDraggingOptions, setTokenDraggingOptions]: [ tokenDraggingOptions: any, setTokenDragginOptions: any ] = useState();
|
||||||
function handleTokenMenuOpen(tokenStateId, tokenImage) {
|
function handleTokenMenuOpen(tokenStateId: string, tokenImage: any) {
|
||||||
setTokenMenuOptions({ tokenStateId, tokenImage });
|
setTokenMenuOptions({ tokenStateId, tokenImage });
|
||||||
setIsTokenMenuOpen(true);
|
setIsTokenMenuOpen(true);
|
||||||
}
|
}
|
||||||
@ -200,7 +294,7 @@ function Map({
|
|||||||
|
|
||||||
const tokenDragOverlay = tokenDraggingOptions && (
|
const tokenDragOverlay = tokenDraggingOptions && (
|
||||||
<TokenDragOverlay
|
<TokenDragOverlay
|
||||||
onTokenStateRemove={(state) => {
|
onTokenStateRemove={(state: any) => {
|
||||||
onMapTokenStateRemove(state);
|
onMapTokenStateRemove(state);
|
||||||
setTokenDraggingOptions(null);
|
setTokenDraggingOptions(null);
|
||||||
}}
|
}}
|
||||||
@ -243,7 +337,6 @@ function Map({
|
|||||||
<MapMeasure
|
<MapMeasure
|
||||||
map={map}
|
map={map}
|
||||||
active={selectedToolId === "measure"}
|
active={selectedToolId === "measure"}
|
||||||
selectedToolSettings={settings[selectedToolId]}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -254,15 +347,15 @@ function Map({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState(false);
|
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false);
|
||||||
const [noteMenuOptions, setNoteMenuOptions] = useState({});
|
const [noteMenuOptions, setNoteMenuOptions] = useState<any>({});
|
||||||
const [noteDraggingOptions, setNoteDraggingOptions] = useState();
|
const [noteDraggingOptions, setNoteDraggingOptions]= useState<any>();
|
||||||
function handleNoteMenuOpen(noteId, noteNode) {
|
function handleNoteMenuOpen(noteId: string, noteNode: any) {
|
||||||
setNoteMenuOptions({ noteId, noteNode });
|
setNoteMenuOptions({ noteId, noteNode });
|
||||||
setIsNoteMenuOpen(true);
|
setIsNoteMenuOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortNotes(a, b, noteDraggingOptions) {
|
function sortNotes(a: any, b: any, noteDraggingOptions: any) {
|
||||||
if (
|
if (
|
||||||
noteDraggingOptions &&
|
noteDraggingOptions &&
|
||||||
noteDraggingOptions.dragging &&
|
noteDraggingOptions.dragging &&
|
||||||
@ -287,7 +380,6 @@ function Map({
|
|||||||
<MapNotes
|
<MapNotes
|
||||||
map={map}
|
map={map}
|
||||||
active={selectedToolId === "note"}
|
active={selectedToolId === "note"}
|
||||||
selectedToolSettings={settings[selectedToolId]}
|
|
||||||
onNoteAdd={onMapNoteChange}
|
onNoteAdd={onMapNoteChange}
|
||||||
onNoteChange={onMapNoteChange}
|
onNoteChange={onMapNoteChange}
|
||||||
notes={
|
notes={
|
||||||
@ -302,7 +394,7 @@ function Map({
|
|||||||
allowNoteEditing &&
|
allowNoteEditing &&
|
||||||
(selectedToolId === "note" || selectedToolId === "move")
|
(selectedToolId === "note" || selectedToolId === "move")
|
||||||
}
|
}
|
||||||
onNoteDragStart={(e, noteId) =>
|
onNoteDragStart={(e: any, noteId: any) =>
|
||||||
setNoteDraggingOptions({ dragging: true, noteId, noteGroup: e.target })
|
setNoteDraggingOptions({ dragging: true, noteId, noteGroup: e.target })
|
||||||
}
|
}
|
||||||
onNoteDragEnd={() =>
|
onNoteDragEnd={() =>
|
||||||
@ -328,7 +420,7 @@ function Map({
|
|||||||
dragging={!!(noteDraggingOptions && noteDraggingOptions.dragging)}
|
dragging={!!(noteDraggingOptions && noteDraggingOptions.dragging)}
|
||||||
noteGroup={noteDraggingOptions && noteDraggingOptions.noteGroup}
|
noteGroup={noteDraggingOptions && noteDraggingOptions.noteGroup}
|
||||||
noteId={noteDraggingOptions && noteDraggingOptions.noteId}
|
noteId={noteDraggingOptions && noteDraggingOptions.noteId}
|
||||||
onNoteRemove={(noteId) => {
|
onNoteRemove={(noteId: any) => {
|
||||||
onMapNoteRemove(noteId);
|
onMapNoteRemove(noteId);
|
||||||
setNoteDraggingOptions(null);
|
setNoteDraggingOptions(null);
|
||||||
}}
|
}}
|
@ -3,10 +3,10 @@ import { EventEmitter } from "events";
|
|||||||
|
|
||||||
const KeyboardContext = React.createContext({ keyEmitter: new EventEmitter() });
|
const KeyboardContext = React.createContext({ keyEmitter: new EventEmitter() });
|
||||||
|
|
||||||
export function KeyboardProvider({ children }) {
|
export function KeyboardProvider({ children }: { children: any}) {
|
||||||
const [keyEmitter] = useState(new EventEmitter());
|
const [keyEmitter] = useState(new EventEmitter());
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleKeyDown(event) {
|
function handleKeyDown(event: Event) {
|
||||||
// Ignore text input
|
// Ignore text input
|
||||||
if (
|
if (
|
||||||
event.target instanceof HTMLInputElement ||
|
event.target instanceof HTMLInputElement ||
|
||||||
@ -17,7 +17,7 @@ export function KeyboardProvider({ children }) {
|
|||||||
keyEmitter.emit("keyDown", event);
|
keyEmitter.emit("keyDown", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp(event) {
|
function handleKeyUp(event: Event) {
|
||||||
// Ignore text input
|
// Ignore text input
|
||||||
if (
|
if (
|
||||||
event.target instanceof HTMLInputElement ||
|
event.target instanceof HTMLInputElement ||
|
||||||
@ -49,7 +49,7 @@ export function KeyboardProvider({ children }) {
|
|||||||
* @param {KeyboardEvent} onKeyDown
|
* @param {KeyboardEvent} onKeyDown
|
||||||
* @param {KeyboardEvent} onKeyUp
|
* @param {KeyboardEvent} onKeyUp
|
||||||
*/
|
*/
|
||||||
export function useKeyboard(onKeyDown, onKeyUp) {
|
export function useKeyboard(onKeyDown: (...args: any[]) => void, onKeyUp: (...args: any[]) => void) {
|
||||||
const context = useContext(KeyboardContext);
|
const context = useContext(KeyboardContext);
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
throw new Error("useKeyboard must be used within a KeyboardProvider");
|
throw new Error("useKeyboard must be used within a KeyboardProvider");
|
||||||
@ -78,7 +78,7 @@ export function useKeyboard(onKeyDown, onKeyUp) {
|
|||||||
* Handler to handle a blur event. Useful when using a shortcut that uses the Alt or Cmd
|
* Handler to handle a blur event. Useful when using a shortcut that uses the Alt or Cmd
|
||||||
* @param {FocusEvent} onBlur
|
* @param {FocusEvent} onBlur
|
||||||
*/
|
*/
|
||||||
export function useBlur(onBlur) {
|
export function useBlur(onBlur: EventListenerOrEventListenerObject) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onBlur) {
|
if (onBlur) {
|
||||||
window.addEventListener("blur", onBlur);
|
window.addEventListener("blur", onBlur);
|
@ -10,13 +10,29 @@ import { decode } from "@msgpack/msgpack";
|
|||||||
import { useAuth } from "./AuthContext";
|
import { useAuth } from "./AuthContext";
|
||||||
import { useDatabase } from "./DatabaseContext";
|
import { useDatabase } from "./DatabaseContext";
|
||||||
|
|
||||||
import { tokens as defaultTokens } from "../tokens";
|
import { DefaultToken, FileToken, Token, tokens as defaultTokens } from "../tokens";
|
||||||
|
|
||||||
const TokenDataContext = React.createContext();
|
type TokenDataContext = {
|
||||||
|
tokens: Token[];
|
||||||
|
ownedTokens: Token[];
|
||||||
|
addToken: (token: Token) => Promise<void>;
|
||||||
|
removeToken: (id: string) => Promise<void>;
|
||||||
|
removeTokens: (ids: string[]) => Promise<void>;
|
||||||
|
updateToken: (id: string, update: Partial<Token>) => Promise<void>;
|
||||||
|
updateTokens: (ids: string[], update: Partial<Token>) => Promise<void>;
|
||||||
|
putToken: (token: Token) => Promise<void>;
|
||||||
|
getToken: (tokenId: string) => Token | undefined
|
||||||
|
tokensById: { [key: string]: Token; };
|
||||||
|
tokensLoading: boolean;
|
||||||
|
getTokenFromDB: (tokenId: string) => Promise<Token>;
|
||||||
|
loadTokens: (tokenIds: string[]) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TokenDataContext = React.createContext<TokenDataContext | undefined>(undefined);
|
||||||
|
|
||||||
const cachedTokenMax = 100;
|
const cachedTokenMax = 100;
|
||||||
|
|
||||||
export function TokenDataProvider({ children }) {
|
export function TokenDataProvider({ children }: { children: any }) {
|
||||||
const { database, databaseStatus, worker } = useDatabase();
|
const { database, databaseStatus, worker } = useDatabase();
|
||||||
const { userId } = useAuth();
|
const { userId } = useAuth();
|
||||||
|
|
||||||
@ -24,7 +40,7 @@ export function TokenDataProvider({ children }) {
|
|||||||
* Contains all tokens without any file data,
|
* Contains all tokens without any file data,
|
||||||
* to ensure file data is present call loadTokens
|
* to ensure file data is present call loadTokens
|
||||||
*/
|
*/
|
||||||
const [tokens, setTokens] = useState([]);
|
const [tokens, setTokens] = useState<Token[]>([]);
|
||||||
const [tokensLoading, setTokensLoading] = useState(true);
|
const [tokensLoading, setTokensLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -32,13 +48,12 @@ export function TokenDataProvider({ children }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
function getDefaultTokens() {
|
function getDefaultTokens() {
|
||||||
const defaultTokensWithIds = [];
|
const defaultTokensWithIds: Required<DefaultToken[]> = [];
|
||||||
for (let defaultToken of defaultTokens) {
|
for (let defaultToken of defaultTokens) {
|
||||||
defaultTokensWithIds.push({
|
defaultTokensWithIds.push({
|
||||||
...defaultToken,
|
...defaultToken,
|
||||||
id: `__default-${defaultToken.name}`,
|
id: `__default-${defaultToken.name}`,
|
||||||
owner: userId,
|
owner: userId,
|
||||||
group: "default",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return defaultTokensWithIds;
|
return defaultTokensWithIds;
|
||||||
@ -46,19 +61,19 @@ export function TokenDataProvider({ children }) {
|
|||||||
|
|
||||||
// Loads tokens without the file data to save memory
|
// Loads tokens without the file data to save memory
|
||||||
async function loadTokens() {
|
async function loadTokens() {
|
||||||
let storedTokens = [];
|
let storedTokens: any = [];
|
||||||
// Try to load tokens with worker, fallback to database if failed
|
// Try to load tokens with worker, fallback to database if failed
|
||||||
const packedTokens = await worker.loadData("tokens");
|
const packedTokens: ArrayLike<number> | BufferSource = await worker.loadData("tokens");
|
||||||
if (packedTokens) {
|
if (packedTokens) {
|
||||||
storedTokens = decode(packedTokens);
|
storedTokens = decode(packedTokens);
|
||||||
} else {
|
} else {
|
||||||
console.warn("Unable to load tokens with worker, loading may be slow");
|
console.warn("Unable to load tokens with worker, loading may be slow");
|
||||||
await database.table("tokens").each((token) => {
|
await database?.table("tokens").each((token: FileToken) => {
|
||||||
const { file, resolutions, ...rest } = token;
|
const { file, ...rest } = token;
|
||||||
storedTokens.push(rest);
|
storedTokens.push(rest);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const sortedTokens = storedTokens.sort((a, b) => b.created - a.created);
|
const sortedTokens = storedTokens.sort((a: any, b: any) => b.created - a.created);
|
||||||
const defaultTokensWithIds = getDefaultTokens();
|
const defaultTokensWithIds = getDefaultTokens();
|
||||||
const allTokens = [...sortedTokens, ...defaultTokensWithIds];
|
const allTokens = [...sortedTokens, ...defaultTokensWithIds];
|
||||||
setTokens(allTokens);
|
setTokens(allTokens);
|
||||||
@ -79,7 +94,7 @@ export function TokenDataProvider({ children }) {
|
|||||||
|
|
||||||
const getTokenFromDB = useCallback(
|
const getTokenFromDB = useCallback(
|
||||||
async (tokenId) => {
|
async (tokenId) => {
|
||||||
let token = await database.table("tokens").get(tokenId);
|
let token = await database?.table("tokens").get(tokenId);
|
||||||
return token;
|
return token;
|
||||||
},
|
},
|
||||||
[database]
|
[database]
|
||||||
@ -90,23 +105,23 @@ export function TokenDataProvider({ children }) {
|
|||||||
* Sorted by when they we're last used
|
* Sorted by when they we're last used
|
||||||
*/
|
*/
|
||||||
const updateCache = useCallback(async () => {
|
const updateCache = useCallback(async () => {
|
||||||
const cachedTokens = await database
|
const cachedTokens: Token[] | undefined = await database?.table("tokens").where("owner").notEqual(userId).sortBy("lastUsed");
|
||||||
.table("tokens")
|
// TODO: handle undefined cachedTokens
|
||||||
.where("owner")
|
if (!cachedTokens) {
|
||||||
.notEqual(userId)
|
return;
|
||||||
.sortBy("lastUsed");
|
}
|
||||||
if (cachedTokens.length > cachedTokenMax) {
|
if (cachedTokens?.length > cachedTokenMax) {
|
||||||
const cacheDeleteCount = cachedTokens.length - cachedTokenMax;
|
const cacheDeleteCount = cachedTokens.length - cachedTokenMax
|
||||||
const idsToDelete = cachedTokens
|
const idsToDelete = cachedTokens
|
||||||
.slice(0, cacheDeleteCount)
|
.slice(0, cacheDeleteCount)
|
||||||
.map((token) => token.id);
|
.map((token) => token.id);
|
||||||
database.table("tokens").where("id").anyOf(idsToDelete).delete();
|
database?.table("tokens").where("id").anyOf(idsToDelete).delete();
|
||||||
}
|
}
|
||||||
}, [database, userId]);
|
}, [database, userId]);
|
||||||
|
|
||||||
const addToken = useCallback(
|
const addToken = useCallback(
|
||||||
async (token) => {
|
async (token) => {
|
||||||
await database.table("tokens").add(token);
|
await database?.table("tokens").add(token);
|
||||||
if (token.owner !== userId) {
|
if (token.owner !== userId) {
|
||||||
await updateCache();
|
await updateCache();
|
||||||
}
|
}
|
||||||
@ -115,23 +130,23 @@ export function TokenDataProvider({ children }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const removeToken = useCallback(
|
const removeToken = useCallback(
|
||||||
async (id) => {
|
async (id: string) => {
|
||||||
await database.table("tokens").delete(id);
|
await database?.table("tokens").delete(id);
|
||||||
},
|
},
|
||||||
[database]
|
[database]
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeTokens = useCallback(
|
const removeTokens = useCallback(
|
||||||
async (ids) => {
|
async (ids: string[]) => {
|
||||||
await database.table("tokens").bulkDelete(ids);
|
await database?.table("tokens").bulkDelete(ids);
|
||||||
},
|
},
|
||||||
[database]
|
[database]
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateToken = useCallback(
|
const updateToken = useCallback(
|
||||||
async (id, update) => {
|
async (id: string, update: any) => {
|
||||||
const change = { lastModified: Date.now(), ...update };
|
const change = { lastModified: Date.now(), ...update };
|
||||||
await database.table("tokens").update(id, change);
|
await database?.table("tokens").update(id, change);
|
||||||
},
|
},
|
||||||
[database]
|
[database]
|
||||||
);
|
);
|
||||||
@ -140,7 +155,7 @@ export function TokenDataProvider({ children }) {
|
|||||||
async (ids, update) => {
|
async (ids, update) => {
|
||||||
const change = { lastModified: Date.now(), ...update };
|
const change = { lastModified: Date.now(), ...update };
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
ids.map((id) => database.table("tokens").update(id, change))
|
ids.map((id: string) => database?.table("tokens").update(id, change))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[database]
|
[database]
|
||||||
@ -148,7 +163,7 @@ export function TokenDataProvider({ children }) {
|
|||||||
|
|
||||||
const putToken = useCallback(
|
const putToken = useCallback(
|
||||||
async (token) => {
|
async (token) => {
|
||||||
await database.table("tokens").put(token);
|
await database?.table("tokens").put(token);
|
||||||
if (token.owner !== userId) {
|
if (token.owner !== userId) {
|
||||||
await updateCache();
|
await updateCache();
|
||||||
}
|
}
|
||||||
@ -157,13 +172,17 @@ export function TokenDataProvider({ children }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const loadTokens = useCallback(
|
const loadTokens = useCallback(
|
||||||
async (tokenIds) => {
|
async (tokenIds: string[]) => {
|
||||||
const loadedTokens = await database.table("tokens").bulkGet(tokenIds);
|
const loadedTokens: FileToken[] | undefined = await database?.table("tokens").bulkGet(tokenIds);
|
||||||
const loadedTokensById = loadedTokens.reduce((obj, token) => {
|
const loadedTokensById = loadedTokens?.reduce((obj: { [key: string]: FileToken }, token: FileToken) => {
|
||||||
obj[token.id] = token;
|
obj[token.id] = token;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
setTokens((prevTokens) => {
|
if (!loadedTokensById) {
|
||||||
|
// TODO: whatever
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTokens((prevTokens: Token[]) => {
|
||||||
return prevTokens.map((prevToken) => {
|
return prevTokens.map((prevToken) => {
|
||||||
if (prevToken.id in loadedTokensById) {
|
if (prevToken.id in loadedTokensById) {
|
||||||
return loadedTokensById[prevToken.id];
|
return loadedTokensById[prevToken.id];
|
||||||
@ -176,22 +195,13 @@ export function TokenDataProvider({ children }) {
|
|||||||
[database]
|
[database]
|
||||||
);
|
);
|
||||||
|
|
||||||
const unloadTokens = useCallback(async () => {
|
|
||||||
setTokens((prevTokens) => {
|
|
||||||
return prevTokens.map((prevToken) => {
|
|
||||||
const { file, ...rest } = prevToken;
|
|
||||||
return rest;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Create DB observable to sync creating and deleting
|
// Create DB observable to sync creating and deleting
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!database || databaseStatus === "loading") {
|
if (!database || databaseStatus === "loading") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTokenChanges(changes) {
|
function handleTokenChanges(changes: any) {
|
||||||
for (let change of changes) {
|
for (let change of changes) {
|
||||||
if (change.table === "tokens") {
|
if (change.table === "tokens") {
|
||||||
if (change.type === 1) {
|
if (change.type === 1) {
|
||||||
@ -230,12 +240,12 @@ export function TokenDataProvider({ children }) {
|
|||||||
|
|
||||||
const ownedTokens = tokens.filter((token) => token.owner === userId);
|
const ownedTokens = tokens.filter((token) => token.owner === userId);
|
||||||
|
|
||||||
const tokensById = tokens.reduce((obj, token) => {
|
const tokensById: { [key: string]: Token; } = tokens.reduce((obj: { [key: string]: Token }, token) => {
|
||||||
obj[token.id] = token;
|
obj[token.id] = token;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const value = {
|
const value: TokenDataContext = {
|
||||||
tokens,
|
tokens,
|
||||||
ownedTokens,
|
ownedTokens,
|
||||||
addToken,
|
addToken,
|
||||||
@ -249,7 +259,6 @@ export function TokenDataProvider({ children }) {
|
|||||||
tokensLoading,
|
tokensLoading,
|
||||||
getTokenFromDB,
|
getTokenFromDB,
|
||||||
loadTokens,
|
loadTokens,
|
||||||
unloadTokens,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -259,7 +268,7 @@ export function TokenDataProvider({ children }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTokenData() {
|
export function useTokenData(): TokenDataContext {
|
||||||
const context = useContext(TokenDataContext);
|
const context = useContext(TokenDataContext);
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
throw new Error("useTokenData must be used within a TokenDataProvider");
|
throw new Error("useTokenData must be used within a TokenDataProvider");
|
1
src/global.d.ts
vendored
1
src/global.d.ts
vendored
@ -5,3 +5,4 @@ declare module 'fake-indexeddb/lib/FDBKeyRange';
|
|||||||
declare module '*.glb';
|
declare module '*.glb';
|
||||||
declare module '*.png';
|
declare module '*.png';
|
||||||
declare module '*.mp4';
|
declare module '*.mp4';
|
||||||
|
declare module '*.bin';
|
@ -70,7 +70,7 @@ type ShapeType = "line" | "rectangle" | "circle" | "triangle"
|
|||||||
* @typedef {("fill"|"stroke")} PathType
|
* @typedef {("fill"|"stroke")} PathType
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// type PathType = "fill" | "stroke"
|
type PathType = "fill" | "stroke"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Path
|
* @typedef Path
|
||||||
@ -83,15 +83,15 @@ type ShapeType = "line" | "rectangle" | "circle" | "triangle"
|
|||||||
* @property {"path"} type
|
* @property {"path"} type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// type Path = {
|
export type Path = {
|
||||||
// blend: boolean,
|
blend: boolean,
|
||||||
// color: string,
|
color: string,
|
||||||
// data: PointsData,
|
data: PointsData,
|
||||||
// id: string,
|
id: string,
|
||||||
// pathType: PathType,
|
pathType: PathType,
|
||||||
// strokeWidth: number,
|
strokeWidth: number,
|
||||||
// type: "path"
|
type: "path"
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Shape
|
* @typedef Shape
|
||||||
@ -104,15 +104,15 @@ type ShapeType = "line" | "rectangle" | "circle" | "triangle"
|
|||||||
* @property {"shape"} type
|
* @property {"shape"} type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// type Shape = {
|
export type Shape = {
|
||||||
// blend: boolean,
|
blend: boolean,
|
||||||
// color: string,
|
color: string,
|
||||||
// data: ShapeData,
|
data: ShapeData,
|
||||||
// id: string,
|
id: string,
|
||||||
// shapeType: ShapeType,
|
shapeType: ShapeType,
|
||||||
// strokeWidth: number,
|
strokeWidth: number,
|
||||||
// type: "shape"
|
type: "shape"
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Fog
|
* @typedef Fog
|
||||||
@ -124,7 +124,7 @@ type ShapeType = "line" | "rectangle" | "circle" | "triangle"
|
|||||||
* @property {boolean} visible
|
* @property {boolean} visible
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type Fog = {
|
export type Fog = {
|
||||||
color: string,
|
color: string,
|
||||||
data: FogData,
|
data: FogData,
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -38,10 +38,10 @@ type GridMeasurement ={
|
|||||||
* @property {GridMeasurement} measurement
|
* @property {GridMeasurement} measurement
|
||||||
*/
|
*/
|
||||||
export type Grid = {
|
export type Grid = {
|
||||||
inset: GridInset,
|
inset?: GridInset,
|
||||||
size: Vector2,
|
size: Vector2,
|
||||||
type: ("square"|"hexVertical"|"hexHorizontal"),
|
type: ("square"|"hexVertical"|"hexHorizontal"),
|
||||||
measurement: GridMeasurement
|
measurement?: GridMeasurement
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,7 +51,7 @@ export type Grid = {
|
|||||||
* @param {number} baseHeight Height of the grid in pixels before inset
|
* @param {number} baseHeight Height of the grid in pixels before inset
|
||||||
* @returns {Size}
|
* @returns {Size}
|
||||||
*/
|
*/
|
||||||
export function getGridPixelSize(grid: Grid, baseWidth: number, baseHeight: number): Size {
|
export function getGridPixelSize(grid: Required<Grid>, baseWidth: number, baseHeight: number): Size {
|
||||||
const width = (grid.inset.bottomRight.x - grid.inset.topLeft.x) * baseWidth;
|
const width = (grid.inset.bottomRight.x - grid.inset.topLeft.x) * baseWidth;
|
||||||
const height = (grid.inset.bottomRight.y - grid.inset.topLeft.y) * baseHeight;
|
const height = (grid.inset.bottomRight.y - grid.inset.topLeft.y) * baseHeight;
|
||||||
return new Size(width, height);
|
return new Size(width, height);
|
||||||
@ -225,7 +225,7 @@ export function getGridDefaultInset(grid: Grid, mapWidth: number, mapHeight: num
|
|||||||
* @param {number} mapHeight Height of the map in pixels before inset
|
* @param {number} mapHeight Height of the map in pixels before inset
|
||||||
* @returns {GridInset}
|
* @returns {GridInset}
|
||||||
*/
|
*/
|
||||||
export function getGridUpdatedInset(grid: Grid, mapWidth: number, mapHeight: number): GridInset {
|
export function getGridUpdatedInset(grid: Required<Grid>, mapWidth: number, mapHeight: number): GridInset {
|
||||||
let inset = grid.inset;
|
let inset = grid.inset;
|
||||||
// Take current inset width and use it to calculate the new height
|
// Take current inset width and use it to calculate the new height
|
||||||
if (grid.size.x > 0 && grid.size.x > 0) {
|
if (grid.size.x > 0 && grid.size.x > 0) {
|
||||||
@ -295,7 +295,7 @@ export function hexOffsetToCube(offset: Vector2, type: ("hexVertical"|"hexHorizo
|
|||||||
* @param {Vector2} b
|
* @param {Vector2} b
|
||||||
* @param {Size} cellSize
|
* @param {Size} cellSize
|
||||||
*/
|
*/
|
||||||
export function gridDistance(grid: Grid, a: Vector2, b: Vector2, cellSize: Size) {
|
export function gridDistance(grid: Required<Grid>, a: Vector2, b: Vector2, cellSize: Size) {
|
||||||
// Get grid coordinates
|
// Get grid coordinates
|
||||||
const aCoord = getNearestCellCoordinates(grid, a.x, a.y, cellSize);
|
const aCoord = getNearestCellCoordinates(grid, a.x, a.y, cellSize);
|
||||||
const bCoord = getNearestCellCoordinates(grid, b.x, b.y, cellSize);
|
const bCoord = getNearestCellCoordinates(grid, b.x, b.y, cellSize);
|
||||||
|
@ -109,7 +109,7 @@ export async function resizeImage(image: HTMLImageElement, size: number, type: s
|
|||||||
* @property {string} id
|
* @property {string} id
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type ImageFile = {
|
export type ImageFile = {
|
||||||
file: Uint8Array | null,
|
file: Uint8Array | null,
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
|
@ -8,7 +8,7 @@ import { groupBy } from "./shared";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Helper for generating search results for items
|
// Helper for generating search results for items
|
||||||
export function useSearch(items: [], search: string) {
|
export function useSearch(items: any[], search: string) {
|
||||||
// TODO: add types to search items -> don't like the never type
|
// TODO: add types to search items -> don't like the never type
|
||||||
const [filteredItems, setFilteredItems]: [
|
const [filteredItems, setFilteredItems]: [
|
||||||
filteredItems: any,
|
filteredItems: any,
|
||||||
@ -18,10 +18,7 @@ export function useSearch(items: [], search: string) {
|
|||||||
filteredItemScores: {},
|
filteredItemScores: {},
|
||||||
setFilteredItemScores: React.Dispatch<React.SetStateAction<{}>>
|
setFilteredItemScores: React.Dispatch<React.SetStateAction<{}>>
|
||||||
] = useState({});
|
] = useState({});
|
||||||
const [fuse, setFuse]: [
|
const [fuse, setFuse] = useState<any>();
|
||||||
fuse: Fuse<never> | undefined,
|
|
||||||
setFuse: React.Dispatch<Fuse<never> | undefined>
|
|
||||||
] = useState();
|
|
||||||
|
|
||||||
// Update search index when items change
|
// Update search index when items change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -85,7 +82,7 @@ export function useGroup(
|
|||||||
export function handleItemSelect(
|
export function handleItemSelect(
|
||||||
item: any,
|
item: any,
|
||||||
selectMode: any,
|
selectMode: any,
|
||||||
selectedIds: number[],
|
selectedIds: string[],
|
||||||
setSelectedIds: any,
|
setSelectedIds: any,
|
||||||
itemsByGroup: any,
|
itemsByGroup: any,
|
||||||
itemGroups: any
|
itemGroups: any
|
||||||
@ -120,15 +117,15 @@ export function handleItemSelect(
|
|||||||
const lastIndex = items.findIndex(
|
const lastIndex = items.findIndex(
|
||||||
(m: any) => m.id === selectedIds[selectedIds.length - 1]
|
(m: any) => m.id === selectedIds[selectedIds.length - 1]
|
||||||
);
|
);
|
||||||
let idsToAdd: number[] = [];
|
let idsToAdd: string[] = [];
|
||||||
let idsToRemove: number[] = [];
|
let idsToRemove: string[] = [];
|
||||||
const direction = mapIndex > lastIndex ? 1 : -1;
|
const direction = mapIndex > lastIndex ? 1 : -1;
|
||||||
for (
|
for (
|
||||||
let i = lastIndex + direction;
|
let i = lastIndex + direction;
|
||||||
direction < 0 ? i >= mapIndex : i <= mapIndex;
|
direction < 0 ? i >= mapIndex : i <= mapIndex;
|
||||||
i += direction
|
i += direction
|
||||||
) {
|
) {
|
||||||
const itemId: number = items[i].id;
|
const itemId: string = items[i].id;
|
||||||
if (selectedIds.includes(itemId)) {
|
if (selectedIds.includes(itemId)) {
|
||||||
idsToRemove.push(itemId);
|
idsToRemove.push(itemId);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ModelJSON, WeightsManifestConfig } from "@tensorflow/tfjs-core/dist/io/types";
|
import { ModelJSON } from "@tensorflow/tfjs-core/dist/io/types";
|
||||||
import blobToBuffer from "../helpers/blobToBuffer";
|
import blobToBuffer from "../helpers/blobToBuffer";
|
||||||
|
|
||||||
class Model {
|
class Model {
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box, Label, Text } from "theme-ui";
|
import { Box, Label, Text } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
function AddPartyMemberModal({ isOpen, onRequestClose, gameId }) {
|
function AddPartyMemberModal({
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
gameId,
|
||||||
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
|
onRequestClose: any;
|
||||||
|
gameId: string;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onRequestClose={onRequestClose}>
|
<Modal isOpen={isOpen} onRequestClose={onRequestClose}>
|
||||||
<Box>
|
<Box>
|
@ -1,27 +1,27 @@
|
|||||||
import React, { useState, useRef } from "react";
|
import { useState, useRef, ChangeEvent, FormEvent } from "react";
|
||||||
import { Box, Input, Button, Label, Flex } from "theme-ui";
|
import { Box, Input, Button, Label, Flex } from "theme-ui";
|
||||||
|
|
||||||
import { useAuth } from "../contexts/AuthContext";
|
import { useAuth } from "../contexts/AuthContext";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
function AuthModal({ isOpen, onSubmit }) {
|
function AuthModal({ isOpen, onSubmit }: { isOpen: boolean, onSubmit: (newPassword: string) => void}) {
|
||||||
const { password, setPassword } = useAuth();
|
const { password, setPassword } = useAuth();
|
||||||
const [tmpPassword, setTempPassword] = useState(password);
|
const [tmpPassword, setTempPassword] = useState<string>(password);
|
||||||
|
|
||||||
function handleChange(event) {
|
function handleChange(event: ChangeEvent<HTMLInputElement>): void {
|
||||||
setTempPassword(event.target.value);
|
setTempPassword(event.target?.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit(event) {
|
function handleSubmit(event: FormEvent<HTMLElement>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setPassword(tmpPassword);
|
setPassword(tmpPassword);
|
||||||
onSubmit(tmpPassword);
|
onSubmit(tmpPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputRef = useRef();
|
const inputRef = useRef<any>();
|
||||||
function focusInput() {
|
function focusInput(): void {
|
||||||
inputRef.current && inputRef.current.focus();
|
inputRef.current && inputRef.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { Box, Input, Button, Label, Flex } from "theme-ui";
|
import { Box, Input, Button, Label, Flex } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
@ -9,10 +9,16 @@ function ChangeNicknameModal({
|
|||||||
onChangeSubmit,
|
onChangeSubmit,
|
||||||
nickname,
|
nickname,
|
||||||
onChange,
|
onChange,
|
||||||
|
}: {
|
||||||
|
isOpen: boolean,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
onChangeSubmit: any,
|
||||||
|
nickname: string,
|
||||||
|
onChange: any,
|
||||||
}) {
|
}) {
|
||||||
const inputRef = useRef();
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
function focusInput() {
|
function focusInput() {
|
||||||
inputRef.current && inputRef.current.focus();
|
inputRef.current && inputRef.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
@ -1,8 +1,16 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box, Label, Flex, Button, Text } from "theme-ui";
|
import { Box, Label, Flex, Button, Text } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
|
type ConfirmModalProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
onConfirm: () => void,
|
||||||
|
confirmText: string,
|
||||||
|
label: string,
|
||||||
|
description: string,
|
||||||
|
}
|
||||||
|
|
||||||
function ConfirmModal({
|
function ConfirmModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
@ -10,12 +18,12 @@ function ConfirmModal({
|
|||||||
confirmText,
|
confirmText,
|
||||||
label,
|
label,
|
||||||
description,
|
description,
|
||||||
}) {
|
}: ConfirmModalProps ) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ maxWidth: "300px" }}
|
style={{ content: { maxWidth: "300px" } }}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Label py={2}>{label}</Label>
|
<Label py={2}>{label}</Label>
|
@ -1,24 +1,32 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Box, Button, Label, Flex } from "theme-ui";
|
import { Box, Button, Label, Flex } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
import Select from "../components/Select";
|
import Select from "../components/Select";
|
||||||
|
|
||||||
|
type EditGroupProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
onChange: any,
|
||||||
|
groups: string[],
|
||||||
|
defaultGroup: string | undefined | false,
|
||||||
|
}
|
||||||
|
|
||||||
function EditGroupModal({
|
function EditGroupModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
onChange,
|
onChange,
|
||||||
groups,
|
groups,
|
||||||
defaultGroup,
|
defaultGroup,
|
||||||
}) {
|
}: EditGroupProps) {
|
||||||
const [value, setValue] = useState();
|
const [value, setValue] = useState<{ value: string; label: string; } | undefined>();
|
||||||
const [options, setOptions] = useState([]);
|
const [options, setOptions] = useState<{ value: string; label: string; }[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (defaultGroup) {
|
if (defaultGroup) {
|
||||||
setValue({ value: defaultGroup, label: defaultGroup });
|
setValue({ value: defaultGroup, label: defaultGroup });
|
||||||
} else {
|
} else {
|
||||||
setValue();
|
setValue(undefined);
|
||||||
}
|
}
|
||||||
}, [defaultGroup]);
|
}, [defaultGroup]);
|
||||||
|
|
||||||
@ -26,7 +34,7 @@ function EditGroupModal({
|
|||||||
setOptions(groups.map((group) => ({ value: group, label: group })));
|
setOptions(groups.map((group) => ({ value: group, label: group })));
|
||||||
}, [groups]);
|
}, [groups]);
|
||||||
|
|
||||||
function handleCreate(group) {
|
function handleCreate(group: string) {
|
||||||
const newOption = { value: group, label: group };
|
const newOption = { value: group, label: group };
|
||||||
setValue(newOption);
|
setValue(newOption);
|
||||||
setOptions((prev) => [...prev, newOption]);
|
setOptions((prev) => [...prev, newOption]);
|
||||||
@ -40,7 +48,7 @@ function EditGroupModal({
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ overflow: "visible" }}
|
style={{ content: { overflow: "visible" } }}
|
||||||
>
|
>
|
||||||
<Box onSubmit={handleChange} sx={{ width: "300px" }}>
|
<Box onSubmit={handleChange} sx={{ width: "300px" }}>
|
||||||
<Label py={2}>Select or add a group</Label>
|
<Label py={2}>Select or add a group</Label>
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button, Flex, Label } from "theme-ui";
|
import { Button, Flex, Label } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
@ -12,8 +12,15 @@ import { isEmpty } from "../helpers/shared";
|
|||||||
import { getGridDefaultInset } from "../helpers/grid";
|
import { getGridDefaultInset } from "../helpers/grid";
|
||||||
|
|
||||||
import useResponsiveLayout from "../hooks/useResponsiveLayout";
|
import useResponsiveLayout from "../hooks/useResponsiveLayout";
|
||||||
|
import { MapState } from "../components/map/Map";
|
||||||
|
|
||||||
function EditMapModal({ isOpen, onDone, mapId }) {
|
type EditMapProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onDone: any,
|
||||||
|
mapId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditMapModal({ isOpen, onDone, mapId }: EditMapProps) {
|
||||||
const {
|
const {
|
||||||
updateMap,
|
updateMap,
|
||||||
updateMapState,
|
updateMapState,
|
||||||
@ -23,8 +30,8 @@ function EditMapModal({ isOpen, onDone, mapId }) {
|
|||||||
} = useMapData();
|
} = useMapData();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [map, setMap] = useState();
|
const [map, setMap] = useState<any>();
|
||||||
const [mapState, setMapState] = useState();
|
const [mapState, setMapState] = useState<MapState>();
|
||||||
// Load full map when modal is opened
|
// Load full map when modal is opened
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function loadMap() {
|
async function loadMap() {
|
||||||
@ -43,8 +50,8 @@ function EditMapModal({ isOpen, onDone, mapId }) {
|
|||||||
if (isOpen && mapId) {
|
if (isOpen && mapId) {
|
||||||
loadMap();
|
loadMap();
|
||||||
} else {
|
} else {
|
||||||
setMap();
|
setMap(undefined);
|
||||||
setMapState();
|
setMapState(undefined);
|
||||||
}
|
}
|
||||||
}, [isOpen, mapId, getMapFromDB, getMapStateFromDB, getMap]);
|
}, [isOpen, mapId, getMapFromDB, getMapStateFromDB, getMap]);
|
||||||
|
|
||||||
@ -64,19 +71,19 @@ function EditMapModal({ isOpen, onDone, mapId }) {
|
|||||||
*/
|
*/
|
||||||
// Local cache of map setting changes
|
// Local cache of map setting changes
|
||||||
// Applied when done is clicked or map selection is changed
|
// Applied when done is clicked or map selection is changed
|
||||||
const [mapSettingChanges, setMapSettingChanges] = useState({});
|
const [mapSettingChanges, setMapSettingChanges] = useState<any>({});
|
||||||
const [mapStateSettingChanges, setMapStateSettingChanges] = useState({});
|
const [mapStateSettingChanges, setMapStateSettingChanges] = useState<any>({});
|
||||||
|
|
||||||
function handleMapSettingsChange(key, value) {
|
function handleMapSettingsChange(key: string, value: string) {
|
||||||
setMapSettingChanges((prevChanges) => ({
|
setMapSettingChanges((prevChanges: any) => ({
|
||||||
...prevChanges,
|
...prevChanges,
|
||||||
[key]: value,
|
[key]: value,
|
||||||
lastModified: Date.now(),
|
lastModified: Date.now(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapStateSettingsChange(key, value) {
|
function handleMapStateSettingsChange(key: string, value: string) {
|
||||||
setMapStateSettingChanges((prevChanges) => ({
|
setMapStateSettingChanges((prevChanges: any) => ({
|
||||||
...prevChanges,
|
...prevChanges,
|
||||||
[key]: value,
|
[key]: value,
|
||||||
}));
|
}));
|
||||||
@ -137,7 +144,7 @@ function EditMapModal({ isOpen, onDone, mapId }) {
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={handleClose}
|
onRequestClose={handleClose}
|
||||||
style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }}
|
style={{ content: {maxWidth: layout.modalSize, width: "calc(100% - 16px)"} }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Button, Flex, Label } from "theme-ui";
|
import { Button, Flex, Label } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
@ -11,12 +11,19 @@ import { useTokenData } from "../contexts/TokenDataContext";
|
|||||||
import { isEmpty } from "../helpers/shared";
|
import { isEmpty } from "../helpers/shared";
|
||||||
|
|
||||||
import useResponsiveLayout from "../hooks/useResponsiveLayout";
|
import useResponsiveLayout from "../hooks/useResponsiveLayout";
|
||||||
|
import { Token } from "../tokens";
|
||||||
|
|
||||||
function EditTokenModal({ isOpen, onDone, tokenId }) {
|
type EditModalProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onDone: () => void,
|
||||||
|
tokenId: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
function EditTokenModal({ isOpen, onDone, tokenId }: EditModalProps) {
|
||||||
const { updateToken, getTokenFromDB } = useTokenData();
|
const { updateToken, getTokenFromDB } = useTokenData();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [token, setToken] = useState();
|
const [token, setToken] = useState<Token>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function loadToken() {
|
async function loadToken() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -27,7 +34,7 @@ function EditTokenModal({ isOpen, onDone, tokenId }) {
|
|||||||
if (isOpen && tokenId) {
|
if (isOpen && tokenId) {
|
||||||
loadToken();
|
loadToken();
|
||||||
} else {
|
} else {
|
||||||
setToken();
|
setToken(undefined);
|
||||||
}
|
}
|
||||||
}, [isOpen, tokenId, getTokenFromDB]);
|
}, [isOpen, tokenId, getTokenFromDB]);
|
||||||
|
|
||||||
@ -41,10 +48,13 @@ function EditTokenModal({ isOpen, onDone, tokenId }) {
|
|||||||
onDone();
|
onDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [tokenSettingChanges, setTokenSettingChanges] = useState({});
|
const [tokenSettingChanges, setTokenSettingChanges] = useState<any>({});
|
||||||
|
|
||||||
function handleTokenSettingsChange(key, value) {
|
function handleTokenSettingsChange(key: any, value: any) {
|
||||||
setTokenSettingChanges((prevChanges) => ({ ...prevChanges, [key]: value }));
|
setTokenSettingChanges((prevChanges: any) => ({
|
||||||
|
...prevChanges,
|
||||||
|
[key]: value,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyTokenChanges() {
|
async function applyTokenChanges() {
|
||||||
@ -72,8 +82,7 @@ function EditTokenModal({ isOpen, onDone, tokenId }) {
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={handleClose}
|
onRequestClose={handleClose}
|
||||||
style={{
|
style={{
|
||||||
maxWidth: layout.modalSize,
|
content: { maxWidth: layout.modalSize, width: "calc(100% - 16px)" },
|
||||||
width: "calc(100% - 16px)",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
@ -1,14 +1,13 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box, Label, Text } from "theme-ui";
|
import { Box, Label, Text } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
function ForceUpdateModal({ isOpen }) {
|
function ForceUpdateModal({ isOpen }: { isOpen: boolean }) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
allowClose={false}
|
allowClose={false}
|
||||||
style={{ maxWidth: "450px" }}
|
style={{ content: { maxWidth: "450px" } }}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Label py={2}>New Update Available</Label>
|
<Label py={2}>New Update Available</Label>
|
@ -1,14 +1,13 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box, Label, Flex, Button, Text } from "theme-ui";
|
import { Box, Label, Flex, Button, Text } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
function GameExpiredModal({ isOpen, onRequestClose }) {
|
function GameExpiredModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: () => void }) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ maxWidth: "450px" }}
|
style={{ content: { maxWidth: "450px" } }}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Label py={2}>Game Timed Out</Label>
|
<Label py={2}>Game Timed Out</Label>
|
@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box, Label, Text } from "theme-ui";
|
import { Box, Label, Text } from "theme-ui";
|
||||||
import raw from "raw.macro";
|
import raw from "raw.macro";
|
||||||
|
|
||||||
@ -8,12 +7,12 @@ import Link from "../components/Link";
|
|||||||
|
|
||||||
const gettingStarted = raw("../docs/howTo/gettingStarted.md");
|
const gettingStarted = raw("../docs/howTo/gettingStarted.md");
|
||||||
|
|
||||||
function GettingStartedModal({ isOpen, onRequestClose }) {
|
function GettingStartedModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: () => void } ) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ maxWidth: "450px" }}
|
style={{ content: { maxWidth: "450px" } }}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Label py={2}>Getting Started</Label>
|
<Label py={2}>Getting Started</Label>
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useState, useEffect } from "react";
|
import { useRef, useState, useEffect } from "react";
|
||||||
import { Box, Label, Text, Button, Flex } from "theme-ui";
|
import { Box, Label, Text, Button, Flex } from "theme-ui";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import * as Comlink from "comlink";
|
import * as Comlink from "comlink";
|
||||||
@ -16,24 +16,25 @@ import { useDatabase } from "../contexts/DatabaseContext";
|
|||||||
import SelectDataModal from "./SelectDataModal";
|
import SelectDataModal from "./SelectDataModal";
|
||||||
|
|
||||||
import { getDatabase } from "../database";
|
import { getDatabase } from "../database";
|
||||||
|
import { Map, MapState, TokenState } from "../components/map/Map";
|
||||||
|
|
||||||
const importDBName = "OwlbearRodeoImportDB";
|
const importDBName = "OwlbearRodeoImportDB";
|
||||||
|
|
||||||
function ImportExportModal({ isOpen, onRequestClose }) {
|
function ImportExportModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: () => void}) {
|
||||||
const { worker } = useDatabase();
|
const { worker } = useDatabase();
|
||||||
const { userId } = useAuth();
|
const { userId } = useAuth();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState();
|
const [error, setError] = useState<Error>();
|
||||||
|
|
||||||
const backgroundTaskRunningRef = useRef(false);
|
const backgroundTaskRunningRef = useRef(false);
|
||||||
const fileInputRef = useRef();
|
const fileInputRef = useRef<any>();
|
||||||
|
|
||||||
const [showImportSelector, setShowImportSelector] = useState(false);
|
const [showImportSelector, setShowImportSelector] = useState(false);
|
||||||
const [showExportSelector, setShowExportSelector] = useState(false);
|
const [showExportSelector, setShowExportSelector] = useState(false);
|
||||||
|
|
||||||
const { addToast } = useToasts();
|
const { addToast } = useToasts();
|
||||||
function addSuccessToast(message, maps, tokens) {
|
function addSuccessToast(message: string, maps: any, tokens: TokenState[]) {
|
||||||
const mapText = `${maps.length} map${maps.length > 1 ? "s" : ""}`;
|
const mapText = `${maps.length} map${maps.length > 1 ? "s" : ""}`;
|
||||||
const tokenText = `${tokens.length} token${tokens.length > 1 ? "s" : ""}`;
|
const tokenText = `${tokens.length} token${tokens.length > 1 ? "s" : ""}`;
|
||||||
if (maps.length > 0 && tokens.length > 0) {
|
if (maps.length > 0 && tokens.length > 0) {
|
||||||
@ -53,11 +54,11 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
|
|
||||||
const loadingProgressRef = useRef(0);
|
const loadingProgressRef = useRef(0);
|
||||||
|
|
||||||
function handleDBProgress({ completedRows, totalRows }) {
|
function handleDBProgress({ completedRows, totalRows }: { completedRows: number, totalRows: number }) {
|
||||||
loadingProgressRef.current = completedRows / totalRows;
|
loadingProgressRef.current = completedRows / totalRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleImportDatabase(file) {
|
async function handleImportDatabase(file: File) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
backgroundTaskRunningRef.current = true;
|
backgroundTaskRunningRef.current = true;
|
||||||
try {
|
try {
|
||||||
@ -94,7 +95,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleBeforeUnload(event) {
|
function handleBeforeUnload(event: any) {
|
||||||
if (backgroundTaskRunningRef.current) {
|
if (backgroundTaskRunningRef.current) {
|
||||||
event.returnValue =
|
event.returnValue =
|
||||||
"Database is still processing, are you sure you want to leave?";
|
"Database is still processing, are you sure you want to leave?";
|
||||||
@ -121,7 +122,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
setShowImportSelector(false);
|
setShowImportSelector(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleImportSelectorConfirm(checkedMaps, checkedTokens) {
|
async function handleImportSelectorConfirm(checkedMaps: any, checkedTokens: TokenState[]) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
backgroundTaskRunningRef.current = true;
|
backgroundTaskRunningRef.current = true;
|
||||||
setShowImportSelector(false);
|
setShowImportSelector(false);
|
||||||
@ -131,11 +132,11 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
const db = getDatabase({});
|
const db = getDatabase({});
|
||||||
try {
|
try {
|
||||||
// Keep track of a mapping of old token ids to new ones to apply them to the map states
|
// Keep track of a mapping of old token ids to new ones to apply them to the map states
|
||||||
let newTokenIds = {};
|
let newTokenIds: {[id: string]: string} = {};
|
||||||
if (checkedTokens.length > 0) {
|
if (checkedTokens.length > 0) {
|
||||||
const tokenIds = checkedTokens.map((token) => token.id);
|
const tokenIds = checkedTokens.map((token) => token.id);
|
||||||
const tokensToAdd = await importDB.table("tokens").bulkGet(tokenIds);
|
const tokensToAdd: TokenState[] = await importDB.table("tokens").bulkGet(tokenIds);
|
||||||
let newTokens = [];
|
let newTokens: TokenState[] = [];
|
||||||
for (let token of tokensToAdd) {
|
for (let token of tokensToAdd) {
|
||||||
const newId = shortid.generate();
|
const newId = shortid.generate();
|
||||||
newTokenIds[token.id] = newId;
|
newTokenIds[token.id] = newId;
|
||||||
@ -146,12 +147,12 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (checkedMaps.length > 0) {
|
if (checkedMaps.length > 0) {
|
||||||
const mapIds = checkedMaps.map((map) => map.id);
|
const mapIds = checkedMaps.map((map: any) => map.id);
|
||||||
const mapsToAdd = await importDB.table("maps").bulkGet(mapIds);
|
const mapsToAdd = await importDB.table("maps").bulkGet(mapIds);
|
||||||
let newMaps = [];
|
let newMaps = [];
|
||||||
let newStates = [];
|
let newStates = [];
|
||||||
for (let map of mapsToAdd) {
|
for (let map of mapsToAdd) {
|
||||||
let state = await importDB.table("states").get(map.id);
|
let state: MapState = await importDB.table("states").get(map.id);
|
||||||
// Apply new token ids to imported state
|
// Apply new token ids to imported state
|
||||||
for (let tokenState of Object.values(state.tokens)) {
|
for (let tokenState of Object.values(state.tokens)) {
|
||||||
if (tokenState.tokenId in newTokenIds) {
|
if (tokenState.tokenId in newTokenIds) {
|
||||||
@ -179,7 +180,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
backgroundTaskRunningRef.current = false;
|
backgroundTaskRunningRef.current = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportSelectorFilter(table, value) {
|
function exportSelectorFilter(table: any, value: Map | TokenState) {
|
||||||
// Only show owned maps and tokens
|
// Only show owned maps and tokens
|
||||||
if (table === "maps" || table === "tokens") {
|
if (table === "maps" || table === "tokens") {
|
||||||
if (value.owner === userId) {
|
if (value.owner === userId) {
|
||||||
@ -197,7 +198,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
setShowExportSelector(false);
|
setShowExportSelector(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleExportSelectorConfirm(checkedMaps, checkedTokens) {
|
async function handleExportSelectorConfirm(checkedMaps: Map[], checkedTokens: TokenState[]) {
|
||||||
setShowExportSelector(false);
|
setShowExportSelector(false);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
backgroundTaskRunningRef.current = true;
|
backgroundTaskRunningRef.current = true;
|
||||||
@ -238,7 +239,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
Select import or export then select the data you wish to use
|
Select import or export then select the data you wish to use
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<input
|
||||||
onChange={(event) => handleImportDatabase(event.target.files[0])}
|
onChange={(event) => event.target.files && handleImportDatabase(event.target.files[0])}
|
||||||
type="file"
|
type="file"
|
||||||
accept=".owlbear"
|
accept=".owlbear"
|
||||||
style={{ display: "none" }}
|
style={{ display: "none" }}
|
||||||
@ -272,7 +273,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<ErrorBanner error={error} onRequestClose={() => setError()} />
|
<ErrorBanner error={error} onRequestClose={() => setError(undefined)} />
|
||||||
<SelectDataModal
|
<SelectDataModal
|
||||||
isOpen={showImportSelector}
|
isOpen={showImportSelector}
|
||||||
onRequestClose={handleImportSelectorClose}
|
onRequestClose={handleImportSelectorClose}
|
@ -1,23 +1,23 @@
|
|||||||
import React, { useState, useRef } from "react";
|
import { useState, useRef, FormEvent, ChangeEvent } from "react";
|
||||||
import { Box, Label, Input, Button, Flex } from "theme-ui";
|
import { Box, Label, Input, Button, Flex } from "theme-ui";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
function JoinModal({ isOpen, onRequestClose }) {
|
function JoinModal({ isOpen, onRequestClose }: any) {
|
||||||
let history = useHistory();
|
let history = useHistory();
|
||||||
const [gameId, setGameId] = useState("");
|
const [gameId, setGameId] = useState("");
|
||||||
|
|
||||||
function handleChange(event) {
|
function handleChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setGameId(event.target.value);
|
setGameId(event.target?.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit(event) {
|
function handleSubmit(event: FormEvent<HTMLDivElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
history.push(`/game/${gameId}`);
|
history.push(`/game/${gameId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputRef = useRef();
|
const inputRef = useRef<any>();
|
||||||
function focusInput() {
|
function focusInput() {
|
||||||
inputRef.current && inputRef.current.focus();
|
inputRef.current && inputRef.current.focus();
|
||||||
}
|
}
|
||||||
@ -27,6 +27,7 @@ function JoinModal({ isOpen, onRequestClose }) {
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
onAfterOpen={focusInput}
|
onAfterOpen={focusInput}
|
||||||
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import { ChangeEvent, useEffect, useState } from "react";
|
||||||
import { Box, Label, Flex, Button, Text, Checkbox, Divider } from "theme-ui";
|
import { Box, Label, Flex, Button, Text, Checkbox, Divider } from "theme-ui";
|
||||||
import SimpleBar from "simplebar-react";
|
import SimpleBar from "simplebar-react";
|
||||||
|
|
||||||
@ -6,6 +6,15 @@ import Modal from "../components/Modal";
|
|||||||
import LoadingOverlay from "../components/LoadingOverlay";
|
import LoadingOverlay from "../components/LoadingOverlay";
|
||||||
|
|
||||||
import { getDatabase } from "../database";
|
import { getDatabase } from "../database";
|
||||||
|
import { Props } from "react-modal";
|
||||||
|
|
||||||
|
type SelectDataProps = Props & {
|
||||||
|
onConfirm: any,
|
||||||
|
confirmText: string,
|
||||||
|
label: string,
|
||||||
|
databaseName: string,
|
||||||
|
filter: any,
|
||||||
|
}
|
||||||
|
|
||||||
function SelectDataModal({
|
function SelectDataModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
@ -15,10 +24,10 @@ function SelectDataModal({
|
|||||||
label,
|
label,
|
||||||
databaseName,
|
databaseName,
|
||||||
filter,
|
filter,
|
||||||
}) {
|
}: SelectDataProps) {
|
||||||
const [maps, setMaps] = useState({});
|
const [maps, setMaps] = useState<any>({});
|
||||||
const [tokensByMap, setTokensByMap] = useState({});
|
const [tokensByMap, setTokensByMap] = useState<any>({});
|
||||||
const [tokens, setTokens] = useState({});
|
const [tokens, setTokens] = useState<any>({});
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const hasMaps = Object.values(maps).length > 0;
|
const hasMaps = Object.values(maps).length > 0;
|
||||||
@ -29,9 +38,9 @@ function SelectDataModal({
|
|||||||
if (isOpen && databaseName) {
|
if (isOpen && databaseName) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const db = getDatabase({ addons: [] }, databaseName);
|
const db = getDatabase({ addons: [] }, databaseName);
|
||||||
let loadedMaps = {};
|
let loadedMaps: any = [];
|
||||||
let loadedTokensByMap = {};
|
let loadedTokensByMap: any = {};
|
||||||
let loadedTokens = {};
|
let loadedTokens: any = [];
|
||||||
await db
|
await db
|
||||||
.table("maps")
|
.table("maps")
|
||||||
.filter((map) => filter("maps", map, map.id))
|
.filter((map) => filter("maps", map, map.id))
|
||||||
@ -44,7 +53,7 @@ function SelectDataModal({
|
|||||||
.each((state) => {
|
.each((state) => {
|
||||||
loadedTokensByMap[state.mapId] = new Set(
|
loadedTokensByMap[state.mapId] = new Set(
|
||||||
Object.values(state.tokens).map(
|
Object.values(state.tokens).map(
|
||||||
(tokenState) => tokenState.tokenId
|
(tokenState: any) => tokenState.tokenId
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -73,9 +82,9 @@ function SelectDataModal({
|
|||||||
}, [isOpen, databaseName, filter]);
|
}, [isOpen, databaseName, filter]);
|
||||||
|
|
||||||
// An object mapping a tokenId to how many checked maps it is currently used in
|
// An object mapping a tokenId to how many checked maps it is currently used in
|
||||||
const [tokenUsedCount, setTokenUsedCount] = useState({});
|
const [tokenUsedCount, setTokenUsedCount] = useState<any>({});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let tokensUsed = {};
|
let tokensUsed: any = {};
|
||||||
for (let mapId in maps) {
|
for (let mapId in maps) {
|
||||||
if (maps[mapId].checked && mapId in tokensByMap) {
|
if (maps[mapId].checked && mapId in tokensByMap) {
|
||||||
for (let tokenId of tokensByMap[mapId]) {
|
for (let tokenId of tokensByMap[mapId]) {
|
||||||
@ -89,7 +98,7 @@ function SelectDataModal({
|
|||||||
}
|
}
|
||||||
setTokenUsedCount(tokensUsed);
|
setTokenUsedCount(tokensUsed);
|
||||||
// Update tokens to ensure used tokens are checked
|
// Update tokens to ensure used tokens are checked
|
||||||
setTokens((prevTokens) => {
|
setTokens((prevTokens: any) => {
|
||||||
let newTokens = { ...prevTokens };
|
let newTokens = { ...prevTokens };
|
||||||
for (let id in newTokens) {
|
for (let id in newTokens) {
|
||||||
if (id in tokensUsed) {
|
if (id in tokensUsed) {
|
||||||
@ -101,13 +110,13 @@ function SelectDataModal({
|
|||||||
}, [maps, tokensByMap]);
|
}, [maps, tokensByMap]);
|
||||||
|
|
||||||
function handleConfirm() {
|
function handleConfirm() {
|
||||||
let checkedMaps = Object.values(maps).filter((map) => map.checked);
|
let checkedMaps = Object.values(maps).filter((map: any) => map.checked);
|
||||||
let checkedTokens = Object.values(tokens).filter((token) => token.checked);
|
let checkedTokens = Object.values(tokens).filter((token: any) => token.checked);
|
||||||
onConfirm(checkedMaps, checkedTokens);
|
onConfirm(checkedMaps, checkedTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectMapsChanged(event) {
|
function handleSelectMapsChanged(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setMaps((prevMaps) => {
|
setMaps((prevMaps: any) => {
|
||||||
let newMaps = { ...prevMaps };
|
let newMaps = { ...prevMaps };
|
||||||
for (let id in newMaps) {
|
for (let id in newMaps) {
|
||||||
newMaps[id].checked = event.target.checked;
|
newMaps[id].checked = event.target.checked;
|
||||||
@ -116,7 +125,7 @@ function SelectDataModal({
|
|||||||
});
|
});
|
||||||
// If all token select is unchecked then ensure all tokens are unchecked
|
// If all token select is unchecked then ensure all tokens are unchecked
|
||||||
if (!event.target.checked && !tokensSelectChecked) {
|
if (!event.target.checked && !tokensSelectChecked) {
|
||||||
setTokens((prevTokens) => {
|
setTokens((prevTokens: any) => {
|
||||||
let newTokens = { ...prevTokens };
|
let newTokens = { ...prevTokens };
|
||||||
for (let id in newTokens) {
|
for (let id in newTokens) {
|
||||||
newTokens[id].checked = false;
|
newTokens[id].checked = false;
|
||||||
@ -126,14 +135,14 @@ function SelectDataModal({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMapChange(event, map) {
|
function handleMapChange(event: ChangeEvent<HTMLInputElement>, map: any) {
|
||||||
setMaps((prevMaps) => ({
|
setMaps((prevMaps: any) => ({
|
||||||
...prevMaps,
|
...prevMaps,
|
||||||
[map.id]: { ...map, checked: event.target.checked },
|
[map.id]: { ...map, checked: event.target.checked },
|
||||||
}));
|
}));
|
||||||
// If all token select is unchecked then ensure tokens assosiated to this map are unchecked
|
// If all token select is unchecked then ensure tokens assosiated to this map are unchecked
|
||||||
if (!event.target.checked && !tokensSelectChecked) {
|
if (!event.target.checked && !tokensSelectChecked) {
|
||||||
setTokens((prevTokens) => {
|
setTokens((prevTokens: any) => {
|
||||||
let newTokens = { ...prevTokens };
|
let newTokens = { ...prevTokens };
|
||||||
for (let id in newTokens) {
|
for (let id in newTokens) {
|
||||||
if (tokensByMap[map.id].has(id) && tokenUsedCount[id] === 1) {
|
if (tokensByMap[map.id].has(id) && tokenUsedCount[id] === 1) {
|
||||||
@ -145,8 +154,8 @@ function SelectDataModal({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectTokensChange(event) {
|
function handleSelectTokensChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setTokens((prevTokens) => {
|
setTokens((prevTokens: any) => {
|
||||||
let newTokens = { ...prevTokens };
|
let newTokens = { ...prevTokens };
|
||||||
for (let id in newTokens) {
|
for (let id in newTokens) {
|
||||||
if (!(id in tokenUsedCount)) {
|
if (!(id in tokenUsedCount)) {
|
||||||
@ -157,8 +166,8 @@ function SelectDataModal({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTokenChange(event, token) {
|
function handleTokenChange(event: ChangeEvent<HTMLInputElement>, token: any) {
|
||||||
setTokens((prevTokens) => ({
|
setTokens((prevTokens: any) => ({
|
||||||
...prevTokens,
|
...prevTokens,
|
||||||
[token.id]: { ...token, checked: event.target.checked },
|
[token.id]: { ...token, checked: event.target.checked },
|
||||||
}));
|
}));
|
||||||
@ -167,14 +176,14 @@ function SelectDataModal({
|
|||||||
// Some tokens are checked not by maps or all tokens are checked by maps
|
// Some tokens are checked not by maps or all tokens are checked by maps
|
||||||
const tokensSelectChecked =
|
const tokensSelectChecked =
|
||||||
Object.values(tokens).some(
|
Object.values(tokens).some(
|
||||||
(token) => !(token.id in tokenUsedCount) && token.checked
|
(token: any) => !(token.id in tokenUsedCount) && token.checked
|
||||||
) || Object.values(tokens).every((token) => token.id in tokenUsedCount);
|
) || Object.values(tokens).every((token: any) => token.id in tokenUsedCount);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ maxWidth: "450px", width: "100%" }}
|
style={{ content: {maxWidth: "450px", width: "100%"} }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -205,13 +214,13 @@ function SelectDataModal({
|
|||||||
<Flex>
|
<Flex>
|
||||||
<Label>
|
<Label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={Object.values(maps).some((map) => map.checked)}
|
checked={Object.values(maps).some((map: any) => map.checked)}
|
||||||
onChange={handleSelectMapsChanged}
|
onChange={handleSelectMapsChanged}
|
||||||
/>
|
/>
|
||||||
Maps
|
Maps
|
||||||
</Label>
|
</Label>
|
||||||
</Flex>
|
</Flex>
|
||||||
{Object.values(maps).map((map) => (
|
{Object.values(maps).map((map: any) => (
|
||||||
<Label
|
<Label
|
||||||
key={map.id}
|
key={map.id}
|
||||||
my={1}
|
my={1}
|
||||||
@ -237,7 +246,7 @@ function SelectDataModal({
|
|||||||
/>
|
/>
|
||||||
Tokens
|
Tokens
|
||||||
</Label>
|
</Label>
|
||||||
{Object.values(tokens).map((token) => (
|
{Object.values(tokens).map((token: any) => (
|
||||||
<Box pl={4} my={1} key={token.id}>
|
<Box pl={4} my={1} key={token.id}>
|
||||||
<Label sx={{ fontFamily: "body2" }}>
|
<Label sx={{ fontFamily: "body2" }}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -265,8 +274,8 @@ function SelectDataModal({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={
|
disabled={
|
||||||
!Object.values(maps).some((map) => map.checked) &&
|
!Object.values(maps).some((map: any) => map.checked) &&
|
||||||
!Object.values(tokens).some((token) => token.checked)
|
!Object.values(tokens).some((token: any) => token.checked)
|
||||||
}
|
}
|
||||||
sx={{ flexGrow: 1 }}
|
sx={{ flexGrow: 1 }}
|
||||||
m={1}
|
m={1}
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Flex, Label, Button } from "theme-ui";
|
import { Flex, Label, Button } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
@ -7,8 +7,16 @@ import DiceTiles from "../components/dice/DiceTiles";
|
|||||||
import { dice } from "../dice";
|
import { dice } from "../dice";
|
||||||
|
|
||||||
import useResponsiveLayout from "../hooks/useResponsiveLayout";
|
import useResponsiveLayout from "../hooks/useResponsiveLayout";
|
||||||
|
import Dice from "../dice/Dice";
|
||||||
|
|
||||||
function SelectDiceModal({ isOpen, onRequestClose, onDone, defaultDice }) {
|
type SelectDiceProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
onDone: any,
|
||||||
|
defaultDice: Dice
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectDiceModal({ isOpen, onRequestClose, onDone, defaultDice }: SelectDiceProps) {
|
||||||
const [selectedDice, setSelectedDice] = useState(defaultDice);
|
const [selectedDice, setSelectedDice] = useState(defaultDice);
|
||||||
const layout = useResponsiveLayout();
|
const layout = useResponsiveLayout();
|
||||||
|
|
||||||
@ -16,7 +24,7 @@ function SelectDiceModal({ isOpen, onRequestClose, onDone, defaultDice }) {
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }}
|
style={{ content: { maxWidth: layout.modalSize, width: "calc(100% - 16px)" } }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import { ChangeEvent, useRef, useState } from "react";
|
||||||
import { Button, Flex, Label } from "theme-ui";
|
import { Button, Flex, Label } from "theme-ui";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
import Case from "case";
|
import Case from "case";
|
||||||
@ -30,6 +30,15 @@ import { useAuth } from "../contexts/AuthContext";
|
|||||||
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
|
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
|
||||||
|
|
||||||
import shortcuts from "../shortcuts";
|
import shortcuts from "../shortcuts";
|
||||||
|
import { MapState } from "../components/map/Map";
|
||||||
|
|
||||||
|
type SelectMapProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onDone: any,
|
||||||
|
onMapChange: any,
|
||||||
|
onMapReset: any,
|
||||||
|
currentMap: any
|
||||||
|
}
|
||||||
|
|
||||||
const defaultMapProps = {
|
const defaultMapProps = {
|
||||||
showGrid: false,
|
showGrid: false,
|
||||||
@ -56,7 +65,7 @@ function SelectMapModal({
|
|||||||
onMapReset,
|
onMapReset,
|
||||||
// The map currently being view in the map screen
|
// The map currently being view in the map screen
|
||||||
currentMap,
|
currentMap,
|
||||||
}) {
|
}: SelectMapProps ) {
|
||||||
const { addToast } = useToasts();
|
const { addToast } = useToasts();
|
||||||
|
|
||||||
const { userId } = useAuth();
|
const { userId } = useAuth();
|
||||||
@ -79,7 +88,7 @@ function SelectMapModal({
|
|||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [filteredMaps, filteredMapScores] = useSearch(ownedMaps, search);
|
const [filteredMaps, filteredMapScores] = useSearch(ownedMaps, search);
|
||||||
|
|
||||||
function handleSearchChange(event) {
|
function handleSearchChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setSearch(event.target.value);
|
setSearch(event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +97,7 @@ function SelectMapModal({
|
|||||||
*/
|
*/
|
||||||
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
||||||
|
|
||||||
async function handleMapsGroup(group) {
|
async function handleMapsGroup(group: any) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setIsGroupModalOpen(false);
|
setIsGroupModalOpen(false);
|
||||||
await updateMaps(selectedMapIds, { group });
|
await updateMaps(selectedMapIds, { group });
|
||||||
@ -106,15 +115,15 @@ function SelectMapModal({
|
|||||||
* Image Upload
|
* Image Upload
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fileInputRef = useRef();
|
const fileInputRef = useRef<any>();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = useState(
|
const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = useState(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
const largeImageWarningFiles = useRef();
|
const largeImageWarningFiles = useRef<any>();
|
||||||
|
|
||||||
async function handleImagesUpload(files) {
|
async function handleImagesUpload(files: any) {
|
||||||
if (navigator.storage) {
|
if (navigator.storage) {
|
||||||
// Attempt to enable persistant storage
|
// Attempt to enable persistant storage
|
||||||
await navigator.storage.persist();
|
await navigator.storage.persist();
|
||||||
@ -166,7 +175,7 @@ function SelectMapModal({
|
|||||||
clearFileInput();
|
clearFileInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleImageUpload(file) {
|
async function handleImageUpload(file: any) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
@ -222,9 +231,9 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create resolutions
|
// Create resolutions
|
||||||
const resolutions = {};
|
const resolutions: any = {};
|
||||||
for (let resolution of mapResolutions) {
|
for (let resolution of mapResolutions) {
|
||||||
const resolutionPixelSize = Vector2.multiply(
|
const resolutionPixelSize: Vector2 = Vector2.multiply(
|
||||||
gridSize,
|
gridSize,
|
||||||
resolution.size
|
resolution.size
|
||||||
);
|
);
|
||||||
@ -234,7 +243,7 @@ function SelectMapModal({
|
|||||||
) {
|
) {
|
||||||
const resized = await resizeImage(
|
const resized = await resizeImage(
|
||||||
image,
|
image,
|
||||||
Vector2.max(resolutionPixelSize),
|
Vector2.max(resolutionPixelSize, undefined) as number,
|
||||||
file.type,
|
file.type,
|
||||||
resolution.quality
|
resolution.quality
|
||||||
);
|
);
|
||||||
@ -284,7 +293,7 @@ function SelectMapModal({
|
|||||||
});
|
});
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
resolve();
|
resolve(undefined);
|
||||||
};
|
};
|
||||||
image.onerror = reject;
|
image.onerror = reject;
|
||||||
image.src = url;
|
image.src = url;
|
||||||
@ -293,7 +302,7 @@ function SelectMapModal({
|
|||||||
|
|
||||||
function openImageDialog() {
|
function openImageDialog() {
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
fileInputRef.current.click();
|
fileInputRef.current?.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,16 +311,16 @@ function SelectMapModal({
|
|||||||
*/
|
*/
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
// The map selected in the modal
|
// The map selected in the modal
|
||||||
const [selectedMapIds, setSelectedMapIds] = useState([]);
|
const [selectedMapIds, setSelectedMapIds] = useState<string[]>([]);
|
||||||
|
|
||||||
const selectedMaps = ownedMaps.filter((map) =>
|
const selectedMaps = ownedMaps.filter((map: any) =>
|
||||||
selectedMapIds.includes(map.id)
|
selectedMapIds.includes(map.id)
|
||||||
);
|
);
|
||||||
const selectedMapStates = mapStates.filter((state) =>
|
const selectedMapStates = mapStates.filter((state: MapState) =>
|
||||||
selectedMapIds.includes(state.mapId)
|
selectedMapIds.includes(state.mapId)
|
||||||
);
|
);
|
||||||
|
|
||||||
async function handleMapAdd(map) {
|
async function handleMapAdd(map: any) {
|
||||||
await addMap(map);
|
await addMap(map);
|
||||||
setSelectedMapIds([map.id]);
|
setSelectedMapIds([map.id]);
|
||||||
}
|
}
|
||||||
@ -346,7 +355,7 @@ function SelectMapModal({
|
|||||||
// Either single, multiple or range
|
// Either single, multiple or range
|
||||||
const [selectMode, setSelectMode] = useState("single");
|
const [selectMode, setSelectMode] = useState("single");
|
||||||
|
|
||||||
function handleMapSelect(map) {
|
function handleMapSelect(map: any) {
|
||||||
handleItemSelect(
|
handleItemSelect(
|
||||||
map,
|
map,
|
||||||
selectMode,
|
selectMode,
|
||||||
@ -392,7 +401,7 @@ function SelectMapModal({
|
|||||||
/**
|
/**
|
||||||
* Shortcuts
|
* Shortcuts
|
||||||
*/
|
*/
|
||||||
function handleKeyDown(event) {
|
function handleKeyDown(event: KeyboardEvent): KeyboardEvent | void {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -406,7 +415,7 @@ function SelectMapModal({
|
|||||||
// Selected maps and none are default
|
// Selected maps and none are default
|
||||||
if (
|
if (
|
||||||
selectedMapIds.length > 0 &&
|
selectedMapIds.length > 0 &&
|
||||||
!selectedMaps.some((map) => map.type === "default")
|
!selectedMaps.some((map: any) => map.type === "default")
|
||||||
) {
|
) {
|
||||||
// Ensure all other modals are closed
|
// Ensure all other modals are closed
|
||||||
setIsGroupModalOpen(false);
|
setIsGroupModalOpen(false);
|
||||||
@ -417,7 +426,7 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp(event) {
|
function handleKeyUp(event: KeyboardEvent) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -444,7 +453,7 @@ function SelectMapModal({
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={handleClose}
|
onRequestClose={handleClose}
|
||||||
style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }}
|
style={{ content: { maxWidth: layout.modalSize, width: "calc(100% - 16px)" } }}
|
||||||
>
|
>
|
||||||
<ImageDrop onDrop={handleImagesUpload} dropText="Drop map to upload">
|
<ImageDrop onDrop={handleImagesUpload} dropText="Drop map to upload">
|
||||||
<input
|
<input
|
||||||
@ -500,15 +509,15 @@ function SelectMapModal({
|
|||||||
isOpen={isGroupModalOpen}
|
isOpen={isGroupModalOpen}
|
||||||
onChange={handleMapsGroup}
|
onChange={handleMapsGroup}
|
||||||
groups={mapGroups.filter(
|
groups={mapGroups.filter(
|
||||||
(group) => group !== "" && group !== "default"
|
(group: any) => group !== "" && group !== "default"
|
||||||
)}
|
)}
|
||||||
onRequestClose={() => setIsGroupModalOpen(false)}
|
onRequestClose={() => setIsGroupModalOpen(false)}
|
||||||
// Select the default group by testing whether all selected maps are the same
|
// Select the default group by testing whether all selected maps are the same
|
||||||
defaultGroup={
|
defaultGroup={
|
||||||
selectedMaps.length > 0 &&
|
selectedMaps.length > 0 &&
|
||||||
selectedMaps
|
selectedMaps
|
||||||
.map((map) => map.group)
|
.map((map: any) => map.group)
|
||||||
.reduce((prev, curr) => (prev === curr ? curr : undefined))
|
.reduce((prev: any, curr: any) => (prev === curr ? curr : undefined))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import { ChangeEvent, useRef, useState } from "react";
|
||||||
import { Flex, Label, Button } from "theme-ui";
|
import { Flex, Label, Button } from "theme-ui";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
import Case from "case";
|
import Case from "case";
|
||||||
@ -24,8 +24,9 @@ import { useAuth } from "../contexts/AuthContext";
|
|||||||
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
|
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
|
||||||
|
|
||||||
import shortcuts from "../shortcuts";
|
import shortcuts from "../shortcuts";
|
||||||
|
import { FileToken, Token } from "../tokens";
|
||||||
|
|
||||||
function SelectTokensModal({ isOpen, onRequestClose }) {
|
function SelectTokensModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: any }) {
|
||||||
const { addToast } = useToasts();
|
const { addToast } = useToasts();
|
||||||
|
|
||||||
const { userId } = useAuth();
|
const { userId } = useAuth();
|
||||||
@ -43,7 +44,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [filteredTokens, filteredTokenScores] = useSearch(ownedTokens, search);
|
const [filteredTokens, filteredTokenScores] = useSearch(ownedTokens, search);
|
||||||
|
|
||||||
function handleSearchChange(event) {
|
function handleSearchChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setSearch(event.target.value);
|
setSearch(event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
*/
|
*/
|
||||||
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
||||||
|
|
||||||
async function handleTokensGroup(group) {
|
async function handleTokensGroup(group: string) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setIsGroupModalOpen(false);
|
setIsGroupModalOpen(false);
|
||||||
await updateTokens(selectedTokenIds, { group });
|
await updateTokens(selectedTokenIds, { group });
|
||||||
@ -70,13 +71,13 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
* Image Upload
|
* Image Upload
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fileInputRef = useRef();
|
const fileInputRef = useRef<any>();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = useState(
|
const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = useState(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
const largeImageWarningFiles = useRef();
|
const largeImageWarningFiles = useRef<File[]>();
|
||||||
|
|
||||||
function openImageDialog() {
|
function openImageDialog() {
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
@ -84,12 +85,17 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleImagesUpload(files) {
|
async function handleImagesUpload(files: FileList | null) {
|
||||||
if (navigator.storage) {
|
if (navigator.storage) {
|
||||||
// Attempt to enable persistant storage
|
// Attempt to enable persistant storage
|
||||||
await navigator.storage.persist();
|
await navigator.storage.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: handle null files
|
||||||
|
if (files === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let tokenFiles = [];
|
let tokenFiles = [];
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (file.size > 5e7) {
|
if (file.size > 5e7) {
|
||||||
@ -129,6 +135,9 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
async function handleLargeImageWarningConfirm() {
|
async function handleLargeImageWarningConfirm() {
|
||||||
setShowLargeImageWarning(false);
|
setShowLargeImageWarning(false);
|
||||||
const files = largeImageWarningFiles.current;
|
const files = largeImageWarningFiles.current;
|
||||||
|
if (!files) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
await handleImageUpload(file);
|
await handleImageUpload(file);
|
||||||
}
|
}
|
||||||
@ -136,7 +145,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
clearFileInput();
|
clearFileInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleImageUpload(file) {
|
async function handleImageUpload(file: File) {
|
||||||
let name = "Unknown Token";
|
let name = "Unknown Token";
|
||||||
if (file.name) {
|
if (file.name) {
|
||||||
// Remove file extension
|
// Remove file extension
|
||||||
@ -180,7 +189,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
height: image.height,
|
height: image.height,
|
||||||
});
|
});
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
resolve();
|
resolve(undefined);
|
||||||
};
|
};
|
||||||
image.onerror = reject;
|
image.onerror = reject;
|
||||||
image.src = url;
|
image.src = url;
|
||||||
@ -190,13 +199,13 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
/**
|
/**
|
||||||
* Token controls
|
* Token controls
|
||||||
*/
|
*/
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState<boolean>(false);
|
||||||
const [selectedTokenIds, setSelectedTokenIds] = useState([]);
|
const [selectedTokenIds, setSelectedTokenIds] = useState<string[]>([]);
|
||||||
const selectedTokens = ownedTokens.filter((token) =>
|
const selectedTokens = ownedTokens.filter((token) =>
|
||||||
selectedTokenIds.includes(token.id)
|
selectedTokenIds.includes(token.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleTokenAdd(token) {
|
function handleTokenAdd(token: FileToken) {
|
||||||
addToken(token);
|
addToken(token);
|
||||||
setSelectedTokenIds([token.id]);
|
setSelectedTokenIds([token.id]);
|
||||||
}
|
}
|
||||||
@ -210,7 +219,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleTokensHide(hideInSidebar) {
|
async function handleTokensHide(hideInSidebar: boolean) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await updateTokens(selectedTokenIds, { hideInSidebar });
|
await updateTokens(selectedTokenIds, { hideInSidebar });
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -219,7 +228,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
// Either single, multiple or range
|
// Either single, multiple or range
|
||||||
const [selectMode, setSelectMode] = useState("single");
|
const [selectMode, setSelectMode] = useState("single");
|
||||||
|
|
||||||
async function handleTokenSelect(token) {
|
async function handleTokenSelect(token: Token) {
|
||||||
handleItemSelect(
|
handleItemSelect(
|
||||||
token,
|
token,
|
||||||
selectMode,
|
selectMode,
|
||||||
@ -233,7 +242,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
/**
|
/**
|
||||||
* Shortcuts
|
* Shortcuts
|
||||||
*/
|
*/
|
||||||
function handleKeyDown(event) {
|
function handleKeyDown(event: KeyboardEvent) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -257,7 +266,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp(event) {
|
function handleKeyUp(event: KeyboardEvent) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -280,11 +289,19 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
|
|
||||||
const layout = useResponsiveLayout();
|
const layout = useResponsiveLayout();
|
||||||
|
|
||||||
|
let tokenId;
|
||||||
|
if (selectedTokens.length === 1 && selectedTokens[0].id) {
|
||||||
|
tokenId = selectedTokens[0].id
|
||||||
|
} else {
|
||||||
|
// TODO: handle tokenId not found
|
||||||
|
tokenId = ""
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }}
|
style={{ content: { maxWidth: layout.modalSize, width: "calc(100% - 16px)" } }}
|
||||||
>
|
>
|
||||||
<ImageDrop onDrop={handleImagesUpload} dropText="Drop token to upload">
|
<ImageDrop onDrop={handleImagesUpload} dropText="Drop token to upload">
|
||||||
<input
|
<input
|
||||||
@ -328,17 +345,19 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</ImageDrop>
|
</ImageDrop>
|
||||||
|
<>
|
||||||
{(isLoading || tokensLoading) && <LoadingOverlay bg="overlay" />}
|
{(isLoading || tokensLoading) && <LoadingOverlay bg="overlay" />}
|
||||||
|
</>
|
||||||
<EditTokenModal
|
<EditTokenModal
|
||||||
isOpen={isEditModalOpen}
|
isOpen={isEditModalOpen}
|
||||||
onDone={() => setIsEditModalOpen(false)}
|
onDone={() => setIsEditModalOpen(false)}
|
||||||
tokenId={selectedTokens.length === 1 && selectedTokens[0].id}
|
tokenId={tokenId}
|
||||||
/>
|
/>
|
||||||
<EditGroupModal
|
<EditGroupModal
|
||||||
isOpen={isGroupModalOpen}
|
isOpen={isGroupModalOpen}
|
||||||
onChange={handleTokensGroup}
|
onChange={handleTokensGroup}
|
||||||
groups={tokenGroups.filter(
|
groups={tokenGroups.filter(
|
||||||
(group) => group !== "" && group !== "default"
|
(group: string) => group !== "" && group !== "default"
|
||||||
)}
|
)}
|
||||||
onRequestClose={() => setIsGroupModalOpen(false)}
|
onRequestClose={() => setIsGroupModalOpen(false)}
|
||||||
// Select the default group by testing whether all selected tokens are the same
|
// Select the default group by testing whether all selected tokens are the same
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Label,
|
Label,
|
||||||
Flex,
|
Flex,
|
||||||
@ -21,8 +21,9 @@ import useSetting from "../hooks/useSetting";
|
|||||||
|
|
||||||
import ConfirmModal from "./ConfirmModal";
|
import ConfirmModal from "./ConfirmModal";
|
||||||
import ImportExportModal from "./ImportExportModal";
|
import ImportExportModal from "./ImportExportModal";
|
||||||
|
import { MapState } from "../components/map/Map";
|
||||||
|
|
||||||
function SettingsModal({ isOpen, onRequestClose }) {
|
function SettingsModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: () => void }) {
|
||||||
const { database, databaseStatus } = useDatabase();
|
const { database, databaseStatus } = useDatabase();
|
||||||
const { userId } = useAuth();
|
const { userId } = useAuth();
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@ -32,7 +33,7 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
);
|
);
|
||||||
const [showFogGuides, setShowFogGuides] = useSetting("fog.showGuides");
|
const [showFogGuides, setShowFogGuides] = useSetting("fog.showGuides");
|
||||||
const [fogEditOpacity, setFogEditOpacity] = useSetting("fog.editOpacity");
|
const [fogEditOpacity, setFogEditOpacity] = useSetting("fog.editOpacity");
|
||||||
const [storageEstimate, setStorageEstimate] = useState();
|
const [storageEstimate, setStorageEstimate] = useState<StorageEstimate>();
|
||||||
const [isImportExportModalOpen, setIsImportExportModalOpen] = useState(false);
|
const [isImportExportModalOpen, setIsImportExportModalOpen] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
async function handleEraseAllData() {
|
async function handleEraseAllData() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
await database.delete();
|
await database?.delete();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +67,11 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
// Clear saved settings
|
// Clear saved settings
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
|
|
||||||
|
//TODO: handle id database is undefined
|
||||||
|
if (!database) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Clear map cache
|
// Clear map cache
|
||||||
await database.table("maps").where("owner").notEqual(userId).delete();
|
await database.table("maps").where("owner").notEqual(userId).delete();
|
||||||
// Find all other peoples tokens who aren't benig used in a map state and delete them
|
// Find all other peoples tokens who aren't benig used in a map state and delete them
|
||||||
@ -74,7 +80,7 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
.where("owner")
|
.where("owner")
|
||||||
.notEqual(userId)
|
.notEqual(userId)
|
||||||
.toArray();
|
.toArray();
|
||||||
const states = await database.table("states").toArray();
|
const states: MapState[] = await database?.table("states").toArray();
|
||||||
for (let token of tokens) {
|
for (let token of tokens) {
|
||||||
let inUse = false;
|
let inUse = false;
|
||||||
for (let state of states) {
|
for (let state of states) {
|
||||||
@ -126,7 +132,7 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
sx={{ width: "initial" }}
|
sx={{ width: "initial" }}
|
||||||
value={fogEditOpacity}
|
value={fogEditOpacity}
|
||||||
onChange={(e) => setFogEditOpacity(parseFloat(e.target.value))}
|
onChange={(e) => setFogEditOpacity(parseFloat(e.target.value))}
|
||||||
labelFunc={(value) => `${Math.round(value * 100)}%`}
|
labelFunc={(value: number) => `${Math.round(value * 100)}%`}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
<Label py={2}>
|
<Label py={2}>
|
||||||
@ -139,7 +145,7 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
sx={{ width: "initial" }}
|
sx={{ width: "initial" }}
|
||||||
value={labelSize}
|
value={labelSize}
|
||||||
onChange={(e) => setLabelSize(parseFloat(e.target.value))}
|
onChange={(e) => setLabelSize(parseFloat(e.target.value))}
|
||||||
labelFunc={(value) => `${value}x`}
|
labelFunc={(value: number) => `${value}x`}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
<Label py={2}>
|
<Label py={2}>
|
||||||
@ -154,7 +160,7 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setGridSnappingSensitivity(parseFloat(e.target.value))
|
setGridSnappingSensitivity(parseFloat(e.target.value))
|
||||||
}
|
}
|
||||||
labelFunc={(value) => `${value * 2}`}
|
labelFunc={(value: number) => `${value * 2}`}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
<Divider bg="text" />
|
<Divider bg="text" />
|
||||||
@ -185,13 +191,13 @@ function SettingsModal({ isOpen, onRequestClose }) {
|
|||||||
Import / Export Data
|
Import / Export Data
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
{storageEstimate && (
|
{storageEstimate !&& (
|
||||||
<Flex sx={{ justifyContent: "center" }}>
|
<Flex sx={{ justifyContent: "center" }}>
|
||||||
<Text variant="caption">
|
<Text variant="caption">
|
||||||
Storage Used: {prettyBytes(storageEstimate.usage)} of{" "}
|
Storage Used: {prettyBytes(storageEstimate.usage as number)} of{" "}
|
||||||
{prettyBytes(storageEstimate.quota)} (
|
{prettyBytes(storageEstimate.quota as number)} (
|
||||||
{Math.round(
|
{Math.round(
|
||||||
(storageEstimate.usage / Math.max(storageEstimate.quota, 1)) *
|
(storageEstimate.usage as number / Math.max(storageEstimate.quota as number, 1)) *
|
||||||
100
|
100
|
||||||
)}
|
)}
|
||||||
%)
|
%)
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef } from "react";
|
import { ChangeEvent, useRef } from "react";
|
||||||
import { Box, Label, Input, Button, Flex, Checkbox } from "theme-ui";
|
import { Box, Label, Input, Button, Flex, Checkbox } from "theme-ui";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
@ -9,20 +9,20 @@ import useSetting from "../hooks/useSetting";
|
|||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
function StartModal({ isOpen, onRequestClose }) {
|
function StartModal({ isOpen, onRequestClose }: { isOpen: boolean, onRequestClose: () => void}) {
|
||||||
let history = useHistory();
|
let history = useHistory();
|
||||||
const { password, setPassword } = useAuth();
|
const { password, setPassword } = useAuth();
|
||||||
|
|
||||||
function handlePasswordChange(event) {
|
function handlePasswordChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setPassword(event.target.value);
|
setPassword(event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [usePassword, setUsePassword] = useSetting("game.usePassword");
|
const [usePassword, setUsePassword] = useSetting("game.usePassword");
|
||||||
function handleUsePasswordChange(event) {
|
function handleUsePasswordChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
setUsePassword(event.target.checked);
|
setUsePassword(event.target.checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit(event) {
|
function handleSubmit(event: ChangeEvent<HTMLInputElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!usePassword) {
|
if (!usePassword) {
|
||||||
setPassword("");
|
setPassword("");
|
||||||
@ -30,7 +30,7 @@ function StartModal({ isOpen, onRequestClose }) {
|
|||||||
history.push(`/game/${shortid.generate()}`);
|
history.push(`/game/${shortid.generate()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputRef = useRef();
|
const inputRef = useRef<any>();
|
||||||
function focusInput() {
|
function focusInput() {
|
||||||
inputRef.current && inputRef.current.focus();
|
inputRef.current && inputRef.current.focus();
|
||||||
}
|
}
|
@ -1,8 +1,19 @@
|
|||||||
import React from "react";
|
|
||||||
import { Box, Text, Button, Label, Flex } from "theme-ui";
|
import { Box, Text, Button, Label, Flex } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
|
type StartStreamProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
isSupported: boolean,
|
||||||
|
unavailableMessage: string,
|
||||||
|
stream: MediaStream,
|
||||||
|
noAudioTrack: boolean,
|
||||||
|
noAudioMessage: string,
|
||||||
|
onStreamStart: any,
|
||||||
|
onStreamEnd: any,
|
||||||
|
}
|
||||||
|
|
||||||
function StartStreamModal({
|
function StartStreamModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
@ -13,7 +24,7 @@ function StartStreamModal({
|
|||||||
noAudioMessage,
|
noAudioMessage,
|
||||||
onStreamStart,
|
onStreamStart,
|
||||||
onStreamEnd,
|
onStreamEnd,
|
||||||
}) {
|
}: StartStreamProps) {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onRequestClose={onRequestClose}>
|
<Modal isOpen={isOpen} onRequestClose={onRequestClose}>
|
||||||
<Box>
|
<Box>
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef } from "react";
|
import { ChangeEvent, useRef } from "react";
|
||||||
import { Box, Label, Input, Button, Flex, Text } from "theme-ui";
|
import { Box, Label, Input, Button, Flex, Text } from "theme-ui";
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
@ -7,14 +7,22 @@ import { getHMSDuration, getDurationHMS } from "../helpers/timer";
|
|||||||
|
|
||||||
import useSetting from "../hooks/useSetting";
|
import useSetting from "../hooks/useSetting";
|
||||||
|
|
||||||
|
type StartTimerProps = {
|
||||||
|
isOpen: boolean,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
onTimerStart: any,
|
||||||
|
onTimerStop: any,
|
||||||
|
timer: any,
|
||||||
|
}
|
||||||
|
|
||||||
function StartTimerModal({
|
function StartTimerModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
onTimerStart,
|
onTimerStart,
|
||||||
onTimerStop,
|
onTimerStop,
|
||||||
timer,
|
timer,
|
||||||
}) {
|
}: StartTimerProps) {
|
||||||
const inputRef = useRef();
|
const inputRef = useRef<any>();
|
||||||
function focusInput() {
|
function focusInput() {
|
||||||
inputRef.current && inputRef.current.focus();
|
inputRef.current && inputRef.current.focus();
|
||||||
}
|
}
|
||||||
@ -23,7 +31,7 @@ function StartTimerModal({
|
|||||||
const [minute, setMinute] = useSetting("timer.minute");
|
const [minute, setMinute] = useSetting("timer.minute");
|
||||||
const [second, setSecond] = useSetting("timer.second");
|
const [second, setSecond] = useSetting("timer.second");
|
||||||
|
|
||||||
function handleSubmit(event) {
|
function handleSubmit(event: ChangeEvent<HTMLInputElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (timer) {
|
if (timer) {
|
||||||
onTimerStop();
|
onTimerStop();
|
||||||
@ -44,7 +52,7 @@ function StartTimerModal({
|
|||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseValue(value, max) {
|
function parseValue(value: string, max: number) {
|
||||||
const num = parseInt(value);
|
const num = parseInt(value);
|
||||||
if (isNaN(num)) {
|
if (isNaN(num)) {
|
||||||
return 0;
|
return 0;
|
@ -32,6 +32,7 @@ import undead from "./Undead.png";
|
|||||||
import warlock from "./Warlock.png";
|
import warlock from "./Warlock.png";
|
||||||
import wizard from "./Wizard.png";
|
import wizard from "./Wizard.png";
|
||||||
import unknown from "./Unknown.png";
|
import unknown from "./Unknown.png";
|
||||||
|
import { ImageFile } from "../helpers/image";
|
||||||
|
|
||||||
export const tokenSources = {
|
export const tokenSources = {
|
||||||
barbarian,
|
barbarian,
|
||||||
@ -80,7 +81,40 @@ function getDefaultTokenSize(key: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tokens = Object.keys(tokenSources).map((key) => ({
|
type TokenCategory = "character" | "vehicle" | "prop"
|
||||||
|
|
||||||
|
export type Token = {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
defaultSize: number,
|
||||||
|
category: TokenCategory,
|
||||||
|
hideInSidebar: boolean,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
owner: string,
|
||||||
|
type: string,
|
||||||
|
group: string | undefined,
|
||||||
|
created: number,
|
||||||
|
lastModified: number,
|
||||||
|
lastUsed: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DefaultToken extends Omit<Token, "id" | "owner" | "created" | "lastModified" | "lastUsed"> {
|
||||||
|
id?: string,
|
||||||
|
owner?: string,
|
||||||
|
created?: number,
|
||||||
|
lastModified?: number,
|
||||||
|
lastUsed?: number,
|
||||||
|
key: string,
|
||||||
|
type: "default",
|
||||||
|
group: "default",
|
||||||
|
}
|
||||||
|
export interface FileToken extends Token {
|
||||||
|
file: Uint8Array,
|
||||||
|
thumbnail: ImageFile,
|
||||||
|
type: "file",
|
||||||
|
}
|
||||||
|
export const tokens: DefaultToken[] = Object.keys(tokenSources).map((key) => ({
|
||||||
key,
|
key,
|
||||||
name: Case.capital(key),
|
name: Case.capital(key),
|
||||||
type: "default",
|
type: "default",
|
||||||
@ -89,6 +123,7 @@ export const tokens = Object.keys(tokenSources).map((key) => ({
|
|||||||
hideInSidebar: false,
|
hideInSidebar: false,
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
|
group: "default",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const unknownSource = unknown;
|
export const unknownSource = unknown;
|
||||||
|
Loading…
Reference in New Issue
Block a user