ExportAllRows.progress.tsx104 lines · main
1import { useCallback, useRef, type ReactNode } from 'react'
2import { toast } from 'sonner'
3import { SonnerProgress } from 'ui'
4
5export const useProgressToasts = () => {
6 const toastIdsRef = useRef(new Map<number, string | number>())
7
8 const startProgressTracker = useCallback(
9 ({
10 id,
11 name,
12 trackPercentage = false,
13 }: {
14 id: number
15 name: string
16 trackPercentage?: boolean
17 }) => {
18 if (toastIdsRef.current.has(id)) return
19
20 if (trackPercentage) {
21 toastIdsRef.current.set(
22 id,
23 toast(<SonnerProgress progress={0} message={`Exporting ${name}...`} />, {
24 closeButton: false,
25 duration: Infinity,
26 })
27 )
28 } else {
29 toastIdsRef.current.set(id, toast.loading(`Exporting ${name}...`))
30 }
31 },
32 []
33 )
34
35 const trackPercentageProgress = useCallback(
36 ({
37 id,
38 name,
39 value,
40 totalRows,
41 }: {
42 id: number
43 name: string
44 value: number
45 totalRows: number
46 }) => {
47 const savedToastId = toastIdsRef.current.get(id)
48
49 const progress = Math.min((value / totalRows) * 100, 100)
50 const newToastId = toast(
51 <SonnerProgress progress={progress} message={`Exporting ${name}...`} />,
52 {
53 id: savedToastId,
54 closeButton: false,
55 duration: Infinity,
56 }
57 )
58
59 if (!savedToastId) toastIdsRef.current.set(id, newToastId)
60 },
61 []
62 )
63
64 const stopTrackerWithError = useCallback(
65 (id: number, name: string, customMessage?: ReactNode) => {
66 const savedToastId = toastIdsRef.current.get(id)
67 if (savedToastId) {
68 toast.dismiss(savedToastId)
69 toastIdsRef.current.delete(id)
70 }
71
72 toast.error(customMessage ?? `There was an error exporting ${name}`)
73 },
74 []
75 )
76
77 const dismissTrackerSilently = useCallback((id: number) => {
78 const savedToastId = toastIdsRef.current.get(id)
79 if (savedToastId) {
80 toast.dismiss(savedToastId)
81 toastIdsRef.current.delete(id)
82 }
83 }, [])
84
85 const markTrackerComplete = useCallback((id: number, totalRows: number) => {
86 const savedToastId = toastIdsRef.current.get(id)
87 const deleteSavedToastId = () => toastIdsRef.current.delete(id)
88
89 toast.success(`Successfully exported ${totalRows} rows`, {
90 id: savedToastId,
91 duration: 4000,
92 onAutoClose: deleteSavedToastId,
93 onDismiss: deleteSavedToastId,
94 })
95 }, [])
96
97 return {
98 startProgressTracker,
99 trackPercentageProgress,
100 stopTrackerWithError,
101 dismissTrackerSilently,
102 markTrackerComplete,
103 }
104}