File size: 2,566 Bytes
8fc8501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useCallback, useEffect, useState } from "react";
import type {
  EmbedDataFile,
  EmbedDataFileMeta,
  EmbedDataStore,
} from "../editor/embeds/embed-data-store";
import {
  MAX_DATA_FILE_SIZE,
  extFromName,
  inferDataShape,
  isAcceptedExt,
} from "../utils/data-files";

interface UseEmbedDataOptions {
  dataStore: EmbedDataStore | null;
  userId: string;
}

export interface UploadResult {
  ok: boolean;
  name?: string;
  error?: string;
}

export function useEmbedData({ dataStore, userId }: UseEmbedDataOptions) {
  const [files, setFiles] = useState<EmbedDataFileMeta[]>(() =>
    dataStore ? dataStore.list() : [],
  );

  useEffect(() => {
    if (!dataStore) {
      setFiles([]);
      return;
    }
    setFiles(dataStore.list());
    return dataStore.observe(() => {
      setFiles(dataStore.list());
    });
  }, [dataStore]);

  const uploadFile = useCallback(
    async (file: File): Promise<UploadResult> => {
      if (!dataStore) return { ok: false, error: "Data store not ready" };
      if (file.size > MAX_DATA_FILE_SIZE) {
        return {
          ok: false,
          error: `File too large (max ${(MAX_DATA_FILE_SIZE / (1024 * 1024)).toFixed(0)} MB)`,
        };
      }
      const ext = extFromName(file.name);
      if (!isAcceptedExt(ext)) {
        return {
          ok: false,
          error: `Unsupported file type ".${ext}". Use CSV, TSV, JSON, NDJSON or TXT.`,
        };
      }

      const content = await file.text();
      const shape = inferDataShape(ext, content);
      const record: EmbedDataFile = {
        meta: {
          name: file.name,
          ext,
          size: new Blob([content]).size,
          uploader: userId,
          addedAt: Date.now(),
          rowCount: shape.rowCount,
          columns: shape.columns,
        },
        content,
      };
      dataStore.set(record);
      return { ok: true, name: file.name };
    },
    [dataStore, userId],
  );

  const uploadFiles = useCallback(
    async (fileList: FileList | File[]): Promise<UploadResult[]> => {
      const arr = Array.from(fileList);
      const results: UploadResult[] = [];
      for (const f of arr) {
        results.push(await uploadFile(f));
      }
      return results;
    },
    [uploadFile],
  );

  const removeFile = useCallback(
    (name: string) => {
      dataStore?.remove(name);
    },
    [dataStore],
  );

  const getFile = useCallback(
    (name: string) => dataStore?.get(name),
    [dataStore],
  );

  return {
    files,
    uploadFile,
    uploadFiles,
    removeFile,
    getFile,
  };
}