Twan07 commited on
Commit
b49adce
·
verified ·
1 Parent(s): c5df42b

Update frontend/hooks/useFileUpload.ts

Browse files
Files changed (1) hide show
  1. frontend/hooks/useFileUpload.ts +104 -52
frontend/hooks/useFileUpload.ts CHANGED
@@ -1,72 +1,124 @@
1
- import { useState, useCallback } from 'react';
2
- import { FileItem, UploadStatus } from '../types';
3
- import { uploadFileToHub } from '../services/hfService';
4
 
5
- export const useFileUpload = () => {
6
- // Local Upload State
7
- const [files, setFiles] = useState<FileItem[]>([]);
8
- const [isUploading, setIsUploading] = useState(false);
9
 
10
- // --- UPLOAD LOGIC ---
 
 
 
 
 
 
 
11
 
12
- const addFiles = useCallback((newFilesList: FileItem[]) => {
13
- setFiles((prev) => [...prev, ...newFilesList]);
14
- }, []);
15
 
16
- const removeFile = useCallback((id: string) => {
17
- setFiles((prev) => prev.filter((f) => f.id !== id));
18
- }, []);
 
 
 
 
 
 
 
 
19
 
20
- const updateFilePath = useCallback((id: string, newPath: string) => {
21
- setFiles((prev) => prev.map((f) => (f.id === id ? { ...f, path: newPath } : f)));
22
  }, []);
23
 
24
- const startUpload = useCallback(async () => {
25
- const filesToUpload = files.filter(
26
- (f) => f.status === UploadStatus.IDLE || f.status === UploadStatus.ERROR
 
 
 
 
 
 
 
 
27
  );
28
 
29
- if (filesToUpload.length === 0) return;
 
 
30
 
31
- setIsUploading(true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- const uploadPromises = filesToUpload.map(async (item) => {
34
- setFiles((prev) =>
35
- prev.map((f) =>
36
- f.id === item.id ? { ...f, status: UploadStatus.UPLOADING, error: undefined } : f
 
 
 
 
 
 
37
  )
38
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- try {
41
- const url = await uploadFileToHub({
42
- file: item.file,
43
- path: item.path
44
- });
45
-
46
- setFiles((prev) =>
47
- prev.map((f) =>
48
- f.id === item.id ? { ...f, status: UploadStatus.SUCCESS, url } : f
49
- )
50
- );
51
- } catch (err: any) {
52
- setFiles((prev) =>
53
- prev.map((f) =>
54
- f.id === item.id ? { ...f, status: UploadStatus.ERROR, error: err.message } : f
55
- )
56
- );
57
  }
58
- });
 
 
 
 
 
 
59
 
60
- await Promise.allSettled(uploadPromises);
61
- setIsUploading(false);
62
- }, [files]);
 
63
 
64
  return {
65
- files,
66
- isUploading,
67
  addFiles,
68
- removeFile,
69
- updateFilePath,
70
- startUpload
 
71
  };
72
- };
 
1
+ import { useState, useCallback } from "react";
 
 
2
 
3
+ /* ================= TYPES ================= */
 
 
 
4
 
5
+ export interface UploadItem {
6
+ id: string;
7
+ file: File;
8
+ progress: number; // 0 → 100
9
+ status: "idle" | "uploading" | "done" | "error";
10
+ url?: string;
11
+ error?: string;
12
+ }
13
 
14
+ /* ================= HOOK ================= */
 
 
15
 
16
+ export function useFileUpload() {
17
+ const [uploads, setUploads] = useState<UploadItem[]>([]);
18
+
19
+ /* ---------- ADD FILE ---------- */
20
+ const addFiles = useCallback((files: FileList | File[]) => {
21
+ const list = Array.from(files).map((file) => ({
22
+ id: crypto.randomUUID(),
23
+ file,
24
+ progress: 0,
25
+ status: "idle" as const,
26
+ }));
27
 
28
+ setUploads((prev) => [...prev, ...list]);
 
29
  }, []);
30
 
31
+ /* ---------- UPLOAD SINGLE FILE ---------- */
32
+ const uploadFile = useCallback(async (item: UploadItem) => {
33
+ const form = new FormData();
34
+ form.append("file", item.file);
35
+
36
+ setUploads((prev) =>
37
+ prev.map((u) =>
38
+ u.id === item.id
39
+ ? { ...u, status: "uploading", progress: 0 }
40
+ : u
41
+ )
42
  );
43
 
44
+ try {
45
+ const xhr = new XMLHttpRequest();
46
+ xhr.open("POST", "/api/upload");
47
 
48
+ xhr.upload.onprogress = (e) => {
49
+ if (!e.lengthComputable) return;
50
+ const percent = Math.round((e.loaded / e.total) * 100);
51
+ setUploads((prev) =>
52
+ prev.map((u) =>
53
+ u.id === item.id ? { ...u, progress: percent } : u
54
+ )
55
+ );
56
+ };
57
+
58
+ const result: any = await new Promise((resolve, reject) => {
59
+ xhr.onload = () => {
60
+ if (xhr.status >= 200 && xhr.status < 300) {
61
+ resolve(JSON.parse(xhr.responseText));
62
+ } else {
63
+ reject(new Error("Upload failed"));
64
+ }
65
+ };
66
+ xhr.onerror = () => reject(new Error("Network error"));
67
+ xhr.send(form);
68
+ });
69
 
70
+ setUploads((prev) =>
71
+ prev.map((u) =>
72
+ u.id === item.id
73
+ ? {
74
+ ...u,
75
+ status: "done",
76
+ progress: 100,
77
+ url: result.url,
78
+ }
79
+ : u
80
  )
81
  );
82
+ } catch (err: any) {
83
+ setUploads((prev) =>
84
+ prev.map((u) =>
85
+ u.id === item.id
86
+ ? {
87
+ ...u,
88
+ status: "error",
89
+ error: err.message || "Upload error",
90
+ }
91
+ : u
92
+ )
93
+ );
94
+ }
95
+ }, []);
96
 
97
+ /* ---------- UPLOAD ALL ---------- */
98
+ const uploadAll = useCallback(async () => {
99
+ for (const item of uploads) {
100
+ if (item.status === "idle") {
101
+ await uploadFile(item);
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
103
+ }
104
+ }, [uploads, uploadFile]);
105
+
106
+ /* ---------- REMOVE FILE ---------- */
107
+ const remove = useCallback((id: string) => {
108
+ setUploads((prev) => prev.filter((u) => u.id !== id));
109
+ }, []);
110
 
111
+ /* ---------- RESET ---------- */
112
+ const reset = useCallback(() => {
113
+ setUploads([]);
114
+ }, []);
115
 
116
  return {
117
+ uploads,
 
118
  addFiles,
119
+ uploadFile,
120
+ uploadAll,
121
+ remove,
122
+ reset,
123
  };
124
+ }