feat: upload file by drag and drop (#1388)

* stash: file upload

* feat: support file upload by drag

* feat: beautify the ui

* feat: support file upload

* stash

* fix: the resource is incorrectly when upload multiple files

* feat: beautify the ui

* chore: reduce unused line

* stash

* chore: deleted unused line

* chore: deleted unused line

* chore

* chore: change the function declare

* feat: support to prompt file is too large

* feat:drop prompt to cover all element

* fix: eslint

* fix: the name of i18n

* chore: refactor the import deps

* feat: beautify the ui

* Update web/src/locales/en.json

Co-authored-by: boojack <stevenlgtm@gmail.com>

* chore: the import of  deps

* fix: the window size of fecting data

---------

Co-authored-by: boojack <stevenlgtm@gmail.com>
pull/1401/head
CorrectRoadH 2 years ago committed by GitHub
parent 026fb3e50e
commit 2ba54c9168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -79,7 +79,9 @@
"no-unused-resources": "No unused resources",
"name": "Name",
"delete-selected-resources": "Delete Selected Resources",
"no-files-selected": "No files selected❗"
"no-files-selected": "No files selected❗",
"upload-successfully": "Upload successfully",
"file-drag-drop-prompt": "Drag and drop your file here to upload file"
},
"archived": {
"archived-memos": "Archived Memos",

@ -79,7 +79,9 @@
"no-unused-resources": "無可刪除的資源",
"name": "資源名稱",
"delete-selected-resources": "刪除選中資源",
"no-files-selected": "沒有文件被選中❗"
"no-files-selected": "沒有文件被選中❗",
"upload-successfully": "上傳成功",
"file-drag-drop-prompt": "將您的文件拖放到此處以上傳文件"
},
"archived": {
"archived-memos": "已封存的 Memo",

@ -79,7 +79,9 @@
"no-unused-resources": "无可删除的资源",
"name": "资源名称",
"delete-selected-resources": "删除选中资源",
"no-files-selected": "没有文件被选中❗"
"no-files-selected": "没有文件被选中❗",
"upload-successfully": "上传成功",
"file-drag-drop-prompt": "将您的文件拖放到此处以上传文件"
},
"archived": {
"archived-memos": "已归档的 Memo",

@ -20,6 +20,7 @@ const ResourcesDashboard = () => {
const [selectedList, setSelectedList] = useState<Array<ResourceId>>([]);
const [isVisible, setIsVisible] = useState<boolean>(false);
const [queryText, setQueryText] = useState<string>("");
const [dragActive, setDragActive] = useState(false);
useEffect(() => {
resourceStore
@ -94,9 +95,52 @@ const ResourcesDashboard = () => {
}
};
const handleDrag = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
if (e.type === "dragenter" || e.type === "dragover") {
setDragActive(true);
} else if (e.type === "dragleave") {
setDragActive(false);
}
};
const handleDrop = async (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setDragActive(false);
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
await resourceStore.createResourcesWithBlob(e.dataTransfer.files).then(
(res) => {
for (const resource of res) {
toast.success(`${resource.filename} ${t("resources.upload-successfully")}`);
}
},
(reason) => {
toast.error(reason);
}
);
}
};
return (
<section className="w-full max-w-2xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
<MobileHeader showSearch={false} />
<div className="w-full relative" onDragEnter={handleDrag}>
{dragActive && (
<div
className="absolute h-full w-full rounded-xl bg-zinc-800 dark:bg-white opacity-60 z-10"
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
>
<div className="flex h-full w-full">
<p className="m-auto text-2xl text-white dark:text-black">{t("resources.file-drag-drop-prompt")}</p>
</div>
</div>
)}
<div className="w-full flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
<div className="relative w-full flex flex-row justify-between items-center">
<p className="flex flex-row justify-start items-center select-none rounded">
@ -160,6 +204,7 @@ const ResourcesDashboard = () => {
)}
</div>
</div>
</div>
</section>
);
};

@ -47,6 +47,24 @@ export const useResourceStore = () => {
store.dispatch(setResources([resource, ...resourceList]));
return resource;
},
async createResourcesWithBlob(files: FileList): Promise<Array<Resource>> {
let newResourceList: Array<Resource> = [];
for (const file of files) {
const { name: filename, size } = file;
if (size > MAX_FILE_SIZE) {
return Promise.reject(`${filename} overload max size: 32MB`);
}
const formData = new FormData();
formData.append("file", file, filename);
const { data } = (await api.createResourceWithBlob(formData)).data;
const resource = convertResponseModelResource(data);
newResourceList = [resource, ...newResourceList];
}
const resourceList = state.resources;
store.dispatch(setResources([...newResourceList, ...resourceList]));
return newResourceList;
},
async deleteResourceById(id: ResourceId) {
await api.deleteResourceById(id);
store.dispatch(deleteResource(id));

Loading…
Cancel
Save