|
1 | | -import { useState, useEffect, useRef } from 'react'; |
| 1 | +import { useState } from 'react'; |
2 | 2 | import { Time, Validate } from './utils'; |
| 3 | +import { useInterval } from './hooks'; |
3 | 4 |
|
4 | | -export default function useTimer(settings) { |
5 | | - const { expiryTimestamp: expiry, onExpire } = settings || {}; |
| 5 | +const DEFAULT_DELAY = 1000; |
| 6 | +export default function useTimer({ expiryTimestamp: expiry, onExpire, autoStart }) { |
6 | 7 | const [expiryTimestamp, setExpiryTimestamp] = useState(expiry); |
7 | 8 | const [seconds, setSeconds] = useState(Time.getSecondsFromExpiry(expiryTimestamp)); |
8 | | - const [isRunning, setIsRunning] = useState(true); |
9 | | - const intervalRef = useRef(); |
10 | | - |
11 | | - function clearIntervalRef() { |
12 | | - if (intervalRef.current) { |
13 | | - setIsRunning(false); |
14 | | - clearInterval(intervalRef.current); |
15 | | - intervalRef.current = undefined; |
16 | | - } |
17 | | - } |
| 9 | + const [isRunning, setIsRunning] = useState(autoStart); |
| 10 | + const [didStart, setDidStart] = useState(autoStart); |
| 11 | + const extraMilliSeconds = Math.floor((seconds - Math.floor(seconds)) * 1000); |
| 12 | + const [delay, setDelay] = useState(extraMilliSeconds > 0 ? extraMilliSeconds : 1000); |
18 | 13 |
|
19 | 14 | function handleExpire() { |
20 | | - clearIntervalRef(); |
21 | 15 | Validate.onExpire(onExpire) && onExpire(); |
22 | | - } |
23 | | - |
24 | | - function start() { |
25 | | - if (!intervalRef.current) { |
26 | | - setIsRunning(true); |
27 | | - intervalRef.current = setInterval(() => { |
28 | | - const secondsValue = Time.getSecondsFromExpiry(expiryTimestamp); |
29 | | - if (secondsValue <= 0) { |
30 | | - handleExpire(); |
31 | | - } |
32 | | - setSeconds(secondsValue); |
33 | | - }, 1000); |
34 | | - } |
| 16 | + setIsRunning(false); |
| 17 | + setDelay(null); |
35 | 18 | } |
36 | 19 |
|
37 | 20 | function pause() { |
38 | | - clearIntervalRef(); |
| 21 | + setIsRunning(false); |
39 | 22 | } |
40 | 23 |
|
41 | | - function resume() { |
42 | | - if (!intervalRef.current) { |
43 | | - setIsRunning(true); |
44 | | - intervalRef.current = setInterval(() => setSeconds((prevSeconds) => { |
45 | | - const secondsValue = prevSeconds - 1; |
46 | | - if (secondsValue <= 0) { |
47 | | - handleExpire(); |
48 | | - } |
49 | | - return secondsValue; |
50 | | - }), 1000); |
51 | | - } |
52 | | - } |
53 | | - |
54 | | - function restart(newExpiryTimestamp) { |
55 | | - clearIntervalRef(); |
| 24 | + function restart(newExpiryTimestamp, newAutoStart) { |
| 25 | + const secondsValue = Time.getSecondsFromExpiry(newExpiryTimestamp); |
| 26 | + const extraMilliSecondsValue = Math.floor((secondsValue - Math.floor(secondsValue)) * 1000); |
| 27 | + setDelay(extraMilliSecondsValue > 0 ? extraMilliSecondsValue : 1000); |
| 28 | + setDidStart(newAutoStart); |
| 29 | + setIsRunning(newAutoStart); |
56 | 30 | setExpiryTimestamp(newExpiryTimestamp); |
| 31 | + setSeconds(secondsValue); |
57 | 32 | } |
58 | 33 |
|
59 | | - function handleExtraMilliSeconds(secondsValue, extraMilliSeconds) { |
60 | | - setIsRunning(true); |
61 | | - intervalRef.current = setTimeout(() => { |
62 | | - const currentSeconds = Time.getSecondsFromExpiry(expiryTimestamp); |
63 | | - setSeconds(currentSeconds); |
64 | | - if (currentSeconds <= 0) { |
65 | | - handleExpire(); |
66 | | - } else { |
67 | | - intervalRef.current = undefined; |
68 | | - start(); |
69 | | - } |
70 | | - }, extraMilliSeconds); |
| 34 | + function resume() { |
| 35 | + const time = new Date(); |
| 36 | + time.setMilliseconds(time.getMilliseconds() + (seconds * 1000)); |
| 37 | + restart(time, true); |
71 | 38 | } |
72 | 39 |
|
73 | | - useEffect(() => { |
74 | | - if (Validate.expiryTimestamp(expiryTimestamp)) { |
75 | | - const secondsValue = Time.getSecondsFromExpiry(expiryTimestamp); |
76 | | - const extraMilliSeconds = Math.floor((secondsValue - Math.floor(secondsValue)) * 1000); |
77 | | - setSeconds(secondsValue); |
78 | | - if (extraMilliSeconds > 0) { |
79 | | - handleExtraMilliSeconds(secondsValue, extraMilliSeconds); |
80 | | - } else { |
81 | | - start(); |
82 | | - } |
| 40 | + function start() { |
| 41 | + if (didStart) { |
| 42 | + setSeconds(Time.getSecondsFromExpiry(expiryTimestamp)); |
| 43 | + setIsRunning(true); |
| 44 | + } else { |
| 45 | + resume(); |
83 | 46 | } |
84 | | - return clearIntervalRef; |
85 | | - }, [expiryTimestamp]); |
| 47 | + } |
86 | 48 |
|
| 49 | + useInterval(() => { |
| 50 | + if (delay !== DEFAULT_DELAY) { |
| 51 | + setDelay(DEFAULT_DELAY); |
| 52 | + } |
| 53 | + const secondsValue = Time.getSecondsFromExpiry(expiryTimestamp); |
| 54 | + setSeconds(secondsValue); |
| 55 | + if (secondsValue <= 0) { |
| 56 | + handleExpire(); |
| 57 | + } |
| 58 | + }, isRunning ? delay : null); |
87 | 59 |
|
88 | 60 | return { |
89 | 61 | ...Time.getTimeFromSeconds(seconds), start, pause, resume, restart, isRunning, |
|
0 commit comments