ExportAllRows.progress.tsx104 lines · main
| 1 | import { useCallback, useRef, type ReactNode } from 'react' |
| 2 | import { toast } from 'sonner' |
| 3 | import { SonnerProgress } from 'ui' |
| 4 | |
| 5 | export 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 | } |