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 .
+// ---------- 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));
+ }
+}