mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import { useCallback, useMemo, useState } from "react";
|
|
import type { FileEntry } from "../components/file-tree";
|
|
|
|
type FetchFolderFn = (
|
|
path: string,
|
|
) => Promise<{ files?: FileEntry[]; directories?: Array<{ name: string; path: string }> }>;
|
|
|
|
type PathTransformFns = {
|
|
strip?: (path: string) => string;
|
|
add?: (path: string) => string;
|
|
};
|
|
|
|
type UseFileBrowserOptions = {
|
|
initialData?: { files?: FileEntry[]; directories?: Array<{ name: string; path: string }> };
|
|
isLoading?: boolean;
|
|
fetchFolder: FetchFolderFn;
|
|
prefetchFolder?: (path: string) => void;
|
|
pathTransform?: PathTransformFns;
|
|
rootPath?: string;
|
|
};
|
|
|
|
export const useFileBrowser = (props: UseFileBrowserOptions) => {
|
|
const { initialData, isLoading, fetchFolder, prefetchFolder, pathTransform, rootPath = "/" } = props;
|
|
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set());
|
|
const [fetchedFolders, setFetchedFolders] = useState<Set<string>>(new Set([rootPath]));
|
|
const [loadingFolders, setLoadingFolders] = useState<Set<string>>(new Set());
|
|
const [allFiles, setAllFiles] = useState<Map<string, FileEntry>>(new Map());
|
|
|
|
const stripPath = pathTransform?.strip;
|
|
const addPath = pathTransform?.add;
|
|
|
|
useMemo(() => {
|
|
if (initialData?.files) {
|
|
const files = initialData.files;
|
|
setAllFiles((prev) => {
|
|
const next = new Map(prev);
|
|
for (const file of files) {
|
|
const path = stripPath ? stripPath(file.path) : file.path;
|
|
if (path !== rootPath) {
|
|
next.set(path, { ...file, path });
|
|
}
|
|
}
|
|
return next;
|
|
});
|
|
if (rootPath) {
|
|
setFetchedFolders((prev) => new Set(prev).add(rootPath));
|
|
}
|
|
} else if (initialData?.directories) {
|
|
const directories = initialData.directories;
|
|
setAllFiles((prev) => {
|
|
const next = new Map(prev);
|
|
for (const dir of directories) {
|
|
next.set(dir.path, { name: dir.name, path: dir.path, type: "folder" });
|
|
}
|
|
return next;
|
|
});
|
|
}
|
|
}, [initialData, stripPath, rootPath]);
|
|
|
|
const fileArray = useMemo(() => Array.from(allFiles.values()), [allFiles]);
|
|
|
|
const handleFolderExpand = useCallback(
|
|
async (folderPath: string) => {
|
|
setExpandedFolders((prev) => {
|
|
const next = new Set(prev);
|
|
next.add(folderPath);
|
|
return next;
|
|
});
|
|
|
|
if (!fetchedFolders.has(folderPath)) {
|
|
setLoadingFolders((prev) => new Set(prev).add(folderPath));
|
|
|
|
try {
|
|
const pathToFetch = addPath ? addPath(folderPath) : folderPath;
|
|
const result = await fetchFolder(pathToFetch);
|
|
|
|
if (result.files) {
|
|
const files = result.files;
|
|
setAllFiles((prev) => {
|
|
const next = new Map(prev);
|
|
for (const file of files) {
|
|
const strippedPath = stripPath ? stripPath(file.path) : file.path;
|
|
// Skip the directory itself
|
|
if (strippedPath !== folderPath) {
|
|
next.set(strippedPath, { ...file, path: strippedPath });
|
|
}
|
|
}
|
|
return next;
|
|
});
|
|
} else if (result.directories) {
|
|
const directories = result.directories;
|
|
setAllFiles((prev) => {
|
|
const next = new Map(prev);
|
|
for (const dir of directories) {
|
|
next.set(dir.path, { name: dir.name, path: dir.path, type: "folder" });
|
|
}
|
|
return next;
|
|
});
|
|
}
|
|
|
|
setFetchedFolders((prev) => new Set(prev).add(folderPath));
|
|
} catch (error) {
|
|
console.error("Failed to fetch folder contents:", error);
|
|
} finally {
|
|
setLoadingFolders((prev) => {
|
|
const next = new Set(prev);
|
|
next.delete(folderPath);
|
|
return next;
|
|
});
|
|
}
|
|
}
|
|
},
|
|
[fetchedFolders, fetchFolder, stripPath, addPath],
|
|
);
|
|
|
|
const handleFolderHover = useCallback(
|
|
(folderPath: string) => {
|
|
if (!fetchedFolders.has(folderPath) && !loadingFolders.has(folderPath) && prefetchFolder) {
|
|
const pathToPrefetch = addPath ? addPath(folderPath) : folderPath;
|
|
prefetchFolder(pathToPrefetch);
|
|
}
|
|
},
|
|
[fetchedFolders, loadingFolders, prefetchFolder, addPath],
|
|
);
|
|
|
|
return {
|
|
fileArray,
|
|
expandedFolders,
|
|
loadingFolders,
|
|
handleFolderExpand,
|
|
handleFolderHover,
|
|
isLoading: isLoading && fileArray.length === 0,
|
|
isEmpty: fileArray.length === 0 && !isLoading,
|
|
};
|
|
};
|