mirror of
https://github.com/marcogll/TaxHacker_s23.git
synced 2026-01-13 13:25:18 +00:00
feat: add progress indication for long downloads
This commit is contained in:
48
hooks/use-download.tsx
Normal file
48
hooks/use-download.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react"
|
||||
|
||||
interface UseDownloadOptions {
|
||||
onSuccess?: () => void
|
||||
onError?: (error: Error) => void
|
||||
}
|
||||
|
||||
export function useDownload(options: UseDownloadOptions = {}) {
|
||||
const [isDownloading, setIsDownloading] = useState(false)
|
||||
|
||||
const download = async (url: string, defaultName: string) => {
|
||||
try {
|
||||
setIsDownloading(true)
|
||||
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) throw new Error("Download failed")
|
||||
|
||||
// Get the filename from the Content-Disposition header
|
||||
const contentDisposition = response.headers.get("Content-Disposition")
|
||||
const filename = contentDisposition ? contentDisposition.split("filename=")[1].replace(/"/g, "") : defaultName
|
||||
|
||||
// Create a blob from the response
|
||||
const blob = await response.blob()
|
||||
|
||||
// Create a download link and trigger it
|
||||
const downloadLink = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement("a")
|
||||
a.href = downloadLink
|
||||
a.download = filename
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(downloadLink)
|
||||
document.body.removeChild(a)
|
||||
|
||||
options.onSuccess?.()
|
||||
} catch (error) {
|
||||
console.error("Download error:", error)
|
||||
options.onError?.(error instanceof Error ? error : new Error("Download failed"))
|
||||
} finally {
|
||||
setIsDownloading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
download,
|
||||
isDownloading,
|
||||
}
|
||||
}
|
||||
85
hooks/use-progress.tsx
Normal file
85
hooks/use-progress.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
interface Progress {
|
||||
id: string
|
||||
current: number
|
||||
total: number
|
||||
type: string
|
||||
data: any
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
interface UseProgressOptions {
|
||||
onSuccess?: (progress: Progress) => void
|
||||
onError?: (error: Error) => void
|
||||
}
|
||||
|
||||
export function useProgress(options: UseProgressOptions = {}) {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [eventSource, setEventSource] = useState<EventSource | null>(null)
|
||||
const [progress, setProgress] = useState<Progress | null>(null)
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (eventSource) {
|
||||
eventSource.close()
|
||||
}
|
||||
}
|
||||
}, [eventSource])
|
||||
|
||||
const startProgress = async (type: string) => {
|
||||
setIsLoading(true)
|
||||
setProgress(null)
|
||||
|
||||
// Close any existing connection
|
||||
if (eventSource) {
|
||||
eventSource.close()
|
||||
}
|
||||
|
||||
try {
|
||||
const progressId = crypto.randomUUID()
|
||||
const source = new EventSource(`/api/progress/${progressId}?type=${type}`)
|
||||
setEventSource(source)
|
||||
|
||||
source.onmessage = (event) => {
|
||||
try {
|
||||
const progress = JSON.parse(event.data)
|
||||
setProgress(progress)
|
||||
options.onSuccess?.(progress)
|
||||
|
||||
if (progress.current === progress.total && progress.total > 0) {
|
||||
source.close()
|
||||
setIsLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to parse progress data:", error)
|
||||
source.close()
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = (error) => {
|
||||
source.close()
|
||||
setIsLoading(false)
|
||||
const err = new Error("Progress tracking failed")
|
||||
console.error("Progress tracking error:", err)
|
||||
options.onError?.(err)
|
||||
}
|
||||
|
||||
return progressId
|
||||
} catch (error) {
|
||||
setIsLoading(false)
|
||||
const err = error instanceof Error ? error : new Error("Failed to start progress")
|
||||
console.error("Failed to start progress:", err)
|
||||
options.onError?.(err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
startProgress,
|
||||
progress,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user