in src/components/Storage/api/useStorageFiles.tsx [43:205]
export function useStorageFiles() {
const storage = useStorage();
const [bucket, setBucket] = useBucket();
const [path, setPath] = usePath();
const { createFolder } = useCreateFolder();
function getLocation(path = '') {
return `gs://${bucket}/${path}`;
}
function getCurrentRef(folder = path) {
return storage.refFromURL(getLocation(folder));
}
const bucketHasAnyFiles = useSwr(
`storage/hasFiles/${bucket}/`,
async (): Promise<boolean> => {
const { items, prefixes } = await getCurrentRef('').listAll();
return !!items.length || !!prefixes.length;
},
{ suspense: true }
);
const result = useSwr(
`storage/${bucket}/${path}`,
async (): Promise<StorageItem[]> => {
const { items, prefixes } = await getCurrentRef().listAll();
return [
...prefixes.map(importFolder),
...(await Promise.all(items.map(importFile))),
];
},
{ suspense: true }
);
const files = result.data || [];
async function deleteFile(path: string): Promise<void> {
return await getCurrentRef('').child(path).delete();
}
async function deleteFolder(path: string): Promise<void> {
const { items, prefixes } = await getCurrentRef(path).listAll();
try {
/**
* We don't know if physical folder exists, or it is inferred from
* nested file path.
*
* So here we attempt to delete physical representation, but if it does
* not exist, we just swallow the error.
*/
await deleteFile(path + '%2f');
} catch {
// quietly swallow any errors.
}
const prefixesPromise: Promise<void>[] = prefixes.map(async (prefix) => {
return await deleteFolder(prefix.fullPath);
});
const filesPromise: Promise<void>[] = items.map(async (file) => {
return await file.delete();
});
await Promise.all([...filesPromise, ...prefixesPromise]);
}
async function deleteAllFiles() {
return await deleteFolder('');
}
function uploadFiles(files: File[], folder?: string) {
return Promise.all(
files.map((file) => {
const path = folder ? `${folder}/${file.name}` : file.name;
return getCurrentRef().child(path).put(file);
})
);
}
async function deleteFiles(paths: string[]) {
return await Promise.all(
paths.map(async (path: string) => {
const file = files.find((file) => file.fullPath === path);
assert(file);
if (file.type === 'folder') {
return await deleteFolder(file.fullPath);
} else {
return await deleteFile(file.fullPath);
}
})
);
}
async function openAllFiles(paths: string[]) {
const links = await Promise.all(
paths
.map((path) => storage.refFromURL(getLocation() + '/' + path))
.map((d) => d.getDownloadURL())
);
links.forEach((url) => {
const a: HTMLAnchorElement = document.createElement('a');
a.href = url;
a.target = '_blank';
a.rel = 'noopener';
a.click();
});
}
return {
files,
setBucket,
bucket,
path,
setPath,
bucketHasAnyFiles: bucketHasAnyFiles.data,
getLocation,
async uploadFiles(uploadedFiles: File[], folderName?: string) {
// Optimistically display new file on top with the loading state.
const names = new Set(uploadedFiles.map(({ name }) => name));
const oldFiles = files.filter((file) => !names.has(file.name));
const newFiles = uploadedFiles.map((file) =>
fileToStorageFile(file, bucket, path)
);
result.mutate([...newFiles, ...oldFiles], false);
await uploadFiles(uploadedFiles, folderName);
await result.mutate();
await bucketHasAnyFiles.mutate();
},
async deleteAllFiles() {
await result.mutate([], false);
setPath('');
await deleteAllFiles();
await result.mutate();
await bucketHasAnyFiles.mutate();
},
openAllFiles,
async createFolder(name: string) {
createFolder(path + '/' + name);
await result.mutate();
await bucketHasAnyFiles.mutate();
},
async deleteFiles(paths: string[]) {
// Optimistically hide file
const pathsSet = new Set(paths);
const remainingFiles = files.filter(
(file) => !pathsSet.has(file.fullPath)
);
result.mutate(remainingFiles, false);
await deleteFiles(paths);
await result.mutate();
await bucketHasAnyFiles.mutate();
},
};
}