diff --git a/components/files/files-list.js b/components/files/files-list.js
index 9355e51..66ffd8f 100644
--- a/components/files/files-list.js
+++ b/components/files/files-list.js
@@ -5,6 +5,7 @@ import ListSubheader from '@mui/material/ListSubheader'
import Typography from '@mui/material/Typography'
import Box from '@mui/material/Box'
import Skeleton from '@mui/material/Skeleton'
+import LinearProgress from '@mui/material/LinearProgress'
import {useTheme, useColorScheme, styled} from '@mui/material/styles'
import Table from '@mui/material/Table'
import TableHead from '@mui/material/TableHead'
@@ -13,7 +14,6 @@ import TableCell, {tableCellClasses} from '@mui/material/TableCell'
import TableRow from '@mui/material/TableRow'
import TableContainer from '@mui/material/TableContainer'
import Paper from '@mui/material/Paper'
-import FileSaver from 'file-saver'
import DescriptionIcon from '@mui/icons-material/Description'
import LibraryMusicIcon from '@mui/icons-material/LibraryMusic'
import {Link} from '@mui/material'
@@ -53,6 +53,7 @@ export default function FilesList({files}) {
const theme = useTheme()
const {mode} = useColorScheme()
const [audioMeta, setAudioMeta] = useState(audioMetaCache)
+ const [downloading, setDownloading] = useState({})
const musicFiles = files.filter(file => file.mime.startsWith('audio'))
const pdfFiles = files.filter(file => file.mime === 'application/pdf')
@@ -148,9 +149,42 @@ export default function FilesList({files}) {
)
}
- const handleClick = (e, url, fileName) => {
+ const handleClick = async (e, url, fileName, fileId) => {
e.stopPropagation()
- FileSaver.saveAs(url, fileName)
+ if (fileId in downloading) return
+
+ setDownloading(prev => ({...prev, [fileId]: 0}))
+ try {
+ const response = await fetch(url)
+ const contentLength = +response.headers.get('content-length')
+ const reader = response.body.getReader()
+ const chunks = []
+ let received = 0
+
+ while (true) {
+ const {done, value} = await reader.read()
+ if (done) break
+ chunks.push(value)
+ received += value.length
+ if (contentLength) {
+ setDownloading(prev => ({...prev, [fileId]: Math.round(received / contentLength * 100)}))
+ }
+ }
+
+ const blob = new Blob(chunks, {type: response.headers.get('content-type')})
+ const blobUrl = URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.href = blobUrl
+ a.download = fileName
+ a.click()
+ URL.revokeObjectURL(blobUrl)
+ } finally {
+ setDownloading(prev => {
+ const next = {...prev}
+ delete next[fileId]
+ return next
+ })
+ }
}
return (
@@ -185,13 +219,24 @@ export default function FilesList({files}) {
handleClick(e, `${apiUrl}${file.url}`, file.name)}
+ onClick={e => handleClick(e, `${apiUrl}${file.url}`, file.name, file.id)}
>
{file.name}
({formatSize(file.size)})
+ {file.id in downloading && (
+
+ 0 ? 'determinate' : 'indeterminate'}
+ value={downloading[file.id]}
+ />
+
+ {downloading[file.id]} %
+
+
+ )}
))}