fix(shell): keep the shell between modules and cancel abandoned requests
- Route reuse treats the shell of every feature module as one, so a click from one module into another no longer rebuilds the shell (menu bar vanishing, menu, notification, business date and approval-stream calls repeated on every such click) - The HTTP cache interceptor cancels the inner request when its caller walks away, so abandoned streams stop holding browser connections and queueing later requests - /approvals/counts is not cached Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,10 @@ export class CacheInterceptor implements HttpInterceptor {
|
||||
'/notes',
|
||||
// The ALM maturity breakdown counts deposits and loans as of the date asked and reads the bank's
|
||||
// own bucket edges from the query; a cached copy would show another date's or another setting's figures.
|
||||
'/almreturn'
|
||||
'/almreturn',
|
||||
// The Approvals badge: a stream that stays open for minutes and a count that moves with every request raised or
|
||||
// actioned. A copy of either is of no use, and the stream must never be held by anything but its own caller.
|
||||
'/approvals/counts'
|
||||
];
|
||||
|
||||
private isCacheable(url: string): boolean {
|
||||
@@ -93,8 +96,9 @@ export class CacheInterceptor implements HttpInterceptor {
|
||||
// Create new response to avoid side-effects
|
||||
subscriber.next(new HttpResponse(cachedData as Object));
|
||||
subscriber.complete();
|
||||
} else {
|
||||
next.handle(request).subscribe(
|
||||
return undefined;
|
||||
}
|
||||
const inner = next.handle(request).subscribe(
|
||||
(event) => {
|
||||
if (environment.httpCacheEnabled && event instanceof HttpResponse) {
|
||||
this.httpCacheService.setCacheData(request.urlWithParams, event);
|
||||
@@ -104,7 +108,10 @@ export class CacheInterceptor implements HttpInterceptor {
|
||||
(error) => subscriber.error(error),
|
||||
() => subscriber.complete()
|
||||
);
|
||||
}
|
||||
// The request is cancelled when its caller walks away (a page is left, a screen is closed). The inner request has to
|
||||
// be cancelled with it: left to run, it kept its connection open. A browser holds about six per site, so a few
|
||||
// streams that were let go but never closed filled them and every later request waited in the queue.
|
||||
return () => inner.unsubscribe();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
/** Angular Imports */
|
||||
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ShellComponent } from '../shell/shell.component';
|
||||
|
||||
/**
|
||||
* A route strategy allowing for explicit route reuse.
|
||||
@@ -31,7 +32,22 @@ export class RouteReusableStrategy extends RouteReuseStrategy {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every feature module registers its own copy of the shell route (Route.withShell), so two pages in different modules
|
||||
* sit under two different shell routes. Treated as different, a click from one module into another destroyed the
|
||||
* shell and built a new one: the menu bar vanished until it had loaded again, and the shell's own calls (menu access,
|
||||
* notifications, business date, the approval stream) went out again on every such click. The shell is the same
|
||||
* component whichever module it wraps, so it is kept: only the page inside it changes, until a hard refresh.
|
||||
*/
|
||||
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
|
||||
return future.routeConfig === curr.routeConfig || future.data.reuse;
|
||||
return (
|
||||
future.routeConfig === curr.routeConfig ||
|
||||
future.data.reuse ||
|
||||
(RouteReusableStrategy.isShell(future) && RouteReusableStrategy.isShell(curr))
|
||||
);
|
||||
}
|
||||
|
||||
private static isShell(route: ActivatedRouteSnapshot): boolean {
|
||||
return route.routeConfig?.component === ShellComponent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,9 @@ export class ShellComponent implements OnInit, OnDestroy {
|
||||
.pipe(map((result) => result.matches));
|
||||
/** Sets the initial state of sidenav as collapsed. Not collapsed if false. */
|
||||
sidenavCollapsed = true;
|
||||
/** Progress bar mode. */
|
||||
progressBarMode: string;
|
||||
/** Progress bar mode. It starts from what is running right now: the template shows the bar for anything that is not
|
||||
* 'none', so an empty start left it running until some request happened to finish. */
|
||||
progressBarMode: string = this.progressBarService.getRequestsRunning() > 0 ? 'indeterminate' : 'none';
|
||||
/** Subscription to progress bar. */
|
||||
progressBar$: Subscription;
|
||||
/** Hides nav/sidebar for full-screen cashier routes. */
|
||||
|
||||
Reference in New Issue
Block a user