diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index 7044831681..55000edde1 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -9,13 +9,13 @@ export * from './lazy-load.service'; export * from './list.service'; export * from './localization.service'; export * from './multi-tenancy.service'; -export * from './navigation-events.service'; export * from './permission.service'; export * from './profile-state.service'; export * from './profile.service'; export * from './replaceable-components.service'; export * from './resource-wait.service'; export * from './rest.service'; +export * from './router-events.service'; export * from './router-wait.service'; export * from './routes.service'; export * from './session-state.service'; diff --git a/npm/ng-packs/packages/core/src/lib/services/navigation-events.service.ts b/npm/ng-packs/packages/core/src/lib/services/navigation-events.service.ts deleted file mode 100644 index ff18b65ed3..0000000000 --- a/npm/ng-packs/packages/core/src/lib/services/navigation-events.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Injectable, Type } from '@angular/core'; -import { - NavigationCancel, - NavigationEnd, - NavigationError, - NavigationStart, - Router, - RouterEvent, -} from '@angular/router'; -import { filter } from 'rxjs/operators'; - -export type NavigationEventType = 'Cancel' | 'End' | 'Error' | 'Start'; - -@Injectable({ providedIn: 'root' }) -export class NavigationEvents { - private eventTypes = new Map>([ - ['Cancel', NavigationCancel], - ['End', NavigationEnd], - ['Error', NavigationError], - ['Start', NavigationStart], - ]); - - constructor(private router: Router) {} - - getOneOf(...eventTypes: NavigationEventType[]) { - return this.router.events.pipe( - filter((event: RouterEvent) => - eventTypes.some(type => event instanceof this.eventTypes.get(type)), - ), - ); - } - - getAny() { - return this.getOneOf(...this.eventTypes.keys()); - } -} diff --git a/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts b/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts new file mode 100644 index 0000000000..22981c641c --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts @@ -0,0 +1,60 @@ +import { Injectable, Type } from '@angular/core'; +import { + NavigationCancel, + NavigationEnd, + NavigationError, + NavigationStart, + Router, + RouterEvent, +} from '@angular/router'; +import { filter } from 'rxjs/operators'; + +export const NavigationEvent = { + Cancel: NavigationCancel, + End: NavigationEnd, + Error: NavigationError, + Start: NavigationStart, +}; + +@Injectable({ providedIn: 'root' }) +export class RouterEvents { + constructor(private router: Router) {} + + getEvents(...eventTypes: T) { + type FilteredRouterEvent = T extends Type[] ? Ctor : never; + + const filterRouterEvents = (event: RouterEvent): event is FilteredRouterEvent => + eventTypes.some(type => event instanceof type); + + return this.router.events.pipe(filter(filterRouterEvents)); + } + + getNavigationEvents(...navigationEventKeys: T) { + type FilteredNavigationEvent = T extends (infer Key)[] + ? Key extends NavigationEventKey + ? InstanceType + : never + : never; + + const filterNavigationEvents = (event: RouterEvent): event is FilteredNavigationEvent => + navigationEventKeys.some(key => event instanceof NavigationEvent[key]); + + return this.router.events.pipe(filter(filterNavigationEvents)); + } + + getAnyEvent() { + return this.router.events; + } + + getAnyNavigationEvent() { + const keys = Object.keys(NavigationEvent) as NavigationEventKeys; + return this.getNavigationEvents(...keys); + } +} + +type RouterEventConstructors = [Type, ...Type[]]; + +type NavigationEventKeys = [NavigationEventKey, ...NavigationEventKey[]]; + +export type NavigationEventType = typeof NavigationEvent; +export type NavigationEventKey = keyof NavigationEventType; diff --git a/npm/ng-packs/packages/core/src/lib/tests/navigation-events.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/navigation-events.service.spec.ts deleted file mode 100644 index cf6d2992d0..0000000000 --- a/npm/ng-packs/packages/core/src/lib/tests/navigation-events.service.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { - NavigationCancel, - NavigationEnd, - NavigationError, - NavigationStart, - Router, - RouterEvent, -} from '@angular/router'; -import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; -import { Subject } from 'rxjs'; -import { take } from 'rxjs/operators'; -import { NavigationEvents } from '../services/navigation-events.service'; - -describe('NavigationEvents', () => { - let spectator: SpectatorService; - let service: NavigationEvents; - const events = new Subject(); - - const createService = createServiceFactory({ - service: NavigationEvents, - providers: [ - { - provide: Router, - useValue: { events }, - }, - ], - }); - - beforeEach(() => { - spectator = createService(); - service = spectator.service; - }); - - describe('getOneOf', () => { - test.each` - filtered | expected - ${['Start', 'Cancel']} | ${[0, 3]} - ${['Error', 'Cancel']} | ${[0, 1]} - ${['Start', 'End']} | ${[2, 3]} - ${['Error', 'End']} | ${[1, 2]} - `('should return a stream of given navigation events', ({ filtered, expected }) => { - const stream = service.getOneOf(...filtered); - const collected: number[] = []; - - stream.pipe(take(2)).subscribe(event => collected.push(event.id)); - - events.next(new NavigationCancel(0, null, null)); - events.next(new NavigationError(1, null, null)); - events.next(new NavigationEnd(2, null, null)); - events.next(new NavigationStart(3, null, null)); - - expect(collected).toEqual(expected); - }); - }); - - describe('getAny', () => { - it('should return a stream of any navigation event', () => { - const stream = service.getAny(); - const collected: number[] = []; - - stream.pipe(take(4)).subscribe(event => collected.push(event.id)); - - events.next(new RouterEvent(0, null)); - events.next(new NavigationStart(1, null, null)); - events.next(new RouterEvent(2, null)); - events.next(new RouterEvent(3, null)); - events.next(new NavigationError(4, null, null)); - events.next(new NavigationEnd(5, null, null)); - events.next(new RouterEvent(6, null)); - events.next(new NavigationCancel(7, null, null)); - - expect(collected).toEqual([1, 4, 5, 7]); - }); - }); -}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/router-events.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/router-events.service.spec.ts new file mode 100644 index 0000000000..7b6af4f3ee --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/router-events.service.spec.ts @@ -0,0 +1,111 @@ +import { + NavigationCancel, + NavigationEnd, + NavigationError, + NavigationStart, + ResolveEnd, + ResolveStart, + Router, + RouterEvent, +} from '@angular/router'; +import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; +import { Subject } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { NavigationEventKey, RouterEvents } from '../services/router-events.service'; + +describe('RouterEvents', () => { + let spectator: SpectatorService; + let service: RouterEvents; + const events = new Subject(); + const emitRouterEvents = () => { + events.next(new RouterEvent(0, null)); + events.next(new NavigationStart(1, null, null)); + events.next(new ResolveStart(2, null, null, null)); + events.next(new RouterEvent(3, null)); + events.next(new NavigationError(4, null, null)); + events.next(new NavigationEnd(5, null, null)); + events.next(new ResolveEnd(6, null, null, null)); + events.next(new NavigationCancel(7, null, null)); + }; + + const createService = createServiceFactory({ + service: RouterEvents, + providers: [ + { + provide: Router, + useValue: { events }, + }, + ], + }); + + beforeEach(() => { + spectator = createService(); + service = spectator.service; + }); + + describe('getNavigationEvents', () => { + test.each` + filtered | expected + ${['Start', 'Cancel']} | ${[1, 7]} + ${['Error', 'Cancel']} | ${[4, 7]} + ${['Start', 'End']} | ${[1, 5]} + ${['Error', 'End']} | ${[4, 5]} + `( + 'should return a stream of given navigation events', + ({ filtered, expected }: NavigationEventTest) => { + const stream = service.getNavigationEvents(...filtered); + const collected: number[] = []; + + stream.pipe(take(2)).subscribe(event => collected.push(event.id)); + + emitRouterEvents(); + + expect(collected).toEqual(expected); + }, + ); + }); + + describe('getAnyNavigationEvent', () => { + it('should return a stream of any navigation event', () => { + const stream = service.getAnyNavigationEvent(); + const collected: number[] = []; + + stream.pipe(take(4)).subscribe(event => collected.push(event.id)); + + emitRouterEvents(); + + expect(collected).toEqual([1, 4, 5, 7]); + }); + }); + + describe('getEvents', () => { + it('should return a stream of given router events', () => { + const stream = service.getEvents(ResolveEnd, ResolveStart); + const collected: number[] = []; + + stream.pipe(take(2)).subscribe(event => collected.push(event.id)); + + emitRouterEvents(); + + expect(collected).toEqual([2, 6]); + }); + }); + + describe('getAnyEvent', () => { + it('should return a stream of any router event', () => { + const stream = service.getAnyEvent(); + const collected: number[] = []; + + stream.pipe(take(8)).subscribe((event: RouterEvent) => collected.push(event.id)); + + emitRouterEvents(); + + expect(collected).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + }); +}); + +type NavigationEventTest = { + filtered: [NavigationEventKey, ...NavigationEventKey[]]; + expected: number[]; +};