diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bced66edb..98c005a7e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -88,6 +88,7 @@ import { SavedSearchesService } from './saved-searches/saved-searches.service'; import { HelpComponent } from './help/help.component'; import { HelpModule } from './help/help.module'; import { DomainRegisterRedirectComponent } from './domainregister/domreg-redirect.component'; +import { PageTitleService } from './page-title.service'; window.addEventListener('dragover', (event) => event.preventDefault()); @@ -197,6 +198,7 @@ const routes: Routes = [ RMM, RMMAuthGuardService, ContactsService, + PageTitleService, SavedSearchesService, StorageService, { provide: HTTP_INTERCEPTORS, useClass: RMMHttpInterceptorService, multi: true }, @@ -207,10 +209,14 @@ const routes: Routes = [ bootstrap: [MainContainerComponent] }) export class AppModule { - constructor (matIconRegistry: MatIconRegistry, domSanitizer: DomSanitizer) { + constructor ( + matIconRegistry: MatIconRegistry, + domSanitizer: DomSanitizer, + pageTitleService: PageTitleService, + ) { + void pageTitleService; matIconRegistry.addSvgIconSet( domSanitizer.bypassSecurityTrustResourceUrl('./assets/mdi.svg') ); } } - diff --git a/src/app/page-title.service.spec.ts b/src/app/page-title.service.spec.ts new file mode 100644 index 000000000..a299ca728 --- /dev/null +++ b/src/app/page-title.service.spec.ts @@ -0,0 +1,47 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see . +// ---------- END RUNBOX LICENSE ---------- + +import { Title } from '@angular/platform-browser'; +import { NavigationEnd, Router } from '@angular/router'; +import { Subject } from 'rxjs'; +import { buildRunboxPageTitle, PageTitleService } from './page-title.service'; + +describe('PageTitleService', () => { + it('builds a mail title for the root route', () => { + expect(buildRunboxPageTitle('/')).toBe('Mail - Runbox 7'); + }); + + it('builds sub-app titles from the current route', () => { + expect(buildRunboxPageTitle('/calendar')).toBe('Calendar - Runbox 7'); + expect(buildRunboxPageTitle('/app/account/identities')).toBe('Account - Runbox 7'); + expect(buildRunboxPageTitle('/contacts/settings?foo=bar')).toBe('Contacts - Runbox 7'); + }); + + it('updates the document title when navigation ends', () => { + const events = new Subject(); + const router = { url: '/', events } as Partial as Router; + const title = jasmine.createSpyObj('Title', ['setTitle']); + + new PageTitleService(router, title); + events.next(new NavigationEnd(1, '/calendar', '/calendar')); + + expect(title.setTitle).toHaveBeenCalledWith('Mail - Runbox 7'); + expect(title.setTitle).toHaveBeenCalledWith('Calendar - Runbox 7'); + }); +}); diff --git a/src/app/page-title.service.ts b/src/app/page-title.service.ts new file mode 100644 index 000000000..495cc12bd --- /dev/null +++ b/src/app/page-title.service.ts @@ -0,0 +1,66 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see <https://www.gnu.org/licenses/>. +// ---------- END RUNBOX LICENSE ---------- + +import { Injectable } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { NavigationEnd, Router } from '@angular/router'; +import { filter } from 'rxjs/operators'; + +const titleBySubApp = new Map([ + ['account', 'Account'], + ['calendar', 'Calendar'], + ['changelog', 'Changelog'], + ['compose', 'Compose'], + ['contacts', 'Contacts'], + ['dev', 'Components'], + ['dkim', 'DKIM'], + ['help', 'Help'], + ['login', 'Login'], + ['onscreen', 'Video meeting'], + ['overview', 'Overview'], + ['start', 'Overview'], + ['welcome', 'Welcome'], +]); + +/** + * Builds the browser document title for a Runbox route. + */ +export function buildRunboxPageTitle(url: string): string { + const path = url.split(/[?#]/)[0]; + const segments = path + .split('/') + .filter((segment) => segment && !['app', 'appdev', 'index_dev.html'].includes(segment)); + const subAppTitle = titleBySubApp.get(segments[0]) || 'Mail'; + + return `${subAppTitle} - Runbox 7`; +} + +@Injectable() +export class PageTitleService { + constructor(router: Router, private title: Title) { + this.updateTitle(router.url); + router.events + .pipe(filter((event): event is NavigationEnd => event instanceof NavigationEnd)) + .subscribe((event) => this.updateTitle(event.urlAfterRedirects)); + } + + private updateTitle(url: string) { + this.title.setTitle(buildRunboxPageTitle(url)); + } +}