mirror of https://github.com/Squidex/squidex.git
43 changed files with 838 additions and 3562 deletions
@ -0,0 +1,31 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; |
|||
import { Injectable} from '@angular/core'; |
|||
import { Observable } from 'rxjs'; |
|||
import { finalize } from 'rxjs/operators'; |
|||
|
|||
import { LoadingService, MathHelper } from './../../internal'; |
|||
|
|||
@Injectable() |
|||
export class LoadingInterceptor implements HttpInterceptor { |
|||
constructor( |
|||
private readonly loadingService: LoadingService |
|||
) { |
|||
} |
|||
|
|||
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { |
|||
const id = MathHelper.guid(); |
|||
|
|||
this.loadingService.startLoading(id); |
|||
|
|||
return next.handle(req).pipe(finalize(() => { |
|||
this.loadingService.completeLoading(id); |
|||
})); |
|||
} |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Event, NavigationError, NavigationStart } from '@angular/router'; |
|||
import { Subject } from 'rxjs'; |
|||
|
|||
import { LoadingService, LoadingServiceFactory } from './loading.service'; |
|||
|
|||
describe('LoadingService', () => { |
|||
let events = new Subject<Event>(); |
|||
|
|||
it('should instantiate from factory', () => { |
|||
const loadingService = LoadingServiceFactory(<any>{ events }); |
|||
|
|||
expect(loadingService).toBeDefined(); |
|||
}); |
|||
|
|||
it('should instantiate', () => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
expect(loadingService).toBeDefined(); |
|||
|
|||
loadingService.ngOnDestroy(); |
|||
}); |
|||
|
|||
it('should set to loaded', () => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
let state = false; |
|||
|
|||
loadingService.loading.subscribe(v => state = v); |
|||
loadingService.startLoading('1'); |
|||
|
|||
expect(state).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should set to loaded on navigation start', () => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
let state = false; |
|||
|
|||
loadingService.loading.subscribe(v => state = v); |
|||
|
|||
events.next(new NavigationStart(0, '')); |
|||
|
|||
expect(state).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should not unset from loaded immediately', () => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
let state = false; |
|||
|
|||
loadingService.loading.subscribe(v => state = v); |
|||
loadingService.startLoading('1'); |
|||
loadingService.completeLoading('1'); |
|||
|
|||
expect(state).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should not unset from loaded delayed', (cb) => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
let state = false; |
|||
|
|||
loadingService.loading.subscribe(v => state = v); |
|||
loadingService.startLoading('1'); |
|||
loadingService.completeLoading('1'); |
|||
|
|||
setTimeout(() => { |
|||
expect(state).toBeFalsy(); |
|||
|
|||
cb(); |
|||
}, 400); |
|||
}); |
|||
|
|||
it('should not unset from loaded delayed on navigation event', (cb) => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
let state = false; |
|||
|
|||
loadingService.loading.subscribe(v => state = v); |
|||
events.next(new NavigationStart(0, '')); |
|||
events.next(new NavigationError(0, '', 0)); |
|||
|
|||
setTimeout(() => { |
|||
expect(state).toBeFalsy(); |
|||
|
|||
cb(); |
|||
}, 400); |
|||
}); |
|||
|
|||
it('should set back to loaded after several completions', (cb) => { |
|||
const loadingService = new LoadingService(<any>{ events }); |
|||
|
|||
let state = false; |
|||
|
|||
loadingService.loading.subscribe(v => state = v); |
|||
loadingService.startLoading('1'); |
|||
loadingService.completeLoading('1'); |
|||
loadingService.completeLoading('1'); |
|||
loadingService.startLoading('2'); |
|||
|
|||
setTimeout(() => { |
|||
expect(state).toBeTruthy(); |
|||
|
|||
cb(); |
|||
}, 400); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,68 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Injectable, OnDestroy } from '@angular/core'; |
|||
import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; |
|||
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; |
|||
import { map } from 'rxjs/operators'; |
|||
|
|||
import { Types } from './../utils/types'; |
|||
|
|||
export const LoadingServiceFactory = (router: Router) => { |
|||
return new LoadingService(router); |
|||
}; |
|||
|
|||
@Injectable() |
|||
export class LoadingService implements OnDestroy { |
|||
private readonly routerSubscription: Subscription; |
|||
private readonly loading$ = new BehaviorSubject(0); |
|||
private readonly loadingOperations: { [key: string]: boolean } = {}; |
|||
|
|||
public get loading(): Observable<boolean> { |
|||
return this.loading$.pipe(map(x => x > 0)); |
|||
} |
|||
|
|||
constructor(router: Router) { |
|||
this.routerSubscription = |
|||
router.events.subscribe(event => { |
|||
if (Types.is(event, NavigationStart)) { |
|||
this.startLoading(event.id.toString()); |
|||
} else if ( |
|||
Types.is(event, NavigationEnd) || |
|||
Types.is(event, NavigationCancel) || |
|||
Types.is(event, NavigationError)) { |
|||
this.completeLoading(event.id.toString()); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.routerSubscription.unsubscribe(); |
|||
} |
|||
|
|||
public startLoading(key: string) { |
|||
if (!this.loadingOperations[key]) { |
|||
this.loadingOperations[key] = true; |
|||
|
|||
this.loading$.next(this.loading$.value + 1); |
|||
} |
|||
} |
|||
|
|||
public completeLoading(key: string) { |
|||
if (this.loadingOperations[key]) { |
|||
delete this.loadingOperations[key]; |
|||
|
|||
setTimeout(() => { |
|||
const value = this.loading$.value; |
|||
|
|||
if (value > 0) { |
|||
this.loading$.next(value - 1); |
|||
} |
|||
}, 250); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 79 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 33 KiB |
Loading…
Reference in new issue