File size: 3,001 Bytes
422e938
81afeb2
422e938
81afeb2
422e938
 
 
 
 
 
 
 
81afeb2
422e938
81afeb2
422e938
 
 
 
 
 
 
 
 
 
 
81afeb2
422e938
81afeb2
 
422e938
 
 
 
 
 
 
 
 
 
 
81afeb2
 
422e938
 
 
81afeb2
422e938
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81afeb2
422e938
 
 
 
 
 
 
 
 
 
81afeb2
 
422e938
 
 
 
 
 
 
 
 
 
 
 
 
 
81afeb2
422e938
 
 
 
 
81afeb2
422e938
 
 
 
 
 
 
81afeb2
422e938
 
 
 
81afeb2
 
422e938
81afeb2
422e938
 
 
 
81afeb2
422e938
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { useState, useCallback } from "react";

/* ================= TYPES ================= */

export interface UploadItem {
  id: string;
  file: File;
  progress: number; // 0 → 100
  status: "idle" | "uploading" | "done" | "error";
  url?: string;
  error?: string;
}

/* ================= HOOK ================= */

export function useFileUpload() {
  const [uploads, setUploads] = useState<UploadItem[]>([]);

  /* ---------- ADD FILE ---------- */
  const addFiles = useCallback((files: FileList | File[]) => {
    const list = Array.from(files).map((file) => ({
      id: crypto.randomUUID(),
      file,
      progress: 0,
      status: "idle" as const,
    }));

    setUploads((prev) => [...prev, ...list]);
  }, []);

  /* ---------- UPLOAD SINGLE FILE ---------- */
  const uploadFile = useCallback(async (item: UploadItem) => {
    const form = new FormData();
    form.append("file", item.file);

    setUploads((prev) =>
      prev.map((u) =>
        u.id === item.id
          ? { ...u, status: "uploading", progress: 0 }
          : u
      )
    );

    try {
      const xhr = new XMLHttpRequest();
      xhr.open("POST", "/api/upload");

      xhr.upload.onprogress = (e) => {
        if (!e.lengthComputable) return;
        const percent = Math.round((e.loaded / e.total) * 100);
        setUploads((prev) =>
          prev.map((u) =>
            u.id === item.id ? { ...u, progress: percent } : u
          )
        );
      };

      const result: any = await new Promise((resolve, reject) => {
        xhr.onload = () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(JSON.parse(xhr.responseText));
          } else {
            reject(new Error("Upload failed"));
          }
        };
        xhr.onerror = () => reject(new Error("Network error"));
        xhr.send(form);
      });

      setUploads((prev) =>
        prev.map((u) =>
          u.id === item.id
            ? {
                ...u,
                status: "done",
                progress: 100,
                url: result.url,
              }
            : u
        )
      );
    } catch (err: any) {
      setUploads((prev) =>
        prev.map((u) =>
          u.id === item.id
            ? {
                ...u,
                status: "error",
                error: err.message || "Upload error",
              }
            : u
        )
      );
    }
  }, []);

  /* ---------- UPLOAD ALL ---------- */
  const uploadAll = useCallback(async () => {
    for (const item of uploads) {
      if (item.status === "idle") {
        await uploadFile(item);
      }
    }
  }, [uploads, uploadFile]);

  /* ---------- REMOVE FILE ---------- */
  const remove = useCallback((id: string) => {
    setUploads((prev) => prev.filter((u) => u.id !== id));
  }, []);

  /* ---------- RESET ---------- */
  const reset = useCallback(() => {
    setUploads([]);
  }, []);

  return {
    uploads,
    addFiles,
    uploadFile,
    uploadAll,
    remove,
    reset,
  };
}