@@ -3,6 +3,7 @@ import React, { useState, useEffect, useCallback, useMemo } from "react";
3
3
import TimerDisplay from "./timer-display" ;
4
4
import TimerControls from "./timer-controls" ;
5
5
import TypeControls from "./type-controls" ;
6
+ import { TimerConfig , defaultConfig } from "./timer-config" ;
6
7
7
8
import TimerWorker from "../timer-worker" ;
8
9
import buildWorker from "../worker-builder" ;
@@ -17,12 +18,19 @@ class TimerType {
17
18
this . breakCount = 0 ;
18
19
}
19
20
20
- duration ( ) {
21
+ /**
22
+ *
23
+ * @param {import("./timer-config").config } config
24
+ * @returns {number } the duration of the timer's current state
25
+ */
26
+ duration ( config ) {
21
27
switch ( this . state ) {
22
28
case "work" :
23
- return 1500 ;
29
+ return config . work ;
24
30
case "break" :
25
- return this . breakCount % 4 === 3 ? 900 : 300 ;
31
+ return ( this . breakCount + 1 ) % config . longfreq === 0
32
+ ? config . longbreak
33
+ : config . break ;
26
34
default :
27
35
return - 1 ;
28
36
}
@@ -49,28 +57,42 @@ class TimerType {
49
57
* @returns {React.ReactNode }
50
58
*/
51
59
function App ( props ) {
52
- /** @type {[DOMHighResTimeStamp?, React.SetStateAction<DOMHighResTimeStamp?>] } */
60
+ /**
61
+ * app config
62
+ * @type {[import("./timer-config").config, React.SetStateAction<import("./timer-config").config>] }
63
+ */
64
+ const [ config , setConfig ] = useState ( defaultConfig ) ;
65
+
66
+ /**
67
+ * timestamp of when the timer was started
68
+ * @type {[DOMHighResTimeStamp?, React.SetStateAction<DOMHighResTimeStamp?>] }
69
+ */
53
70
const [ timerStart , setTimerStart ] = useState ( undefined ) ;
54
71
const [ elapsed , setElapsed ] = useState ( 0 ) ;
55
72
const [ timerType , setTimerType ] = useState ( new TimerType ( "work" ) ) ;
56
- const [ duration , setDuration ] = useState ( timerType . duration ( ) ) ;
73
+ const [ duration , setDuration ] = useState ( timerType . duration ( config ) ) ;
57
74
75
+ // timer worker (runs in background)
58
76
const worker = useMemo ( ( ) => {
59
77
let worker = buildWorker ( TimerWorker ) ;
60
78
worker . postMessage ( [ performance . timeOrigin ] ) ; // post time origin
61
79
return worker ;
62
80
} , [ ] ) ;
63
81
82
+ // time update from worker
64
83
worker . onmessage = ( e ) => {
65
84
let elapsedTime = e . data ;
66
- // console.log(elapsedTime);
67
85
if ( elapsedTime !== elapsed ) {
68
86
if ( elapsedTime >= duration ) timerFinish ( ) ;
69
87
else setElapsed ( elapsedTime ) ;
70
88
}
71
89
} ;
72
90
73
- const bell = useMemo ( ( ) => new Audio ( `${ process . env . PUBLIC_URL } /bell.mp3` ) , [ ] ) ;
91
+ // load the bell sound
92
+ const bell = useMemo (
93
+ ( ) => new Audio ( `${ process . env . PUBLIC_URL } /bell.mp3` ) ,
94
+ [ ]
95
+ ) ;
74
96
75
97
const isPlaying = useCallback ( ( ) => {
76
98
return timerStart !== undefined ;
@@ -87,22 +109,28 @@ function App(props) {
87
109
} ;
88
110
89
111
const restart = ( ) => {
90
- setDuration ( timerType . duration ( ) ) ;
112
+ setDuration ( timerType . duration ( config ) ) ;
91
113
if ( isPlaying ( ) ) setTimerStart ( performance . now ( ) ) ;
92
114
} ;
93
115
94
116
const timerFinish = useCallback ( ( ) => {
117
+ // make a new notification
95
118
const notifyFinish = ( ) => {
96
119
new Notification ( "Timer done" , {
97
- body : timerType . state === "work" ? "It's time to get back to work!" : "It's time to take a break!" ,
120
+ body :
121
+ timerType . state === "work"
122
+ ? "It's time to get back to work!"
123
+ : "It's time to take a break!" ,
98
124
} ) ;
99
125
} ;
100
126
127
+ // reset timer
101
128
setTimerStart ( undefined ) ;
102
129
setElapsed ( 0 ) ;
103
130
setTimerType ( timerType . next ( ) ) ;
104
- setDuration ( timerType . duration ( ) ) ;
131
+ setDuration ( timerType . duration ( config ) ) ;
105
132
133
+ // send notification
106
134
if ( ! ( "Notification" in window ) ) {
107
135
alert ( "Timer done!" ) ;
108
136
} else if ( Notification . permission === "granted" ) {
@@ -115,30 +143,47 @@ function App(props) {
115
143
} ) ;
116
144
}
117
145
bell . play ( ) ;
118
- } , [ timerType , bell ] ) ;
119
146
147
+ if ( config . autoStart ) {
148
+ play ( ) ;
149
+ }
150
+ } , [ timerType , bell , config ] ) ;
151
+
152
+ // update time for woker
120
153
useEffect ( ( ) => {
121
154
worker . postMessage ( timerStart ) ;
122
155
} , [ timerStart , worker ] ) ;
123
156
157
+ // update timer duration if config is updated and timer is not running
158
+ useEffect ( ( ) => {
159
+ if ( ! isPlaying ( ) ) setDuration ( timerType . duration ( config ) ) ;
160
+ } , [ config , isPlaying , timerType ] ) ;
161
+
124
162
let playing = isPlaying ( ) ;
125
163
return (
126
- < div className = { "timer-app" + ( timerType . state === "break" ? " break" : "" ) } >
164
+ < div
165
+ className = { "timer-app" + ( timerType . state === "break" ? " break" : "" ) }
166
+ >
127
167
< div className = "timer-content" >
128
168
< TimerDisplay time = { duration - elapsed } />
129
- < TimerControls mode = { playing ? "pause" : "play" } onplay = { playing ? pause : play } onrestart = { restart } />
169
+ < TimerControls
170
+ mode = { playing ? "pause" : "play" }
171
+ onplay = { playing ? pause : play }
172
+ onrestart = { restart }
173
+ />
130
174
< TypeControls
131
175
type = { timerType . state }
132
176
setType = { ( type ) => {
133
177
let newType = new TimerType ( type ) ;
134
178
setTimerStart ( undefined ) ;
135
179
setElapsed ( 0 ) ;
136
180
setTimerType ( newType ) ;
137
- setDuration ( newType . duration ( ) ) ;
181
+ setDuration ( newType . duration ( config ) ) ;
138
182
} }
139
183
active = { ! playing }
140
184
/>
141
185
</ div >
186
+ < TimerConfig config = { config } setConfig = { setConfig } />
142
187
</ div >
143
188
) ;
144
189
}
0 commit comments