diff --git a/src/app/components/chrono/chrono.ts b/src/app/components/chrono/chrono.ts new file mode 100644 index 000000000..179ad69f8 --- /dev/null +++ b/src/app/components/chrono/chrono.ts @@ -0,0 +1,128 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + Component, + Input, + OnInit, + OnChanges, + OnDestroy, + Output, + EventEmitter, + SimpleChange, + ChangeDetectorRef, +} from '@angular/core'; + +/** + * This component shows a chronometer in format HH:MM:SS. + * + * If no startTime is provided, it will start at 00:00:00. + * If an endTime is provided, the chrono will stop and emit an event in the onEnd output when that number of milliseconds is + * reached. E.g. if startTime=60000 and endTime=120000, the chrono will start at 00:01:00 and end when it reaches 00:02:00. + * + * This component has 2 boolean inputs to control the timer: running (to start and stop it) and reset. + * + * Example usage: + * + */ +@Component({ + selector: 'core-chrono', + templateUrl: 'core-chrono.html', +}) +export class CoreChronoComponent implements OnInit, OnChanges, OnDestroy { + + @Input() running?: boolean; // Set it to true to start the chrono. Set it to false to stop it. + @Input() startTime = 0; // Number of milliseconds to put in the chrono before starting. + @Input() endTime?: number; // Number of milliseconds to stop the chrono. + @Input() reset?: boolean; // Set it to true to reset the chrono. + @Output() onEnd: EventEmitter; // Will emit an event when the endTime is reached. + + time = 0; + protected interval?: number; + + constructor(protected changeDetectorRef: ChangeDetectorRef) { + this.onEnd = new EventEmitter(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.time = this.startTime || 0; + } + + /** + * Component being changed. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if (changes && changes.running) { + if (changes.running.currentValue) { + this.start(); + } else { + this.stop(); + } + } + if (changes && changes.reset && changes.reset.currentValue) { + this.resetChrono(); + } + } + + /** + * Reset the chrono, stopping it and setting it to startTime. + */ + protected resetChrono(): void { + this.stop(); + this.time = this.startTime || 0; + } + + /** + * Start the chrono if it isn't running. + */ + protected start(): void { + if (this.interval) { + // Already setup. + return; + } + + let lastExecTime = Date.now(); + + this.interval = window.setInterval(() => { + // Increase the chrono. + this.time += Date.now() - lastExecTime; + lastExecTime = Date.now(); + + if (typeof this.endTime != 'undefined' && this.time > this.endTime) { + // End time reached, stop the timer and call the end function. + this.stop(); + this.onEnd.emit(); + } + + // Force change detection. Angular doesn't detect these async operations. + this.changeDetectorRef.detectChanges(); + }, 200); + } + + /** + * Stop the chrono, leaving the same time it has. + */ + protected stop(): void { + clearInterval(this.interval); + delete this.interval; + } + + ngOnDestroy(): void { + this.stop(); + } + +} diff --git a/src/app/components/chrono/core-chrono.html b/src/app/components/chrono/core-chrono.html new file mode 100644 index 000000000..5f0f28ade --- /dev/null +++ b/src/app/components/chrono/core-chrono.html @@ -0,0 +1 @@ +{{ time / 1000 | coreSecondsToHMS }} \ No newline at end of file diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index 1e314e6d9..b67263e6a 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -17,6 +17,7 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from '@ionic/angular'; import { TranslateModule } from '@ngx-translate/core'; +import { CoreChronoComponent } from './chrono/chrono'; import { CoreDownloadRefreshComponent } from './download-refresh/download-refresh'; import { CoreFileComponent } from './file/file'; import { CoreIconComponent } from './icon/icon'; @@ -35,6 +36,7 @@ import { CorePipesModule } from '@app/pipes/pipes.module'; @NgModule({ declarations: [ + CoreChronoComponent, CoreDownloadRefreshComponent, CoreFileComponent, CoreIconComponent, @@ -56,6 +58,7 @@ import { CorePipesModule } from '@app/pipes/pipes.module'; CorePipesModule, ], exports: [ + CoreChronoComponent, CoreDownloadRefreshComponent, CoreFileComponent, CoreIconComponent,