From 98f7744c1feffc19fef07c6c9eba17ccfb748a2b Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 6 Jun 2021 21:04:46 +1000 Subject: [PATCH] Add a global image drop for dropping directly to the map screen --- src/components/Modal.js | 12 +++ src/components/file/GlobalImageDrop.js | 122 +++++++++++++++++++++++++ src/components/{ => file}/ImageDrop.js | 2 +- src/modals/ImageTypeModal.js | 34 +++++++ src/modals/SelectMapModal.js | 5 +- src/modals/SelectTokensModal.js | 5 +- src/routes/Game.js | 5 +- 7 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 src/components/file/GlobalImageDrop.js rename src/components/{ => file}/ImageDrop.js (97%) create mode 100644 src/modals/ImageTypeModal.js diff --git a/src/components/Modal.js b/src/components/Modal.js index ae2fdcf..43db923 100644 --- a/src/components/Modal.js +++ b/src/components/Modal.js @@ -44,6 +44,18 @@ function StyledModal({ {content} )} + overlayElement={(props, content) => ( +
{ + // Prevent drag event from triggering with a modal open + e.preventDefault(); + e.stopPropagation(); + }} + {...props} + > + {content} +
+ )} {...props} > {children} diff --git a/src/components/file/GlobalImageDrop.js b/src/components/file/GlobalImageDrop.js new file mode 100644 index 0000000..a03578b --- /dev/null +++ b/src/components/file/GlobalImageDrop.js @@ -0,0 +1,122 @@ +import React, { useState, useRef } from "react"; +import { Box } from "theme-ui"; +import { useToasts } from "react-toast-notifications"; + +import ImageDrop from "./ImageDrop"; + +import LoadingOverlay from "../LoadingOverlay"; + +import ImageTypeModal from "../../modals/ImageTypeModal"; +import ConfirmModal from "../../modals/ConfirmModal"; + +import { createMapFromFile } from "../../helpers/map"; +import { createTokenFromFile } from "../../helpers/token"; + +import { useAuth } from "../../contexts/AuthContext"; +import { useMapData } from "../../contexts/MapDataContext"; +import { useTokenData } from "../../contexts/TokenDataContext"; +import { useAssets } from "../../contexts/AssetsContext"; + +function GlobalImageDrop({ children }) { + const { addToast } = useToasts(); + + const { userId } = useAuth(); + const { addMap } = useMapData(); + const { addToken } = useTokenData(); + const { addAssets } = useAssets(); + + const [isImageTypeModalOpen, setIsImageTypeModalOpen] = useState(false); + const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = useState( + false + ); + const [isLoading, setIsLoading] = useState(false); + + const droppedImagesRef = useRef(); + + async function handleDrop(files) { + if (navigator.storage) { + // Attempt to enable persistant storage + await navigator.storage.persist(); + } + + droppedImagesRef.current = []; + for (let file of files) { + if (file.size > 5e7) { + addToast(`Unable to import image ${file.name} as it is over 50MB`); + } else { + droppedImagesRef.current.push(file); + } + } + + // Any file greater than 20MB + if (droppedImagesRef.current.some((file) => file.size > 2e7)) { + setShowLargeImageWarning(true); + return; + } + + setIsImageTypeModalOpen(true); + } + + function handleLargeImageWarningCancel() { + droppedImagesRef.current = undefined; + setShowLargeImageWarning(false); + } + + async function handleLargeImageWarningConfirm() { + setShowLargeImageWarning(false); + setIsImageTypeModalOpen(true); + } + + async function handleMaps() { + setIsImageTypeModalOpen(false); + setIsLoading(true); + for (let file of droppedImagesRef.current) { + const { map, assets } = await createMapFromFile(file, userId); + await addMap(map); + await addAssets(assets); + } + setIsLoading(false); + droppedImagesRef.current = undefined; + } + + async function handleTokens() { + setIsImageTypeModalOpen(false); + setIsLoading(true); + for (let file of droppedImagesRef.current) { + const { token, assets } = await createTokenFromFile(file, userId); + await addToken(token); + await addAssets(assets); + } + setIsLoading(false); + droppedImagesRef.current = undefined; + } + + function handleImageTypeClose() { + droppedImagesRef.current = undefined; + setIsImageTypeModalOpen(false); + } + + return ( + + {children} + 1} + /> + + {isLoading && } + + ); +} + +export default GlobalImageDrop; diff --git a/src/components/ImageDrop.js b/src/components/file/ImageDrop.js similarity index 97% rename from src/components/ImageDrop.js rename to src/components/file/ImageDrop.js index 7ee9c74..85bf8af 100644 --- a/src/components/ImageDrop.js +++ b/src/components/file/ImageDrop.js @@ -68,7 +68,7 @@ function ImageDrop({ onDrop, dropText, children }) { } return ( - + {children} {dragging && ( + + + + + + + + + ); +} + +export default ImageTypeModal; diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index 468444a..32a6532 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -7,9 +7,10 @@ import EditMapModal from "./EditMapModal"; import ConfirmModal from "./ConfirmModal"; import Modal from "../components/Modal"; -import ImageDrop from "../components/ImageDrop"; import LoadingOverlay from "../components/LoadingOverlay"; +import ImageDrop from "../components/file/ImageDrop"; + import MapTiles from "../components/map/MapTiles"; import MapEditBar from "../components/map/MapEditBar"; import SelectMapSelectButton from "../components/map/SelectMapSelectButton"; @@ -198,7 +199,7 @@ function SelectMapModal({ onRequestClose={handleClose} style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }} > - + handleImagesUpload(event.target.files)} type="file" diff --git a/src/modals/SelectTokensModal.js b/src/modals/SelectTokensModal.js index df27357..fb0a90b 100644 --- a/src/modals/SelectTokensModal.js +++ b/src/modals/SelectTokensModal.js @@ -7,9 +7,10 @@ import EditTokenModal from "./EditTokenModal"; import ConfirmModal from "./ConfirmModal"; import Modal from "../components/Modal"; -import ImageDrop from "../components/ImageDrop"; import LoadingOverlay from "../components/LoadingOverlay"; +import ImageDrop from "../components/file/ImageDrop"; + import TokenTiles from "../components/token/TokenTiles"; import TokenEditBar from "../components/token/TokenEditBar"; @@ -199,7 +200,7 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) { onRequestClose={onRequestClose} style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }} > - + handleImagesUpload(event.target.files)} type="file" diff --git a/src/routes/Game.js b/src/routes/Game.js index aa324c2..113ffae 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -8,6 +8,7 @@ import OfflineBanner from "../components/banner/OfflineBanner"; import LoadingOverlay from "../components/LoadingOverlay"; import Link from "../components/Link"; import MapLoadingOverlay from "../components/map/MapLoadingOverlay"; +import GlobalImageDrop from "../components/file/GlobalImageDrop"; import AuthModal from "../modals/AuthModal"; import GameExpiredModal from "../modals/GameExpiredModal"; @@ -114,7 +115,7 @@ function Game() { - + - + setPeerError(null)}