import { WebApplication } from '@/Application/Application' import { PrefDefaults } from '@/Constants/PrefDefaults' import { FilesController } from '@/Controllers/FilesController' import { formatDateForContextMenu } from '@/Utils/DateUtils' import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType' import { formatSizeToReadableString } from '@standardnotes/filepicker' import { ContentType, FileItem, SortableItem, PrefKey, ApplicationEvent, naturalSort } from '@standardnotes/snjs' import { useState, useEffect, useCallback, useMemo, useRef } from 'react' import { FileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction' import { getFileIconComponent } from '../FilePreview/getFileIconComponent' import Popover from '../Popover/Popover' import Table from '../Table/Table' import { TableColumn } from '../Table/CommonTypes' import { useTable } from '../Table/useTable' import Menu from '../Menu/Menu' import FileMenuOptions from '../FileContextMenu/FileMenuOptions' import Icon from '../Icon/Icon' import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem' import LinkedItemBubble from '../LinkedItems/LinkedItemBubble' import LinkedItemsPanel from '../LinkedItems/LinkedItemsPanel' import { LinkingController } from '@/Controllers/LinkingController' import { FeaturesController } from '@/Controllers/FeaturesController' const ContextMenuCell = ({ files, filesController }: { files: FileItem[]; filesController: FilesController }) => { const [contextMenuVisible, setContextMenuVisible] = useState(false) const anchorElementRef = useRef(null) return ( <> { setContextMenuVisible(false) }} side="bottom" align="start" className="py-2" > { setContextMenuVisible(false) }} filesController={filesController} shouldShowRenameOption={false} shouldShowAttachOption={false} selectedFiles={files} /> ) } const FileLinksCell = ({ file, filesController, linkingController, featuresController, }: { file: FileItem filesController: FilesController linkingController: LinkingController featuresController: FeaturesController }) => { const [contextMenuVisible, setContextMenuVisible] = useState(false) const anchorElementRef = useRef(null) return ( <> { setContextMenuVisible(false) }} side="bottom" align="start" className="py-2" > ) } type Props = { application: WebApplication filesController: FilesController featuresController: FeaturesController linkingController: LinkingController } const FilesTableView = ({ application, filesController, featuresController, linkingController }: Props) => { const files = application.items .getDisplayableNotesAndFiles() .filter((item) => item.content_type === ContentType.File) as FileItem[] const [sortBy, setSortBy] = useState( application.getPreference(PrefKey.SortNotesBy, PrefDefaults[PrefKey.SortNotesBy]), ) const [sortReversed, setSortReversed] = useState( application.getPreference(PrefKey.SortNotesReverse, PrefDefaults[PrefKey.SortNotesReverse]), ) useEffect(() => { return application.addEventObserver(async (event) => { if (event === ApplicationEvent.PreferencesChanged) { setSortBy(application.getPreference(PrefKey.SortNotesBy, PrefDefaults[PrefKey.SortNotesBy])) setSortReversed(application.getPreference(PrefKey.SortNotesReverse, PrefDefaults[PrefKey.SortNotesReverse])) } }) }, [application]) const onSortChange = useCallback( async (sortBy: keyof SortableItem, sortReversed: boolean) => { await application.setPreference(PrefKey.SortNotesBy, sortBy) await application.setPreference(PrefKey.SortNotesReverse, sortReversed) }, [application], ) const [contextMenuFile, setContextMenuFile] = useState(undefined) const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | undefined>(undefined) const columnDefs: TableColumn[] = useMemo( () => [ { name: 'Name', sortBy: 'title', cell: (file) => { return (
{getFileIconComponent(getIconForFileType(file.mimeType), 'w-6 h-6 flex-shrink-0')} {file.title} {file.protected && ( )}
) }, }, { name: 'Upload date', sortBy: 'created_at', cell: (file) => { return formatDateForContextMenu(file.created_at) }, }, { name: 'Size', cell: (file) => { return formatSizeToReadableString(file.decryptedSize) }, }, { name: 'Attached to', cell: (file) => { const links = [ ...naturalSort(application.items.referencesForItem(file), 'title').map((item) => createLinkFromItem(item, 'linked'), ), ...naturalSort(application.items.itemsReferencingItem(file), 'title').map((item) => createLinkFromItem(item, 'linked-by'), ), ...application.items.getSortedTagsForItem(file).map((item) => createLinkFromItem(item, 'linked')), ] if (!links.length) { return null } return (
{ void application.items.unlinkItems(file, itemToUnlink) }} isBidirectional={false} /> {links.length > 1 && and {links.length - 1} more...}
) }, }, ], [application.items], ) const getRowId = useCallback((file: FileItem) => file.uuid, []) const table = useTable({ data: files, sortBy, sortReversed, onSortChange, getRowId, columns: columnDefs, enableRowSelection: true, enableMultipleRowSelection: true, onRowDoubleClick(file) { void filesController.handleFileAction({ type: FileItemActionType.PreviewFile, payload: { file, }, }) }, onRowContextMenu(x, y, file) { setContextMenuPosition({ x, y }) setContextMenuFile(file) }, rowActions: (file) => { const links = [ ...naturalSort(application.items.referencesForItem(file), 'title').map((item) => createLinkFromItem(item, 'linked'), ), ...naturalSort(application.items.itemsReferencingItem(file), 'title').map((item) => createLinkFromItem(item, 'linked-by'), ), ...application.items.getSortedTagsForItem(file).map((item) => createLinkFromItem(item, 'linked')), ] return (
{links.length > 0 && ( )}
) }, selectionActions: (fileIds) => ( fileIds.includes(file.uuid))} filesController={filesController} /> ), showSelectionActions: true, }) return ( <> {contextMenuPosition && contextMenuFile && ( { setContextMenuPosition(undefined) setContextMenuFile(undefined) }} side="bottom" align="start" className="py-2" > { setContextMenuPosition(undefined) setContextMenuFile(undefined) }} filesController={filesController} shouldShowRenameOption={false} shouldShowAttachOption={false} selectedFiles={[contextMenuFile]} /> )} ) } export default FilesTableView