11import React from 'react' ;
2+ import { useMachine } from '@xstate/react' ;
23import beepBreakFile from '../BeepBreak.mp3' ;
34import beepBreakLongFile from '../BeepBreakLong.mp3' ;
45import beepWorkFile from '../BeepWork.mp3' ;
56import beepWorkLongFile from '../BeepWorkLong.mp3' ;
67import { subSeconds , getSeconds , addSeconds , getMinutes } from 'date-fns' ;
78import { useAudio } from './useAudio' ;
9+ import { Status } from '../model/Status' ;
810import { hasOneSecondElapsed } from '../utils/hasOneSecondElapsed' ;
11+ import { Machine } from 'xstate' ;
912const SECONDS_PER_MINUTE = 60 ;
1013const PREP_TIME_SECONDS = 5 ;
11- const Status = {
12- stopped : 'stopped' ,
13- prework : 'prework' ,
14- work : 'work' ,
15- break : 'break' ,
16- } ;
1714
15+ const statusEvents = {
16+ START : 'START' ,
17+ WORK : 'WORK' ,
18+ BREAK : 'BREAK' ,
19+ STOP : 'STOP' ,
20+ } ;
21+ const statusMachine = Machine ( {
22+ id : 'status' ,
23+ initial : Status . STOPPED ,
24+ states : {
25+ [ Status . STOPPED ] : {
26+ on : { [ statusEvents . START ] : Status . PREWORK } ,
27+ } ,
28+ [ Status . PREWORK ] : {
29+ on : { [ statusEvents . WORK ] : Status . WORK , STOP : Status . STOPPED } ,
30+ } ,
31+ [ Status . WORK ] : {
32+ on : {
33+ [ statusEvents . STOP ] : Status . STOPPED ,
34+ [ statusEvents . BREAK ] : Status . BREAK ,
35+ } ,
36+ } ,
37+ [ Status . BREAK ] : {
38+ on : {
39+ [ statusEvents . WORK ] : Status . WORK ,
40+ [ statusEvents . STOP ] : Status . STOPPED ,
41+ } ,
42+ } ,
43+ } ,
44+ } ) ;
1845export function useWorkoutTimer ( ) {
1946 const { audio : beepBreak } = useAudio ( beepBreakFile ) ;
2047 const { audio : beepWork } = useAudio ( beepWorkFile ) ;
2148 const { audio : beepBreakLong } = useAudio ( beepBreakLongFile ) ;
2249 const { audio : beepWorkLong } = useAudio ( beepWorkLongFile ) ;
23- const [ status , setStatus ] = React . useState ( Status . stopped ) ;
50+ const [ status , send ] = useMachine ( statusMachine ) ;
2451 const [ timeLeft , setTimeLeft ] = React . useState ( new Date ( 0 ) ) ;
2552 const [ rounds , setRounds ] = React . useState ( 1 ) ;
2653 const [ roundsLeft , setRoundsLeft ] = React . useState ( rounds ) ;
2754 const [ workInterval , setWorkInterval ] = React . useState ( new Date ( 0 ) ) ;
2855 const [ breakInterval , setBreakInterval ] = React . useState ( new Date ( 0 ) ) ;
2956 const timestamp = React . useRef ( Date . now ( ) ) ;
30- let statusRef = React . useRef ( status ) ;
31- statusRef . current = status ;
57+ let statusRef = React . useRef ( status . value ) ;
58+ statusRef . current = status . value ;
3259 let roundsLeftRef = React . useRef ( roundsLeft ) ;
3360 roundsLeftRef . current = roundsLeft ;
3461 let timeLeftRef = React . useRef ( timeLeft ) ;
@@ -37,19 +64,20 @@ export function useWorkoutTimer() {
3764 setRounds ( Number ( value ) ) ;
3865 } ;
3966 const start = React . useCallback ( ( ) => {
40- const shouldStart = status === Status . stopped ;
67+ const shouldStart = status . value === Status . STOPPED ;
4168 if ( shouldStart ) {
4269 timestamp . current = Date . now ( ) ;
43- setStatus ( Status . prework ) ;
70+ send ( statusEvents . START ) ;
4471 setTimeLeft ( addSeconds ( new Date ( 0 ) , PREP_TIME_SECONDS ) ) ;
4572 setRoundsLeft ( rounds ) ;
4673 } else {
47- setStatus ( Status . stopped ) ;
74+ send ( statusEvents . STOP ) ;
4875 }
49- } , [ rounds , status ] ) ;
76+ } , [ rounds , status , send ] ) ;
5077 React . useEffect ( ( ) => {
5178 let interval = null ;
52- const startWasInitiated = status !== Status . stopped && interval === null ;
79+ const startWasInitiated =
80+ status . value !== Status . STOPPED && interval === null ;
5381 if ( startWasInitiated ) {
5482 interval = setInterval ( function countDown ( ) {
5583 if ( hasOneSecondElapsed ( timestamp . current ) ) {
@@ -62,7 +90,8 @@ export function useWorkoutTimer() {
6290 const shouldBeepFromTwoDown = secondsLeft <= 4 ;
6391 if ( shouldBeepFromTwoDown ) {
6492 const shouldGetReady =
65- status === Status . prework || status === Status . break ;
93+ status . value === Status . PREWORK ||
94+ status . value === Status . BREAK ;
6695 if ( shouldGetReady ) {
6796 beepBreak . play ( ) ;
6897 } else {
@@ -72,24 +101,24 @@ export function useWorkoutTimer() {
72101 setTimeLeft ( previousTimeLeft => subSeconds ( previousTimeLeft , 1 ) ) ;
73102 } else {
74103 const shouldSwitchToWork =
75- ( statusRef . current === Status . prework ||
76- statusRef . current === Status . break ) &&
104+ ( statusRef . current === Status . PREWORK ||
105+ statusRef . current === Status . BREAK ) &&
77106 roundsLeftRef . current > 0 &&
78107 workInterval . valueOf ( ) !== 0 ;
79108 const shouldSwitchToRest =
80- statusRef . current === Status . work && roundsLeftRef . current > 0 ;
109+ statusRef . current === Status . WORK && roundsLeftRef . current > 0 ;
81110
82111 if ( shouldSwitchToWork ) {
83112 beepBreakLong . play ( ) ;
84- setStatus ( Status . work ) ;
113+ send ( statusEvents . WORK ) ;
85114 setTimeLeft ( workInterval ) ;
86115 setRoundsLeft ( prevRoundsLeft => prevRoundsLeft - 1 ) ;
87116 } else if ( shouldSwitchToRest ) {
88- setStatus ( Status . break ) ;
117+ send ( statusEvents . BREAK ) ;
89118 setTimeLeft ( breakInterval ) ;
90119 beepWorkLong . play ( ) ;
91120 } else {
92- setStatus ( Status . stopped ) ;
121+ send ( statusEvents . STOP ) ;
93122 clearInterval ( interval ) ;
94123 }
95124 }
@@ -108,6 +137,7 @@ export function useWorkoutTimer() {
108137 beepBreak ,
109138 beepBreakLong ,
110139 beepWorkLong ,
140+ send ,
111141 ] ) ;
112142 return {
113143 rounds,
@@ -118,7 +148,7 @@ export function useWorkoutTimer() {
118148 setBreakInterval,
119149 start,
120150 timeLeft,
121- status,
151+ status : status . value ,
122152 roundsLeft,
123153 } ;
124154}
0 commit comments