Convert image drop to typescript
This commit is contained in:
parent
f6d695a48a
commit
72b6994a2e
@ -20,9 +20,23 @@ import { useTokenData } from "../../contexts/TokenDataContext";
|
||||
import { useAssets } from "../../contexts/AssetsContext";
|
||||
import { useMapStage } from "../../contexts/MapStageContext";
|
||||
|
||||
import useImageDrop from "../../hooks/useImageDrop";
|
||||
import useImageDrop, { ImageDropEvent } from "../../hooks/useImageDrop";
|
||||
|
||||
function GlobalImageDrop({ children, onMapChange, onMapTokensStateCreate }) {
|
||||
import { Map } from "../../types/Map";
|
||||
import { MapState } from "../../types/MapState";
|
||||
import { TokenState } from "../../types/TokenState";
|
||||
|
||||
type GlobalImageDropProps = {
|
||||
children?: React.ReactNode;
|
||||
onMapChange: (map: Map, mapState: MapState) => void;
|
||||
onMapTokensStateCreate: (states: TokenState[]) => void;
|
||||
};
|
||||
|
||||
function GlobalImageDrop({
|
||||
children,
|
||||
onMapChange,
|
||||
onMapTokensStateCreate,
|
||||
}: GlobalImageDropProps) {
|
||||
const { addToast } = useToasts();
|
||||
|
||||
const userId = useUserId();
|
||||
@ -32,17 +46,15 @@ function GlobalImageDrop({ children, onMapChange, onMapTokensStateCreate }) {
|
||||
|
||||
const mapStageRef = useMapStage();
|
||||
|
||||
const [isLargeImageWarningModalOpen, setShowLargeImageWarning] = 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");
|
||||
const droppedImagesRef = useRef<File[]>();
|
||||
const dropPositionRef = useRef<Vector2>();
|
||||
const [droppingType, setDroppingType] = useState<"maps" | "tokens">("maps");
|
||||
|
||||
async function handleDrop(files, dropPosition) {
|
||||
async function handleDrop({ files, dropPosition }: ImageDropEvent) {
|
||||
if (navigator.storage) {
|
||||
// Attempt to enable persistant storage
|
||||
await navigator.storage.persist();
|
||||
@ -87,55 +99,63 @@ function GlobalImageDrop({ children, onMapChange, onMapTokensStateCreate }) {
|
||||
}
|
||||
|
||||
async function handleMaps() {
|
||||
setIsLoading(true);
|
||||
let maps = [];
|
||||
for (let file of droppedImagesRef.current) {
|
||||
const { map, assets } = await createMapFromFile(file, userId);
|
||||
await addMap(map);
|
||||
await addAssets(assets);
|
||||
maps.push(map);
|
||||
}
|
||||
if (droppedImagesRef.current && userId) {
|
||||
setIsLoading(true);
|
||||
let maps = [];
|
||||
for (let file of droppedImagesRef.current) {
|
||||
const { map, assets } = await createMapFromFile(file, userId);
|
||||
await addMap(map);
|
||||
await addAssets(assets);
|
||||
maps.push(map);
|
||||
}
|
||||
|
||||
// Change map if only 1 dropped
|
||||
if (maps.length === 1) {
|
||||
const mapState = await getMapState(maps[0].id);
|
||||
onMapChange(maps[0], mapState);
|
||||
}
|
||||
// Change map if only 1 dropped
|
||||
if (maps.length === 1) {
|
||||
const mapState = await getMapState(maps[0].id);
|
||||
onMapChange(maps[0], mapState);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
droppedImagesRef.current = undefined;
|
||||
setIsLoading(false);
|
||||
droppedImagesRef.current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTokens() {
|
||||
setIsLoading(true);
|
||||
// Keep track of tokens so we can add them to the map
|
||||
let tokens = [];
|
||||
for (let file of droppedImagesRef.current) {
|
||||
const { token, assets } = await createTokenFromFile(file, userId);
|
||||
await addToken(token);
|
||||
await addAssets(assets);
|
||||
tokens.push(token);
|
||||
}
|
||||
setIsLoading(false);
|
||||
droppedImagesRef.current = undefined;
|
||||
if (droppedImagesRef.current && userId) {
|
||||
setIsLoading(true);
|
||||
// Keep track of tokens so we can add them to the map
|
||||
let tokens = [];
|
||||
for (let file of droppedImagesRef.current) {
|
||||
const { token, assets } = await createTokenFromFile(file, userId);
|
||||
await addToken(token);
|
||||
await addAssets(assets);
|
||||
tokens.push(token);
|
||||
}
|
||||
setIsLoading(false);
|
||||
droppedImagesRef.current = undefined;
|
||||
|
||||
const dropPosition = dropPositionRef.current;
|
||||
const mapStage = mapStageRef.current;
|
||||
if (mapStage && dropPosition) {
|
||||
const mapPosition = clientPositionToMapPosition(mapStage, dropPosition);
|
||||
if (mapPosition) {
|
||||
let tokenStates = [];
|
||||
let offset = new Vector2(0, 0);
|
||||
for (let token of tokens) {
|
||||
if (token) {
|
||||
tokenStates.push(
|
||||
createTokenState(token, Vector2.add(mapPosition, offset), userId)
|
||||
);
|
||||
offset = Vector2.add(offset, 0.01);
|
||||
const dropPosition = dropPositionRef.current;
|
||||
const mapStage = mapStageRef.current;
|
||||
if (mapStage && dropPosition) {
|
||||
const mapPosition = clientPositionToMapPosition(mapStage, dropPosition);
|
||||
if (mapPosition) {
|
||||
let tokenStates = [];
|
||||
let offset = new Vector2(0, 0);
|
||||
for (let token of tokens) {
|
||||
if (token) {
|
||||
tokenStates.push(
|
||||
createTokenState(
|
||||
token,
|
||||
Vector2.add(mapPosition, offset),
|
||||
userId
|
||||
)
|
||||
);
|
||||
offset = Vector2.add(offset, 0.01);
|
||||
}
|
||||
}
|
||||
if (tokenStates.length > 0) {
|
||||
onMapTokensStateCreate(tokenStates);
|
||||
}
|
||||
}
|
||||
if (tokenStates.length > 0) {
|
||||
onMapTokensStateCreate(tokenStates);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,9 +169,8 @@ function GlobalImageDrop({ children, onMapChange, onMapTokensStateCreate }) {
|
||||
setDroppingType("tokens");
|
||||
}
|
||||
|
||||
const { dragging, containerListeners, overlayListeners } = useImageDrop(
|
||||
handleDrop
|
||||
);
|
||||
const { dragging, containerListeners, overlayListeners } =
|
||||
useImageDrop(handleDrop);
|
||||
|
||||
return (
|
||||
<Flex sx={{ height: "100%", flexGrow: 1 }} {...containerListeners}>
|
@ -1,12 +1,17 @@
|
||||
import React from "react";
|
||||
import { Box, Flex, Text } from "theme-ui";
|
||||
|
||||
import useImageDrop from "../../hooks/useImageDrop";
|
||||
import useImageDrop, { ImageDropEvent } from "../../hooks/useImageDrop";
|
||||
|
||||
function ImageDrop({ onDrop, dropText, children }) {
|
||||
const { dragging, containerListeners, overlayListeners } = useImageDrop(
|
||||
onDrop
|
||||
);
|
||||
type ImageDropProps = {
|
||||
onDrop: (event: ImageDropEvent) => void;
|
||||
dropText: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
function ImageDrop({ onDrop, dropText, children }: ImageDropProps) {
|
||||
const { dragging, containerListeners, overlayListeners } =
|
||||
useImageDrop(onDrop);
|
||||
return (
|
||||
<Box {...containerListeners}>
|
||||
{children}
|
@ -3,41 +3,51 @@ import { useToasts } from "react-toast-notifications";
|
||||
|
||||
import Vector2 from "../helpers/Vector2";
|
||||
|
||||
export type ImageDropEvent = {
|
||||
files: File[];
|
||||
dropPosition: Vector2;
|
||||
};
|
||||
|
||||
function useImageDrop(
|
||||
onImageDrop,
|
||||
onImageDrop: (event: ImageDropEvent) => void,
|
||||
supportFileTypes = ["image/jpeg", "image/gif", "image/png", "image/webp"]
|
||||
) {
|
||||
const { addToast } = useToasts();
|
||||
|
||||
const [dragging, setDragging] = useState(false);
|
||||
function onDragEnter(event) {
|
||||
function onDragEnter(event: React.DragEvent<HTMLDivElement>) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setDragging(true);
|
||||
}
|
||||
|
||||
function onDragLeave(event) {
|
||||
function onDragLeave(event: React.DragEvent<HTMLDivElement>) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setDragging(false);
|
||||
}
|
||||
|
||||
function onDragOver(event) {
|
||||
function onDragOver(event: React.DragEvent<HTMLDivElement>) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
}
|
||||
|
||||
async function onDrop(event) {
|
||||
async function onDrop(event: React.DragEvent<HTMLDivElement>) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
let imageFiles = [];
|
||||
|
||||
// Check if the dropped image is from a URL
|
||||
const html = event.dataTransfer.getData("text/html");
|
||||
const html = event.dataTransfer?.getData("text/html");
|
||||
if (html) {
|
||||
try {
|
||||
const urlMatch = html.match(/src="?([^"\s]+)"?\s*/);
|
||||
if (!urlMatch) {
|
||||
throw new Error("Unable to find image source");
|
||||
}
|
||||
const url = urlMatch[1].replace("&", "&"); // Reverse html encoding of url parameters
|
||||
let name = "";
|
||||
const altMatch = html.match(/alt="?([^"]+)"?\s*/);
|
||||
@ -46,8 +56,7 @@ function useImageDrop(
|
||||
}
|
||||
const response = await fetch(url);
|
||||
if (response.ok) {
|
||||
const file = await response.blob();
|
||||
file.name = name;
|
||||
const file = new File([await response.blob()], name);
|
||||
if (supportFileTypes.includes(file.type)) {
|
||||
imageFiles.push(file);
|
||||
} else {
|
||||
@ -63,7 +72,7 @@ function useImageDrop(
|
||||
}
|
||||
}
|
||||
|
||||
const files = event.dataTransfer.files;
|
||||
const files = event.dataTransfer?.files || [];
|
||||
for (let file of files) {
|
||||
if (supportFileTypes.includes(file.type)) {
|
||||
imageFiles.push(file);
|
||||
@ -72,7 +81,7 @@ function useImageDrop(
|
||||
}
|
||||
}
|
||||
const dropPosition = new Vector2(event.clientX, event.clientY);
|
||||
onImageDrop(imageFiles, dropPosition);
|
||||
onImageDrop({ files: imageFiles, dropPosition });
|
||||
setDragging(false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user