Skip to content

Commit

Permalink
fix(web): fix upload file entry 100 items limit (#1629)
Browse files Browse the repository at this point in the history
* fix(web): fix upload file entry 100 items limit

* feat: add failed function retry
  • Loading branch information
newfish-cmyk authored Nov 1, 2023
1 parent 8d95fff commit c92d405
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 53 deletions.
6 changes: 4 additions & 2 deletions web/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@
"DomainUpdateSuccess": "Domain Update Success",
"DomainDeleteSuccess": "Domain Delete Success",
"CustomApplicationDomain": "Custom Application Domain",
"BucketNameisRequired": "Bucket name is required"
"BucketNameisRequired": "Bucket name is required",
"Fail": "upload failed",
"UploadFailTip": "{{number}} files upload failed"
},
"TriggerPanel": {
"AddTrigger": "Add Trigger",
Expand Down Expand Up @@ -681,4 +683,4 @@
"Byte": "byte",
"Second": "second",
"Optional": "optional"
}
}
6 changes: 4 additions & 2 deletions web/public/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@
"DomainUpdateSuccess": "域名修改成功",
"DomainDeleteSuccess": "域名删除成功",
"CustomApplicationDomain": "自定义应用域名",
"BucketNameisRequired": "请输入 Bucket 名称"
"BucketNameisRequired": "请输入 Bucket 名称",
"Fail": "上传失败",
"UploadFailTip": "{{number}} 个文件上传失败"
},
"TriggerPanel": {
"AddTrigger": "新建触发器",
Expand Down Expand Up @@ -681,4 +683,4 @@
"Byte": "字节",
"Second": "",
"Optional": "可选"
}
}
6 changes: 4 additions & 2 deletions web/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@
"DomainUpdateSuccess": "域名修改成功",
"DomainDeleteSuccess": "域名删除成功",
"CustomApplicationDomain": "自定义应用域名",
"BucketNameisRequired": "请输入 Bucket 名称"
"BucketNameisRequired": "请输入 Bucket 名称",
"Fail": "上传失败",
"UploadFailTip": "{{number}} 个文件上传失败"
},
"TriggerPanel": {
"AddTrigger": "新建触发器",
Expand Down Expand Up @@ -681,4 +683,4 @@
"Byte": "字节",
"Second": "",
"Optional": "可选"
}
}
15 changes: 14 additions & 1 deletion web/src/components/FileUpload/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function FileUpload(props: { onUpload: (files: any) => void; darkMode: boolean }
});
} else {
const dirReader = file.createReader();
dirReader.readEntries(function (entries: any) {
readAllEntries(dirReader, []).then((entries: any) => {
const promises = [];
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
Expand All @@ -76,6 +76,19 @@ function FileUpload(props: { onUpload: (files: any) => void; darkMode: boolean }
});
};

function readAllEntries(dirReader: any, entries: any) {
return new Promise((resolve, reject) => {
dirReader.readEntries(function (newEntries: any) {
if (newEntries.length === 0) {
resolve(entries);
} else {
entries = entries.concat(newEntries);
readAllEntries(dirReader, entries).then(resolve).catch(reject);
}
});
});
}

// triggers when file is selected with click
const handleChange = function (e: React.ChangeEvent<HTMLInputElement>) {
e.preventDefault();
Expand Down
139 changes: 93 additions & 46 deletions web/src/pages/app/storages/mods/UploadButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { CheckCircleIcon } from "@chakra-ui/icons";
import { Trans, useTranslation } from "react-i18next";
import { CheckCircleIcon, WarningIcon } from "@chakra-ui/icons";
import {
Button,
Modal,
ModalCloseButton,
ModalContent,
Expand All @@ -21,23 +22,85 @@ import useAwsS3 from "@/hooks/useAwsS3";
import useGlobalStore from "@/pages/globalStore";

export type TFileItem = {
status: boolean;
status: string;
fileName: string;
};
function UploadButton(props: { onUploadSuccess: Function; children: React.ReactElement }) {
const { isOpen, onOpen, onClose } = useDisclosure();
const { currentStorage, prefix } = useStorageStore();
const { showSuccess } = useGlobalStore();
const { showSuccess, showError } = useGlobalStore();
const { uploadFile } = useAwsS3();
const [fileList, setFileList] = React.useState<TFileItem[]>([]);
const { t } = useTranslation();
const darkMode = useColorMode().colorMode === "dark";
const { onUploadSuccess, children } = props;
const [failedFileList, setFailedFileList] = React.useState<any>([]);

const handleUpload = async (files: any) => {
setFailedFileList([]);
const newFileList = Array.from(files).map((item: any) => ({
fileName:
files[0] instanceof File
? item.webkitRelativePath
? item.webkitRelativePath.replace(/^[^/]*\//, "")
: item.name
: item.webkitRelativePath
? item.webkitRelativePath
: item.file.name,
status: "pending",
}));
setFileList(newFileList);
const tasks = [];
const failedFiles: any[] = [];
for (let i = 0; i < files.length; i++) {
const file = files[0] instanceof File ? files[i] : files[i].file;
const fileName =
files[0] instanceof File
? file.webkitRelativePath
? file.webkitRelativePath.replace(/^[^/]*\//, "")
: file.name
: files[i].webkitRelativePath
? files[i].webkitRelativePath
: file.name;
const task = uploadFile(currentStorage?.name!, prefix + fileName, file, {
contentType: file.type,
})
.then(() => {
setFileList((pre) => {
const newList = [...pre];
newList[i].status = "success";
return newList;
});
return true;
})
.catch(() => {
setFileList((pre) => {
const newList = [...pre];
newList[i].status = "fail";
failedFiles.push(files[i]);
return newList;
});
return false;
});
tasks.push(task);
}
const res = await Promise.all(tasks);
onUploadSuccess();
if (!res.includes(false)) {
onClose();
showSuccess(t("StoragePanel.Success"));
} else {
setFailedFileList(failedFiles);
showError(t("StoragePanel.Fail"));
}
};

return (
<div>
{React.cloneElement(children, {
onClick: () => {
setFileList([]);
setFailedFileList([]);
onOpen();
},
})}
Expand All @@ -48,45 +111,27 @@ function UploadButton(props: { onUploadSuccess: Function; children: React.ReactE
<ModalHeader>{t("StoragePanel.UploadFile")}</ModalHeader>
<ModalCloseButton />
<div className="p-6">
<FileUpload
onUpload={async (files) => {
const newFileList = Array.from(files).map((item: any) => ({
fileName:
files[0] instanceof File
? item.webkitRelativePath
? item.webkitRelativePath.replace(/^[^/]*\//, "")
: item.name
: item.webkitRelativePath
? item.webkitRelativePath
: item.file.name,
status: false,
}));
setFileList(newFileList);
for (let i = 0; i < files.length; i++) {
const file = files[0] instanceof File ? files[i] : files[i].file;
const fileName =
files[0] instanceof File
? file.webkitRelativePath
? file.webkitRelativePath.replace(/^[^/]*\//, "")
: file.name
: files[i].webkitRelativePath
? files[i].webkitRelativePath
: file.name;
await uploadFile(currentStorage?.name!, prefix + fileName, file, {
contentType: file.type,
});
setFileList((pre) => {
const newList = [...pre];
newList[i].status = true;
return newList;
});
}
onUploadSuccess();
onClose();
showSuccess(t("StoragePanel.Success"));
}}
darkMode={darkMode}
/>
<FileUpload onUpload={handleUpload} darkMode={darkMode} />
{!!failedFileList.length && (
<div className="mx-4 mt-4 flex justify-between text-lg text-red-500">
<Trans
t={t}
i18nKey="StoragePanel.UploadFailTip"
values={{
number: failedFileList.length,
}}
/>
<Button
variant="link"
className="!text-blue-600"
onClick={() => {
handleUpload(failedFileList);
}}
>
{t("Retry")}
</Button>
</div>
)}
<div className="mt-2 max-h-40 overflow-auto">
{fileList.map((item) => {
return (
Expand All @@ -98,10 +143,12 @@ function UploadButton(props: { onUploadSuccess: Function; children: React.ReactE
)}
>
<span className="text-slate-500">{item.fileName}</span>
{item.status ? (
{item.status === "success" && (
<CheckCircleIcon color="green.500" fontSize={20} />
) : (
<Spinner size="xs" className="mr-1" />
)}
{item.status === "pending" && <Spinner size="xs" className="mr-1" />}
{item.status === "fail" && (
<WarningIcon className="!text-red-400" fontSize={20} />
)}
</div>
);
Expand Down

0 comments on commit c92d405

Please sign in to comment.