import * as plugins from './plugins.js'; import * as appstate from './appstate.js'; const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter; export const validViews = ['overview', 'network', 'emails', 'logs', 'configuration', 'security'] as const; export const validEmailFolders = ['queued', 'sent', 'failed', 'security'] as const; export type TValidView = typeof validViews[number]; export type TValidEmailFolder = typeof validEmailFolders[number]; class AppRouter { private router: InstanceType; private initialized = false; private suppressStateUpdate = false; constructor() { this.router = new SmartRouter({ debug: false }); } public init(): void { if (this.initialized) return; this.setupRoutes(); this.setupStateSync(); this.handleInitialRoute(); this.initialized = true; } private setupRoutes(): void { // Main views for (const view of validViews) { if (view === 'emails') { // Email root - default to queued this.router.on('/emails', async () => { this.updateViewState('emails'); this.updateEmailFolder('queued'); }); // Email with folder parameter this.router.on('/emails/:folder', async (routeInfo) => { const folder = routeInfo.params.folder as string; if (validEmailFolders.includes(folder as TValidEmailFolder)) { this.updateViewState('emails'); this.updateEmailFolder(folder as TValidEmailFolder); } else { // Invalid folder, redirect to queued this.navigateTo('/emails/queued'); } }); } else { this.router.on(`/${view}`, async () => { this.updateViewState(view); }); } } // Root redirect this.router.on('/', async () => { this.navigateTo('/overview'); }); } private setupStateSync(): void { // Sync URL when state changes programmatically (not from router) appstate.uiStatePart.state.subscribe((uiState) => { if (this.suppressStateUpdate) return; const currentPath = window.location.pathname; const expectedPath = this.getExpectedPath(uiState.activeView); // Only update URL if it doesn't match current state if (!currentPath.startsWith(expectedPath)) { this.suppressStateUpdate = true; if (uiState.activeView === 'emails') { const emailState = appstate.emailOpsStatePart.getState(); this.router.pushUrl(`/emails/${emailState.currentView}`); } else { this.router.pushUrl(`/${uiState.activeView}`); } this.suppressStateUpdate = false; } }); } private getExpectedPath(view: string): string { if (view === 'emails') { return '/emails'; } return `/${view}`; } private handleInitialRoute(): void { const path = window.location.pathname; if (!path || path === '/') { // Redirect root to overview this.router.pushUrl('/overview'); } else { // Parse current path and update state const segments = path.split('/').filter(Boolean); const view = segments[0]; if (validViews.includes(view as TValidView)) { this.updateViewState(view as TValidView); if (view === 'emails' && segments[1]) { const folder = segments[1]; if (validEmailFolders.includes(folder as TValidEmailFolder)) { this.updateEmailFolder(folder as TValidEmailFolder); } else { this.updateEmailFolder('queued'); } } else if (view === 'emails') { this.updateEmailFolder('queued'); } } else { // Invalid view, redirect to overview this.router.pushUrl('/overview'); } } } private updateViewState(view: string): void { this.suppressStateUpdate = true; const currentState = appstate.uiStatePart.getState(); if (currentState.activeView !== view) { appstate.uiStatePart.setState({ ...currentState, activeView: view, }); } this.suppressStateUpdate = false; } private updateEmailFolder(folder: TValidEmailFolder): void { this.suppressStateUpdate = true; const currentState = appstate.emailOpsStatePart.getState(); if (currentState.currentView !== folder) { appstate.emailOpsStatePart.setState({ ...currentState, currentView: folder as appstate.IEmailOpsState['currentView'], }); } this.suppressStateUpdate = false; } public navigateTo(path: string): void { this.router.pushUrl(path); } public navigateToView(view: string): void { if (validViews.includes(view as TValidView)) { this.navigateTo(`/${view}`); } else { this.navigateTo('/overview'); } } public navigateToEmailFolder(folder: string): void { if (validEmailFolders.includes(folder as TValidEmailFolder)) { this.navigateTo(`/emails/${folder}`); } else { this.navigateTo('/emails/queued'); } } public getCurrentView(): string { return appstate.uiStatePart.getState().activeView; } public getCurrentEmailFolder(): string { return appstate.emailOpsStatePart.getState().currentView; } public destroy(): void { this.router.destroy(); this.initialized = false; } } export const appRouter = new AppRouter();