refactor(tabs): simplify tab navigation service and add URL blacklist

Removed unnecessary logging effects and improved tab navigation
service by implementing a URL blacklist to prevent cluttering
the navigation history with specific routes. Added constants
for better management of excluded URLs.
This commit is contained in:
Lorenz Hilpert
2025-09-30 20:48:20 +02:00
parent c767c60d31
commit 47a051c214
7 changed files with 59 additions and 22 deletions

View File

@@ -257,9 +257,4 @@ if (isDevMode()) {
exports: [RouterModule],
providers: [provideScrollPositionRestoration()],
})
export class AppRoutingModule {
constructor() {
// Loading TabNavigationService to ensure tab state is synced with tab location
inject(TabNavigationService);
}
}
export class AppRoutingModule {}

View File

@@ -79,6 +79,7 @@ import {
} from '@isa/core/logging';
import { IDBStorageProvider, UserStorageProvider } from '@isa/core/storage';
import { Store } from '@ngrx/store';
import { TabNavigationService } from '@isa/core/tabs';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
@@ -117,7 +118,6 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
const strategy = injector.get(LoginStrategy);
await strategy.login();
}
statusElement.innerHTML = 'Native Container wird initialisiert...';
const nativeContainer = injector.get(NativeContainerService);
await nativeContainer.init();
@@ -139,6 +139,9 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
store.pipe(debounceTime(1000)).subscribe((state) => {
userStorage.set('store', { ...state, version });
});
// Inject tab navigation service to initialize it
injector.get(TabNavigationService);
} catch (error) {
console.error('Error during app initialization', error);
laoderElement.remove();

View File

@@ -44,18 +44,10 @@ export class ShellProcessBarItemComponent
tab = computed(() => this.#tabService.entityMap()[this.process().id]);
tabEffect = effect(() => {
console.log('tabEffect', this.tab());
});
shoppingCartId = computed(() => {
return this.#checkoutMetadataService.getShoppingCartId(this.process().id);
});
shoppingCartIdEffect = effect(() => {
console.log('shoppingCartIdEffect', this.shoppingCartId());
});
private _process$ = new BehaviorSubject<ApplicationProcess>(undefined);
process$ = this._process$.asObservable();

View File

@@ -6,11 +6,15 @@ import { filter, firstValueFrom, retry, switchMap, tap, timer } from 'rxjs';
import { USER_SUB } from '../tokens';
import { Debounce, ValidateParam } from '@isa/common/decorators';
import z from 'zod';
import { logger } from '@isa/core/logging';
type UserState = Record<string, unknown>;
@Injectable({ providedIn: 'root' })
export class UserStorageProvider implements StorageProvider {
#logger = logger(() => ({
context: 'UserStorageProvider',
}));
#userStateService = inject(UserStateService);
#userSub = toObservable(inject(USER_SUB));
@@ -21,7 +25,7 @@ export class UserStorageProvider implements StorageProvider {
retry({
count: 3,
delay: (error, retryCount) => {
console.warn(
this.#logger.warn(
`Retrying to load user state, attempt #${retryCount}`,
error,
);
@@ -32,7 +36,12 @@ export class UserStorageProvider implements StorageProvider {
),
tap((res) => {
if (res?.result?.content) {
this.#state = JSON.parse(res.result.content);
const parsed = JSON.parse(res.result.content);
if (parsed && typeof parsed === 'object') {
this.#state = parsed;
} else {
this.#state = {};
}
}
}),
);
@@ -60,12 +69,13 @@ export class UserStorageProvider implements StorageProvider {
@Debounce({ wait: 1000 })
private postNewState(): void {
const state = JSON.stringify(this.#state);
firstValueFrom(
this.#userStateService.UserStateSetUserState({
content: JSON.stringify(this.#state),
content: state,
}),
).catch((error) => {
console.error('Error saving user state:', error);
this.#logger.error('Error saving user state:', error);
});
}
@@ -81,7 +91,6 @@ export class UserStorageProvider implements StorageProvider {
@ValidateParam(0, z.string().min(1))
get(key: string): unknown {
const data = structuredClone(this.#state[key]);
console.log('Got user state data:', key, data);
return data;
}

View File

@@ -4,5 +4,6 @@ export * from './lib/tab.resolver-fn';
export * from './lib/schemas';
export * from './lib/tab';
export * from './lib/tab-navigation.service';
export * from './lib/tab-navigation.constants';
export * from './lib/tab-config';
export * from './lib/helpers';

View File

@@ -0,0 +1,21 @@
/**
* @fileoverview Constants for tab navigation behavior and URL filtering.
*
* This module provides configuration constants that control how URLs are
* handled in the tab navigation history system.
*/
/**
* URLs that should not be added to tab navigation history.
*
* These routes are excluded to prevent cluttering the history stack with
* frequently visited pages that don't need to be tracked in tab history.
*
* @example
* ```typescript
* // Dashboard routes are excluded because they serve as entry points
* // and don't represent meaningful navigation steps in a workflow
* '/kunde/dashboard'
* ```
*/
export const HISTORY_BLACKLIST_URLS = ['/kunde/dashboard'];

View File

@@ -1,10 +1,10 @@
import { DOCUMENT } from '@angular/common';
import { Injectable, inject } from '@angular/core';
import { NavigationEnd, Router, UrlTree } from '@angular/router';
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';
import { TabService } from './tab';
import { TabLocation } from './schemas';
import { Title } from '@angular/platform-browser';
import { HISTORY_BLACKLIST_URLS } from './tab-navigation.constants';
/**
* Service that automatically syncs browser navigation events to tab location history.
@@ -15,6 +15,7 @@ import { Title } from '@angular/platform-browser';
*
* Key features:
* - Automatic browser navigation synchronization
* - URL blacklist to exclude specific routes from history
* - History pruning awareness with fallback navigation
* - Back/forward navigation with step-by-step movement
* - Prevention of infinite loops during navigation
@@ -42,6 +43,11 @@ export class TabNavigationService {
}
#syncNavigationToTab(event: NavigationEnd) {
// Skip blacklisted URLs
if (this.#shouldSkipHistory(event.url)) {
return;
}
const activeTabId = this.#getActiveTabId(event.url);
if (!activeTabId) {
return;
@@ -60,6 +66,16 @@ export class TabNavigationService {
}
}
/**
* Checks if a URL should be excluded from tab navigation history.
*
* @param url - The URL to check against the blacklist
* @returns true if the URL should be skipped, false otherwise
*/
#shouldSkipHistory(url: string): boolean {
return HISTORY_BLACKLIST_URLS.includes(url);
}
#getActiveTabId(url: string): number | null {
// Extract tabId from URL pattern /:tabId/...
const tabIdMatch = url.match(/^\/(\d+)(?:\/|$)/);