mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-28 18:20:42 +00:00
Merge branch 'feat/repeated-countdown-reminder'
* feat/repeated-countdown-reminder: feat(simpleCounter): add translations feat(simpleCounter): make config for new counter type work feat(simpleCounter): make new simple counter type work feat(simpleCounter): outline new type
This commit is contained in:
commit
28d0771463
12 changed files with 161 additions and 34 deletions
|
|
@ -59,6 +59,10 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection<SimpleCounterConfig> = {
|
|||
label: T.F.SIMPLE_COUNTER.FORM.TYPE_CLICK_COUNTER,
|
||||
value: SimpleCounterType.ClickCounter,
|
||||
},
|
||||
{
|
||||
label: T.F.SIMPLE_COUNTER.FORM.TYPE_REPEATED_COUNTDOWN,
|
||||
value: SimpleCounterType.RepeatedCountdownReminder,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -79,6 +83,27 @@ export const SIMPLE_COUNTER_FORM: ConfigFormSection<SimpleCounterConfig> = {
|
|||
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(30 * 60000);
|
||||
}
|
||||
},
|
||||
},
|
||||
templateOptions: {
|
||||
required: false,
|
||||
isAllowSeconds: false,
|
||||
label: T.F.SIMPLE_COUNTER.FORM.L_COUNTDOWN_DURATION,
|
||||
description: T.G.DURATION_DESCRIPTION,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'triggerOnActions',
|
||||
type: 'select',
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
<mat-dialog-content>
|
||||
<div class="form-wrapper">
|
||||
<mat-form-field *ngIf="data.simpleCounter.type === SimpleCounterType.ClickCounter">
|
||||
<mat-form-field
|
||||
*ngIf="data.simpleCounter.type === SimpleCounterType.ClickCounter || data.simpleCounter.type === SimpleCounterType.RepeatedCountdownReminder"
|
||||
>
|
||||
<input
|
||||
(ngModelChange)="onModelChange($event)"
|
||||
[ngModel]="val"
|
||||
|
|
@ -37,7 +39,7 @@
|
|||
mat-button
|
||||
type="button"
|
||||
>
|
||||
{{T.G.CANCEL|translate}}
|
||||
{{ T.G.CANCEL|translate }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary submit-button"
|
||||
|
|
@ -46,7 +48,7 @@
|
|||
type="submit"
|
||||
>
|
||||
<mat-icon>save</mat-icon>
|
||||
{{T.G.SAVE|translate}}
|
||||
{{ T.G.SAVE|translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,28 @@
|
|||
<div
|
||||
*ngIf="simpleCounter"
|
||||
*ngIf="simpleCounter() as sc"
|
||||
class="wrapper"
|
||||
>
|
||||
<ng-container *ngIf="simpleCounter.type===SimpleCounterType.StopWatch">
|
||||
<ng-container *ngIf="sc.type===SimpleCounterType.StopWatch">
|
||||
<button
|
||||
(click)="toggleStopwatch()"
|
||||
(contextmenu)="edit($event)"
|
||||
(longPress)="edit()"
|
||||
[color]="simpleCounter.isOn? 'accent': ''"
|
||||
[color]="sc.isOn? 'accent': ''"
|
||||
class="main-btn stopwatch"
|
||||
mat-mini-fab
|
||||
>
|
||||
<mat-icon *ngIf="!simpleCounter.isOn">{{simpleCounter.icon}}</mat-icon>
|
||||
<mat-icon *ngIf="simpleCounter.isOn"
|
||||
>{{simpleCounter.iconOn||simpleCounter.icon}}</mat-icon
|
||||
>
|
||||
<mat-icon *ngIf="!sc.isOn">{{sc.icon}}</mat-icon>
|
||||
<mat-icon *ngIf="sc.isOn">{{sc.iconOn||sc.icon}}</mat-icon>
|
||||
</button>
|
||||
<div
|
||||
*ngIf="simpleCounter.countOnDay[todayStr]"
|
||||
*ngIf="sc.countOnDay[todayStr]"
|
||||
class="label"
|
||||
>
|
||||
{{simpleCounter.countOnDay[todayStr]|msToMinuteClockString}}
|
||||
{{sc.countOnDay[todayStr]|msToMinuteClockString}}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="simpleCounter.type===SimpleCounterType.ClickCounter">
|
||||
<ng-container *ngIf="sc.type===SimpleCounterType.ClickCounter">
|
||||
<button
|
||||
(click)="toggleCounter()"
|
||||
(contextmenu)="edit($event)"
|
||||
|
|
@ -33,13 +31,35 @@
|
|||
color=""
|
||||
mat-mini-fab
|
||||
>
|
||||
<mat-icon>{{simpleCounter.icon}}</mat-icon>
|
||||
<mat-icon>{{sc.icon}}</mat-icon>
|
||||
</button>
|
||||
<div
|
||||
*ngIf="simpleCounter.countOnDay[todayStr]"
|
||||
*ngIf="sc.countOnDay[todayStr]"
|
||||
class="label"
|
||||
>
|
||||
{{simpleCounter.countOnDay[todayStr]}}
|
||||
{{sc.countOnDay[todayStr]}}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="sc.type===SimpleCounterType.RepeatedCountdownReminder">
|
||||
<button
|
||||
(click)="toggleStopwatch()"
|
||||
(contextmenu)="edit($event)"
|
||||
(longPress)="edit()"
|
||||
[color]="sc.isOn? 'accent': ''"
|
||||
class="main-btn repeated-countdown"
|
||||
mat-mini-fab
|
||||
>
|
||||
<mat-icon *ngIf="!sc.isOn">{{sc.icon}}</mat-icon>
|
||||
<mat-icon *ngIf="sc.isOn">{{sc.iconOn||sc.icon}}</mat-icon>
|
||||
</button>
|
||||
<div class="extra-label">{{countdownTime$|async|msToMinuteClockString}}</div>
|
||||
|
||||
<div
|
||||
*ngIf="sc.countOnDay[todayStr]"
|
||||
class="label"
|
||||
>
|
||||
{{sc.countOnDay[todayStr]}}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 { merge, of, Subject, Subscription } from 'rxjs';
|
||||
import { DateService } from 'src/app/core/date/date.service';
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
import { distinctUntilChanged, filter, map, scan, switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'simple-counter-button',
|
||||
|
|
@ -26,10 +28,35 @@ export class SimpleCounterButtonComponent implements OnDestroy, OnInit {
|
|||
SimpleCounterType: typeof SimpleCounterType = SimpleCounterType;
|
||||
todayStr: string = this._dateService.todayStr();
|
||||
|
||||
@Input() simpleCounter?: SimpleCounter;
|
||||
simpleCounter = input<SimpleCounter>();
|
||||
|
||||
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,
|
||||
|
|
@ -46,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 {
|
||||
|
|
@ -53,35 +95,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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
[formGroup]="form"
|
||||
>
|
||||
<formly-form
|
||||
[fields]="section?.items"
|
||||
[fields]="items"
|
||||
[form]="form"
|
||||
[(model)]="editModel"
|
||||
[options]="options"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from '../../config/global-config.model';
|
||||
import { ProjectCfgFormKey } from '../../project/project.model';
|
||||
import { SimpleCounterConfig } from '../simple-counter.model';
|
||||
import { FormlyFormOptions } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { T } from 'src/app/t.const';
|
||||
import { SimpleCounterService } from '../simple-counter.service';
|
||||
|
|
@ -20,6 +20,7 @@ import { map } from 'rxjs/operators';
|
|||
import { Observable, Subscription } from 'rxjs';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confirm.component';
|
||||
import { adjustToLiveFormlyForm } from '../../../util/adjust-to-live-formly-form';
|
||||
|
||||
@Component({
|
||||
selector: 'simple-counter-cfg',
|
||||
|
|
@ -28,8 +29,14 @@ import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confir
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SimpleCounterCfgComponent implements OnDestroy {
|
||||
@Input() section?: ConfigFormSection<SimpleCounterConfig>;
|
||||
@Input() cfg?: SimpleCounterConfig;
|
||||
|
||||
@Input() set section(section: ConfigFormSection<SimpleCounterConfig>) {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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[] = [
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -66,11 +66,12 @@ export class SimpleCounterEffects {
|
|||
|
||||
checkTimedCounters$: Observable<unknown> = 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) =>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue