import { Injectable } from "@angular/core"; import { Title } from "@angular/platform-browser"; import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; import { filter, firstValueFrom } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { KeyDefinition, ROUTER_DISK, StateProvider, GlobalState, } from "@bitwarden/common/platform/state"; const DEEP_LINK_REDIRECT_URL = new KeyDefinition(ROUTER_DISK, "deepLinkRedirectUrl", { deserializer: (value: string) => value, }); @Injectable() export class RouterService { /** * The string value of the URL the user tried to navigate to while unauthenticated. * * Developed to allow users to deep link even when the navigation gets interrupted * by the authentication process. */ private deepLinkRedirectUrlState: GlobalState; private previousUrl: string = undefined; private currentUrl: string = undefined; constructor( private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title, private stateProvider: StateProvider, i18nService: I18nService, ) { this.deepLinkRedirectUrlState = this.stateProvider.getGlobal(DEEP_LINK_REDIRECT_URL); this.currentUrl = this.router.url; router.events .pipe(filter((e) => e instanceof NavigationEnd)) .subscribe((event: NavigationEnd) => { this.currentUrl = event.url; let title = i18nService.t("bitWebVault"); if (this.currentUrl.includes("/sm/")) { title = i18nService.t("bitSecretsManager"); } let child = this.activatedRoute.firstChild; while (child.firstChild) { child = child.firstChild; } const titleId: string = child?.snapshot?.data?.titleId; const rawTitle: string = child?.snapshot?.data?.title; const updateUrl = !child?.snapshot?.data?.doNotSaveUrl ?? true; if (titleId != null || rawTitle != null) { const newTitle = rawTitle != null ? rawTitle : i18nService.t(titleId); if (newTitle != null && newTitle !== "") { title = newTitle + " | " + title; } } this.titleService.setTitle(title); if (updateUrl) { this.setPreviousUrl(this.currentUrl); } }); } getPreviousUrl(): string | undefined { return this.previousUrl; } setPreviousUrl(url: string): void { this.previousUrl = url; } /** * Save URL to Global State. This service is used during the login process * @param url URL being saved to the Global State */ async persistLoginRedirectUrl(url: string): Promise { await this.deepLinkRedirectUrlState.update(() => url); } /** * Fetch and clear persisted LoginRedirectUrl if present in state */ async getAndClearLoginRedirectUrl(): Promise | undefined { const persistedPreLoginUrl = await firstValueFrom(this.deepLinkRedirectUrlState.state$); if (!Utils.isNullOrEmpty(persistedPreLoginUrl)) { await this.persistLoginRedirectUrl(null); return persistedPreLoginUrl; } return; } }