| 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, |
| }; |
| } |
|
|