From 0c09e6aba6be6d21b756f2bac7c461ed712bc203 Mon Sep 17 00:00:00 2001 From: Joao Leonardo Pereira Date: Fri, 19 Jul 2024 18:34:34 -0300 Subject: [PATCH] extract countdown logic into dedicated component --- .../account-detail.component.html | 2 +- .../account-detail.component.ts | 56 +++++++++++-------- .../countdown-timer.component.html | 4 +- .../countdown-timer.component.ts | 51 ++++++++++++++++- src/app/home/home.module.ts | 4 +- src/app/models/account2FA.model.ts | 2 +- 6 files changed, 86 insertions(+), 33 deletions(-) diff --git a/src/app/components/account-detail/account-detail.component.html b/src/app/components/account-detail/account-detail.component.html index aaf40c3..26e1004 100644 --- a/src/app/components/account-detail/account-detail.component.html +++ b/src/app/components/account-detail/account-detail.component.html @@ -22,7 +22,7 @@ -

{{timer}}s

+
diff --git a/src/app/components/account-detail/account-detail.component.ts b/src/app/components/account-detail/account-detail.component.ts index 593af26..41861a9 100644 --- a/src/app/components/account-detail/account-detail.component.ts +++ b/src/app/components/account-detail/account-detail.component.ts @@ -1,7 +1,9 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, ViewChild } from '@angular/core'; import { ToastController } from '@ionic/angular'; import { Account2FA } from 'src/app/models/account2FA.model'; import { OtpService } from 'src/app/services/otp.service'; +import { CountdownTimerComponent } from '../countdown-timer/countdown-timer.component'; +import { debounceTime, pipe } from 'rxjs'; @Component({ selector: 'app-account-detail', @@ -9,17 +11,18 @@ import { OtpService } from 'src/app/services/otp.service'; styleUrls: ['./account-detail.component.scss'], }) export class AccountDetailComponent { + @ViewChild(CountdownTimerComponent) countdownTimer!: CountdownTimerComponent; - private timerRefreshInterval: any private _account?: Account2FA - timer: number = 0 private _token = '000 000' - + private _tokenCountdown = 30 + private debounceTimeout: any @Input() set account(value: Account2FA | undefined) { this._account = value + this.updateTokenCountdown() this.updateCode() - this.updateTimer() } + get account(): Account2FA | undefined { return this._account } @@ -54,6 +57,17 @@ export class AccountDetailComponent { return this._token } + private set tokenCountdown(value: number) { + this._tokenCountdown = value + setTimeout(() => { + this.countdownTimer?.startTimer() + }, 50); + } + + get tokenCountdown(): number { + return this._tokenCountdown + } + async copyCode(evt: any) { if(!this.account) { return @@ -70,24 +84,11 @@ export class AccountDetailComponent { await toast.present() } - updateTimer() { - if(this.timerRefreshInterval) { - clearInterval(this.timerRefreshInterval) - } - if(this.account) { - this.timer = this.account.getNextRollingTimeLeft() - this.timerRefreshInterval = setInterval(() => { - if(!this.account) { - clearInterval(this.timerRefreshInterval) - this.timer = NaN - } else { - this.timer = this.account.getNextRollingTimeLeft() - if (this.timer == this.account.interval) { // new code needed - this.updateCode() - } - } - }, 500) // for precision purposes update every 500ms - } + timerEnd() { + setTimeout(() => { + this.updateCode() + this.updateTokenCountdown() + }, 1000); } updateCode() { @@ -96,4 +97,13 @@ export class AccountDetailComponent { this.token = this.otpService.generateTOTP(this.account.secret, this.account.interval) } } + + private updateTokenCountdown() { + if(!this.debounceTimeout) { + this.debounceTimeout = setTimeout(() => { + this.tokenCountdown = this.account?.getNextRollingTimeLeft() || this.account?.interval || 30 + this.debounceTimeout = undefined + }, 150) + } + } } diff --git a/src/app/components/countdown-timer/countdown-timer.component.html b/src/app/components/countdown-timer/countdown-timer.component.html index 5219430..5233275 100644 --- a/src/app/components/countdown-timer/countdown-timer.component.html +++ b/src/app/components/countdown-timer/countdown-timer.component.html @@ -1,3 +1 @@ -

- countdown-timer works! -

+

{{timerLabel}}s

\ No newline at end of file diff --git a/src/app/components/countdown-timer/countdown-timer.component.ts b/src/app/components/countdown-timer/countdown-timer.component.ts index 2d42875..2056300 100644 --- a/src/app/components/countdown-timer/countdown-timer.component.ts +++ b/src/app/components/countdown-timer/countdown-timer.component.ts @@ -1,14 +1,59 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-countdown-timer', templateUrl: './countdown-timer.component.html', styleUrls: ['./countdown-timer.component.scss'], }) -export class CountdownTimerComponent implements OnInit { +export class CountdownTimerComponent { + private timerRefreshInterval: any + private _timerStartTime = 0 + private _seconds = 0 + @Input() set seconds(value: number) { + this._seconds = value + this.timerLabel = value + this.stopTimer() + } + get seconds(): number { + return this._seconds + } + + @Output() timerEnd = new EventEmitter(); + + timerLabel: number = 0; constructor() { } - ngOnInit() {} + public startTimer() { + this.setupTimerInterval() + } + + private stopTimer() { + if(this.timerRefreshInterval) { + clearInterval(this.timerRefreshInterval) + } + } + + private setupTimerInterval() { + if(this.timerRefreshInterval) { + clearInterval(this.timerRefreshInterval) + } + + this.timerLabel = this.seconds // reset timer label + this._timerStartTime = Date.now() // reset timer start time + + this.timerRefreshInterval = setInterval(() => { + this.updateTimerLabel() + if(this.timerLabel <= 0) { + clearInterval(this.timerRefreshInterval) + this.timerEnd.emit() + } + }, 250) // for precision purposes check every 250ms + } + + private updateTimerLabel() { + const elapsedTime = Math.ceil((Date.now() - this._timerStartTime)/1000) + this.timerLabel = this.seconds - elapsedTime + } } diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts index 83b7925..bdfc9ea 100644 --- a/src/app/home/home.module.ts +++ b/src/app/home/home.module.ts @@ -9,6 +9,7 @@ import { AccountFilterPipe } from '../pipes/account-filter.pipe'; import { NgxScannerQrcodeModule } from 'ngx-scanner-qrcode'; import { AccountListComponent } from '../components/account-list/account-list.component'; import { AccountDetailComponent } from '../components/account-detail/account-detail.component'; +import { CountdownTimerComponent } from '../components/countdown-timer/countdown-timer.component'; @NgModule({ @@ -20,8 +21,7 @@ import { AccountDetailComponent } from '../components/account-detail/account-det HomePageRoutingModule, AccountFilterPipe, NgxScannerQrcodeModule, - ], - declarations: [HomePage, AccountListComponent, AccountDetailComponent] + declarations: [HomePage, AccountListComponent, AccountDetailComponent, CountdownTimerComponent] }) export class HomePageModule {} diff --git a/src/app/models/account2FA.model.ts b/src/app/models/account2FA.model.ts index 1ea1a23..2aaed27 100644 --- a/src/app/models/account2FA.model.ts +++ b/src/app/models/account2FA.model.ts @@ -71,7 +71,7 @@ export class Account2FA implements IAccount2FA { } getNextRollingTimeLeft(): number { - return (this.interval || 30) - (Math.floor(Date.now() / 1000) % (this.interval || 30)); + return this.interval - (Math.ceil(Date.now() / 1000) % this.interval); } typeErased(): Object {