All files / client/src/hooks use-file-manager.ts

61.53% Statements 24/39
66.66% Branches 14/21
57.14% Functions 4/7
66.66% Lines 24/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                      10x 10x   10x 1x     10x 1x 1x 1x 1x   1x                                           10x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x     1x     10x                
import { useRef, useCallback, useState } from "react";
 
export type FileEntry = { name: string; content: string };
 
export interface UseFileManagerOptions {
  tabs?: Array<{ name: string; content: string }>;
  onFilesLoaded?: (files: FileEntry[], replaceAll: boolean) => void;
  toast?: (params: { title: string; description?: string; variant?: string }) => void;
}
 
export function useFileManager({ tabs = [], onFilesLoaded, toast }: UseFileManagerOptions = {}) {
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const [lastLoadedFiles, setLastLoadedFiles] = useState<FileEntry[] | null>(null);
 
  const onLoadFiles = useCallback(() => {
    fileInputRef.current?.click();
  }, []);
 
  const downloadAllFiles = useCallback(async (providedTabs?: Array<{ name: string; content: string }>) => {
    const which = providedTabs ?? tabs ?? [];
    Eif (!which || which.length === 0) {
      try {
        toast?.({ title: "Nothing to download", description: "There are no open files to download" });
      } catch {}
      return;
    }
 
    which.forEach((tab, index) => {
      setTimeout(() => {
        const element = document.createElement("a");
        element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(tab.content));
        element.setAttribute("download", tab.name);
        (element as any).style.display = "none";
        document.body.appendChild(element);
        (element as any).click();
        document.body.removeChild(element);
      }, index * 200);
    });
 
    setTimeout(() => {
      try {
        toast?.({ title: "Download started", description: `${which.length} file(s) will be downloaded` });
      } catch {}
    }, which.length * 200 + 100);
  }, [tabs, toast]);
 
  const handleHiddenFileInput = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
    const fl = e.target.files;
    Iif (!fl || fl.length === 0) return;
    const files: FileEntry[] = [];
    for (const f of Array.from(fl)) {
      Iif (!f.name.endsWith(".ino") && !f.name.endsWith(".h")) continue;
      try {
        const txt = await f.text();
        files.push({ name: f.name, content: txt });
      } catch {}
    }
    Eif (files.length > 0) {
      setLastLoadedFiles(files);
      onFilesLoaded?.(files, false);
    }
    // clear input value to allow re-upload of same file
    Iif (fileInputRef.current) fileInputRef.current.value = "";
  }, [onFilesLoaded]);
 
  return {
    fileInputRef,
    onLoadFiles,
    downloadAllFiles,
    handleHiddenFileInput,
    lastLoadedFiles,
  } as const;
}