import { Button } from '@components/common/ui/Button.js';
import React from 'react';
import './FileBrowser.scss';
import { useQuery } from 'urql';
import Spinner from '@components/admin/Spinner.js';
import { Input } from '@components/common/ui/Input.js';
const GetApisQuery = `
query Query ($filters: [FilterInput!]) {
browserApi: url(routeId: "fileBrowser", params: [{key: "0", value: ""}])
deleteApi: url(routeId: "fileDelete", params: [{key: "0", value: ""}])
uploadApi: url(routeId: "imageUpload", params: [{key: "0", value: ""}])
folderCreateApi: url(routeId: "folderCreate")
}
`;
export interface File {
isSelected?: boolean;
name: string;
url: string;
}
const File: React.FC<{
file: File;
select: (url: File) => void;
}> = ({ file, select }) => {
const className = file.isSelected === true ? 'selected' : '';
return (
);
};
const FileBrowser: React.FC<{
onInsert: (url: string) => void;
isMultiple: boolean;
close: () => void;
}> = ({ onInsert, isMultiple, close }) => {
const [error, setError] = React.useState('');
const [loading, setLoading] = React.useState(false);
const [folders, setFolders] = React.useState([]);
const [files, setFiles] = React.useState([]);
const [currentPath, setCurrentPath] = React.useState<
{
name: string;
index: number;
}[]
>([{ name: '', index: 0 }]);
const newFolderRefInput = React.useRef(null);
const browserApiRef = React.useRef('');
const deleteApiRef = React.useRef('');
const uploadApiRef = React.useRef('');
const folderCreateApiRef = React.useRef('');
const onSelectFolder = (e, f) => {
e.preventDefault();
setCurrentPath(
currentPath.concat({ name: f, index: currentPath.length + 1 })
);
};
const onSelectFolderFromBreadcrumb = (e, index) => {
e.preventDefault();
const newPath = [] as { name: string; index: number }[];
currentPath.forEach((f) => {
if (f.index <= index) newPath.push(f);
});
setCurrentPath(newPath);
};
const onSelectFile = (f) => {
if (isMultiple === false) {
setFiles(
files.map((file) => {
if (f.name === file.name) {
file.isSelected = !file.isSelected;
} else {
file.isSelected = false;
}
return file;
})
);
} else {
setFiles(
files.map((file) => {
if (f.name === file.name) {
file.isSelected = true;
} else {
file.isSelected = false;
}
return file;
})
);
}
};
const closeFileBrowser = (e) => {
e.preventDefault();
close();
};
const createFolder = (e, folder) => {
e.preventDefault();
if (!folder || !folder.trim()) {
setError('Invalid folder name');
return;
}
const path = currentPath.map((f) => f.name);
path.push(folder.trim());
setLoading(true);
fetch(folderCreateApiRef.current, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ path: path.join('/') }),
credentials: 'same-origin'
})
.then((res) => res.json())
.then((response) => {
if (!response.error) {
// Get the first level folder, incase of recursive folder creation
const recursiveFolders = folder.split('/');
setFolders([...new Set(folders.concat(recursiveFolders[0]))]);
} else {
setError(response.error.message);
}
})
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
};
const deleteFile = () => {
let file;
files.forEach((f) => {
if (f.isSelected === true) {
file = f;
}
});
if (!file) {
setError('No file selected');
} else {
const path = currentPath.map((f) => f.name);
path.push(file.name);
setLoading(true);
fetch(deleteApiRef.current + path.join('/'), {
method: 'DELETE'
})
.then((res) => res.json())
.then((response) => {
if (!response.error) {
setCurrentPath(currentPath.map((f) => f));
} else {
setError(response.error.message);
}
})
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
}
};
const insertFile = () => {
let file;
files.forEach((f) => {
if (f.isSelected === true) {
file = f;
}
});
if (!file) {
setError('No file selected');
} else {
onInsert(file.url);
}
};
const onUpload = (e) => {
e.persist();
const formData = new FormData();
for (let i = 0; i < e.target.files.length; i += 1)
formData.append('images', e.target.files[i]);
const path = [] as string[];
currentPath.forEach((f) => {
path.push(f.name);
});
setLoading(true);
fetch(uploadApiRef.current + path.join('/'), {
method: 'POST',
body: formData
})
.then((res) => res.json())
.then((response) => {
if (!response.error) {
setCurrentPath(currentPath.map((f) => f));
} else {
setError(response.error.message);
}
})
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
};
// Create a function to fetch files and folders to avoid code duplication
const fetchFilesAndFolders = React.useCallback(() => {
if (!browserApiRef.current) {
return;
}
const path = currentPath.map((f) => f.name);
setLoading(true);
fetch(browserApiRef.current + path.join('/'), {
method: 'GET'
})
.then((res) => res.json())
.then((response) => {
if (!response.error) {
setFolders(response.data.folders);
setFiles(response.data.files);
} else {
setError(response.error.message);
}
})
.catch((e) => setError(e.message))
.finally(() => setLoading(false));
}, [currentPath]);
const [result] = useQuery({
query: GetApisQuery
});
const { data, fetching, error: err } = result;
if (data) {
browserApiRef.current = data.browserApi;
deleteApiRef.current = data.deleteApi;
uploadApiRef.current = data.uploadApi;
folderCreateApiRef.current = data.folderCreateApi;
}
// Fetch files and folders when APIs are ready
React.useEffect(() => {
if (data) {
fetchFilesAndFolders();
}
}, [currentPath, fetchFilesAndFolders, data]);
if (err) {
return (
There was an error fetching file browser APIs.
{err.message}
);
}
if (fetching) {
return (
);
}
return (
{loading === true && (
)}
You are here:
{currentPath
.filter((f) => f.name !== '')
.map((f, index) => (
))}
{error}
{files.length === 0 &&
There is no file to display.
}
{files.map((f) => (
))}
);
};
export { FileBrowser };