diff --git a/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts index a981ac400a..7292490a4b 100644 --- a/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts @@ -1,13 +1,14 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { HttpRequest } from '@angular/common/http'; import { InternalStore } from '../utils/internal-store-utils'; import { getPathName } from '../utils/http-utils'; -import { debounceTime, tap } from 'rxjs/operators'; +import { map, mapTo, switchMap, takeUntil, tap } from 'rxjs/operators'; +import { of, Subject, timer } from 'rxjs'; +import { LOADER_DELAY } from '../tokens/lodaer-delay.token'; export interface HttpWaitState { - requests: Set>; + requests: HttpRequest[]; filteredRequests: Array; - delay: number; } export interface HttpRequestInfo { method: string; @@ -18,19 +19,35 @@ export interface HttpRequestInfo { }) export class HttpWaitService { protected store = new InternalStore({ - requests: new Set(), + requests: [], filteredRequests: [], - delay: 0, }); + private delay: number; + private destroy$ = new Subject(); + + constructor(injector: Injector) { + this.delay = injector.get(LOADER_DELAY, 500); + } + getLoading() { return !!this.applyFilter(this.store.state.requests).length; } getLoading$() { return this.store - .sliceState(({ requests }) => !!this.applyFilter(requests).length) - .pipe(this.debounceWhenLoading()); + .sliceState(({ requests }) => requests) + .pipe( + map(requests => !!this.applyFilter(requests).length), + switchMap(condition => + condition + ? this.delay === 0 + ? of(true) + : timer(this.delay).pipe(mapTo(true), takeUntil(this.destroy$)) + : of(false), + ), + tap(() => this.destroy$.next()), + ); } updateLoading$() { @@ -38,19 +55,15 @@ export class HttpWaitService { } clearLoading() { - this.store.patch({ requests: new Set() }); + this.store.patch({ requests: [] }); } addRequest(request: HttpRequest) { - let { requests } = this.store.state; - requests = new Set(requests.values()); - requests.add(request); - this.store.patch({ requests }); + this.store.patch({ requests: [...this.store.state.requests, request] }); } deleteRequest(request: HttpRequest) { - const { requests } = this.store.state; - requests.delete(request); + const requests = this.store.state.requests.filter(r => r !== request); this.store.patch({ requests }); } @@ -73,13 +86,9 @@ export class HttpWaitService { this.store.patch({ filteredRequests }); } - setDelay(delay: number) { - this.store.patch({ delay }); - } - - private applyFilter(requests: Set>) { + private applyFilter(requests: HttpRequest[]) { const { filteredRequests } = this.store.state; - return Array.from(requests).filter( + return requests.filter( ({ method, url }) => !filteredRequests.find(filteredRequest => this.isSameRequest(filteredRequest, { method, endpoint: getPathName(url) }), @@ -91,10 +100,4 @@ export class HttpWaitService { const { method, endpoint } = filteredRequest; return endpoint === request.endpoint && method === request.method; } - - private debounceWhenLoading() { - return this.store.state.delay && !!this.applyFilter(this.store.state.requests).length - ? debounceTime(this.store.state.delay) - : tap(); - } } diff --git a/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts index d1306e63c9..56240d8ca2 100644 --- a/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts @@ -1,13 +1,9 @@ -import { Injectable } from '@angular/core'; -import { - NavigationCancel, - NavigationEnd, - NavigationError, - NavigationStart, - Router, -} from '@angular/router'; -import { filter } from 'rxjs/operators'; +import { Injectable, Injector } from '@angular/core'; +import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; +import { filter, map, mapTo, switchMap, takeUntil, tap } from 'rxjs/operators'; import { InternalStore } from '../utils/internal-store-utils'; +import { of, Subject, timer } from 'rxjs'; +import { LOADER_DELAY } from '../tokens/lodaer-delay.token'; export interface RouterWaitState { loading: boolean; @@ -18,7 +14,10 @@ export interface RouterWaitState { }) export class RouterWaitService { private store = new InternalStore({ loading: false }); - constructor(private router: Router) { + private destroy$ = new Subject(); + private delay: number; + constructor(private router: Router, injector: Injector) { + this.delay = injector.get(LOADER_DELAY, 500); this.router.events .pipe( filter( @@ -28,10 +27,18 @@ export class RouterWaitService { event instanceof NavigationError || event instanceof NavigationCancel, ), + map(event => event instanceof NavigationStart), + switchMap(condition => + condition + ? this.delay === 0 + ? of(true) + : timer(this.delay || 0).pipe(mapTo(true), takeUntil(this.destroy$)) + : of(false), + ), + tap(() => this.destroy$.next()), ) - .subscribe(event => { - if (event instanceof NavigationStart) this.setLoading(true); - else this.setLoading(false); + .subscribe(status => { + this.setLoading(status); }); } diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index 8d23d581a5..2e79e70ef3 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -1,2 +1,3 @@ export * from './list.token'; +export * from './lodaer-delay.token'; export * from './options.token'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/lodaer-delay.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/lodaer-delay.token.ts similarity index 100% rename from npm/ng-packs/packages/theme-shared/src/lib/tokens/lodaer-delay.token.ts rename to npm/ng-packs/packages/core/src/lib/tokens/lodaer-delay.token.ts diff --git a/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts b/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts index adfbaca816..1718946cab 100644 --- a/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts +++ b/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts @@ -1,9 +1,10 @@ import { ABP, BaseCoreModule, - coreOptionsFactory, CORE_OPTIONS, + coreOptionsFactory, LIST_QUERY_DEBOUNCE_TIME, + LOADER_DELAY, PermissionService, RestService, } from '@abp/ng.core'; @@ -52,6 +53,10 @@ export class CoreTestingModule { provide: RestService, useClass: MockRestService, }, + { + provide: LOADER_DELAY, + useValue: 0, + }, provideRoutes(routes), ], }; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts index bae858ad09..3fd01d1b4d 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -1,8 +1,7 @@ import { HttpWaitService, RouterWaitService, SubscriptionService } from '@abp/ng.core'; -import { ChangeDetectorRef, Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { combineLatest, Subscription, timer } from 'rxjs'; -import { LOADER_DELAY } from '../../tokens/lodaer-delay.token'; @Component({ selector: 'abp-loader-bar', @@ -77,11 +76,8 @@ export class LoaderBarComponent implements OnDestroy, OnInit { private cdRef: ChangeDetectorRef, private subscription: SubscriptionService, private httpWaitService: HttpWaitService, - private routerWaiterService: RouterWaitService, - @Inject(LOADER_DELAY) delay: number, - ) { - this.httpWaitService.setDelay(delay); - } + private routerWaitService: RouterWaitService, + ) {} ngOnInit() { this.subscribeLoading(); @@ -89,7 +85,7 @@ export class LoaderBarComponent implements OnDestroy, OnInit { subscribeLoading() { this.subscription.addOne( - combineLatest([this.httpWaitService.getLoading$(), this.routerWaiterService.getLoading$()]), + combineLatest([this.httpWaitService.getLoading$(), this.routerWaitService.getLoading$()]), ([httpLoading, routerLoading]) => { if (httpLoading || routerLoading) this.startLoading(); else this.stopLoading(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/models/common.ts b/npm/ng-packs/packages/theme-shared/src/lib/models/common.ts index 48c40abfe8..64047e0db7 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/models/common.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/models/common.ts @@ -4,7 +4,6 @@ import { Validation } from '@ngx-validate/core'; export interface RootParams { httpErrorConfig: HttpErrorConfig; validation?: Partial; - loaderDelay?: number; } export type ErrorScreenErrorCodes = 401 | 403 | 404 | 500; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts index af1f8d874a..2326009044 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts @@ -2,9 +2,8 @@ import { NavigationEnd, NavigationError, NavigationStart, Router } from '@angula import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest'; import { Subject, timer } from 'rxjs'; import { LoaderBarComponent } from '../components/loader-bar/loader-bar.component'; -import { HttpWaitService, SubscriptionService } from '@abp/ng.core'; +import { HttpWaitService, LOADER_DELAY, SubscriptionService } from '@abp/ng.core'; import { HttpRequest } from '@angular/common/http'; -import { LOADER_DELAY } from '../tokens/lodaer-delay.token'; describe('LoaderBarComponent', () => { let spectator: Spectator; @@ -44,12 +43,11 @@ describe('LoaderBarComponent', () => { }, 10); }); - test.skip('should be interval unsubscribed', done => { + it('should be interval unsubscribed', done => { spectator.detectChanges(); const httpWaitService = spectator.inject(HttpWaitService); httpWaitService.addRequest(new HttpRequest('GET', 'test')); expect(spectator.component.interval.closed).toBe(false); - timer(400).subscribe(() => { expect(spectator.component.interval.closed).toBe(true); done(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 4aa8ba7d85..8d64febdbf 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -36,7 +36,6 @@ import { THEME_SHARED_ROUTE_PROVIDERS } from './providers/route.provider'; import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token'; import { DateParserFormatter } from './utils/date-parser-formatter'; -import { LOADER_DELAY } from './tokens/lodaer-delay.token'; const declarationsWithExports = [ BreadcrumbComponent, @@ -78,7 +77,7 @@ export class BaseThemeSharedModule {} }) export class ThemeSharedModule { static forRoot( - { httpErrorConfig, validation = {}, loaderDelay = 500 } = {} as RootParams, + { httpErrorConfig, validation = {} } = {} as RootParams, ): ModuleWithProviders { return { ngModule: ThemeSharedModule, @@ -124,10 +123,6 @@ export class ThemeSharedModule { provide: VALIDATION_VALIDATE_ON_SUBMIT, useValue: validation.validateOnSubmit, }, - { - provide: LOADER_DELAY, - useValue: loaderDelay, - }, ], }; } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts index 2065727e3e..18a2aac2f6 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts @@ -1,5 +1,4 @@ export * from './append-content.token'; export * from './http-error.token'; export * from './lazy-styles.token'; -export * from './lodaer-delay.token'; export * from './suppress-unsaved-changes-warning.token'; diff --git a/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts b/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts index 629ebaad79..52abb12ffe 100644 --- a/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts +++ b/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts @@ -3,7 +3,6 @@ import { DateParserFormatter, DEFAULT_VALIDATION_BLUEPRINTS, THEME_SHARED_ROUTE_PROVIDERS, - LOADER_DELAY, } from '@abp/ng.theme.shared'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; @@ -47,10 +46,6 @@ export class ThemeSharedTestingModule { provide: VALIDATION_VALIDATE_ON_SUBMIT, useValue: validation.validateOnSubmit, }, - { - provide: LOADER_DELAY, - useValue: 0, - }, ], }; }