import { Injectable } from '@angular/core' import { concatMap, finalize } from 'rxjs/operators' import { Observable, from, Subject } from 'rxjs' import { fromAsync$, fromAsyncP, emitAfter$, fromSync$ } from '../util/rxjs.util' import { LoadingController } from '@ionic/angular' import { LoadingOptions } from '@ionic/core' @Injectable({ providedIn: 'root', }) export class LoaderService { private loadingOptions: LoadingOptions = defaultOptions() constructor (private readonly loadingCtrl: LoadingController) { } private loader: HTMLIonLoadingElement public get ionLoader (): HTMLIonLoadingElement { return this.loader } public get ctrl () { return this.loadingCtrl } private setOptions (l: LoadingOptions): LoaderService { this.loadingOptions = l return this } of (overrideOptions: LoadingOptions): LoaderService { return new LoaderService(this.loadingCtrl).setOptions(Object.assign(defaultOptions(), overrideOptions)) } displayDuring$ (o: Observable): Observable { let shouldDisplay = true const displayIfItsBeenAtLeast = 10 // ms return fromAsync$( async () => { this.loader = await this.loadingCtrl.create(this.loadingOptions) emitAfter$(displayIfItsBeenAtLeast).subscribe(() => { if (shouldDisplay) this.loader.present() }) }, ).pipe( concatMap(() => o), finalize(() => { this.loader.dismiss(); shouldDisplay = false; this.loader = undefined }), ) } displayDuringP (p: Promise): Promise { return this.displayDuring$(from(p)).toPromise() } displayDuringAsync (thunk: () => Promise): Promise { return this.displayDuringP(fromAsyncP(thunk)) } } export function markAsLoadingDuring$ ($trigger$: Subject, o: Observable): Observable { let shouldBeOn = true const displayIfItsBeenAtLeast = 5 // ms return fromSync$(() => { emitAfter$(displayIfItsBeenAtLeast).subscribe(() => { if (shouldBeOn) $trigger$.next(true) }) }).pipe( concatMap(() => o), finalize(() => { $trigger$.next(false) shouldBeOn = false }), ) } const defaultOptions: () => LoadingOptions = () => ({ spinner: 'lines', cssClass: 'loader', backdropDismiss: true, })