import * as React from "react"; import "./file-browser.css"; import Dropzone from "react-dropzone"; import Icon from "./icon"; import Alert from "./alert"; import {Popup} from "./popup"; import {useState} from "react"; export function FileBrowser(props) { let files = props.files || { }; let api = props.api; let tokenObj = props.token || { valid: false }; let onSelectFile = props.onSelectFile || function() { }; let onFetchFiles = props.onFetchFiles || function() { }; let directories = props.directories || {}; let [popup, setPopup] = useState({ visible: false, directoryName: "", directory: 0, type: "upload" }); let [alerts, setAlerts] = useState( []); let [filesToUpload, setFilesToUpload] = useState([]); function svgMiddle(scale=1.0) { let width = 48 * scale; let height = 64 * scale; return ; } function svgEnd(scale=1.0) { let width = 48 * scale; let height = 64 * scale; return { /* vertical line */} { /* horizontal line */} ; } function svgLeft(scale=1.0) { let width = 48 * scale; let height = 64 * scale; return { /* vertical line */} ; } function createFileIcon(mimeType, size=2) { let icon = ""; if (mimeType !== null) { mimeType = mimeType.toLowerCase().trim(); let types = ["image", "text", "audio", "video"]; let languages = ["php", "java", "python", "cpp"]; let archives = ["zip", "tar", "archive"]; let [mainType, subType] = mimeType.split("/"); if (mainType === "text" && languages.find(a => subType.includes(a))) { icon = "code"; } else if (mainType === "application" && archives.find(a => subType.includes(a))) { icon = "archive"; } else if (mainType === "application" && subType === "pdf") { icon = "pdf"; } else if (mainType === "application" && (subType.indexOf("powerpoint") > -1 || subType.indexOf("presentation") > -1)) { icon = "powerpoint"; } else if (mainType === "application" && (subType.indexOf("word") > -1 || subType.indexOf("opendocument") > -1)) { icon = "word"; } else if (mainType === "application" && (subType.indexOf("excel") > -1 || subType.indexOf("sheet") > -1)) { icon = "excel"; } else if (mainType === "application" && subType.indexOf("directory") > -1) { icon = "folder"; } else if (types.indexOf(mainType) > -1) { if (mainType === "text") { icon = "alt"; } else { icon = mainType; } } } if (icon !== "folder") { icon = "file" + (icon ? ("-" + icon) : icon); } return } function formatSize(size) { const suffixes = ["B","KiB","MiB","GiB","TiB"]; let i = 0; for (; i < suffixes.length && size >= 1024; i++) { size /= 1024.0; } if (i === 0 || Math.round(size) === size) { return size + " " + suffixes[i]; } else { return size.toFixed(1) + " " + suffixes[i]; } } function canUpload() { return api.loggedIn || (tokenObj.valid && tokenObj.type === "upload"); } function onAddUploadFiles(acceptedFiles) { let files = filesToUpload.slice(); files.push(...acceptedFiles); setFilesToUpload(files); } function getSelectedIds(items = null, recursive = true) { let ids = []; items = items || files; for (const fileItem of Object.values(items)) { if (fileItem.selected) { ids.push(fileItem.uid); } if (recursive && fileItem.isDirectory) { ids.push(...getSelectedIds(fileItem.items)); } } return ids; } function createFileList(elements, indentation=0) { let rows = []; let i = 0; const values = Object.values(elements); for (const fileElement of values) { let name = fileElement.name; let uid = fileElement.uid; let type = (fileElement.isDirectory ? "Directory" : fileElement.mimeType); let size = (fileElement.isDirectory ? "" : formatSize(fileElement.size)); let mimeType = (fileElement.isDirectory ? "application/x-directory" : fileElement.mimeType); let token = (tokenObj && tokenObj.valid ? "&token=" + tokenObj.value : ""); let svg = []; if (indentation > 0) { for (let i = 0; i < indentation - 1; i++) { svg.push(svgLeft(0.75)); } if (i === values.length - 1) { svg.push(svgEnd(0.75)); } else { svg.push(svgMiddle(0.75)); } } rows.push( { svg } { createFileIcon(mimeType) } {fileElement.isDirectory ? name : {name} } {type} {size} onSelectFile(e, uid)} /> ); if (fileElement.isDirectory) { rows.push(...createFileList(fileElement.items, indentation + 1)); } i++; } return rows; } let rows = createFileList(files); let selectedIds = getSelectedIds(); let selectedCount = selectedIds.length; let uploadZone = <>; let writePermissions = canUpload(); let uploadedFiles = []; let alertElements = []; for (let i = 0; i < alerts.length; i++) { const alert = alerts[i]; alertElements.push( removeAlert(i)} /> ); } let options = []; for (const [uid, dir] of Object.entries(directories)) { options.push( ); } if (writePermissions) { for(let i = 0; i < filesToUpload.length; i++) { const file = filesToUpload[i]; uploadedFiles.push( { createFileIcon(file.type, 3) } {file.name} onRemoveUploadedFile(e, i)}/> ); } uploadZone = <> {({getRootProps, getInputProps}) => (

Drag 'n' drop some files here, or click to select files

{ uploadedFiles.length === 0 ? :
{uploadedFiles}
}
)}
; } return <>

File Browser

{ rows.length > 0 ? rows : }
Name Type Size
No files uploaded yet
{ api.loggedIn ? : <> } { writePermissions ? <> : <> }
{ uploadZone }
{ alertElements }
{ popup.type !== "upload" ?
onPopupChange(e, "directoryName")}/>
: <> }
; function onPopupOpen(type) { setPopup({ ...popup, visible: true, type: type }); } function onPopupClose() { setPopup({ ...popup, visible: false }); } function onPopupChange(e, key) { setPopup({ ...popup, [key]: e.target.value }); } function onPopupButton(btn) { if (btn === "Ok") { let parentId = popup.directory === 0 ? null : popup.directory; if (popup.type === "createDirectory") { api.createDirectory(popup.directoryName, parentId).then((res) => { if (!res.success) { pushAlert(res, "Error creating directory"); } else { fetchFiles(); } }); } else if (popup.type === "upload") { onUpload(); } } onPopupClose(); } function fetchFiles() { if (tokenObj.valid) { api.validateToken(tokenObj.value).then((res) => { if (res) { onFetchFiles(res.files); } else { pushAlert(res); } }); } else if (api.loggedIn) { api.listFiles().then((res) => { if (res) { onFetchFiles(res.files); } else { pushAlert(res); } }); } } function onRemoveUploadedFile(e, i) { e.stopPropagation(); let files = filesToUpload.slice(); files.splice(i, 1); setFilesToUpload(files); } function pushAlert(res, title) { let newAlerts = alerts.slice(); newAlerts.push({ type: "danger", message: res.msg, title: title }); setAlerts(newAlerts); } function removeAlert(i) { if (i >= 0 && i < alerts.length) { let newAlerts = alerts.slice(); newAlerts.splice(i, 1); setAlerts(newAlerts); } } function deleteFiles(selectedIds) { if (selectedIds && selectedIds.length > 0) { let token = (api.loggedIn ? null : tokenObj.value); api.delete(selectedIds, token).then((res) => { if (res.success) { fetchFiles(); } else { pushAlert(res); } }); } } function onUpload() { let token = (api.loggedIn ? null : tokenObj.value); let parentId = ((!api.loggedIn || popup.directory === 0) ? null : popup.directory); api.upload(filesToUpload, token, parentId).then((res) => { if (res.success) { setFilesToUpload([]); fetchFiles(); } else { pushAlert(res); } }); } function onDownload(selectedIds) { if (selectedIds && selectedIds.length > 0) { let token = (api.loggedIn ? "" : "&token=" + tokenObj.value); let ids = selectedIds.map(id => "id[]=" + id).join("&"); let downloadUrl = "/api/file/download?" + ids + token; fetch(downloadUrl) .then(response => { let header = response.headers.get("Content-Disposition") || ""; let fileNameFields = header.split(";").filter(c => c.trim().toLowerCase().startsWith("filename=")); let fileName = null; if (fileNameFields.length > 0) { fileName = fileNameFields[0].trim().substr("filename=".length); } else { fileName = null; } response.blob().then(blob => { let url = window.URL.createObjectURL(blob); let a = document.createElement('a'); a.href = url; if (fileName !== null) a.download = fileName; a.click(); }); }); } } }