multiple file download with progress bar and percentage
This commit is contained in:
parent
93376e071e
commit
e20a2f52a5
|
@ -14,10 +14,13 @@ import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
|
|||
import { extensions, preview } from '../utils/getPreviewType'
|
||||
import { useProtectedSWRInfinite } from '../utils/fetchWithSWR'
|
||||
import { getBaseUrl } from '../utils/getBaseUrl'
|
||||
import { downloadMultipleFiles, downloadTreelikeMultipleFiles, traverseFolder } from '../utils/downloadMultipleFiles'
|
||||
import {
|
||||
DownloadingToast,
|
||||
downloadMultipleFiles,
|
||||
downloadTreelikeMultipleFiles,
|
||||
traverseFolder,
|
||||
} from './MultiFileDownloader'
|
||||
|
||||
import { VideoPreview } from './previews/VideoPreview'
|
||||
import { AudioPreview } from './previews/AudioPreview'
|
||||
import Loading, { LoadingIcon } from './Loading'
|
||||
import FourOhFour from './FourOhFour'
|
||||
import Auth from './Auth'
|
||||
|
@ -25,6 +28,8 @@ import TextPreview from './previews/TextPreview'
|
|||
import MarkdownPreview from './previews/MarkdownPreview'
|
||||
import CodePreview from './previews/CodePreview'
|
||||
import OfficePreview from './previews/OfficePreview'
|
||||
import AudioPreview from './previews/AudioPreview'
|
||||
import VideoPreview from './previews/VideoPreview'
|
||||
import DownloadBtn from './DownloadBtn'
|
||||
|
||||
// Disabling SSR for some previews (image gallery view, and PDF view)
|
||||
|
@ -285,17 +290,16 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||
el.remove()
|
||||
} else if (files.length > 1) {
|
||||
setTotalGenerating(true)
|
||||
const toastId = toast.loading('Downloading selected files. Refresh to cancel, this may take some time...')
|
||||
downloadMultipleFiles({ toastId, files, folder })
|
||||
|
||||
const toastId = toast.loading(DownloadingToast(router))
|
||||
downloadMultipleFiles({ toastId, router, files, folder })
|
||||
.then(() => {
|
||||
setTotalGenerating(false)
|
||||
toast.dismiss(toastId)
|
||||
toast.success('Finished downloading selected files.')
|
||||
toast.success('Finished downloading selected files.', { id: toastId })
|
||||
})
|
||||
.catch(() => {
|
||||
setTotalGenerating(false)
|
||||
toast.dismiss(toastId)
|
||||
toast.error('Failed to download selected files.')
|
||||
toast.error('Failed to download selected files.', { id: toastId })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -314,18 +318,16 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||
})()
|
||||
|
||||
setFolderGenerating({ ...folderGenerating, [id]: true })
|
||||
const toastId = toast.loading('Downloading folder. Refresh to cancel, this may take some time...')
|
||||
const toastId = toast.loading(DownloadingToast(router))
|
||||
|
||||
downloadTreelikeMultipleFiles({ toastId, files, basePath: path, folder: name })
|
||||
downloadTreelikeMultipleFiles({ toastId, router, files, basePath: path, folder: name })
|
||||
.then(() => {
|
||||
setFolderGenerating({ ...folderGenerating, [id]: false })
|
||||
toast.dismiss(toastId)
|
||||
toast.success('Finished downloading folder.')
|
||||
toast.success('Finished downloading folder.', { id: toastId })
|
||||
})
|
||||
.catch(() => {
|
||||
setFolderGenerating({ ...folderGenerating, [id]: false })
|
||||
toast.dismiss(toastId)
|
||||
toast.error('Failed to download folder.')
|
||||
toast.error('Failed to download folder.', { id: toastId })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,37 @@
|
|||
import { NextRouter } from 'next/router'
|
||||
import toast from 'react-hot-toast'
|
||||
import JSZip from 'jszip'
|
||||
|
||||
import { fetcher } from './fetchWithSWR'
|
||||
import { getStoredToken } from './protectedRouteHandler'
|
||||
import { fetcher } from '../utils/fetchWithSWR'
|
||||
import { getStoredToken } from '../utils/protectedRouteHandler'
|
||||
|
||||
/**
|
||||
* Generates a loading toast with file download progress support
|
||||
* @param router Next router instance, used for reloading the page
|
||||
* @param progress Current downloading and compression progress (returned by jszip metadata)
|
||||
* @returns Toast component with loading progress
|
||||
*/
|
||||
export function DownloadingToast(router: NextRouter, progress?: string) {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-56">
|
||||
<span>Downloading {progress ? `${progress}%` : 'selected files...'}</span>
|
||||
|
||||
<div className="relative mt-2">
|
||||
<div className="overflow-hidden h-1 flex rounded bg-gray-100">
|
||||
<div style={{ width: `${progress}%` }} className="text-white bg-gray-500 transition-all duration-100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="p-2 rounded bg-red-500 hover:bg-red-400 text-white focus:outline-none focus:ring focus:ring-red-300"
|
||||
onClick={() => router.reload()}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Blob download helper
|
||||
export function downloadBlob({ blob, name }: { blob: Blob; name: string }) {
|
||||
|
@ -28,10 +57,12 @@ export function downloadBlob({ blob, name }: { blob: Blob; name: string }) {
|
|||
*/
|
||||
export async function downloadMultipleFiles({
|
||||
toastId,
|
||||
router,
|
||||
files,
|
||||
folder,
|
||||
}: {
|
||||
toastId: string
|
||||
router: NextRouter
|
||||
files: { name: string; url: string }[]
|
||||
folder?: string
|
||||
}): Promise<void> {
|
||||
|
@ -50,9 +81,7 @@ export async function downloadMultipleFiles({
|
|||
|
||||
// Create zip file and download it
|
||||
const b = await zip.generateAsync({ type: 'blob' }, metadata => {
|
||||
toast.loading(`Downloading ${metadata.percent.toFixed(0)}%. Refresh to cancel...`, {
|
||||
id: toastId,
|
||||
})
|
||||
toast.loading(DownloadingToast(router, metadata.percent.toFixed(0)), { id: toastId })
|
||||
})
|
||||
downloadBlob({ blob: b, name: folder ? folder + '.zip' : 'download.zip' })
|
||||
}
|
||||
|
@ -68,14 +97,15 @@ export async function downloadMultipleFiles({
|
|||
* @param basePath Root dir path of files to be downloaded
|
||||
* @param folder Optional folder name to hold files, otherwise flatten files in the zip
|
||||
*/
|
||||
|
||||
export async function downloadTreelikeMultipleFiles({
|
||||
toastId,
|
||||
router,
|
||||
files,
|
||||
basePath,
|
||||
folder,
|
||||
}: {
|
||||
toastId: string
|
||||
router: NextRouter
|
||||
files: AsyncGenerator<{
|
||||
name: string
|
||||
url?: string
|
||||
|
@ -117,9 +147,7 @@ export async function downloadTreelikeMultipleFiles({
|
|||
|
||||
// Create zip file and download it
|
||||
const b = await zip.generateAsync({ type: 'blob' }, metadata => {
|
||||
toast.loading(`Downloading ${metadata.percent.toFixed(0)}%. Refresh to cancel...`, {
|
||||
id: toastId,
|
||||
})
|
||||
toast.loading(DownloadingToast(router, metadata.percent.toFixed(0)), { id: toastId })
|
||||
})
|
||||
downloadBlob({ blob: b, name: folder ? folder + '.zip' : 'download.zip' })
|
||||
}
|
|
@ -11,7 +11,7 @@ enum PlayerState {
|
|||
Paused,
|
||||
}
|
||||
|
||||
export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||
const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
||||
|
||||
return (
|
||||
|
@ -79,3 +79,5 @@ export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AudioPreview
|
||||
|
|
|
@ -6,9 +6,9 @@ import { useClipboard } from 'use-clipboard-copy'
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import toast, { Toaster } from 'react-hot-toast'
|
||||
|
||||
import { getBaseUrl } from "../../utils/getBaseUrl"
|
||||
import { getBaseUrl } from '../../utils/getBaseUrl'
|
||||
|
||||
export const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||
const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||
const { asPath } = useRouter()
|
||||
const clipboard = useClipboard()
|
||||
|
||||
|
@ -81,3 +81,5 @@ export const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default VideoPreview
|
||||
|
|
Loading…
Reference in a new issue