import { ArrowRightOutlined, AudioOutlined, BranchesOutlined, CopyOutlined, DeleteOutlined, DownloadOutlined, CloudDownloadOutlined, EditOutlined, FileImageOutlined, FileOutlined, FilePdfOutlined, FolderOpenOutlined, GlobalOutlined, ProfileOutlined, ScissorOutlined, ShareAltOutlined, SnippetsOutlined, TeamOutlined, VideoCameraOutlined } from '@ant-design/icons' import { Descriptions, Menu, Modal, Table, Tag, Typography } from 'antd' import { SorterResult } from 'antd/lib/table/interface' import moment from 'moment' import prettyBytes from 'pretty-bytes' import React, { useCallback, useEffect, useRef, useState } from 'react' import { DndProvider, useDrag, useDrop } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import useSWR from 'swr' import { directDownload } from '../../../utils/Download' import { apiUrl, fetcher } from '../../../utils/Fetcher' interface Props { files?: any, tab: string, me?: any, onChange: (...args: any[]) => void, onDelete: (row: any) => void, onRename: (row: any) => void, onShare: (row: any, action: string) => void, onRowClick: (row: any) => void, onCut?: (row: any) => void, onCopy?: (row: any) => void, onPaste?: (rows: any[]) => void, onCutAndPaste?: (dragRow: any, hoverRow: any) => void, loading?: boolean, sorterData?: SorterResult, dataSource: any[], action?: string, dataSelect: [any[], (data: any[]) => void] } const TableFiles: React.FC = ({ files, tab, me, onChange, onDelete, onRename, onShare, onRowClick, onCut, onCopy, onPaste, onCutAndPaste, loading, sorterData, dataSource, action, dataSelect: [selected, setSelected] }) => { const [popup, setPopup] = useState<{ visible: boolean, x?: number, y?: number, row?: any }>() const [showDetails, setShowDetails] = useState() const { data: user } = useSWR(showDetails ? `/users/${showDetails.user_id}` : null, fetcher) const { data: filesParts } = useSWR(showDetails ? `/files?name.like=${showDetails.name.replace(/\.part0*\d+$/, '')}&user_id=${showDetails.user_id}&parent_id${showDetails.parent_id ? `=${showDetails.parent_id}` : '=null'}${tab === 'shared' ? '&shared=1' : ''}` : null, fetcher) const pasteEnabled = useRef(null) useEffect(() => { pasteEnabled.current = Boolean(selected?.length && action) const context = document.querySelector('.App') context?.addEventListener('contextmenu', function rightClick(e) { if (pasteEnabled.current && !(e.target as any)?.outerHTML.match(/^\ { if (type === 'image') { return } else if (type === 'video') { return } else if (type === 'document') { return } else if (type === 'folder') { return } else if (type === 'audio') { return } else { return } } const ContextMenu = () => { const baseProps = { style: { margin: 0 } } if (!popup?.visible) return <> if (popup?.row) { return } key="details" onClick={() => setShowDetails(popup?.row)}>Details {tab === 'mine' && <> } key="rename" onClick={() => onRename(popup?.row)}>Rename {!popup?.row.link_id ? } key="copy" onClick={() => onCopy?.(popup?.row)}>Copy : ''} } key="cut" onClick={() => onCut?.(popup?.row)}>Cut } key="share" onClick={() => onShare(popup?.row, 'share')}>Share {popup?.row.type !== 'folder' ? } key="send" onClick={() => onShare(popup?.row, 'forward')}>Send to : ''} {popup?.row.type !== 'folder' ? } key="download" onClick={async () => { location.replace(`${apiUrl}/files/${popup?.row.id}?raw=1&dl=1`) }}>Download : ''} {popup?.row.type !== 'folder' ? } key="fastdownload" onClick={async () => { popup?.row && await directDownload(popup?.row.id, popup?.row.name.replace(/\.part0*\d+$/, '')) }}>Fast Download beta : ''} } key="delete" danger onClick={() => onDelete(popup?.row)}>Delete } } if (selected?.length && action) { return } key="paste" onClick={() => onPaste?.(selected)}>Paste } return <> } const columns = [ { title: 'File', dataIndex: 'name', key: 'type', sorter: true, sortOrder: sorterData?.column?.dataIndex === 'name' ? sorterData.order : undefined, filters: [ { text: 'Folder', value: 'folder' }, { text: 'Image', value: 'image' }, { text: 'Video', value: 'video' }, { text: 'Audio', value: 'audio' }, { text: 'Document', value: 'document' }, { text: 'Unknown', value: 'unknown' } ], ellipsis: true, onCell: (row: any) => ({ onClick: () => onRowClick(row) }), render: (_: any, row: any) => { let type: any if (row.sharing_options?.includes('*')) { type = } else if (row.sharing_options?.length) { type = } return <> {row.link_id ? : '' } {type} {row.name?.replace(/\.part0*\d+$/, '')} } }, { title: 'Size', dataIndex: 'size', key: 'size', sorter: true, sortOrder: sorterData?.column?.key === 'size' ? sorterData.order : undefined, responsive: ['md'], width: 100, align: 'center', render: (value: any) => { if (Number(value) === 2_000_000_000) { return '> 2 GB' } return value ? prettyBytes(Number(value)) : '-' } }, { title: 'Uploaded At', dataIndex: 'uploaded_at', key: 'uploaded_at', sorter: true, sortOrder: sorterData?.column?.key === 'uploaded_at' ? sorterData.order : undefined, responsive: ['md'], width: 250, align: 'center', render: (value: any, row: any) => row.upload_progress !== null ? <>Uploading... : moment(value).local().format('llll') } ] const DraggableBodyRow = ({ index, moveRow, className, style, ...restProps }) => { const ref = useRef() const [{ isOver, dropClassName }, drop] = useDrop({ accept: 'DraggableBodyRow', collect: (monitor: any) => { const { index: dragIndex } = monitor.getItem() || {} if (dragIndex === index) { return {} } return { isOver: monitor.isOver(), dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward', } }, drop: (item: any) => { moveRow(item.index, index) }, }) const [, drag] = useDrag({ type: 'DraggableBodyRow', item: { index }, collect: monitor => ({ isDragging: monitor.isDragging(), }), }) drop(drag(ref)) return ( ) } return <> row.key), onChange: (_: React.Key[], rows: any[]) => setSelected(rows) }} dataSource={dataSource} columns={columns as any} components={{ body: { row: DraggableBodyRow } }} onChange={onChange} pagination={false} scroll={{ x: 330 }} onRow={(row, index) => ({ index, moveRow: useCallback((dragIndex, hoverIndex) => { const hoverRow = dataSource[hoverIndex] const dragRow = dataSource[dragIndex] if (hoverRow.type === 'folder') { onCutAndPaste?.(dragRow, hoverRow) } }, [dataSource, selected]), onContextMenu: e => { // if (tab !== 'mine') return e.preventDefault() if (!popup?.visible) { document.addEventListener('click', function onClickOutside() { setPopup({ visible: false }) document.removeEventListener('click', onClickOutside) }) } const parent = document.querySelector('.ant-col-24.ant-col-md-20.ant-col-md-offset-2') setPopup({ row, visible: true, x: e.clientX - (parent?.getBoundingClientRect().left || 0), y: e.clientY - (parent?.getBoundingClientRect().top || 0) }) } })} expandable={me?.settings?.expandable_rows && window.innerWidth < 752 ? { expandedRowRender: (row: any) => {row.size ? prettyBytes(Number(row.size)) : '-'} {row.upload_progress !== null ? <>Uploading {Number((row.upload_progress * 100).toFixed(2))}% : moment(row.uploaded_at).local().format('lll')} , rowExpandable: (_: any) => window.innerWidth < 752, } : undefined} /> {showDetails?.name.replace(/\.part0*\d+$/, '')}} visible={Boolean(showDetails)} onCancel={() => setShowDetails(undefined)} okText="View" onOk={() => { setShowDetails(undefined) onRowClick(showDetails) }} cancelButtonProps={{ shape: 'round' }} okButtonProps={{ shape: 'round' }}> {filesParts?.length ? prettyBytes(filesParts?.files.reduce((res: number, file: any) => res + Number(file.size), 0)) + ` (${filesParts?.length} parts)` : showDetails?.size && prettyBytes(Number(showDetails?.size || 0))} {moment(showDetails?.uploaded_at).local().format('lll')} @{user?.user.username} } export default TableFiles