| import { useState, useEffect, useMemo, useCallback } from 'react'; | |
| export default function useProgress(initialProgress = 0.01, increment = 0.007, fileSize?: number) { | |
| const calculateIncrement = useCallback( | |
| (size?: number) => { | |
| const baseRate = 0.05; | |
| const minRate = 0.002; | |
| const sizeMB = size ? size / (1024 * 1024) : 0; | |
| if (!size) { | |
| return increment; | |
| } | |
| if (sizeMB <= 1) { | |
| return baseRate * 2; | |
| } else { | |
| return Math.max(baseRate / Math.sqrt(sizeMB), minRate); | |
| } | |
| }, | |
| [increment], | |
| ); | |
| const incrementValue = useMemo( | |
| () => calculateIncrement(fileSize), | |
| [fileSize, calculateIncrement], | |
| ); | |
| const [progress, setProgress] = useState(initialProgress); | |
| const getDynamicIncrement = useCallback( | |
| (currentProgress: number) => { | |
| if (!fileSize) { | |
| return incrementValue; | |
| } | |
| if (currentProgress < 0.7) { | |
| return incrementValue; | |
| } else { | |
| return Math.max(0.0005, incrementValue * (1 - currentProgress)); | |
| } | |
| }, | |
| [incrementValue, fileSize], | |
| ); | |
| useEffect(() => { | |
| let timeout: ReturnType<typeof setTimeout>; | |
| let timer: ReturnType<typeof setInterval>; | |
| if (initialProgress >= 1 && progress >= 1) { | |
| return; | |
| } else if (initialProgress >= 1 && progress < 1) { | |
| setProgress(0.99); | |
| timeout = setTimeout(() => { | |
| setProgress(1); | |
| }, 200); | |
| } else { | |
| timer = setInterval(() => { | |
| setProgress((prevProgress) => { | |
| if (prevProgress >= 1) { | |
| clearInterval(timer); | |
| return 1; | |
| } | |
| const currentIncrement = getDynamicIncrement(prevProgress); | |
| return Math.min(prevProgress + currentIncrement, 0.95); | |
| }); | |
| }, 200); | |
| } | |
| return () => { | |
| clearInterval(timer); | |
| clearTimeout(timeout); | |
| }; | |
| }, [progress, initialProgress, incrementValue, fileSize, getDynamicIncrement]); | |
| return progress; | |
| } | |