diff --git a/frontend/src/components/Dropzone.js b/frontend/src/components/Dropzone.js new file mode 100644 index 00000000..621e74b5 --- /dev/null +++ b/frontend/src/components/Dropzone.js @@ -0,0 +1,84 @@ +import React, { useRef, useState } from 'react'; +import { useEffect } from 'react'; +import BackupIcon from '@material-ui/icons/Backup'; + +const dragOverStyle = { + backgroundColor: 'rgba(0,0,0,0.8)', + border: '5px dashed white', + borderRadius: '8px', + width: '100%', + height: '100%', + position: 'absolute', + overflow: 'hidden', + zIndex: 100, + display: 'flex', + alignItems: 'center', + justifyContent: 'center' +}; + +const Dropzone = ({ children, style, onDrop }) => { + const dropzoneRef = useRef(null); + const [dragging, setDragging] = useState(false); + + let dragCounter = 0; + + const handleDragOver = (e) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const handleDragEnter = (e) => { + e.preventDefault(); + e.stopPropagation(); + dragCounter++; + if (e.dataTransfer.items && e.dataTransfer.items.length > 0) + setDragging(true); + }; + + const handleDragLeave = (e) => { + e.preventDefault(); + e.stopPropagation(); + dragCounter--; + if (dragCounter === 0) setDragging(false); + }; + + const handleDrop = (e) => { + e.preventDefault(); + e.stopPropagation(); + setDragging(false); + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + onDrop(e); + e.dataTransfer.clearData(); + dragCounter = 0; + } + }; + + useEffect(() => { + if (!dropzoneRef.current) return; + + dropzoneRef.current.addEventListener('dragover', handleDragOver); + dropzoneRef.current.addEventListener('dragenter', handleDragEnter); + dropzoneRef.current.addEventListener('dragleave', handleDragLeave); + dropzoneRef.current.addEventListener('drop', handleDrop); + + return () => { + dropzoneRef.current.removeEventListener('dragover', handleDragOver); + dropzoneRef.current.removeEventListener('dragenter', handleDragEnter); + dropzoneRef.current.removeEventListener('dragleave', handleDragLeave); + dropzoneRef.current.removeEventListener('drop', handleDrop); + }; + }, [dropzoneRef]); + + return ( +
+ {dragging && ( +
+ +
+ )} + {children} +
+ ); +}; + +export default Dropzone; diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index d5f0cddc..fe2fd6ad 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect } from 'react'; import { useInterval } from 'react-powerhooks'; @@ -37,6 +37,7 @@ import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import CircularProgress from '@material-ui/core/CircularProgress'; +import Dropzone from '../components/Dropzone'; const surfaceColor = "#27292D" const inputColor = "#383B40" @@ -134,7 +135,8 @@ const Apps = (props) => { const [cursearch, setCursearch] = React.useState("") const [sharingConfiguration, setSharingConfiguration] = React.useState("you") - const upload = useRef(null); + const [isDropzone, setIsDropzone] = React.useState(false); + const upload = React.useRef(null); const { start, stop } = useInterval({ duration: 5000, @@ -179,7 +181,6 @@ const Apps = (props) => { color: "#ffffff", width: "100%", display: "flex", - margin: 20, } const paperAppStyle = { @@ -792,8 +793,37 @@ const Apps = (props) => { //} } + const uploadFile = (e) => { + const isDropzone = e.dataTransfer?.files.length > 0; + const files = isDropzone ? e.dataTransfer.files : e.target.files; + + const reader = new FileReader(); + + reader.addEventListener('load', (e) => { + const content = e.target.result; + setOpenApiData(content); + setIsDropzone(isDropzone); + }); + + reader.readAsText(files[0]); + }; + + useEffect(() => { + if (openApiData.length > 0) { + setOpenApiError(''); + validateOpenApi(openApiData); + } + }, [openApiData]); + + useEffect(() => { + if (appValidation && isDropzone) { + redirectOpenApi(); + setIsDropzone(false); + } + }, [appValidation, isDropzone]); + const appView = isLoggedIn ? -
1366 ? 1366 : 1200, margin: "auto",}}> + 1366 ? 1366 : 1200, margin: "auto", padding: 20 }} onDrop={uploadFile}>
@@ -905,7 +935,7 @@ const Apps = (props) => {
- + : null @@ -1176,7 +1206,7 @@ const Apps = (props) => { }) .then((responseJson) => { if (responseJson.success) { - setAppValidation(responseJson.id) + setAppValidation(responseJson.id); } else { if (responseJson.reason !== undefined) { setOpenApiError(responseJson.reason) @@ -1200,25 +1230,6 @@ const Apps = (props) => { setLoadAppsModalOpen(false) } - const uploadFile = (e) => { - const file = e.target.files[0]; - const reader = new FileReader(); - - reader.addEventListener('load', (e) => { - const content = e.target.result; - setOpenApiData(content); - }); - - reader.readAsText(file); - }; - - useEffect(() => { - if(openApiData.length > 0) { - setOpenApiError(''); - validateOpenApi(openApiData); - } - }, [openApiData]); - const deleteModal = deleteModalOpen ?