import { useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { AiOutlineCloudUpload } from "react-icons/ai"; import { toast } from "react-toastify"; import { Flex, Spinner, Text } from "theme-ui"; import { uploadSliceScreenshot } from "@/features/slices/actions/uploadSliceScreenshot"; import { ScreenshotPreview } from "@/legacy/components/ScreenshotPreview"; import { acceptedImagesTypes } from "@/legacy/lib/consts"; import { ComponentUI } from "@/legacy/lib/models/common/ComponentUI"; import useSliceMachineActions from "@/modules/useSliceMachineActions"; import useCustomScreenshot from "./useCustomScreenshot"; interface DropZoneProps { imageTypes?: string[]; variationID: string; slice: ComponentUI; onUploadSuccess?: (newSlice: ComponentUI) => void; } const DragActiveView = () => { return ( Drop file here Maximum upload size file: 128Mb ); }; export const UploadIcon = ({ isActive, }: { isActive: boolean; }): JSX.Element => ( ); const DropZone: React.FC = ({ variationID, slice, imageTypes = acceptedImagesTypes, onUploadSuccess, }) => { const maybeScreenshot = slice.screenshots[variationID]; const { saveSliceCustomScreenshotSuccess } = useSliceMachineActions(); const [isDragActive, setIsDragActive] = useState(false); const [isHover, setIsHover] = useState(false); const [isUploadingScreenshot, setIsUploadingScreenshot] = useState(false); useHotkeys(["meta+v", "ctrl+v"], () => void handlePaste(), [ variationID, slice, ]); const { FileInputRenderer, fileInputProps } = useCustomScreenshot({ onHandleFile: async (file: File, isDragActive: boolean) => { setIsUploadingScreenshot(true); const newSlice = await uploadSliceScreenshot({ slice, file, method: isDragActive ? "dragAndDrop" : "upload", variationId: variationID, }); setIsHover(false); const screenshot = newSlice?.screenshots[variationID]; if (screenshot) { // Sync with redux store saveSliceCustomScreenshotSuccess(variationID, screenshot, newSlice); onUploadSuccess && onUploadSuccess(newSlice); } setIsUploadingScreenshot(false); }, }); const handleFile = async (file: File) => { if (file.size > 128000000) { return toast.error("File is too big. Max file size: 128Mb."); } setIsUploadingScreenshot(true); const newSlice = await uploadSliceScreenshot({ slice, file, method: "dragAndDrop", variationId: variationID, }); const screenshot = newSlice?.screenshots[variationID]; if (screenshot) { // Sync with redux store saveSliceCustomScreenshotSuccess(variationID, screenshot, newSlice); onUploadSuccess && onUploadSuccess(newSlice); } setIsUploadingScreenshot(false); }; const supportsClipboardRead = typeof navigator.clipboard.read === "function"; const handlePaste = async () => { if (!supportsClipboardRead) return; try { const clipboardItems = await navigator.clipboard.read(); if (clipboardItems[0] !== undefined) { const maybeType = clipboardItems[0].types.find((type) => imageTypes.map((t) => `image/${t}`).includes(type), ); if (maybeType !== undefined) { const blob = await clipboardItems[0].getType(maybeType); const file = new File([blob], "file"); return handleFile(file); } } } catch (e) { console.error("Could not paste file", e); } }; const handleDrop = (event: React.DragEvent) => { event.preventDefault(); setIsDragActive(false); const maybeFile = event.dataTransfer.files?.[0]; if (maybeFile !== undefined) { if (imageTypes.some((t) => `image/${t}` === maybeFile.type)) { return void handleFile(maybeFile); } return toast.error( `Only files of type ${imageTypes.join(", ")} are accepted.`, ); } }; const createDragActiveState = (bool: boolean) => (e: React.DragEvent) => { e.preventDefault(); setIsDragActive(bool); }; const createHoverState = (bool: boolean) => (e: React.MouseEvent) => { e.preventDefault(); setIsHover(bool); }; return ( e.preventDefault()} onDrop={handleDrop} > {isUploadingScreenshot ? ( <> Uploading file ... ) : null} {!isUploadingScreenshot && maybeScreenshot !== undefined ? ( ) : null} {!isUploadingScreenshot ? ( {supportsClipboardRead ? "Paste, drop or ..." : "Drop or ..."} Maximum file size: 128Mb ) : null} {isDragActive ? : null} ); }; export default DropZone;