Finished the pomodoro timer

except for a sound, the settings and perfect centering on mobile devices
This commit is contained in:
2024-11-15 18:54:28 +01:00
parent c75f322a0c
commit 4e3431e7a7
3 changed files with 107 additions and 86 deletions

View File

@@ -9,8 +9,11 @@ app-tusha-page {
justify-content: space-between; justify-content: space-between;
} }
.countdown-box { .fullwidth {
width: 320px; width: 320px;
}
.countdown-box {
height: 320px; height: 320px;
border: 2px solid; border: 2px solid;
padding: 0.5em 0.5em 0.5em 0.5em; padding: 0.5em 0.5em 0.5em 0.5em;
@@ -30,31 +33,31 @@ app-tusha-page {
color: darkred; color: darkred;
} }
.countdown-state-short-break-box { .countdown-state-short_break-box {
background-color: lightgreen; background-color: lightcyan;
border-color: darkgreen; border-color: darkgreen;
} }
.countdown-state-short-break-text { .countdown-state-short_break-text {
color: darkgreen; color: darkgreen;
} }
.countdown-state-long-break-box { .countdown-state-long_break-box {
background-color: peachpuff; background-color: peachpuff;
border-color: orangered; border-color: orangered;
} }
.countdown-state-long-break-text { .countdown-state-long_break-text {
color: orangered; color: orangered;
} }
.countdown-state-in-focus-box { .countdown-state-in_focus-box {
background-color: lightblue; background-color: lightblue;
border-color: darkblue; border-color: darkblue;
} }
.countdown-state-in-focus-text { .countdown-state-in_focus-text {
color: darkblue; color: darkblue;
} }
@@ -72,6 +75,19 @@ app-tusha-page {
background-color: lavender; background-color: lavender;
} }
.countdown-text {
position: relative;
transform: translate(25%, -225%);
font-size: 4em;
color: var(--ion-color-primary);
}
ion-button {
position: relative;
width: 120px;
}
@@ -98,21 +114,9 @@ app-tusha-page {
background-color: beige; background-color: beige;
} }
ion-button {
position: relative;
left: 50%;
margin-top: 20px;
transform: translate(-50%, 0%);
}
.countdown-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 4em;
color: var(--ion-color-primary);
}
.timer { .timer {
width: 350px; width: 350px;

View File

@@ -14,15 +14,24 @@
</ion-breadcrumb> </ion-breadcrumb>
</ion-breadcrumbs> </ion-breadcrumbs>
<!-- TODO add settings cogwheel -->
<!-- TODO add default action to div --> <!-- TODO add default action to div -->
<!-- TODO add are you sure pop-ups --> <!-- TODO add are you sure pop-ups -->
<div class="countdown-box countdown-state-{{state}}-box"> <div class="leftright-line fullwidth">
<ion-button (click)="focus()" color="primary">
<ion-icon slot="start" name="aperture"></ion-icon>
Focus
</ion-button>
<ion-button (click)="stopTimer()" color="danger">
<ion-icon slot="start" name="close-circle"></ion-icon>
Give up
</ion-button>
</div>
<div class="countdown-box countdown-state-{{state}}-box fullwidth">
<div class="leftright-line"> <div class="leftright-line">
<span class="countdown-state-text countdown-state-{{state}}-text">{{state}}</span> <span class="countdown-state-text countdown-state-{{state}}-text">{{state}}</span>
<!-- TODO add behaviour to settings cogwheel -->
<ion-icon name="settings-outline"></ion-icon> <ion-icon name="settings-outline"></ion-icon>
</div> </div>
<div class="countdown-timer"> <div class="countdown-timer">
@@ -43,47 +52,50 @@
<div class="countdown-text">{{ minutesText }}:{{ secondsText }}</div> <div class="countdown-text">{{ minutesText }}:{{ secondsText }}</div>
</div> </div>
</div> </div>
<div class="leftright-line">
<ion-button (click)="startTimer(25)">Focus</ion-button> <div class="leftright-line fullwidth">
<ion-button (click)="stopTimer()">Give up</ion-button> <ion-button (click)="reload()" color="secondary">
</div> <ion-icon slot="start" name="refresh-circle" ></ion-icon>
<div class="leftright-line"> Reload
<ion-button (click)="startTimer(5)">Reload</ion-button> </ion-button>
<ion-button (click)="startTimer(15)">Recharge</ion-button> <ion-button (click)="recharge()" color="warning">
<ion-icon slot="start" name="battery-charging"></ion-icon>
Recharge
</ion-button>
</div> </div>
<div class="timer"> <!-- <div class="timer">-->
<div class="countdown focusmode"> <!-- <div class="countdown focusmode">-->
<svg class="focustimer" width="100%" height="100%" viewBox="0 0 200 200" style="background-color: lavender;" (click)="startWhereWeAre()"> <!-- <svg class="focustimer" width="100%" height="100%" viewBox="0 0 200 200" style="background-color: lavender;" (click)="startWhereWeAre()">-->
<!-- Definieer de gradient --> <!-- &lt;!&ndash; Definieer de gradient &ndash;&gt;-->
<defs> <!-- <defs>-->
<!-- have the gradient follow the path is possible but too difficult for now - can be read at https://stackoverflow.com/questions/14633363/can-i-apply-a-gradient-along-an-svg-path --> <!-- &lt;!&ndash; have the gradient follow the path is possible but too difficult for now - can be read at https://stackoverflow.com/questions/14633363/can-i-apply-a-gradient-along-an-svg-path &ndash;&gt;-->
<linearGradient id="grad1" x1="1" y1="0" x2="0" y2="0"> <!-- <linearGradient id="grad1" x1="1" y1="0" x2="0" y2="0">-->
<stop offset="0%" stop-color="blue"></stop> <!-- <stop offset="0%" stop-color="blue"></stop>-->
<stop offset="80%" stop-color="green"></stop> <!-- <stop offset="80%" stop-color="green"></stop>-->
<stop offset="100%" stop-color="red"></stop> <!-- <stop offset="100%" stop-color="red"></stop>-->
</linearGradient> <!-- </linearGradient>-->
</defs> <!-- </defs>-->
<path id="1stPath" [attr.d]="pathD" <!-- <path id="1stPath" [attr.d]="pathD"-->
fill="none" stroke="url(#grad1)" stroke-width="30" stroke-dasharray="0" [attr.stroke-dashoffset]="dashOffset" /> <!-- fill="none" stroke="url(#grad1)" stroke-width="30" stroke-dasharray="0" [attr.stroke-dashoffset]="dashOffset" />-->
</svg> <!-- </svg>-->
<div class="countdown-text">{{ minutesText }}:{{ secondsText }}</div> <!-- <div class="countdown-text">{{ minutesText }}:{{ secondsText }}</div>-->
</div> <!-- </div>-->
</div> <!-- </div>-->
<div class="state state-{{state}}">{{state}}</div> <!-- <div class="state state-{{state}}">{{state}}</div>-->
<!-- TODO decide buttons to show on state --> <!-- &lt;!&ndash; TODO decide buttons to show on state &ndash;&gt;-->
<div class="buttonBar"> <!-- <div class="buttonBar">-->
<ion-button (click)="startTimer(25)">Focus</ion-button> <!-- <ion-button (click)="startTimer(25)">Focus</ion-button>-->
<ion-button (click)="stopTimer()">Give up</ion-button> <!-- <ion-button (click)="stopTimer()">Give up</ion-button>-->
</div> <!-- </div>-->
<div class="buttonBar"> <!-- <div class="buttonBar">-->
<ion-button (click)="startTimer(5)">Reload</ion-button> <!-- <ion-button (click)="startTimer(5)">Reload</ion-button>-->
<ion-button (click)="startTimer(15)">Recharge</ion-button> <!-- <ion-button (click)="startTimer(15)">Recharge</ion-button>-->
</div> <!-- </div>-->
</app-tusha-page> </app-tusha-page>

View File

@@ -10,13 +10,11 @@ import {FocusState} from "./focus-state";
imports: [IonBreadcrumbs, IonBreadcrumb, IonIcon, TushaPageComponent, IonProgressBar, IonContent, IonButton] imports: [IonBreadcrumbs, IonBreadcrumb, IonIcon, TushaPageComponent, IonProgressBar, IonContent, IonButton]
}) })
export class FocusPage implements OnInit, OnDestroy { export class FocusPage implements OnInit, OnDestroy {
duration: number = 0.5; // default duration in minutes duration: number = 0; // default duration in minutes
minutes: number = 25;
minutesText: string = ''; minutesText: string = '';
seconds: number = 0;
secondsText: string = ''; secondsText: string = '';
private interval: any; private interval: number | undefined;
private startTime: number | null = null; private timerStarted: number | null = null;
state: FocusState = FocusState.STOPPED; state: FocusState = FocusState.STOPPED;
@@ -42,30 +40,24 @@ export class FocusPage implements OnInit, OnDestroy {
clearInterval(this.interval); clearInterval(this.interval);
} }
this.startTime = Date.now(); this.timerStarted = Date.now();
this.update();
this.interval = setInterval(() => this.update(), 1000); this.interval = setInterval(() => this.update(), 1000);
} }
private update() { private update() {
const elapsed = Math.round((Date.now() - (this.startTime as number)) / 1000); // in seconden const elapsed = Math.round((Date.now() - (this.timerStarted!)) / 1000); // in seconden
let progress = elapsed / (this.duration * 60); let progress = elapsed / (this.duration * 60); // from 0 to 1
if( progress === 0) {
progress = 0.0000001; // otherwise no graph
}
if (progress >= 1) { if (progress >= 1) {
console.log('timer reader');
progress = 1; progress = 1;
clearInterval(this.interval); clearInterval(this.interval);
} }
const path1 = document.getElementById('1stPath') as unknown as SVGPathElement;
const totalLength1 = path1.getTotalLength();
// path1.style.strokeDasharray = `${totalLength1}`;
// path1.style.strokeDashoffset = `${totalLength1 - totalLength1 * this.progress}`;
//
// path2.style.strokeDasharray = `${totalLength2}`;
// path2.style.strokeDashoffset = `${totalLength2 - totalLength2 * this.progress}`;
this.renderer.setStyle( this.renderer.setStyle(
document.documentElement, document.documentElement,
'--progress', '--progress',
@@ -82,12 +74,9 @@ export class FocusPage implements OnInit, OnDestroy {
this.pathD = 'M100 10 A90 90 0 ' + (percentage > 0.5 ? '1' : '0') + ' 1 ' + circleEndX + ' ' + circleEndY; 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 twoNumbers = (n: number) => n < 10 && n >= 0 ? '0' + n.toString() : n.toString();
this.minutes = Math.max(Math.floor(timeLeft / 60), 0); this.minutesText = twoNumbers(Math.max(Math.floor(timeLeft / 60), 0));
this.seconds = Math.max(Math.floor(timeLeft % 60), 0); this.secondsText = twoNumbers(Math.max(Math.floor(timeLeft % 60), 0));
this.minutesText = twoNumbers(this.minutes);
this.secondsText = twoNumbers(this.seconds);
const progress = -1 * (elapsed / (this.duration * 60));
this.dashOffset = elapsed; this.dashOffset = elapsed;
} }
@@ -96,6 +85,22 @@ export class FocusPage implements OnInit, OnDestroy {
} }
stopTimer() { stopTimer() {
clearInterval(this.interval); this.state = FocusState.STOPPED;
this.startTimer(0);
}
focus() {
this.state = FocusState.FOCUS;
this.startTimer(25);
}
reload() {
this.state = FocusState.SHORT_BREAK;
this.startTimer(5);
}
recharge() {
this.state = FocusState.LONG_BREAK;
this.startTimer(15);
} }
} }