diff --git a/frontend/package.json b/frontend/package.json index f97d5b0..f61fc80 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.23.0", + "react-virtuoso": "^4.7.11", "recoil": "^0.7.7", "rxjs": "^7.8.1" }, @@ -31,8 +32,8 @@ "@types/react-helmet": "^6.1.11", "@types/react-router-dom": "^5.3.3", "@vitejs/plugin-react-swc": "^3.6.0", + "million": "^3.0.6", "typescript": "^5.4.3", - "vite": "^5.2.11", - "million": "^3.0.6" + "vite": "^5.2.11" } } \ No newline at end of file diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index cf15211..5421100 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: react-router-dom: specifier: ^6.23.0 version: 6.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-virtuoso: + specifier: ^4.7.11 + version: 4.7.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) recoil: specifier: ^0.7.7 version: 0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1013,6 +1016,13 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' + react-virtuoso@4.7.11: + resolution: {integrity: sha512-Kdn9qEtQI2ulEuBMzW2BTkDsfijB05QUd6lpZ1K36oyA3k65Cz4lG4EKrh2pCfUafX4C2uMSZOwzMOhbrMOTFA==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16 || >=17 || >= 18' + react-dom: '>=16 || >=17 || >= 18' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -2036,6 +2046,11 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-virtuoso@4.7.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 diff --git a/frontend/src/components/DownloadsTableView.tsx b/frontend/src/components/DownloadsTableView.tsx index 9330733..b6b12b3 100644 --- a/frontend/src/components/DownloadsTableView.tsx +++ b/frontend/src/components/DownloadsTableView.tsx @@ -18,12 +18,61 @@ import { TableRow, Typography } from "@mui/material" +import { forwardRef } from 'react' +import { TableComponents, TableVirtuoso } from 'react-virtuoso' import { useRecoilValue } from 'recoil' import { activeDownloadsState } from '../atoms/downloads' import { serverURL } from '../atoms/settings' import { useRPC } from '../hooks/useRPC' +import { RPCResult } from '../types' import { base64URLEncode, formatSize, formatSpeedMiB } from "../utils" + +const columns = [ + { + width: 8, + label: 'Status', + dataKey: 'status', + numeric: false, + }, + { + width: 500, + label: 'Title', + dataKey: 'title', + numeric: false, + }, + { + width: 50, + label: 'Speed', + dataKey: 'speed', + numeric: true, + }, + { + width: 150, + label: 'Progress', + dataKey: 'progress', + numeric: true, + }, + { + width: 80, + label: 'Size', + dataKey: 'size', + numeric: true, + }, + { + width: 100, + label: 'Added on', + dataKey: 'addedon', + numeric: true, + }, + { + width: 80, + label: 'Actions', + dataKey: 'actions', + numeric: true, + }, +] as const + function LinearProgressWithLabel(props: LinearProgressProps & { value: number }) { return ( @@ -39,10 +88,40 @@ function LinearProgressWithLabel(props: LinearProgressProps & { value: number }) ) } -const DownloadsTableView: React.FC = () => { - const serverAddr = useRecoilValue(serverURL) - const downloads = useRecoilValue(activeDownloadsState) +const VirtuosoTableComponents: TableComponents = { + Scroller: forwardRef((props, ref) => ( + + )), + Table: (props) => ( + + ), + TableHead, + TableRow: ({ item: _item, ...props }) => , + TableBody: forwardRef((props, ref) => ( + + )), +} +function fixedHeaderContent() { + return ( + + {columns.map((column) => ( + + {column.label} + + ))} + + ) +} + +const DownloadsTableView: React.FC = () => { + const downloads = useRecoilValue(activeDownloadsState) + const serverAddr = useRecoilValue(serverURL) const { client } = useRPC() const abort = (id: string) => client.kill(id) @@ -57,102 +136,78 @@ const DownloadsTableView: React.FC = () => { window.open(`${serverAddr}/archive/d/${encoded}?token=${localStorage.getItem('token')}`) } - return ( -
- - - - Status - - - Title - - - Speed - - - Progress - - - Size - - - Added on - - - Actions - - - - - { - downloads.map(download => ( - - - {download.progress.percentage === '-1' - ? - : - } - - {download.info.title} - {formatSpeedMiB(download.progress.speed)} - - - - {formatSize(download.info.filesize_approx ?? 0)} - - {new Date(download.info.created_at).toLocaleString()} - - - - abort(download.id)} - > - {download.progress.percentage === '-1' ? : } - - - {download.progress.percentage === '-1' && - <> - viewFile(download.output.savedFilePath)} - > - - - downloadFile(download.output.savedFilePath)} - > - - - - } - - - - )) + function rowContent(_index: number, download: RPCResult) { + return ( + <> + + {download.progress.percentage === '-1' + ? + : } - -
-
+ + {download.info.title} + {formatSpeedMiB(download.progress.speed)} + + + + {formatSize(download.info.filesize_approx ?? 0)} + + {new Date(download.info.created_at).toLocaleString()} + + + + abort(download.id)} + > + {download.progress.percentage === '-1' ? : } + + + {download.progress.percentage === '-1' && + <> + viewFile(download.output.savedFilePath)} + > + + + downloadFile(download.output.savedFilePath)} + > + + + + } + + + + ) + } + + return ( + + ) }