grungnet/src/hooks/useImageDrop.ts

100 lines
2.9 KiB
TypeScript
Raw Normal View History

import { useState } from "react";
import { useToasts } from "react-toast-notifications";
import Vector2 from "../helpers/Vector2";
2021-07-09 07:19:00 +00:00
export type ImageDropEvent = {
files: File[];
dropPosition: Vector2;
};
function useImageDrop(
2021-07-09 07:19:00 +00:00
onImageDrop: (event: ImageDropEvent) => void,
supportFileTypes = ["image/jpeg", "image/gif", "image/png", "image/webp"]
) {
const { addToast } = useToasts();
const [dragging, setDragging] = useState(false);
2021-07-09 07:19:00 +00:00
function onDragEnter(event: React.DragEvent<HTMLDivElement>) {
event.preventDefault();
event.stopPropagation();
setDragging(true);
}
2021-07-09 07:19:00 +00:00
function onDragLeave(event: React.DragEvent<HTMLDivElement>) {
event.preventDefault();
event.stopPropagation();
setDragging(false);
}
2021-07-09 07:19:00 +00:00
function onDragOver(event: React.DragEvent<HTMLDivElement>) {
event.preventDefault();
event.stopPropagation();
2021-07-09 07:19:00 +00:00
if (event.dataTransfer) {
event.dataTransfer.dropEffect = "copy";
}
}
2021-07-09 07:19:00 +00:00
async function onDrop(event: React.DragEvent<HTMLDivElement>) {
event.preventDefault();
event.stopPropagation();
let imageFiles = [];
2020-12-11 05:36:27 +00:00
// Check if the dropped image is from a URL
2021-07-09 07:19:00 +00:00
const html = event.dataTransfer?.getData("text/html");
2020-12-11 05:36:27 +00:00
if (html) {
try {
const urlMatch = html.match(/src="?([^"\s]+)"?\s*/);
2021-07-09 07:19:00 +00:00
if (!urlMatch) {
throw new Error("Unable to find image source");
}
2021-01-19 07:40:22 +00:00
const url = urlMatch[1].replace("&amp;", "&"); // Reverse html encoding of url parameters
2020-12-11 05:36:27 +00:00
let name = "";
const altMatch = html.match(/alt="?([^"]+)"?\s*/);
if (altMatch && altMatch.length > 1) {
name = altMatch[1];
}
const response = await fetch(url);
if (response.ok) {
const blob = await response.blob();
const file = new File([blob], name, {
type: blob.type,
});
if (supportFileTypes.includes(file.type)) {
imageFiles.push(file);
} else {
addToast(`Unsupported file type for ${file.name}`);
}
2020-12-11 05:36:27 +00:00
}
} catch (e) {
2022-04-01 03:56:55 +00:00
if (e instanceof Error) {
if (e.message === "Failed to fetch") {
addToast("Unable to import image: failed to fetch");
} else {
addToast("Unable to import image");
}
}
}
2020-12-11 05:36:27 +00:00
}
2021-07-09 07:19:00 +00:00
const files = event.dataTransfer?.files || [];
for (let file of files) {
if (supportFileTypes.includes(file.type)) {
imageFiles.push(file);
} else {
addToast(`Unsupported file type for ${file.name}`);
}
}
const dropPosition = new Vector2(event.clientX, event.clientY);
2021-07-09 07:19:00 +00:00
onImageDrop({ files: imageFiles, dropPosition });
setDragging(false);
}
const containerListeners = { onDragEnter };
const overlayListeners = { onDragLeave, onDragOver, onDrop };
return { dragging, containerListeners, overlayListeners };
}
export default useImageDrop;