Finished the pomodoro timer
except for a sound, the settings and perfect centering on mobile devices
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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 -->
|
<!-- <!– Definieer de gradient –>-->
|
||||||
<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 -->
|
<!-- <!– 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 –>-->
|
||||||
<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 -->
|
<!-- <!– TODO decide buttons to show on state –>-->
|
||||||
<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>
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user