1+ // === Utility Functions ===
2+ function formatTime ( seconds ) {
3+ let mins = Math . floor ( seconds / 60 ) ;
4+ let secs = ( seconds % 60 ) . toString ( ) . padStart ( 2 , '0' ) ;
5+ return `${ mins } :${ secs } ` ;
6+ }
7+
8+ function getTimeStr ( ) {
9+ let d = new Date ( ) ;
10+ return `${ d . getHours ( ) . toString ( ) . padStart ( 2 , '0' ) } :${ d . getMinutes ( ) . toString ( ) . padStart ( 2 , '0' ) } ` ;
11+ }
12+
13+ function updateCachedLeftTime ( ) {
14+ cachedLeftTime = "Left: " + formatTime ( state . remainingTotal ) ;
15+ }
16+
17+ // === Constants ===
118const FILE = "jwalk.json" ;
219const DEFAULTS = {
320 totalDuration : 30 ,
421 intervalDuration : 3 ,
522 startMode : 0 ,
6- modeBuzzerDuration : 1000 ,
7- finishBuzzerDuration : 1500 ,
23+ modeBuzzerDuration : 1000 ,
24+ finishBuzzerDuration : 1500 ,
25+ showClock : true ,
26+ updateWhileLocked : false
827} ;
928
29+ // === Settings and State ===
1030let settings = require ( "Storage" ) . readJSON ( FILE , 1 ) || DEFAULTS ;
1131
1232let state = {
@@ -20,28 +40,14 @@ let state = {
2040 forceDraw : false ,
2141} ;
2242
23- let drawTimerInterval ;
2443let cachedLeftTime = "" ;
44+ let lastMinuteStr = getTimeStr ( ) ;
45+ let drawTimerInterval ;
2546
26- function formatTime ( seconds ) {
27- let mins = Math . floor ( seconds / 60 ) ;
28- let secs = ( seconds % 60 ) . toString ( ) . padStart ( 2 , '0' ) ;
29- return `${ mins } :${ secs } ` ;
30- }
31-
32- function updateCachedLeftTime ( ) {
33- cachedLeftTime = "Left: " + formatTime ( state . remainingTotal ) ;
34- }
35-
36- function getTimeStr ( ) {
37- let d = new Date ( ) ;
38- return `${ d . getHours ( ) . toString ( ) . padStart ( 2 , '0' ) } :${ d . getMinutes ( ) . toString ( ) . padStart ( 2 , '0' ) } ` ;
39- }
40-
47+ // === UI Rendering ===
4148function drawUI ( ) {
4249 let y = Bangle . appRect . y + 8 ;
4350 g . reset ( ) . setBgColor ( g . theme . bg ) . clearRect ( Bangle . appRect ) ;
44-
4551 g . setColor ( g . theme . fg ) ;
4652
4753 let displayInterval = state . paused
@@ -76,17 +82,46 @@ function drawUI() {
7682 g . drawString ( state . currentMode , g . getWidth ( ) / 2 , y + 15 ) ;
7783 g . drawString ( cachedLeftTime , g . getWidth ( ) / 2 , cy + 15 ) ;
7884
79- g . setFontAlign ( 1 , 0 ) ;
80- g . drawString ( getTimeStr ( ) , g . getWidth ( ) - 4 , y ) ;
85+ if ( settings . showClock ) {
86+ g . setFontAlign ( 1 , 0 ) ;
87+ g . drawString ( lastMinuteStr , g . getWidth ( ) - 4 , y ) ;
88+ }
8189 g . flip ( ) ;
8290}
8391
92+ // === Workout Logic ===
8493function toggleMode ( ) {
8594 state . currentMode = state . currentMode === "Relax" ? "Intense" : "Relax" ;
8695 Bangle . buzz ( settings . modeBuzzerDuration ) ;
8796 state . forceDraw = true ;
8897}
8998
99+ function startNextInterval ( ) {
100+ if ( state . remainingTotal <= 0 ) {
101+ finishWorkout ( ) ;
102+ return ;
103+ }
104+
105+ state . remainingInterval = Math . min ( state . intervalDuration , state . remainingTotal ) ;
106+ state . remainingTotal -= state . remainingInterval ;
107+ updateCachedLeftTime ( ) ;
108+ state . intervalEnd = Date . now ( ) + state . remainingInterval * 1000 ;
109+ state . forceDraw = true ;
110+ }
111+
112+ function togglePause ( ) {
113+ if ( state . finished ) return ;
114+
115+ if ( ! state . paused ) {
116+ state . remainingInterval = Math . max ( 0 , Math . floor ( ( state . intervalEnd - Date . now ( ) ) / 1000 ) ) ;
117+ state . paused = true ;
118+ } else {
119+ state . intervalEnd = Date . now ( ) + state . remainingInterval * 1000 ;
120+ state . paused = false ;
121+ }
122+ drawUI ( ) ;
123+ }
124+
90125function finishWorkout ( ) {
91126 clearInterval ( drawTimerInterval ) ;
92127 Bangle . buzz ( settings . finishBuzzerDuration ) ;
@@ -110,58 +145,34 @@ function finishWorkout() {
110145 } , 500 ) ;
111146}
112147
113-
114- function startNextInterval ( ) {
115- if ( state . remainingTotal <= 0 ) {
116- finishWorkout ( ) ;
117- return ;
118- }
119-
120- state . remainingInterval = Math . min ( state . intervalDuration , state . remainingTotal ) ;
121- state . remainingTotal -= state . remainingInterval ;
122- updateCachedLeftTime ( ) ;
123- state . intervalEnd = Date . now ( ) + state . remainingInterval * 1000 ;
124-
125- state . forceDraw = true ;
126- }
127-
148+ // === Timer Tick ===
128149function tick ( ) {
129150 if ( state . finished ) return ;
130151
131- if ( ! state . paused ) {
132- if ( ( state . intervalEnd - Date . now ( ) ) / 1000 <= 0 ) {
133- toggleMode ( ) ;
134- startNextInterval ( ) ;
135- return ;
136- }
152+ const currentMinuteStr = getTimeStr ( ) ;
153+ if ( currentMinuteStr !== lastMinuteStr ) {
154+ lastMinuteStr = currentMinuteStr ;
155+ state . forceDraw = true ;
137156 }
138157
139- if ( ! Bangle . isLocked ( ) || state . forceDraw ) {
140- drawUI ( ) ;
141- state . forceDraw = false ;
158+ if ( ! state . paused && ( state . intervalEnd - Date . now ( ) ) / 1000 <= 0 ) {
159+ toggleMode ( ) ;
160+ startNextInterval ( ) ;
161+ return ;
142162 }
143- }
144-
145163
146- function togglePause ( ) {
147- if ( state . finished ) return ;
148-
149- if ( ! state . paused ) {
150- state . remainingInterval = Math . max ( 0 , Math . floor ( ( state . intervalEnd - Date . now ( ) ) / 1000 ) ) ;
151- state . paused = true ;
152- } else {
153- state . intervalEnd = Date . now ( ) + state . remainingInterval * 1000 ;
154- state . paused = false ;
164+ if ( state . forceDraw || settings . updateWhileLocked || ! Bangle . isLocked ( ) ) {
165+ drawUI ( ) ;
166+ state . forceDraw = false ;
155167 }
156- drawUI ( ) ;
157168}
158169
159- // Setup
170+ // === Initialization ===
160171Bangle . on ( "touch" , togglePause ) ;
161172Bangle . loadWidgets ( ) ;
162173Bangle . drawWidgets ( ) ;
163174
164175updateCachedLeftTime ( ) ;
165176startNextInterval ( ) ;
166177drawUI ( ) ;
167- drawTimerInterval = setInterval ( tick , 1000 ) ;
178+ drawTimerInterval = setInterval ( tick , 1000 ) ;
0 commit comments