import {Component, OnDestroy, OnInit, Renderer2} from "@angular/core"; import { IonBreadcrumb, IonBreadcrumbs, IonButton, IonCard, IonContent, IonIcon, IonItem, IonLabel, IonList, IonProgressBar } from "@ionic/angular/standalone"; import {TushaPageComponent} from "../../../tusha/tusha-page/tusha-page.component"; import {FocusState} from "./focus-state"; import {FocusService} from "./focus.service"; import {Observable} from "rxjs"; import {FocusStats} from "./focus-stats"; import { CommonModule } from '@angular/common'; @Component({ templateUrl: 'focus.component.html', styleUrls: ['focus.component.css'], standalone: true, imports: [IonBreadcrumbs, IonBreadcrumb, IonIcon, TushaPageComponent, IonProgressBar, IonContent, IonButton, IonCard, IonItem, IonLabel, IonList, CommonModule] }) export class FocusPage implements OnInit, OnDestroy { duration: number = 0; // default duration in minutes minutesText: string = ''; secondsText: string = ''; private interval: number | undefined; private timerStarted: number | null = null; state: FocusState = FocusState.STOPPED; private focusRuns = 0; $stats: Observable = this.focusService.$stats; private endOfTimerChime: HTMLAudioElement; private forcedEndOfTimerChime: HTMLAudioElement; private windUpChime: HTMLAudioElement; pathD: string = 'M100 10 A90 90 0 1 1 0 0'; dashOffset = 0; constructor(private renderer: Renderer2, private focusService: FocusService) { this.endOfTimerChime = new Audio('../../../assets/race-start-beeps-125125.mp3'); this.forcedEndOfTimerChime = new Audio('../../../assets/hardstop.mp3'); this.windUpChime = new Audio('../../../assets/wind-up.mp3'); } ngOnInit() { this.updateTimeDisplay(this.duration * 60, 0, 1); } ngOnDestroy() { if (this.interval) { clearInterval(this.interval); } } startTimer(duration: number) { this.duration = duration; if (this.interval) { clearInterval(this.interval); } this.timerStarted = Date.now(); this.update(); this.interval = setInterval(() => this.update(), 1000); } private update() { const elapsed = this.timerStarted ? Math.round((Date.now() - (this.timerStarted)) / 1000) : 0; // in seconden let progress = elapsed / (this.duration * 60); // from 0 to 1 if( progress === 0) { progress = 0.0000001; // otherwise no graph } if (progress >= 1 || isNaN(progress)) { progress = 1; if (this.duration === 0) { this.forcedEndOfTimerChime.play().catch(error => { console.error('Error playing audio:', error); }); clearInterval(this.interval); this.timerStarted = null; } else if (progress === 1) { this.endOfTimerChime.play().catch(error => { console.error('Error playing audio:', error); }); } progress = 1; } this.renderer.setStyle( document.documentElement, '--progress', progress.toString() ); this.updateTimeDisplay(this.duration * 60 - elapsed, elapsed, 1 - progress); } private updateTimeDisplay(timeLeft: number, elapsed: number, percentage: number) { const theta = 2 * Math.PI * (percentage - 0.25); const circleEndX = 100 + 90 * Math.cos(theta); const circleEndY = 100 + 90 * Math.sin(theta); this.pathD = 'M100 10 A90 90 0 ' + (percentage > 0.5 ? '1' : '0') + ' 1 ' + circleEndX + ' ' + circleEndY; const twoNumbers = (n: number) => n < 10 && n >= 0 ? '0' + n.toString() : n.toString(); const floorPositiveCeilNegative = (n: number): number => n > 0 ? Math.floor(n) : Math.ceil(n); this.minutesText = twoNumbers(Math.abs(floorPositiveCeilNegative(timeLeft / 60))); this.secondsText = twoNumbers(Math.abs(floorPositiveCeilNegative(timeLeft % 60))); this.dashOffset = elapsed; } startWhereWeAre() { switch (this.state) { case FocusState.FOCUS: this.focusRuns++; if (this.focusRuns > 3) { this.focusRuns = 0; this.recharge(); } else { this.reload(); } break; default: this.focus(); break; } } stopTimer() { this.focusRuns = 0; this.state = FocusState.STOPPED; this.stopStatsCounter(); this.startTimer(0); } private stopStatsCounter() { this.focusService.stopCurrent(this.timerStarted !== null); } focus() { this.windUpChime.play().catch(error => { console.error('Error playing audio:', error); }); this.state = FocusState.FOCUS; this.stopStatsCounter(); this.focusService.startFocus(); this.startTimer(1); // 25 } reload() { this.windUpChime.play().catch(error => { console.error('Error playing audio:', error); }); if (!!this.timerStarted) { this.focusRuns = 0; } this.state = FocusState.SHORT_BREAK; this.stopStatsCounter(); this.focusService.startShortBreak(); this.startTimer(0.1); // 5 } recharge() { this.windUpChime.play().catch(error => { console.error('Error playing audio:', error); }); if (!!this.timerStarted) { this.focusRuns = 0; } this.state = FocusState.LONG_BREAK; this.stopStatsCounter(); this.focusService.startLongBreak(); this.startTimer(0.3); // 15 } }