Refactor: 퍼센테이지 복구
This commit is contained in:
parent
1b93d67f34
commit
c6e45cad63
@ -5,7 +5,6 @@ import useAuthStore from '@/stores/useAuthStore';
|
||||
import { CircleCheckBig, CircleDashed, CircleX, X } from 'lucide-react';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import useUploadFiles from '@/hooks/useUploadFiles';
|
||||
import useUploadImagePresignedQuery from '@/queries/images/useUploadImagePresignedQuery';
|
||||
import { unzipFilesWithPath, extractFilesRecursivelyWithPath } from '@/utils/fileUtils';
|
||||
|
||||
interface ImagePreSignedFormProps {
|
||||
@ -34,20 +33,16 @@ export default function ImagePreSignedForm({
|
||||
const [isUploading, setIsUploading] = useState<boolean>(false);
|
||||
const [isUploaded, setIsUploaded] = useState<boolean>(false);
|
||||
const [isFailed, setIsFailed] = useState<boolean>(false);
|
||||
const [progress, setProgress] = useState<number>(0);
|
||||
const [uploadStatus, setUploadStatus] = useState<(boolean | null)[]>([]);
|
||||
const [uploadStatus, setUploadStatus] = useState<('uploading' | 'success' | 'failed' | null)[]>([]);
|
||||
|
||||
// Ensure to destructure the uploadFiles function properly from the hook
|
||||
const { uploadFiles } = useUploadFiles();
|
||||
const uploadImageFile = useUploadImagePresignedQuery();
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setFiles([]);
|
||||
setInputKey((prevKey) => prevKey + 1);
|
||||
setIsUploading(false);
|
||||
setIsUploaded(false);
|
||||
setIsFailed(false);
|
||||
setProgress(0);
|
||||
setIsUploaded(false);
|
||||
setUploadStatus([]);
|
||||
};
|
||||
|
||||
@ -92,9 +87,10 @@ export default function ImagePreSignedForm({
|
||||
event.stopPropagation();
|
||||
setIsDragging(false);
|
||||
|
||||
let processedFiles: { path: string; file: File }[] = [];
|
||||
|
||||
if (uploadType === 'folder') {
|
||||
const droppedItems = event.dataTransfer.items;
|
||||
let processedFiles: { path: string; file: File }[] = [];
|
||||
|
||||
for (let i = 0; i < droppedItems.length; i++) {
|
||||
const item = droppedItems[i];
|
||||
@ -106,20 +102,17 @@ export default function ImagePreSignedForm({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
||||
setUploadStatus((prevStatus) => [...prevStatus, ...processedFiles.map(() => null)]);
|
||||
} else {
|
||||
const droppedFiles = event.dataTransfer.files;
|
||||
if (droppedFiles) {
|
||||
const processedFiles: { path: string; file: File }[] = [];
|
||||
for (const file of Array.from(droppedFiles)) {
|
||||
processedFiles.push({ path: file.name, file });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
||||
setUploadStatus((prevStatus) => [...prevStatus, ...processedFiles.map(() => null)]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveFile = (index: number) => {
|
||||
@ -130,72 +123,52 @@ export default function ImagePreSignedForm({
|
||||
const handleUpload = async () => {
|
||||
if (files.length > 0) {
|
||||
setIsUploading(true);
|
||||
setIsUploaded(false);
|
||||
setIsFailed(false);
|
||||
setIsUploaded(false);
|
||||
|
||||
setUploadStatus(files.map(() => 'uploading'));
|
||||
|
||||
let finalFiles: { path: string; file: File }[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
if (file.file.type === 'application/zip' || file.file.type === 'application/x-zip-compressed') {
|
||||
console.log('업로드 전에 ZIP 파일 해제:', file.file.name);
|
||||
const unzippedFiles = await unzipFilesWithPath(file.file);
|
||||
console.log('해제된 파일:', unzippedFiles);
|
||||
finalFiles = [...finalFiles, ...unzippedFiles];
|
||||
} else {
|
||||
finalFiles.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadType === 'file') {
|
||||
uploadImageFile.mutate(
|
||||
{
|
||||
memberId,
|
||||
projectId,
|
||||
folderId,
|
||||
files: finalFiles.map(({ file }) => file), // Extract only the file
|
||||
progressCallback: (index: number) => {
|
||||
setUploadStatus((prevStatus) => {
|
||||
const newStatus = [...prevStatus];
|
||||
newStatus[index] = true; // Mark as uploaded
|
||||
return newStatus;
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleRefetch();
|
||||
setIsUploaded(true);
|
||||
},
|
||||
onError: () => {
|
||||
setIsFailed(true);
|
||||
setUploadStatus((prevStatus) => prevStatus.map((status) => (status === null ? false : status)));
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await uploadFiles({
|
||||
files: finalFiles,
|
||||
projectId,
|
||||
folderId,
|
||||
memberId,
|
||||
onProgress: (progressValue: number) => {
|
||||
setProgress(progressValue);
|
||||
onProgress: (progress) => {
|
||||
setUploadStatus((prevStatus) => {
|
||||
const completedFiles = Math.round((progress / 100) * files.length);
|
||||
const newStatus = prevStatus.map((status, index) => (index < completedFiles ? 'success' : status));
|
||||
return newStatus;
|
||||
});
|
||||
},
|
||||
useSingleUpload: uploadType === 'file',
|
||||
});
|
||||
|
||||
setUploadStatus(finalFiles.map(() => true));
|
||||
setUploadStatus((prevStatus) => prevStatus.map(() => 'success'));
|
||||
setIsUploaded(true);
|
||||
handleRefetch();
|
||||
} catch (error) {
|
||||
setUploadStatus((prevStatus) => prevStatus.map((status) => (status === 'uploading' ? 'failed' : status)));
|
||||
setIsFailed(true);
|
||||
setUploadStatus(finalFiles.map(() => false));
|
||||
console.error('업로드 실패:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 전체 진행 상황 계산
|
||||
const totalProgress = Math.round((uploadStatus.filter((status) => status !== null).length / files.length) * 100);
|
||||
|
||||
useEffect(() => {
|
||||
onFileCount(files.length);
|
||||
}, [files, onFileCount]);
|
||||
@ -237,7 +210,6 @@ export default function ImagePreSignedForm({
|
||||
</div>
|
||||
)}
|
||||
{files.length > 0 && (
|
||||
<ul className="m-0 max-h-[260px] list-none overflow-y-auto p-0">
|
||||
<FixedSizeList
|
||||
height={260}
|
||||
itemCount={files.length}
|
||||
@ -245,38 +217,37 @@ export default function ImagePreSignedForm({
|
||||
width="100%"
|
||||
>
|
||||
{({ index, style }) => (
|
||||
<li
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-1"
|
||||
className="flex items-center justify-between border-b border-gray-200 p-2"
|
||||
style={style}
|
||||
>
|
||||
<span className="truncate">{files[index].path}</span>
|
||||
{isUploading ? (
|
||||
<div className="p-2">
|
||||
{uploadStatus[index] === true ? (
|
||||
<div className="flex items-center">
|
||||
{uploadStatus[index] === 'success' ? (
|
||||
<CircleCheckBig
|
||||
className="stroke-green-500"
|
||||
size={16}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
) : uploadStatus[index] === false ? (
|
||||
) : uploadStatus[index] === 'failed' ? (
|
||||
<CircleX
|
||||
className="stroke-red-500"
|
||||
size={16}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
) : (
|
||||
) : uploadStatus[index] === 'uploading' ? (
|
||||
<CircleDashed
|
||||
className="stroke-gray-500"
|
||||
className="animate-spin stroke-gray-500"
|
||||
size={16}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
) : null}
|
||||
{!isUploading && (
|
||||
<button
|
||||
className="cursor-pointer p-2"
|
||||
className="ml-2 cursor-pointer p-1"
|
||||
onClick={() => handleRemoveFile(index)}
|
||||
disabled={uploadStatus[index] === 'success'}
|
||||
>
|
||||
<X
|
||||
color="red"
|
||||
@ -285,18 +256,23 @@ export default function ImagePreSignedForm({
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</FixedSizeList>
|
||||
</ul>
|
||||
)}
|
||||
{isUploading ? (
|
||||
<Button
|
||||
onClick={handleClose}
|
||||
variant={isFailed ? 'red' : 'blue'}
|
||||
disabled={!isUploaded && !isFailed}
|
||||
>
|
||||
{isFailed ? '업로드 실패 (닫기)' : isUploaded ? '업로드 완료 (닫기)' : `업로드 중... ${progress}%`}
|
||||
{isFailed
|
||||
? '업로드 실패 (닫기)'
|
||||
: isUploaded
|
||||
? '업로드 완료 (닫기)'
|
||||
: totalProgress === 0
|
||||
? '업로드 준비 중...'
|
||||
: `업로드 중... ${totalProgress}%`}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
|
@ -11,15 +11,18 @@ export default function useUploadFiles() {
|
||||
folderId,
|
||||
memberId,
|
||||
onProgress,
|
||||
useSingleUpload = false,
|
||||
}: {
|
||||
files: { path: string; file: File }[];
|
||||
projectId: number;
|
||||
folderId: number;
|
||||
memberId: number;
|
||||
onProgress: (progress: number) => void;
|
||||
useSingleUpload?: boolean;
|
||||
}) => {
|
||||
const folderIdMap: { [path: string]: number } = { '': folderId };
|
||||
|
||||
if (!useSingleUpload) {
|
||||
const foldersToCreate = Array.from(new Set(files.map(({ path }) => path.split('/').slice(0, -1).join('/'))));
|
||||
foldersToCreate.sort();
|
||||
|
||||
@ -42,10 +45,23 @@ export default function useUploadFiles() {
|
||||
folderIdMap[folderPath] = newFolder.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let progress = 0;
|
||||
let completedFiles = 0;
|
||||
const totalFiles = files.length;
|
||||
|
||||
if (useSingleUpload) {
|
||||
await uploadImageMutation.mutateAsync({
|
||||
memberId,
|
||||
projectId,
|
||||
folderId,
|
||||
files: files.map(({ file }) => file),
|
||||
progressCallback: (progressValue: number) => {
|
||||
const progress = (progressValue / totalFiles) * 100;
|
||||
onProgress(Math.round(progress));
|
||||
},
|
||||
});
|
||||
} else {
|
||||
for (const { path, file } of files) {
|
||||
const folderPath = path.split('/').slice(0, -1).join('/');
|
||||
const targetFolderId = folderIdMap[folderPath] || folderId;
|
||||
@ -55,11 +71,14 @@ export default function useUploadFiles() {
|
||||
projectId,
|
||||
folderId: targetFolderId,
|
||||
files: [file],
|
||||
progressCallback: (value) => {
|
||||
progress += value / totalFiles;
|
||||
progressCallback: (progressValue: number) => {
|
||||
const progress = ((completedFiles + progressValue / 100) / totalFiles) * 100;
|
||||
onProgress(Math.round(progress));
|
||||
},
|
||||
});
|
||||
|
||||
completedFiles += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user