import { BranchesOutlined, GlobalOutlined, HomeOutlined, ProfileOutlined, TeamOutlined } from '@ant-design/icons' import { Button, Col, Descriptions, Input, Layout, Menu, Modal, Row, Table, TablePaginationConfig, Typography } from 'antd' import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/lib/table/interface' import moment from 'moment' import prettyBytes from 'pretty-bytes' import QueryString from 'qs' import React, { useEffect, useState } from 'react' import { useThemeSwitcher } from 'react-css-theme-switcher' import { useHistory } from 'react-router-dom' import useSWR from 'swr' import { fetcher } from '../../../utils/Fetcher' import Footer from '../../components/Footer' import Navbar from '../../components/Navbar' import Breadcrumb from '../../dashboard/components/Breadcrumb' import Icon from './Icon' interface Props { me: any, data: any } const TableFiles: React.FC = ({ me, data }) => { const history = useHistory() const [dataChanges, setDataChanges] = useState<{ pagination?: TablePaginationConfig, filters?: Record, sorter?: SorterResult | SorterResult[] }>() const { currentTheme } = useThemeSwitcher() const [parent, setParent] = useState | null>() const [breadcrumbs, setBreadcrumbs] = useState([data?.file || { id: null, name: <> }]) const [scrollTop, setScrollTop] = useState(0) const [keyword, setKeyword] = useState() const [filesData, setFilesData] = useState() const [params, setParams] = useState() const [loading, setLoading] = useState(false) const [showDetails, setShowDetails] = useState() const { data: filesParts } = useSWR(showDetails ? `/files?name.like=${showDetails.name.replace(/\.part0*\d+$/, '')}&user_id=${showDetails.user_id}${me?.user.id !== showDetails.user_id ? '&shared=1' : ''}&parent_id${showDetails.parent_id ? `=${showDetails.parent_id}` : '=null'}` : null, fetcher) const [popup, setPopup] = useState<{ visible: boolean, x?: number, y?: number, row?: any }>() const { data: files, mutate: _refetch } = useSWR(data?.file.type === 'folder' && data?.file.sharing_options?.includes('*') && params ? `/files?exclude_parts=1&${QueryString.stringify(params)}` : null, fetcher, { onSuccess: files => { setLoading(false) if (files?.files) { let newData: any[] = [] if (!params?.offset || !dataChanges?.pagination?.current || dataChanges?.pagination?.current === 1) { newData = files.files.map((file: any) => ({ ...file, key: file.id })) } else { newData = [ ...filesData.map(row => files.files.find((file: any) => file.id === row.id) || row).map(file => ({ ...file, key: file.id })), ...files.files.map((file: any) => ({ ...file, key: file.id })) ].reduce((res, row) => [ ...res, !res.filter(Boolean).find((r: any) => r.id === row.id) ? row : null ], []).filter(Boolean) } setFilesData(newData) } } }) const PAGE_SIZE = 10 const fetch = (pagination?: TablePaginationConfig, filters?: Record, sorter?: SorterResult | SorterResult[], actions?: TableCurrentDataSource) => { setLoading(true) setParams({ // ...parent?.id ? { parent_id: parent.link_id || parent.id } : { 'parent_id.is': 'null' }, parent_id: parent?.link_id || parent?.id || data?.file.id, ...keyword ? { 'name.ilike': `%${keyword}%` } : {}, shared: 1, limit: PAGE_SIZE, offset: pagination?.current === 1 || actions?.action || keyword && params?.offset ? 0 : filesData?.length, ...Object.keys(filters || {})?.reduce((res, key: string) => { if (!filters) return res if (key === 'type' && filters[key]?.length) { return { ...res, [`${key}.in`]: `(${filters[key]?.map(val => `'${val}'`).join(',')})` } } return { ...res, [key]: filters[key]?.[0] } }, {}), ...(sorter as SorterResult)?.order ? { sort: `${(sorter as SorterResult).column?.dataIndex}:${(sorter as SorterResult).order?.replace(/end$/gi, '')}` } : { sort: 'created_at:desc' }, t: new Date().getTime() }) } const onChange = async (pagination?: TablePaginationConfig, filters?: Record, sorter?: SorterResult | SorterResult[], actions?: TableCurrentDataSource) => { setDataChanges({ pagination, filters, sorter }) fetch(pagination, filters, sorter, actions) } const onRowClick = (row: any) => { if (row.type === 'folder') { setParent(row) setBreadcrumbs([...breadcrumbs, row]) const searchParams = new URLSearchParams(window.location.search) searchParams.set('parent', row.id) return history.push(`${location.pathname}?${searchParams.toString()}`) } else { return history.push(`/view/${row.id}`) } } useEffect(() => { const nextPage = () => { setScrollTop(document.body.scrollTop) } nextPage() document.body.addEventListener('scroll', nextPage) }, []) useEffect(() => { const footer = document.querySelector('.ant-layout-footer') if (scrollTop >= document.body.scrollHeight - document.body.clientHeight - (footer?.clientHeight || 0) && files?.files.length >= PAGE_SIZE) { onChange({ ...dataChanges?.pagination, current: (dataChanges?.pagination?.current || 1) + 1 }, dataChanges?.filters, dataChanges?.sorter) } }, [scrollTop]) useEffect(() => { if (parent !== undefined || keyword !== undefined) { onChange({ ...dataChanges?.pagination, current: 1 }, dataChanges?.filters, dataChanges?.sorter) setScrollTop(0) } }, [keyword, parent]) const ContextMenu = () => { const baseProps = { style: { margin: 0 } } if (!popup?.visible) return <> if (popup?.row) { return } key="details" onClick={() => setShowDetails(popup?.row)}>Details } return <> } return )?.column?.dataIndex === 'name' ? (dataChanges?.sorter as SorterResult).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 } }, { title: 'Size', dataIndex: 'size', key: 'size', sorter: true, sortOrder: (dataChanges?.sorter as SorterResult)?.column?.key === 'size' ? (dataChanges?.sorter as SorterResult).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: (dataChanges?.sorter as SorterResult)?.column?.key === 'uploaded_at' ? (dataChanges?.sorter as SorterResult).order : undefined, responsive: ['md'], width: 250, align: 'center', render: (value: any, row: any) => row.upload_progress !== null ? <>Uploading {Number((row.upload_progress * 100).toFixed(2))}% : moment(value).local().format('llll') } ]} onChange={onChange} pagination={false} scroll={{ x: 330 }} onRow={(row, index) => ({ index, 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, y: e.clientY // x: e.clientX - (parent?.getBoundingClientRect().left || 0), // y: e.clientY - (parent?.getBoundingClientRect().top || 0) }) } })} />
{showDetails?.name.replace(/\.part0*\d+$/, '')}} visible={Boolean(showDetails)} onCancel={() => setShowDetails(undefined)} okText="View" onOk={() => 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')} } export default TableFiles