Skip to content

Commit 8370281

Browse files
committed
refactor: use router navigated observable connected to a single updater
1 parent 84805a3 commit 8370281

File tree

1 file changed

+63
-79
lines changed

1 file changed

+63
-79
lines changed

packages/router-component-store/src/lib/router-history-store/router-history.store.ts

Lines changed: 63 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,24 @@ import {
77
} from '@angular/core';
88
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
99
import { ComponentStore, provideComponentStore } from '@ngrx/component-store';
10-
import { filter, Observable } from 'rxjs';
10+
import { filter, map, Observable, switchMap, take } from 'rxjs';
11+
import { filterRouterEvents } from '../filter-router-event.operator';
1112

1213
interface RouterHistoryState {
1314
/**
14-
* The history of all router navigation sequences.
15+
* The history of all router navigated sequences.
1516
*
1617
* The key is the navigation ID.
1718
*/
18-
readonly history: RouterNavigationHistory;
19+
readonly history: RouterNavigatedHistory;
20+
/**
21+
* The ID of the most recent router navigated sequence events.
22+
*/
23+
readonly maxNavigatedId?: number;
1924
}
2025

2126
type RouterNavigatedSequence = readonly [NavigationStart, NavigationEnd];
22-
type RouterNavigationHistory = Record<number, RouterNavigationSequence>;
23-
type RouterNavigationSequence = RouterRequestSequence | RouterNavigatedSequence;
24-
type RouterRequestSequence = readonly [NavigationStart];
25-
26-
function isRouterNavigatedSequence(
27-
sequence: RouterNavigationSequence
28-
): sequence is RouterNavigatedSequence {
29-
return (
30-
sequence.length === 2 &&
31-
sequence[0] instanceof NavigationStart &&
32-
sequence[1] instanceof NavigationEnd
33-
);
34-
}
27+
type RouterNavigatedHistory = Readonly<Record<number, RouterNavigatedSequence>>;
3528

3629
/**
3730
* Provide and initialize the `RouterHistoryStore`.
@@ -56,44 +49,48 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
5649
#history$ = this.select((state) => state.history).pipe(
5750
filter((history) => Object.keys(history).length > 0)
5851
);
52+
#maxNavigatedId$ = this.select((state) => state.maxNavigatedId).pipe(
53+
filter(
54+
(maxNavigatedId): maxNavigatedId is number => maxNavigatedId !== undefined
55+
)
56+
);
5957
/**
6058
* All `NavigationEnd` events.
6159
*/
6260
#navigationEnd$: Observable<NavigationEnd> = this.#router.events.pipe(
63-
filter((event): event is NavigationEnd => event instanceof NavigationEnd)
61+
filterRouterEvents(NavigationEnd)
6462
);
6563
/**
6664
* All `NavigationStart` events.
6765
*/
6866
#navigationStart$: Observable<NavigationStart> = this.#router.events.pipe(
69-
filter(
70-
(event): event is NavigationStart => event instanceof NavigationStart
71-
)
67+
filterRouterEvents(NavigationStart)
7268
);
73-
7469
/**
75-
* The navigation ID of the most recent router navigated sequence.
70+
* All router navigated sequences, that is `NavigationStart` followed by `NavigationEnd`.
7671
*/
77-
#maxRouterNavigatedSequenceId$ = this.select(
78-
this.#history$.pipe(filter(this.#selectHasRouterNavigated)),
79-
(history) =>
80-
Number(
81-
// This callback is only triggered when at least one navigation has
82-
// completed
83-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84-
Object.entries(history)
85-
.reverse()
86-
.find(([, navigation]) => navigation.length === 2)![0]
72+
#routerNavigated$: Observable<RouterNavigatedSequence> =
73+
this.#navigationStart$.pipe(
74+
switchMap((navigationStart) =>
75+
this.#navigationEnd$.pipe(
76+
filter((navigationEnd) => navigationEnd.id === navigationStart.id),
77+
take(1),
78+
map(
79+
(navigationEnd) =>
80+
[navigationStart, navigationEnd] as RouterNavigatedSequence
81+
)
82+
)
8783
)
88-
);
84+
);
85+
8986
/**
9087
* The most recent completed navigation.
9188
*/
9289
#latestRouterNavigatedSequence$ = this.select(
93-
this.#maxRouterNavigatedSequenceId$,
90+
this.#maxNavigatedId$,
9491
this.#history$,
95-
(maxRouterNavigatedSequenceId, history) =>
96-
history[maxRouterNavigatedSequenceId] as RouterNavigatedSequence,
92+
(maxNavigatedId, history) =>
93+
history[maxNavigatedId] as RouterNavigatedSequence,
9794
{
9895
debounce: true,
9996
}
@@ -104,7 +101,7 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
104101
*/
105102
currentUrl$: Observable<string> = this.select(
106103
this.#latestRouterNavigatedSequence$,
107-
([, end]) => end.urlAfterRedirects
104+
([, navigationEnd]) => navigationEnd.urlAfterRedirects
108105
);
109106
/**
110107
* The previous URL when taking `popstate` events into account.
@@ -114,28 +111,28 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
114111
*/
115112
previousUrl$: Observable<string | undefined> = this.select(
116113
this.#history$,
117-
this.#maxRouterNavigatedSequenceId$,
118-
(history, maxCompletedNavigationId) => {
119-
if (maxCompletedNavigationId === 1) {
114+
this.#maxNavigatedId$,
115+
(history, maxNavigatedId) => {
116+
if (maxNavigatedId === 1) {
120117
return undefined;
121118
}
122119

123-
const [completedNavigationSourceStart] = this.#getNavigationSource(
124-
maxCompletedNavigationId,
120+
const [sourceNavigationStart] = this.#getNavigationSource(
121+
maxNavigatedId,
125122
history
126123
);
127124

128-
if (completedNavigationSourceStart.id === 1) {
125+
if (sourceNavigationStart.id === 1) {
129126
return undefined;
130127
}
131128

132-
const previousNavigationId = completedNavigationSourceStart.id - 1;
133-
const [, previousNavigationSourceEnd] = this.#getNavigationSource(
129+
const previousNavigationId = sourceNavigationStart.id - 1;
130+
const [, previousNavigationEnd] = this.#getNavigationSource(
134131
previousNavigationId,
135132
history
136133
);
137134

138-
return previousNavigationSourceEnd.urlAfterRedirects;
135+
return previousNavigationEnd.urlAfterRedirects;
139136
},
140137
{
141138
debounce: true,
@@ -145,34 +142,28 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
145142
constructor() {
146143
super(initialState);
147144

148-
this.#addNavigationStart(this.#navigationStart$);
149-
this.#addNavigationEnd(this.#navigationEnd$);
145+
this.#addRouterNavigatedSequence(this.#routerNavigated$);
150146
}
151147

152148
/**
153-
* Add a `NavigationEnd` event to the navigation history.
154-
*/
155-
#addNavigationEnd = this.updater<NavigationEnd>(
156-
(state, event): RouterHistoryState => ({
157-
...state,
158-
history: {
159-
...state.history,
160-
[event.id]: [state.history[event.id][0], event],
161-
},
162-
})
163-
);
164-
165-
/**
166-
* Add a `NavigationStart` event to the navigation history.
149+
* Add a router navigated sequence to the router navigated history.
167150
*/
168-
#addNavigationStart = this.updater<NavigationStart>(
169-
(state, event): RouterHistoryState => ({
170-
...state,
171-
history: {
172-
...state.history,
173-
[event.id]: [event],
174-
},
175-
})
151+
#addRouterNavigatedSequence = this.updater<RouterNavigatedSequence>(
152+
(state, routerNavigated): RouterHistoryState => {
153+
const [{ id: navigationId }] = routerNavigated;
154+
155+
return {
156+
...state,
157+
history: {
158+
...state.history,
159+
[navigationId]: routerNavigated,
160+
},
161+
maxNavigatedId:
162+
navigationId > (state.maxNavigatedId ?? 0)
163+
? navigationId
164+
: state.maxNavigatedId,
165+
};
166+
}
176167
);
177168

178169
/**
@@ -187,7 +178,7 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
187178
*/
188179
#getNavigationSource(
189180
navigationId: number,
190-
history: RouterNavigationHistory
181+
history: RouterNavigatedHistory
191182
): RouterNavigatedSequence {
192183
let navigation = history[navigationId];
193184

@@ -201,14 +192,7 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
201192
];
202193
}
203194

204-
return navigation as RouterNavigatedSequence;
205-
}
206-
207-
#selectHasRouterNavigated(history: RouterNavigationHistory): boolean {
208-
const firstNavigationId = 1;
209-
const firstNavigation = history[firstNavigationId] ?? [];
210-
211-
return isRouterNavigatedSequence(firstNavigation);
195+
return navigation;
212196
}
213197
}
214198

0 commit comments

Comments
 (0)