Merge pull request #7034 from abpframework/auto-merge/rel-4-1/43

Merge branch dev with rel-4.1
pull/7038/head
Mehmet Erim 5 years ago committed by GitHub
commit 98febec586
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,13 +1,14 @@
import { Injectable } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { HttpRequest } from '@angular/common/http'; import { HttpRequest } from '@angular/common/http';
import { InternalStore } from '../utils/internal-store-utils'; import { InternalStore } from '../utils/internal-store-utils';
import { getPathName } from '../utils/http-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 { export interface HttpWaitState {
requests: Set<HttpRequest<any>>; requests: HttpRequest<any>[];
filteredRequests: Array<HttpRequestInfo>; filteredRequests: Array<HttpRequestInfo>;
delay: number;
} }
export interface HttpRequestInfo { export interface HttpRequestInfo {
method: string; method: string;
@ -18,19 +19,35 @@ export interface HttpRequestInfo {
}) })
export class HttpWaitService { export class HttpWaitService {
protected store = new InternalStore<HttpWaitState>({ protected store = new InternalStore<HttpWaitState>({
requests: new Set(), requests: [],
filteredRequests: [], filteredRequests: [],
delay: 0,
}); });
private delay: number;
private destroy$ = new Subject();
constructor(injector: Injector) {
this.delay = injector.get(LOADER_DELAY, 500);
}
getLoading() { getLoading() {
return !!this.applyFilter(this.store.state.requests).length; return !!this.applyFilter(this.store.state.requests).length;
} }
getLoading$() { getLoading$() {
return this.store return this.store
.sliceState(({ requests }) => !!this.applyFilter(requests).length) .sliceState(({ requests }) => requests)
.pipe(this.debounceWhenLoading()); .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$() { updateLoading$() {
@ -38,19 +55,15 @@ export class HttpWaitService {
} }
clearLoading() { clearLoading() {
this.store.patch({ requests: new Set() }); this.store.patch({ requests: [] });
} }
addRequest(request: HttpRequest<any>) { addRequest(request: HttpRequest<any>) {
let { requests } = this.store.state; this.store.patch({ requests: [...this.store.state.requests, request] });
requests = new Set(requests.values());
requests.add(request);
this.store.patch({ requests });
} }
deleteRequest(request: HttpRequest<any>) { deleteRequest(request: HttpRequest<any>) {
const { requests } = this.store.state; const requests = this.store.state.requests.filter(r => r !== request);
requests.delete(request);
this.store.patch({ requests }); this.store.patch({ requests });
} }
@ -73,13 +86,9 @@ export class HttpWaitService {
this.store.patch({ filteredRequests }); this.store.patch({ filteredRequests });
} }
setDelay(delay: number) { private applyFilter(requests: HttpRequest<any>[]) {
this.store.patch({ delay });
}
private applyFilter(requests: Set<HttpRequest<any>>) {
const { filteredRequests } = this.store.state; const { filteredRequests } = this.store.state;
return Array.from(requests).filter( return requests.filter(
({ method, url }) => ({ method, url }) =>
!filteredRequests.find(filteredRequest => !filteredRequests.find(filteredRequest =>
this.isSameRequest(filteredRequest, { method, endpoint: getPathName(url) }), this.isSameRequest(filteredRequest, { method, endpoint: getPathName(url) }),
@ -91,10 +100,4 @@ export class HttpWaitService {
const { method, endpoint } = filteredRequest; const { method, endpoint } = filteredRequest;
return endpoint === request.endpoint && method === request.method; 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();
}
} }

@ -1,13 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
NavigationCancel, import { filter, map, mapTo, switchMap, takeUntil, tap } from 'rxjs/operators';
NavigationEnd,
NavigationError,
NavigationStart,
Router,
} from '@angular/router';
import { filter } from 'rxjs/operators';
import { InternalStore } from '../utils/internal-store-utils'; import { InternalStore } from '../utils/internal-store-utils';
import { of, Subject, timer } from 'rxjs';
import { LOADER_DELAY } from '../tokens/lodaer-delay.token';
export interface RouterWaitState { export interface RouterWaitState {
loading: boolean; loading: boolean;
@ -18,7 +14,10 @@ export interface RouterWaitState {
}) })
export class RouterWaitService { export class RouterWaitService {
private store = new InternalStore<RouterWaitState>({ loading: false }); private store = new InternalStore<RouterWaitState>({ 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 this.router.events
.pipe( .pipe(
filter( filter(
@ -28,10 +27,18 @@ export class RouterWaitService {
event instanceof NavigationError || event instanceof NavigationError ||
event instanceof NavigationCancel, 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 => { .subscribe(status => {
if (event instanceof NavigationStart) this.setLoading(true); this.setLoading(status);
else this.setLoading(false);
}); });
} }

@ -1,2 +1,3 @@
export * from './list.token'; export * from './list.token';
export * from './lodaer-delay.token';
export * from './options.token'; export * from './options.token';

@ -1,9 +1,10 @@
import { import {
ABP, ABP,
BaseCoreModule, BaseCoreModule,
coreOptionsFactory,
CORE_OPTIONS, CORE_OPTIONS,
coreOptionsFactory,
LIST_QUERY_DEBOUNCE_TIME, LIST_QUERY_DEBOUNCE_TIME,
LOADER_DELAY,
PermissionService, PermissionService,
RestService, RestService,
} from '@abp/ng.core'; } from '@abp/ng.core';
@ -52,6 +53,10 @@ export class CoreTestingModule {
provide: RestService, provide: RestService,
useClass: MockRestService, useClass: MockRestService,
}, },
{
provide: LOADER_DELAY,
useValue: 0,
},
provideRoutes(routes), provideRoutes(routes),
], ],
}; };

@ -1,8 +1,7 @@
import { HttpWaitService, RouterWaitService, SubscriptionService } from '@abp/ng.core'; 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 { Router } from '@angular/router';
import { combineLatest, Subscription, timer } from 'rxjs'; import { combineLatest, Subscription, timer } from 'rxjs';
import { LOADER_DELAY } from '../../tokens/lodaer-delay.token';
@Component({ @Component({
selector: 'abp-loader-bar', selector: 'abp-loader-bar',
@ -77,11 +76,8 @@ export class LoaderBarComponent implements OnDestroy, OnInit {
private cdRef: ChangeDetectorRef, private cdRef: ChangeDetectorRef,
private subscription: SubscriptionService, private subscription: SubscriptionService,
private httpWaitService: HttpWaitService, private httpWaitService: HttpWaitService,
private routerWaiterService: RouterWaitService, private routerWaitService: RouterWaitService,
@Inject(LOADER_DELAY) delay: number, ) {}
) {
this.httpWaitService.setDelay(delay);
}
ngOnInit() { ngOnInit() {
this.subscribeLoading(); this.subscribeLoading();
@ -89,7 +85,7 @@ export class LoaderBarComponent implements OnDestroy, OnInit {
subscribeLoading() { subscribeLoading() {
this.subscription.addOne( this.subscription.addOne(
combineLatest([this.httpWaitService.getLoading$(), this.routerWaiterService.getLoading$()]), combineLatest([this.httpWaitService.getLoading$(), this.routerWaitService.getLoading$()]),
([httpLoading, routerLoading]) => { ([httpLoading, routerLoading]) => {
if (httpLoading || routerLoading) this.startLoading(); if (httpLoading || routerLoading) this.startLoading();
else this.stopLoading(); else this.stopLoading();

@ -4,7 +4,6 @@ import { Validation } from '@ngx-validate/core';
export interface RootParams { export interface RootParams {
httpErrorConfig: HttpErrorConfig; httpErrorConfig: HttpErrorConfig;
validation?: Partial<Validation.Config>; validation?: Partial<Validation.Config>;
loaderDelay?: number;
} }
export type ErrorScreenErrorCodes = 401 | 403 | 404 | 500; export type ErrorScreenErrorCodes = 401 | 403 | 404 | 500;

@ -2,9 +2,8 @@ import { NavigationEnd, NavigationError, NavigationStart, Router } from '@angula
import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest'; import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest';
import { Subject, timer } from 'rxjs'; import { Subject, timer } from 'rxjs';
import { LoaderBarComponent } from '../components/loader-bar/loader-bar.component'; 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 { HttpRequest } from '@angular/common/http';
import { LOADER_DELAY } from '../tokens/lodaer-delay.token';
describe('LoaderBarComponent', () => { describe('LoaderBarComponent', () => {
let spectator: Spectator<LoaderBarComponent>; let spectator: Spectator<LoaderBarComponent>;
@ -44,12 +43,11 @@ describe('LoaderBarComponent', () => {
}, 10); }, 10);
}); });
test.skip('should be interval unsubscribed', done => { it('should be interval unsubscribed', done => {
spectator.detectChanges(); spectator.detectChanges();
const httpWaitService = spectator.inject(HttpWaitService); const httpWaitService = spectator.inject(HttpWaitService);
httpWaitService.addRequest(new HttpRequest('GET', 'test')); httpWaitService.addRequest(new HttpRequest('GET', 'test'));
expect(spectator.component.interval.closed).toBe(false); expect(spectator.component.interval.closed).toBe(false);
timer(400).subscribe(() => { timer(400).subscribe(() => {
expect(spectator.component.interval.closed).toBe(true); expect(spectator.component.interval.closed).toBe(true);
done(); done();

@ -36,7 +36,6 @@ import { THEME_SHARED_ROUTE_PROVIDERS } from './providers/route.provider';
import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token';
import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token'; import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token';
import { DateParserFormatter } from './utils/date-parser-formatter'; import { DateParserFormatter } from './utils/date-parser-formatter';
import { LOADER_DELAY } from './tokens/lodaer-delay.token';
const declarationsWithExports = [ const declarationsWithExports = [
BreadcrumbComponent, BreadcrumbComponent,
@ -78,7 +77,7 @@ export class BaseThemeSharedModule {}
}) })
export class ThemeSharedModule { export class ThemeSharedModule {
static forRoot( static forRoot(
{ httpErrorConfig, validation = {}, loaderDelay = 500 } = {} as RootParams, { httpErrorConfig, validation = {} } = {} as RootParams,
): ModuleWithProviders<ThemeSharedModule> { ): ModuleWithProviders<ThemeSharedModule> {
return { return {
ngModule: ThemeSharedModule, ngModule: ThemeSharedModule,
@ -124,10 +123,6 @@ export class ThemeSharedModule {
provide: VALIDATION_VALIDATE_ON_SUBMIT, provide: VALIDATION_VALIDATE_ON_SUBMIT,
useValue: validation.validateOnSubmit, useValue: validation.validateOnSubmit,
}, },
{
provide: LOADER_DELAY,
useValue: loaderDelay,
},
], ],
}; };
} }

@ -1,5 +1,4 @@
export * from './append-content.token'; export * from './append-content.token';
export * from './http-error.token'; export * from './http-error.token';
export * from './lazy-styles.token'; export * from './lazy-styles.token';
export * from './lodaer-delay.token';
export * from './suppress-unsaved-changes-warning.token'; export * from './suppress-unsaved-changes-warning.token';

@ -3,7 +3,6 @@ import {
DateParserFormatter, DateParserFormatter,
DEFAULT_VALIDATION_BLUEPRINTS, DEFAULT_VALIDATION_BLUEPRINTS,
THEME_SHARED_ROUTE_PROVIDERS, THEME_SHARED_ROUTE_PROVIDERS,
LOADER_DELAY,
} from '@abp/ng.theme.shared'; } from '@abp/ng.theme.shared';
import { ModuleWithProviders, NgModule } from '@angular/core'; import { ModuleWithProviders, NgModule } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
@ -47,10 +46,6 @@ export class ThemeSharedTestingModule {
provide: VALIDATION_VALIDATE_ON_SUBMIT, provide: VALIDATION_VALIDATE_ON_SUBMIT,
useValue: validation.validateOnSubmit, useValue: validation.validateOnSubmit,
}, },
{
provide: LOADER_DELAY,
useValue: 0,
},
], ],
}; };
} }

Loading…
Cancel
Save