import { useMemo } from "react"; import { FileResponse, FileType, Metadata, NavigatorCapability } from "../../../api/explorer.ts"; import { getCachedThumbExts } from "../../../redux/thunks/thumb.ts"; import { GroupPermission } from "../../../api/user.ts"; import { defaultPath } from "../../../hooks/useNavigation.tsx"; import { ContextMenuTypes } from "../../../redux/fileManagerSlice.ts"; import { Viewers, ViewersByID } from "../../../redux/siteConfigSlice.ts"; import { ExpandedViewerSetting } from "../../../redux/thunks/viewer.ts"; import SessionManager from "../../../session"; import { fileExtension } from "../../../util"; import Boolset from "../../../util/boolset.ts"; import CrUri, { Filesystem } from "../../../util/uri.ts"; import { FileManagerIndex } from "../FileManager.tsx"; const supportedArchiveTypes = ["zip", "gz", "xz", "tar", "rar", "7z", "bz2"]; export const canManageVersion = (file: FileResponse, bs: Boolset) => { return ( file.type == FileType.file && (!file.metadata || !file.metadata[Metadata.share_redirect]) && bs.enabled(NavigatorCapability.version_control) ); }; export const canShowInfo = (cap: Boolset) => { return cap.enabled(NavigatorCapability.info); }; export const canUpdate = (opt: DisplayOption) => { return !!( opt.allUpdatable && opt.hasFile && opt.orCapability?.enabled(NavigatorCapability.upload_file) && opt.allUpdatable ); }; export interface DisplayOption { allReadable: boolean; allUpdatable: boolean; hasReadable?: boolean; hasUpdatable?: boolean; hasTrashFile?: boolean; hasFile?: boolean; hasFolder?: boolean; hasOwned?: boolean; showEnter?: boolean; showOpen?: boolean; showOpenWithCascading?: () => boolean; showOpenWith?: () => boolean; showDownload?: boolean; showGoToSharedLink?: boolean; showExtractArchive?: boolean; showTorrentRemoteDownload?: boolean; showGoToParent?: boolean; showDelete?: boolean; showRestore?: boolean; showRename?: boolean; showPin?: boolean; showOrganize?: boolean; showCopy?: boolean; showShare?: boolean; showInfo?: boolean; showDirectLink?: boolean; showMove?: boolean; showTags?: boolean; showChangeFolderColor?: boolean; showChangeIcon?: boolean; showCustomProps?: boolean; showMore?: boolean; showVersionControl?: boolean; showDirectLinkManagement?: boolean; showManageShares?: boolean; showCreateArchive?: boolean; showResetThumb?: boolean; andCapability?: Boolset; orCapability?: Boolset; showCreateFolder?: boolean; showCreateFile?: boolean; showRefresh?: boolean; showNewFileFromTemplate?: boolean; showUpload?: boolean; showRemoteDownload?: boolean; } const capabilityMap: { [key: string]: Boolset } = {}; export const getActionOpt = ( targets: FileResponse[], viewerSetting?: ExpandedViewerSetting, type?: string, parent?: FileResponse, fmIndex: number = 0, ): DisplayOption => { const currentUser = SessionManager.currentLoginOrNull(); const currentUserAnonymous = SessionManager.currentUser(); const groupBs = SessionManager.currentUserGroupPermission(); const display: DisplayOption = { allReadable: true, allUpdatable: true, }; if (type == ContextMenuTypes.empty || type == ContextMenuTypes.new) { display.showRefresh = type == ContextMenuTypes.empty; display.showRemoteDownload = groupBs.enabled(GroupPermission.remote_download) && !!currentUser; if (!parent || parent.type != FileType.folder) { display.showRemoteDownload = display.showRemoteDownload && type == ContextMenuTypes.new; return display; } const parentCap = new Boolset(parent.capability); display.showCreateFolder = parentCap.enabled(NavigatorCapability.create_file) && parent.owned; display.showCreateFile = display.showCreateFolder && fmIndex == FileManagerIndex.main; display.showUpload = display.showCreateFile; if (display.showCreateFile) { const allViewers = Object.entries(ViewersByID); for (let i = 0; i < allViewers.length; i++) { if (allViewers[i][1] && allViewers[i][1].templates) { display.showNewFileFromTemplate = true; break; } } } return display; } if (type == ContextMenuTypes.searchResult) { display.showGoToParent = true; } const parentUrl = new CrUri(targets?.[0]?.path ?? defaultPath); targets.forEach((target) => { let readable = true; let updatable = target.owned && parentUrl.fs() != Filesystem.share; if (display.allReadable && !readable) { display.allReadable = false; } if (display.allUpdatable && !updatable) { display.allUpdatable = false; } if (!display.hasReadable && readable) { display.hasReadable = true; } if (!display.hasUpdatable && updatable) { display.hasUpdatable = true; } if (target.metadata && target.metadata[Metadata.restore_uri]) { display.hasTrashFile = true; } if (target.type == FileType.file) { display.hasFile = true; } if (target.type == FileType.folder) { display.hasFolder = true; } if (target.owned) { display.hasOwned = true; } if (target.capability) { let bs = capabilityMap[target.capability]; if (!bs) { bs = new Boolset(target.capability); capabilityMap[target.capability] = bs; } if (!display.andCapability) { display.andCapability = bs; } display.andCapability = display.andCapability.and(bs); if (!display.orCapability) { display.orCapability = bs; } display.orCapability = display.orCapability.or(bs); } }); const firstFileSuffix = fileExtension(targets[0]?.name ?? ""); display.showPin = !display.hasTrashFile && targets.length == 1 && display.hasFolder; display.showDelete = display.hasUpdatable && display.orCapability && (display.orCapability.enabled(NavigatorCapability.soft_delete) || display.orCapability.enabled(NavigatorCapability.delete_file)); display.showRestore = display.andCapability?.enabled(NavigatorCapability.restore); display.showRename = targets.length == 1 && display.allUpdatable && display.orCapability && display.orCapability.enabled(NavigatorCapability.rename_file); display.showCopy = display.hasUpdatable && !!display.orCapability; display.showShare = targets.length == 1 && !!currentUser && groupBs.enabled(GroupPermission.share) && display.allUpdatable && (targets[0].owned || groupBs.enabled(GroupPermission.is_admin)) && display.orCapability && display.orCapability.enabled(NavigatorCapability.share) && (!targets[0].metadata || (!targets[0].metadata[Metadata.share_redirect] && !targets[0].metadata[Metadata.restore_uri])); display.showMove = display.hasUpdatable && !!display.orCapability; display.showTags = display.hasUpdatable && display.orCapability && display.orCapability.enabled(NavigatorCapability.update_metadata); display.showChangeFolderColor = display.hasUpdatable && !display.hasFile && display.orCapability && display.orCapability.enabled(NavigatorCapability.update_metadata); display.showChangeIcon = display.hasUpdatable && display.orCapability && display.orCapability.enabled(NavigatorCapability.update_metadata); display.showCustomProps = display.showChangeIcon; display.showDownload = display.hasReadable && display.orCapability && display.orCapability.enabled(NavigatorCapability.download_file); display.showDirectLink = (display.hasOwned || groupBs.enabled(GroupPermission.is_admin)) && display.orCapability && (currentUserAnonymous?.group?.direct_link_batch_size ?? 0) >= targets.length && display.orCapability.enabled(NavigatorCapability.download_file); display.showDirectLinkManagement = display.showDirectLink && targets.length == 1 && display.hasFile; display.showOpen = targets.length == 1 && display.hasFile && display.showDownload && !!viewerSetting && !!firstFileSuffix && !!viewerSetting?.[firstFileSuffix]; display.showEnter = targets.length == 1 && display.hasFolder && display.orCapability?.enabled(NavigatorCapability.enter_folder) && display.allReadable; display.showExtractArchive = targets.length == 1 && display.hasFile && display.showDownload && !!currentUser && groupBs.enabled(GroupPermission.archive_task) && supportedArchiveTypes.includes(firstFileSuffix ?? ""); display.showTorrentRemoteDownload = targets.length == 1 && display.hasFile && display.showDownload && !!currentUser && groupBs.enabled(GroupPermission.remote_download) && firstFileSuffix == "torrent"; display.showOpenWithCascading = () => false; display.showOpenWith = () => targets.length == 1 && !!display.hasFile && !!display.showDownload; if (display.showOpen) { display.showOpenWithCascading = () => !!(display.showOpen && viewerSetting && viewerSetting[firstFileSuffix ?? ""]?.length >= 1); display.showOpenWith = () => !!(display.showOpen && viewerSetting && viewerSetting[firstFileSuffix ?? ""]?.length < 1); } display.showOrganize = display.showPin || display.showMove || display.showChangeFolderColor || display.showChangeIcon; display.showGoToSharedLink = targets.length == 1 && display.hasFile && targets[0].metadata && !!targets[0].metadata[Metadata.share_redirect]; display.showInfo = targets.length == 1 && display.orCapability && canShowInfo(display.orCapability); display.showVersionControl = targets.length == 1 && display.orCapability && display.hasReadable && canManageVersion(targets[0], display.orCapability); display.showManageShares = targets.length == 1 && targets[0].shared && display.orCapability && !!currentUser && groupBs.enabled(GroupPermission.share) && display.orCapability.enabled(NavigatorCapability.share); display.showCreateArchive = display.hasReadable && !!currentUser && groupBs.enabled(GroupPermission.archive_task) && display.orCapability && display.orCapability.enabled(NavigatorCapability.download_file); // Reset thumbnail is available when at least one file is selected and // current capability allows generating thumbnails // Show only when at least one selected file has a supported extension, // based on cached supported thumbnail extensions. const cache = getCachedThumbExts(); const anySupported = cache instanceof Set ? targets.some((f) => f.type == FileType.file && cache.has((fileExtension(f.name) || "").toLowerCase())) : false; display.showResetThumb = display.hasFile && anySupported; display.showMore = display.showVersionControl || display.showManageShares || display.showCreateArchive || display.showDirectLinkManagement || display.showResetThumb; return display; }; const useActionDisplayOpt = (targets: FileResponse[], type?: string, parent?: FileResponse, fmIndex: number = 0) => { const opt = useMemo(() => { return getActionOpt(targets, Viewers, type, parent, fmIndex); }, [targets, type, parent, fmIndex]); return opt; }; export default useActionDisplayOpt;