1
+ <script lang =" ts" >
2
+ import { Timer , Play , Pause , Square } from ' lucide-svelte'
3
+ import { showRestCompleteNotification } from ' ../utils'
4
+ import { uiStore } from ' ../stores'
5
+
6
+ interface LiSSTimerProps {
7
+ duration: number // Duration in minutes
8
+ }
9
+
10
+ let { duration }: LiSSTimerProps = $props ()
11
+
12
+ // Access LiSS timer directly from store
13
+ const lissTimer = $derived ($uiStore .lissTimer )
14
+
15
+ // Update store function
16
+ const updateLissTimer = (updates : Partial <typeof lissTimer >) => {
17
+ uiStore .update (state => ({
18
+ ... state ,
19
+ lissTimer: { ... state .lissTimer , ... updates }
20
+ }))
21
+ }
22
+
23
+ // Start LiSS timer function
24
+ const startLissTimer = () => {
25
+ const durationInSeconds = duration * 60
26
+ const now = Date .now ()
27
+
28
+ if (lissTimer .isPaused ) {
29
+ // Resume from pause
30
+ updateLissTimer ({
31
+ isActive: true ,
32
+ isPaused: false ,
33
+ startTime: now - (lissTimer .totalTime - lissTimer .timeLeft ) * 1000
34
+ })
35
+ } else {
36
+ // Start fresh
37
+ updateLissTimer ({
38
+ isActive: true ,
39
+ isPaused: false ,
40
+ timeLeft: durationInSeconds ,
41
+ totalTime: durationInSeconds ,
42
+ startTime: now ,
43
+ pausedTime: 0
44
+ })
45
+ }
46
+ }
47
+
48
+ // Pause LiSS timer function
49
+ const pauseLissTimer = () => {
50
+ updateLissTimer ({
51
+ isActive: false ,
52
+ isPaused: true ,
53
+ pausedTime: Date .now ()
54
+ })
55
+ }
56
+
57
+ // Stop LiSS timer function
58
+ const stopLissTimer = () => {
59
+ updateLissTimer ({
60
+ isActive: false ,
61
+ isPaused: false ,
62
+ timeLeft: 0 ,
63
+ totalTime: 0 ,
64
+ startTime: 0 ,
65
+ pausedTime: 0
66
+ })
67
+ }
68
+
69
+ // Derived progress percentage
70
+ const progressPercent = $derived (() => {
71
+ if (lissTimer .totalTime === 0 ) return 0
72
+ return ((lissTimer .totalTime - lissTimer .timeLeft ) / lissTimer .totalTime ) * 100
73
+ })
74
+
75
+ // Timer interval effect - timestamp-based to prevent background throttling
76
+ $effect (() => {
77
+ let interval: number | undefined
78
+
79
+ if (lissTimer .isActive && lissTimer .timeLeft > 0 ) {
80
+ interval = setInterval (() => {
81
+ const now = Date .now ()
82
+ const elapsedSeconds = Math .floor ((now - lissTimer .startTime ) / 1000 )
83
+ const newTimeLeft = Math .max (0 , lissTimer .totalTime - elapsedSeconds )
84
+
85
+ updateLissTimer ({ timeLeft: newTimeLeft })
86
+ }, 100 ) // Check more frequently for smoother updates
87
+ }
88
+
89
+ return () => {
90
+ if (interval ) clearInterval (interval )
91
+ }
92
+ })
93
+
94
+ // Separate effect to handle notification when timer reaches 0
95
+ $effect (() => {
96
+ if (lissTimer .isActive && lissTimer .timeLeft === 0 ) {
97
+ showRestCompleteNotification ()
98
+ // Auto-pause when complete
99
+ updateLissTimer ({ isActive: false , isPaused: false })
100
+ }
101
+ })
102
+
103
+ // Derived timer display values
104
+ const minutes = $derived (() => Math .floor (lissTimer .timeLeft / 60 ))
105
+ const seconds = $derived (() => lissTimer .timeLeft % 60 )
106
+ const isTimerActive = $derived (() => lissTimer .isActive || lissTimer .isPaused || lissTimer .timeLeft > 0 )
107
+ </script >
108
+
109
+ {#if isTimerActive ()}
110
+ <div class =" mb-4 p-4 bg-white rounded-lg border-2 border-blue-200" >
111
+ <div class =" flex items-center justify-between mb-2" >
112
+ <div class =" flex items-center gap-2" >
113
+ <Timer class =" w-5 h-5 text-blue-600" />
114
+ <span class =" text-sm font-semibold text-gray-700" >
115
+ LiSS Timer
116
+ </span >
117
+ </div >
118
+ <div class =" text-lg font-bold text-gray-900" >
119
+ {minutes ()}:{seconds ().toString ().padStart (2 , ' 0' )}
120
+ </div >
121
+ </div >
122
+
123
+ <div class =" mb-3" >
124
+ <div class =" bg-gray-200 h-3 rounded-full overflow-hidden" >
125
+ <div
126
+ class =" h-full transition-all duration-300 bg-blue-500"
127
+ style ="width: {progressPercent ()}%"
128
+ ></div >
129
+ </div >
130
+ </div >
131
+
132
+ <div class =" flex gap-2" >
133
+ {#if ! lissTimer .isActive && ! lissTimer .isPaused }
134
+ <!-- Start button - only show when timer is not started -->
135
+ <button
136
+ onclick ={startLissTimer }
137
+ class =" flex-1 bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-lg transition-colors text-sm flex items-center justify-center gap-2"
138
+ >
139
+ <Play class =" w-4 h-4" />
140
+ Start
141
+ </button >
142
+ {:else if lissTimer .isActive }
143
+ <!-- Pause button - show when timer is running -->
144
+ <button
145
+ onclick ={pauseLissTimer }
146
+ class =" flex-1 bg-yellow-500 hover:bg-yellow-600 text-white font-medium py-2 px-4 rounded-lg transition-colors text-sm flex items-center justify-center gap-2"
147
+ >
148
+ <Pause class =" w-4 h-4" />
149
+ Pause
150
+ </button >
151
+ {:else if lissTimer .isPaused }
152
+ <!-- Resume button - show when timer is paused -->
153
+ <button
154
+ onclick ={startLissTimer }
155
+ class =" flex-1 bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg transition-colors text-sm flex items-center justify-center gap-2"
156
+ >
157
+ <Play class =" w-4 h-4" />
158
+ Resume
159
+ </button >
160
+ {/if }
161
+
162
+ <!-- Stop button - always available when timer exists -->
163
+ <button
164
+ onclick ={stopLissTimer }
165
+ class =" flex-1 bg-red-500 hover:bg-red-600 text-white font-medium py-2 px-4 rounded-lg transition-colors text-sm flex items-center justify-center gap-2"
166
+ >
167
+ <Square class =" w-4 h-4" />
168
+ Stop
169
+ </button >
170
+ </div >
171
+ </div >
172
+ {:else }
173
+ <!-- Start timer button when no timer is active -->
174
+ <div class =" mb-4" >
175
+ <button
176
+ onclick ={startLissTimer }
177
+ class =" w-full bg-blue-500 hover:bg-blue-600 text-white font-semibold py-3 rounded-lg transition-colors flex items-center justify-center gap-2"
178
+ >
179
+ <Timer class =" w-5 h-5" />
180
+ Start {duration } min timer
181
+ </button >
182
+ </div >
183
+ {/if }
0 commit comments