From 00fed99c188c6f06f2d761e9013e046caecd64e9 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Tue, 12 Nov 2024 18:49:26 +0100 Subject: [PATCH 1/4] feat(simpleCounter): outline new type --- .../form-cfgs/simple-counter-form.const.ts | 25 ++++++++++ .../simple-counter-button.component.html | 50 ++++++++++++++----- .../simple-counter-button.component.scss | 6 +++ .../simple-counter-button.component.ts | 35 +++++++++---- .../simple-counter/simple-counter.const.ts | 10 +++- .../simple-counter/simple-counter.model.ts | 4 ++ 6 files changed, 106 insertions(+), 24 deletions(-) diff --git a/src/app/features/config/form-cfgs/simple-counter-form.const.ts b/src/app/features/config/form-cfgs/simple-counter-form.const.ts index e8d0d00544..1ae9087048 100644 --- a/src/app/features/config/form-cfgs/simple-counter-form.const.ts +++ b/src/app/features/config/form-cfgs/simple-counter-form.const.ts @@ -59,6 +59,10 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection = { label: T.F.SIMPLE_COUNTER.FORM.TYPE_CLICK_COUNTER, value: SimpleCounterType.ClickCounter, }, + { + label: T.F.SIMPLE_COUNTER.FORM.TYPE_CLICK_COUNTER, + value: SimpleCounterType.RepeatedCountdownReminder, + }, ], }, }, @@ -79,6 +83,27 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection = { label: T.F.SIMPLE_COUNTER.FORM.L_ICON_ON, }, }, + { + key: 'countdownDuration', + type: 'duration', + hideExpression: (model: any) => { + return model.type !== SimpleCounterType.RepeatedCountdownReminder; + }, + hooks: { + onInit: (field) => { + console.log(field?.formControl?.value); + if (!field?.formControl?.value && field?.formControl?.value !== null) { + field?.formControl?.setValue(2 * 60 * 60000); + } + }, + }, + templateOptions: { + required: false, + isAllowSeconds: false, + label: T.F.SIMPLE_COUNTER.FORM.L_TITLE, + description: T.G.DURATION_DESCRIPTION, + }, + }, { key: 'triggerOnActions', type: 'select', diff --git a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html index 98510117ac..e36908284e 100644 --- a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html +++ b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html @@ -1,30 +1,28 @@
- +
- {{simpleCounter.countOnDay[todayStr]|msToMinuteClockString}} + {{sc.countOnDay[todayStr]|msToMinuteClockString}}
- +
- {{simpleCounter.countOnDay[todayStr]}} + {{sc.countOnDay[todayStr]}} +
+
+ + + +
+ {{sc.countOnDay[todayStr]|msToMinuteClockString}} +
+ +
+ {{sc.countOnDay[todayStr]}}
diff --git a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.scss b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.scss index 7621e3ec6b..b991e3b1d3 100644 --- a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.scss +++ b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.scss @@ -25,6 +25,7 @@ } } +.extra-label, .label { margin-left: 0; position: absolute; @@ -52,6 +53,11 @@ } } +.extra-label { + bottom: auto; + top: -4px; +} + .controls { transition: $transition-standard; position: absolute; diff --git a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.ts b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.ts index 5670d0d4ed..52fbb5128e 100644 --- a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.ts +++ b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - Input, + input, OnDestroy, OnInit, } from '@angular/core'; @@ -12,8 +12,10 @@ import { MatDialog } from '@angular/material/dialog'; import { DialogSimpleCounterEditComponent } from '../dialog-simple-counter-edit/dialog-simple-counter-edit.component'; import { T } from 'src/app/t.const'; import { GlobalTrackingIntervalService } from '../../../core/global-tracking-interval/global-tracking-interval.service'; -import { Subscription } from 'rxjs'; +import { EMPTY, Subscription } from 'rxjs'; import { DateService } from 'src/app/core/date/date.service'; +import { toObservable } from '@angular/core/rxjs-interop'; +import { switchMap } from 'rxjs/operators'; @Component({ selector: 'simple-counter-button', @@ -26,7 +28,13 @@ export class SimpleCounterButtonComponent implements OnDestroy, OnInit { SimpleCounterType: typeof SimpleCounterType = SimpleCounterType; todayStr: string = this._dateService.todayStr(); - @Input() simpleCounter?: SimpleCounter; + simpleCounter = input(); + + repeatedStopWatchTime$ = toObservable(this.simpleCounter).pipe( + switchMap((sc) => { + return sc?.isOn ? this._globalTrackingIntervalService.tick$ : EMPTY; + }), + ); private _todayStr$ = this._globalTrackingIntervalService.todayDateStr$; private _subs = new Subscription(); @@ -53,35 +61,42 @@ export class SimpleCounterButtonComponent implements OnDestroy, OnInit { } toggleStopwatch(): void { - if (!this.simpleCounter) { + const c = this.simpleCounter(); + if (!c) { throw new Error('No simple counter model'); } - this._simpleCounterService.toggleCounter(this.simpleCounter.id); + this._simpleCounterService.toggleCounter(c.id); } toggleCounter(): void { - if (!this.simpleCounter) { + const c = this.simpleCounter(); + if (!c) { throw new Error('No simple counter model'); } - this._simpleCounterService.increaseCounterToday(this.simpleCounter.id, 1); + this._simpleCounterService.increaseCounterToday(c.id, 1); } reset(): void { - if (!this.simpleCounter) { + const c = this.simpleCounter(); + if (!c) { throw new Error('No simple counter model'); } - this._simpleCounterService.setCounterToday(this.simpleCounter.id, 0); + this._simpleCounterService.setCounterToday(c.id, 0); } edit(ev?: Event): void { if (ev) { ev.preventDefault(); } + const c = this.simpleCounter(); + if (!c) { + throw new Error('No simple counter model'); + } this._matDialog.open(DialogSimpleCounterEditComponent, { restoreFocus: true, data: { - simpleCounter: this.simpleCounter, + simpleCounter: c, }, }); } diff --git a/src/app/features/simple-counter/simple-counter.const.ts b/src/app/features/simple-counter/simple-counter.const.ts index 2c6d957fc0..993bca9e23 100644 --- a/src/app/features/simple-counter/simple-counter.const.ts +++ b/src/app/features/simple-counter/simple-counter.const.ts @@ -40,11 +40,19 @@ export const DEFAULT_SIMPLE_COUNTERS: SimpleCounter[] = [ }, { ...EMPTY_SIMPLE_COUNTER, - id: 'COFFEE COUNTER', + id: 'COFFEE_COUNTER', title: 'Coffee Counter', type: SimpleCounterType.ClickCounter, icon: 'free_breakfast', }, + { + ...EMPTY_SIMPLE_COUNTER, + id: 'STRETCHING_COUNTER', + title: 'Stretching Counter', + type: SimpleCounterType.RepeatedCountdownReminder, + icon: 'fitness_center', + countdownDuration: 30 * 60 * 1000, + }, ]; export const SIMPLE_COUNTER_TRIGGER_ACTIONS: string[] = [ diff --git a/src/app/features/simple-counter/simple-counter.model.ts b/src/app/features/simple-counter/simple-counter.model.ts index 508eff5dca..4b3619e618 100644 --- a/src/app/features/simple-counter/simple-counter.model.ts +++ b/src/app/features/simple-counter/simple-counter.model.ts @@ -4,6 +4,7 @@ import { MODEL_VERSION_KEY } from '../../app.constants'; export enum SimpleCounterType { StopWatch = 'StopWatch', ClickCounter = 'ClickCounter', + RepeatedCountdownReminder = 'RepeatedCountdownReminder', } export interface SimpleCounterCfgFields { @@ -19,6 +20,9 @@ export interface SimpleCounterCfgFields { // adv cfg triggerOnActions: string[]; triggerOffActions?: string[]; + + // repeated countdown reminder + countdownDuration?: number; } export interface SimpleCounterCopy extends SimpleCounterCfgFields { From a377bbafbbb0d1480ebc151f04d21d09935f9c9c Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Tue, 12 Nov 2024 19:40:00 +0100 Subject: [PATCH 2/4] feat(simpleCounter): make new simple counter type work --- .../form-cfgs/simple-counter-form.const.ts | 2 +- .../dialog-simple-counter-edit.component.html | 8 +-- .../simple-counter-button.component.html | 8 +-- .../simple-counter-button.component.ts | 50 ++++++++++++++++--- .../store/simple-counter.effects.ts | 9 ++-- 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/app/features/config/form-cfgs/simple-counter-form.const.ts b/src/app/features/config/form-cfgs/simple-counter-form.const.ts index 1ae9087048..d096f5f44d 100644 --- a/src/app/features/config/form-cfgs/simple-counter-form.const.ts +++ b/src/app/features/config/form-cfgs/simple-counter-form.const.ts @@ -93,7 +93,7 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection = { onInit: (field) => { console.log(field?.formControl?.value); if (!field?.formControl?.value && field?.formControl?.value !== null) { - field?.formControl?.setValue(2 * 60 * 60000); + field?.formControl?.setValue(30 * 60000); } }, }, diff --git a/src/app/features/simple-counter/dialog-simple-counter-edit/dialog-simple-counter-edit.component.html b/src/app/features/simple-counter/dialog-simple-counter-edit/dialog-simple-counter-edit.component.html index 7de1cf37bd..1455bbff66 100644 --- a/src/app/features/simple-counter/dialog-simple-counter-edit/dialog-simple-counter-edit.component.html +++ b/src/app/features/simple-counter/dialog-simple-counter-edit/dialog-simple-counter-edit.component.html @@ -3,7 +3,9 @@
- + - {{T.G.CANCEL|translate}} + {{ T.G.CANCEL|translate }}
diff --git a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html index e36908284e..0c048ca51f 100644 --- a/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html +++ b/src/app/features/simple-counter/simple-counter-button/simple-counter-button.component.html @@ -43,6 +43,7 @@ -
- {{sc.countOnDay[todayStr]|msToMinuteClockString}} -
+
{{countdownTime$|async|msToMinuteClockString}}
(); - repeatedStopWatchTime$ = toObservable(this.simpleCounter).pipe( - switchMap((sc) => { - return sc?.isOn ? this._globalTrackingIntervalService.tick$ : EMPTY; - }), - ); - private _todayStr$ = this._globalTrackingIntervalService.todayDateStr$; private _subs = new Subscription(); + private _resetCountdown$ = new Subject(); + private _countdownDuration$ = toObservable(this.simpleCounter).pipe( + map((c) => c?.countdownDuration), + filter((v): v is number => typeof v === 'number' && v > 0), + distinctUntilChanged(), + ); + + countdownTime$ = this._countdownDuration$.pipe( + switchMap((countdownDuration) => + merge(of(true), this._resetCountdown$).pipe( + switchMap(() => + this._globalTrackingIntervalService.tick$.pipe( + scan((acc, tick) => { + if (!this.simpleCounter()?.isOn) { + return acc; + } + + const newVal = acc - tick.duration; + return newVal < 0 ? 0 : newVal; + }, countdownDuration), + ), + ), + ), + ), + ); constructor( private _simpleCounterService: SimpleCounterService, @@ -54,6 +73,21 @@ export class SimpleCounterButtonComponent implements OnDestroy, OnInit { this._cd.detectChanges(); }), ); + + if (this.simpleCounter()?.type === SimpleCounterType.RepeatedCountdownReminder) { + this._subs.add( + this.countdownTime$.subscribe((countdownTime) => { + // console.log(countdownTime); + if (countdownTime === 0) { + alert( + `${this.simpleCounter()?.title || 'Simple Counter Countdown'} is finished!`, + ); + this.toggleCounter(); + this._resetCountdown$.next(); + } + }), + ); + } } ngOnDestroy(): void { diff --git a/src/app/features/simple-counter/store/simple-counter.effects.ts b/src/app/features/simple-counter/store/simple-counter.effects.ts index 51b951a484..137b5ab031 100644 --- a/src/app/features/simple-counter/store/simple-counter.effects.ts +++ b/src/app/features/simple-counter/store/simple-counter.effects.ts @@ -66,11 +66,12 @@ export class SimpleCounterEffects { checkTimedCounters$: Observable = createEffect(() => this._simpleCounterService.enabledAndToggledSimpleCounters$.pipe( - switchMap((items) => - items && items.length + switchMap((itemsI) => { + const items = itemsI.filter((item) => item.type === SimpleCounterType.StopWatch); + return items && items.length ? this._timeTrackingService.tick$.pipe(map((tick) => ({ tick, items }))) - : EMPTY, - ), + : EMPTY; + }), mergeMap(({ items, tick }) => { const today = this._dateService.todayStr(); return items.map((item) => From d223041d7297018c21a606c66a81c5ae42ac4070 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Tue, 12 Nov 2024 20:11:43 +0100 Subject: [PATCH 3/4] feat(simpleCounter): make config for new counter type work --- .../simple-counter-cfg.component.html | 2 +- .../simple-counter-cfg.component.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/features/simple-counter/simple-counter-cfg/simple-counter-cfg.component.html b/src/app/features/simple-counter/simple-counter-cfg/simple-counter-cfg.component.html index 5d63999827..dfe2bb5cd2 100644 --- a/src/app/features/simple-counter/simple-counter-cfg/simple-counter-cfg.component.html +++ b/src/app/features/simple-counter/simple-counter-cfg/simple-counter-cfg.component.html @@ -3,7 +3,7 @@ [formGroup]="form" > ; @Input() cfg?: SimpleCounterConfig; + + @Input() set section(section: ConfigFormSection) { + if (section.items) { + this.items = adjustToLiveFormlyForm(section.items); + } + } + @Output() save: EventEmitter<{ sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey; config: any; @@ -46,6 +53,7 @@ export class SimpleCounterCfgComponent implements OnDestroy { })), ); + items: FormlyFieldConfig[] = []; editModel?: SimpleCounterConfig; private _inModelCopy?: SimpleCounterConfig; From 25f3c8858adc5577c7601361fad111c363d7ac2c Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Tue, 12 Nov 2024 20:15:28 +0100 Subject: [PATCH 4/4] feat(simpleCounter): add translations --- .../features/config/form-cfgs/simple-counter-form.const.ts | 4 ++-- src/app/t.const.ts | 2 ++ src/assets/i18n/en.json | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/features/config/form-cfgs/simple-counter-form.const.ts b/src/app/features/config/form-cfgs/simple-counter-form.const.ts index d096f5f44d..c36941a2f7 100644 --- a/src/app/features/config/form-cfgs/simple-counter-form.const.ts +++ b/src/app/features/config/form-cfgs/simple-counter-form.const.ts @@ -60,7 +60,7 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection = { value: SimpleCounterType.ClickCounter, }, { - label: T.F.SIMPLE_COUNTER.FORM.TYPE_CLICK_COUNTER, + label: T.F.SIMPLE_COUNTER.FORM.TYPE_REPEATED_COUNTDOWN, value: SimpleCounterType.RepeatedCountdownReminder, }, ], @@ -100,7 +100,7 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection = { templateOptions: { required: false, isAllowSeconds: false, - label: T.F.SIMPLE_COUNTER.FORM.L_TITLE, + label: T.F.SIMPLE_COUNTER.FORM.L_COUNTDOWN_DURATION, description: T.G.DURATION_DESCRIPTION, }, }, diff --git a/src/app/t.const.ts b/src/app/t.const.ts index 3dbd0f8952..92168f97e0 100644 --- a/src/app/t.const.ts +++ b/src/app/t.const.ts @@ -831,6 +831,7 @@ const T = { L_AUTO_COUNT_UP: 'F.SIMPLE_COUNTER.FORM.L_AUTO_COUNT_UP', L_AUTO_SWITCH_OFF: 'F.SIMPLE_COUNTER.FORM.L_AUTO_SWITCH_OFF', L_AUTO_SWITCH_ON: 'F.SIMPLE_COUNTER.FORM.L_AUTO_SWITCH_ON', + L_COUNTDOWN_DURATION: 'F.SIMPLE_COUNTER.FORM.L_COUNTDOWN_DURATION', L_ICON: 'F.SIMPLE_COUNTER.FORM.L_ICON', L_ICON_ON: 'F.SIMPLE_COUNTER.FORM.L_ICON_ON', L_IS_ENABLED: 'F.SIMPLE_COUNTER.FORM.L_IS_ENABLED', @@ -838,6 +839,7 @@ const T = { L_TYPE: 'F.SIMPLE_COUNTER.FORM.L_TYPE', TITLE: 'F.SIMPLE_COUNTER.FORM.TITLE', TYPE_CLICK_COUNTER: 'F.SIMPLE_COUNTER.FORM.TYPE_CLICK_COUNTER', + TYPE_REPEATED_COUNTDOWN: 'F.SIMPLE_COUNTER.FORM.TYPE_REPEATED_COUNTDOWN', TYPE_STOPWATCH: 'F.SIMPLE_COUNTER.FORM.TYPE_STOPWATCH', }, }, diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index d75e189f6d..66e4a71aae 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -818,6 +818,7 @@ "L_AUTO_COUNT_UP": "Auto trigger count up for", "L_AUTO_SWITCH_OFF": "Auto trigger switch off for", "L_AUTO_SWITCH_ON": "Auto trigger switch on for", + "L_COUNTDOWN_DURATION": "Countdown duration", "L_ICON": "Icon", "L_ICON_ON": "Icon when toggled", "L_IS_ENABLED": "Enabled", @@ -825,6 +826,7 @@ "L_TYPE": "Type", "TITLE": "Simple Counters", "TYPE_CLICK_COUNTER": "Click Counter", + "TYPE_REPEATED_COUNTDOWN": "Repeated Countdown", "TYPE_STOPWATCH": "Stopwatch" } },