@@ -6,9 +6,11 @@ import { combineLatest, EMPTY, merge, Observable, of, Subject } from 'rxjs';
6
6
import {
7
7
distinctUntilChanged ,
8
8
filter ,
9
+ first ,
9
10
map ,
10
11
shareReplay ,
11
12
switchMap ,
13
+ throttleTime ,
12
14
withLatestFrom ,
13
15
} from 'rxjs/operators' ;
14
16
import { realTimer$ } from '../../util/real-timer' ;
@@ -22,8 +24,13 @@ import { T } from '../../t.const';
22
24
import { TranslateService } from '@ngx-translate/core' ;
23
25
import { TimeTrackingConfig } from '../config/global-config.model' ;
24
26
import { IS_TOUCH_ONLY } from '../../util/is-touch-only' ;
25
- import { DateService } from 'src/app /core/date/date.service' ;
27
+ import { DateService } from '../.. /core/date/date.service' ;
26
28
import { TakeABreakService } from '../take-a-break/take-a-break.service' ;
29
+ import { NotifyService } from '../../core/notify/notify.service' ;
30
+ import { UiHelperService } from '../ui-helper/ui-helper.service' ;
31
+ import { playSound } from '../../util/play-sound' ;
32
+
33
+ const DESKTOP_NOTIFICATION_THROTTLE = 60 * 1000 ;
27
34
28
35
@Injectable ( {
29
36
providedIn : 'root' ,
@@ -37,6 +44,8 @@ export class TrackingReminderService {
37
44
private _translateService = inject ( TranslateService ) ;
38
45
private _dateService = inject ( DateService ) ;
39
46
private _takeABreakService = inject ( TakeABreakService ) ;
47
+ private _notifyService = inject ( NotifyService ) ;
48
+ private _uiHelperService = inject ( UiHelperService ) ;
40
49
41
50
_cfg$ : Observable < TimeTrackingConfig > = this . _globalConfigService . cfg$ . pipe (
42
51
map ( ( cfg ) => cfg ?. timeTracking ) ,
@@ -73,6 +82,9 @@ export class TrackingReminderService {
73
82
shareReplay ( ) ,
74
83
) ;
75
84
85
+ // New throttled notification stream
86
+ private _throttledNotificationTrigger$ = new Subject < string > ( ) ;
87
+
76
88
init ( ) : void {
77
89
this . remindCounter$ . subscribe ( ( count ) => {
78
90
this . _triggerBanner ( count ) ;
@@ -81,6 +93,30 @@ export class TrackingReminderService {
81
93
this . _hideTrigger$ . subscribe ( ( v ) => {
82
94
this . _hideBanner ( ) ;
83
95
} ) ;
96
+
97
+ // Set up throttled notification handling
98
+ this . _throttledNotificationTrigger$
99
+ . pipe (
100
+ throttleTime ( DESKTOP_NOTIFICATION_THROTTLE ) ,
101
+ // Get all needed config in one go
102
+ withLatestFrom (
103
+ this . _globalConfigService . sound$ ,
104
+ this . _globalConfigService . cfg$ . pipe ( map ( ( cfg ) => cfg . timeTracking ) ) ,
105
+ ) ,
106
+ )
107
+ . subscribe ( ( [ durationStr , soundCfg , timeTrackingCfg ] ) => {
108
+ this . _showNotification ( durationStr ) ;
109
+
110
+ // Play sound if configured
111
+ if ( soundCfg . trackTimeSound ) {
112
+ playSound ( soundCfg . trackTimeSound as string ) ;
113
+ }
114
+
115
+ // Focus window if enabled
116
+ if ( timeTrackingCfg . isTrackingReminderFocusWindow ) {
117
+ this . _focusWindow ( ) ;
118
+ }
119
+ } ) ;
84
120
}
85
121
86
122
private _hideBanner ( ) : void {
@@ -109,6 +145,20 @@ export class TrackingReminderService {
109
145
fn : ( ) => this . _dismissBanner ( ) ,
110
146
} ,
111
147
} ) ;
148
+
149
+ // Handle desktop notification if enabled
150
+ this . _globalConfigService . cfg$
151
+ . pipe (
152
+ filter ( ( cfg ) => ! ! cfg ) ,
153
+ first ( ) ,
154
+ )
155
+ . subscribe ( ( cfg ) => {
156
+ // Show desktop notification if enabled
157
+ if ( cfg . timeTracking . isTrackingReminderNotify ) {
158
+ // Instead of showing directly, queue it through the throttled subject
159
+ this . _throttledNotificationTrigger$ . next ( durationStr ) ;
160
+ }
161
+ } ) ;
112
162
}
113
163
114
164
private _openDialog ( ) : void {
@@ -153,4 +203,20 @@ export class TrackingReminderService {
153
203
this . _bannerService . dismiss ( BannerId . StartTrackingReminder ) ;
154
204
this . _manualReset$ . next ( ) ;
155
205
}
206
+
207
+ private _showNotification ( durationStr : string ) : void {
208
+ this . _notifyService . notify ( {
209
+ title : this . _translateService . instant (
210
+ T . F . TIME_TRACKING . D_TRACKING_REMINDER . NOTIFICATION_TITLE ,
211
+ ) ,
212
+ body : this . _translateService . instant ( T . F . TIME_TRACKING . B_TTR . MSG , {
213
+ time : durationStr ,
214
+ } ) ,
215
+ requireInteraction : true ,
216
+ } ) ;
217
+ }
218
+
219
+ private _focusWindow ( ) : void {
220
+ this . _uiHelperService . focusApp ( ) ;
221
+ }
156
222
}
0 commit comments