From eb9afcc66a2291a7c7b2538d0a4cbf257690e7b8 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Mon, 7 Jun 2021 21:08:14 +1000 Subject: [PATCH] Refactor image drop and to a hook and move global image drop --- .../{file => image}/GlobalImageDrop.js | 102 +++++++++++++----- src/components/image/ImageDrop.js | 37 +++++++ .../ImageDrop.js => hooks/useImageDrop.js} | 62 ++++------- src/modals/ImageTypeModal.js | 34 ------ src/modals/SelectMapModal.js | 2 +- src/modals/SelectTokensModal.js | 2 +- src/network/NetworkedMapAndTokens.js | 6 +- src/routes/Game.js | 23 ++-- 8 files changed, 153 insertions(+), 115 deletions(-) rename src/components/{file => image}/GlobalImageDrop.js (56%) create mode 100644 src/components/image/ImageDrop.js rename src/{components/file/ImageDrop.js => hooks/useImageDrop.js} (58%) delete mode 100644 src/modals/ImageTypeModal.js diff --git a/src/components/file/GlobalImageDrop.js b/src/components/image/GlobalImageDrop.js similarity index 56% rename from src/components/file/GlobalImageDrop.js rename to src/components/image/GlobalImageDrop.js index a03578b..5287b72 100644 --- a/src/components/file/GlobalImageDrop.js +++ b/src/components/image/GlobalImageDrop.js @@ -1,12 +1,9 @@ import React, { useState, useRef } from "react"; -import { Box } from "theme-ui"; +import { Flex, Text } 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"; @@ -17,7 +14,9 @@ import { useMapData } from "../../contexts/MapDataContext"; import { useTokenData } from "../../contexts/TokenDataContext"; import { useAssets } from "../../contexts/AssetsContext"; -function GlobalImageDrop({ children }) { +import useImageDrop from "../../hooks/useImageDrop"; + +function GlobalImageDrop({ children, onMapTokensStateCreate }) { const { addToast } = useToasts(); const { userId } = useAuth(); @@ -25,20 +24,24 @@ function GlobalImageDrop({ children }) { const { addToken } = useTokenData(); const { addAssets } = useAssets(); - const [isImageTypeModalOpen, setIsImageTypeModalOpen] = useState(false); const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = useState( false ); const [isLoading, setIsLoading] = useState(false); const droppedImagesRef = useRef(); + const dropPositionRef = useRef(); + // maps or tokens + const [droppingType, setDroppingType] = useState("maps"); - async function handleDrop(files) { + async function handleDrop(files, dropPosition) { if (navigator.storage) { // Attempt to enable persistant storage await navigator.storage.persist(); } + dropPositionRef.current = dropPosition; + droppedImagesRef.current = []; for (let file of files) { if (file.size > 5e7) { @@ -54,7 +57,11 @@ function GlobalImageDrop({ children }) { return; } - setIsImageTypeModalOpen(true); + if (droppingType === "maps") { + await handleMaps(); + } else { + await handleTokens(); + } } function handleLargeImageWarningCancel() { @@ -64,11 +71,14 @@ function GlobalImageDrop({ children }) { async function handleLargeImageWarningConfirm() { setShowLargeImageWarning(false); - setIsImageTypeModalOpen(true); + if (droppingType === "maps") { + await handleMaps(); + } else { + await handleTokens(); + } } async function handleMaps() { - setIsImageTypeModalOpen(false); setIsLoading(true); for (let file of droppedImagesRef.current) { const { map, assets } = await createMapFromFile(file, userId); @@ -80,7 +90,6 @@ function GlobalImageDrop({ children }) { } async function handleTokens() { - setIsImageTypeModalOpen(false); setIsLoading(true); for (let file of droppedImagesRef.current) { const { token, assets } = await createTokenFromFile(file, userId); @@ -91,21 +100,66 @@ function GlobalImageDrop({ children }) { droppedImagesRef.current = undefined; } - function handleImageTypeClose() { - droppedImagesRef.current = undefined; - setIsImageTypeModalOpen(false); + function handleMapsOver() { + setDroppingType("maps"); } + function handleTokensOver() { + setDroppingType("tokens"); + } + + const { dragging, containerListeners, overlayListeners } = useImageDrop( + handleDrop + ); + return ( - - {children} - 1} - /> + + {children} + {dragging && ( + + + + {"Drop image to import as a map"} + + + + + {"Drop image to import as a token"} + + + + )} {isLoading && } - + ); } diff --git a/src/components/image/ImageDrop.js b/src/components/image/ImageDrop.js new file mode 100644 index 0000000..f93aa97 --- /dev/null +++ b/src/components/image/ImageDrop.js @@ -0,0 +1,37 @@ +import React from "react"; +import { Box, Flex, Text } from "theme-ui"; + +import useImageDrop from "../../hooks/useImageDrop"; + +function ImageDrop({ onDrop, dropText, children }) { + const { dragging, containerListeners, overlayListeners } = useImageDrop( + onDrop + ); + return ( + + {children} + {dragging && ( + + + {dropText || "Drop image to import"} + + + )} + + ); +} + +export default ImageDrop; diff --git a/src/components/file/ImageDrop.js b/src/hooks/useImageDrop.js similarity index 58% rename from src/components/file/ImageDrop.js rename to src/hooks/useImageDrop.js index 85bf8af..bf38044 100644 --- a/src/components/file/ImageDrop.js +++ b/src/hooks/useImageDrop.js @@ -1,26 +1,34 @@ -import React, { useState } from "react"; -import { Box, Flex, Text } from "theme-ui"; +import { useState } from "react"; import { useToasts } from "react-toast-notifications"; -const supportFileTypes = ["image/jpeg", "image/gif", "image/png", "image/webp"]; +import Vector2 from "../helpers/Vector2"; -function ImageDrop({ onDrop, dropText, children }) { +function useImageDrop( + onImageDrop, + supportFileTypes = ["image/jpeg", "image/gif", "image/png", "image/webp"] +) { const { addToast } = useToasts(); const [dragging, setDragging] = useState(false); - function handleImageDragEnter(event) { + function onDragEnter(event) { event.preventDefault(); event.stopPropagation(); setDragging(true); } - function handleImageDragLeave(event) { + function onDragLeave(event) { event.preventDefault(); event.stopPropagation(); setDragging(false); } - async function handleImageDrop(event) { + function onDragOver(event) { + event.preventDefault(); + event.stopPropagation(); + event.dataTransfer.dropEffect = "copy"; + } + + async function onDrop(event) { event.preventDefault(); event.stopPropagation(); let imageFiles = []; @@ -63,41 +71,15 @@ function ImageDrop({ onDrop, dropText, children }) { addToast(`Unsupported file type for ${file.name}`); } } - onDrop(imageFiles); + const dropPosition = new Vector2(event.clientX, event.clientY); + onImageDrop(imageFiles, dropPosition); setDragging(false); } - return ( - - {children} - {dragging && ( - { - e.preventDefault(); - e.stopPropagation(); - e.dataTransfer.dropEffect = "copy"; - }} - onDrop={handleImageDrop} - > - - {dropText || "Drop image to import"} - - - )} - - ); + const containerListeners = { onDragEnter }; + const overlayListeners = { onDragLeave, onDragOver, onDrop }; + + return { dragging, containerListeners, overlayListeners }; } -export default ImageDrop; +export default useImageDrop; diff --git a/src/modals/ImageTypeModal.js b/src/modals/ImageTypeModal.js deleted file mode 100644 index 7726494..0000000 --- a/src/modals/ImageTypeModal.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import { Box, Label, Flex, Button } from "theme-ui"; - -import Modal from "../components/Modal"; - -function ImageTypeModal({ - isOpen, - onRequestClose, - multiple, - onTokens, - onMaps, -}) { - return ( - - - - - - - - - - ); -} - -export default ImageTypeModal; diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index 32a6532..dab60d0 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -9,7 +9,7 @@ import ConfirmModal from "./ConfirmModal"; import Modal from "../components/Modal"; import LoadingOverlay from "../components/LoadingOverlay"; -import ImageDrop from "../components/file/ImageDrop"; +import ImageDrop from "../components/image/ImageDrop"; import MapTiles from "../components/map/MapTiles"; import MapEditBar from "../components/map/MapEditBar"; diff --git a/src/modals/SelectTokensModal.js b/src/modals/SelectTokensModal.js index fb0a90b..b81d3eb 100644 --- a/src/modals/SelectTokensModal.js +++ b/src/modals/SelectTokensModal.js @@ -9,7 +9,7 @@ import ConfirmModal from "./ConfirmModal"; import Modal from "../components/Modal"; import LoadingOverlay from "../components/LoadingOverlay"; -import ImageDrop from "../components/file/ImageDrop"; +import ImageDrop from "../components/image/ImageDrop"; import TokenTiles from "../components/token/TokenTiles"; import TokenEditBar from "../components/token/TokenEditBar"; diff --git a/src/network/NetworkedMapAndTokens.js b/src/network/NetworkedMapAndTokens.js index 6c258d1..9f587aa 100644 --- a/src/network/NetworkedMapAndTokens.js +++ b/src/network/NetworkedMapAndTokens.js @@ -20,6 +20,8 @@ import Session from "./Session"; import Map from "../components/map/Map"; import TokenBar from "../components/token/TokenBar"; +import GlobalImageDrop from "../components/image/GlobalImageDrop"; + const defaultMapActions = { mapDrawActions: [], mapDrawActionIndex: -1, @@ -457,7 +459,7 @@ function NetworkedMapAndTokens({ session }) { } return ( - <> + - + ); } diff --git a/src/routes/Game.js b/src/routes/Game.js index 113ffae..2e06da5 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -8,7 +8,6 @@ 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"; @@ -115,18 +114,16 @@ function Game() { - - - - - - + + + + setPeerError(null)}