mirror of
https://github.com/bitwarden/browser
synced 2026-02-19 19:04:01 +00:00
* rename bit-icon to bit-svg; create new bit-icon for font icons Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * find and replace current usage Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * add custom eslint warning Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix incorrect usage * fix tests * fix tests * Update libs/components/src/svg/index.ts Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> * Update libs/eslint/components/no-bwi-class-usage.spec.mjs Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> * update component api * update class name * use icon type in iconButton component * update type Icon --> BitSvg * fix bad renames * fix more renames * fix bad input * revert iconButton type * fix lint * fix more inputs * misc fixes Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix test * add eslint ignore * fix lint * add comparison story --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
225 lines
7.2 KiB
TypeScript
225 lines
7.2 KiB
TypeScript
// FIXME: Update this file to be type safe and remove this and next line
|
|
// @ts-strict-ignore
|
|
import { CommonModule } from "@angular/common";
|
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
|
import { ActivatedRoute, Data, NavigationEnd, Router, RouterModule } from "@angular/router";
|
|
import { Subject, filter, switchMap, takeUntil, tap } from "rxjs";
|
|
|
|
import { BitwardenLogo, BitSvg } from "@bitwarden/assets/svg";
|
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
import {
|
|
SvgModule,
|
|
Translation,
|
|
AnonLayoutComponent,
|
|
AnonLayoutWrapperData,
|
|
AnonLayoutWrapperDataService,
|
|
} from "@bitwarden/components";
|
|
import { I18nPipe } from "@bitwarden/ui-common";
|
|
|
|
import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component";
|
|
import { AccountSwitcherService } from "../../../auth/popup/account-switching/services/account-switcher.service";
|
|
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
|
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
|
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
|
|
|
export interface ExtensionAnonLayoutWrapperData extends AnonLayoutWrapperData {
|
|
showAcctSwitcher?: boolean;
|
|
showBackButton?: boolean;
|
|
showLogo?: boolean;
|
|
hideFooter?: boolean;
|
|
}
|
|
|
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
@Component({
|
|
templateUrl: "extension-anon-layout-wrapper.component.html",
|
|
imports: [
|
|
AnonLayoutComponent,
|
|
CommonModule,
|
|
CurrentAccountComponent,
|
|
I18nPipe,
|
|
SvgModule,
|
|
PopOutComponent,
|
|
PopupPageComponent,
|
|
PopupHeaderComponent,
|
|
RouterModule,
|
|
],
|
|
})
|
|
export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|
private destroy$ = new Subject<void>();
|
|
|
|
protected showAcctSwitcher: boolean;
|
|
protected showBackButton: boolean;
|
|
protected showLogo: boolean = true;
|
|
|
|
protected pageTitle: string;
|
|
protected pageSubtitle: string;
|
|
protected pageIcon: BitSvg;
|
|
protected showReadonlyHostname: boolean;
|
|
protected maxWidth: "md" | "3xl";
|
|
protected hasLoggedInAccount: boolean = false;
|
|
protected hideFooter: boolean;
|
|
protected hideCardWrapper: boolean = false;
|
|
|
|
protected theme: string;
|
|
protected logo = BitwardenLogo;
|
|
|
|
constructor(
|
|
private router: Router,
|
|
private route: ActivatedRoute,
|
|
private i18nService: I18nService,
|
|
private extensionAnonLayoutWrapperDataService: AnonLayoutWrapperDataService,
|
|
private accountSwitcherService: AccountSwitcherService,
|
|
) {}
|
|
|
|
async ngOnInit(): Promise<void> {
|
|
// Set the initial page data on load
|
|
this.setAnonLayoutWrapperDataFromRouteData(this.route.snapshot.firstChild?.data);
|
|
|
|
// Listen for page changes and update the page data appropriately
|
|
this.listenForPageDataChanges();
|
|
this.listenForServiceDataChanges();
|
|
|
|
this.accountSwitcherService.availableAccounts$
|
|
.pipe(takeUntil(this.destroy$))
|
|
.subscribe((accounts) => {
|
|
this.hasLoggedInAccount = accounts.some((account) => account.id !== "addAccount");
|
|
});
|
|
}
|
|
|
|
private listenForPageDataChanges() {
|
|
this.router.events
|
|
.pipe(
|
|
filter((event) => event instanceof NavigationEnd),
|
|
// reset page data on page changes
|
|
tap(() => this.resetPageData()),
|
|
switchMap(() => this.route.firstChild?.data || null),
|
|
takeUntil(this.destroy$),
|
|
)
|
|
.subscribe((firstChildRouteData: Data | null) => {
|
|
this.setAnonLayoutWrapperDataFromRouteData(firstChildRouteData);
|
|
});
|
|
}
|
|
|
|
private setAnonLayoutWrapperDataFromRouteData(firstChildRouteData: Data | null) {
|
|
if (!firstChildRouteData) {
|
|
return;
|
|
}
|
|
|
|
if (firstChildRouteData["pageTitle"] !== undefined) {
|
|
this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
|
|
}
|
|
|
|
if (firstChildRouteData["pageSubtitle"] !== undefined) {
|
|
this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
|
|
}
|
|
|
|
if (firstChildRouteData["pageIcon"] !== undefined) {
|
|
this.pageIcon = firstChildRouteData["pageIcon"];
|
|
}
|
|
|
|
this.hideFooter = Boolean(firstChildRouteData["hideFooter"]);
|
|
this.showReadonlyHostname = Boolean(firstChildRouteData["showReadonlyHostname"]);
|
|
this.maxWidth = firstChildRouteData["maxWidth"];
|
|
|
|
if (firstChildRouteData["showAcctSwitcher"] !== undefined) {
|
|
this.showAcctSwitcher = Boolean(firstChildRouteData["showAcctSwitcher"]);
|
|
}
|
|
|
|
if (firstChildRouteData["showBackButton"] !== undefined) {
|
|
this.showBackButton = Boolean(firstChildRouteData["showBackButton"]);
|
|
}
|
|
|
|
if (firstChildRouteData["showLogo"] !== undefined) {
|
|
this.showLogo = Boolean(firstChildRouteData["showLogo"]);
|
|
}
|
|
|
|
if (firstChildRouteData["hideCardWrapper"] !== undefined) {
|
|
this.hideCardWrapper = Boolean(firstChildRouteData["hideCardWrapper"]);
|
|
}
|
|
}
|
|
|
|
private listenForServiceDataChanges() {
|
|
this.extensionAnonLayoutWrapperDataService
|
|
.anonLayoutWrapperData$()
|
|
.pipe(takeUntil(this.destroy$))
|
|
.subscribe((data: ExtensionAnonLayoutWrapperData) => {
|
|
this.setAnonLayoutWrapperData(data);
|
|
});
|
|
}
|
|
|
|
private setAnonLayoutWrapperData(data: ExtensionAnonLayoutWrapperData) {
|
|
if (!data) {
|
|
return;
|
|
}
|
|
|
|
// Null emissions are used to reset the page data as all fields are optional.
|
|
|
|
if (data.pageTitle !== undefined) {
|
|
this.pageTitle =
|
|
data.pageTitle !== null ? this.handleStringOrTranslation(data.pageTitle) : null;
|
|
}
|
|
|
|
if (data.pageSubtitle !== undefined) {
|
|
this.pageSubtitle =
|
|
data.pageSubtitle !== null ? this.handleStringOrTranslation(data.pageSubtitle) : null;
|
|
}
|
|
|
|
if (data.pageIcon !== undefined) {
|
|
this.pageIcon = data.pageIcon !== null ? data.pageIcon : null;
|
|
}
|
|
|
|
if (data.hideFooter !== undefined) {
|
|
this.hideFooter = data.hideFooter !== null ? data.hideFooter : null;
|
|
}
|
|
|
|
if (data.showReadonlyHostname !== undefined) {
|
|
this.showReadonlyHostname = data.showReadonlyHostname;
|
|
}
|
|
|
|
if (data.hideCardWrapper !== undefined) {
|
|
this.hideCardWrapper = data.hideCardWrapper;
|
|
}
|
|
|
|
if (data.showAcctSwitcher !== undefined) {
|
|
this.showAcctSwitcher = data.showAcctSwitcher;
|
|
}
|
|
|
|
if (data.showBackButton !== undefined) {
|
|
this.showBackButton = data.showBackButton;
|
|
}
|
|
|
|
if (data.showLogo !== undefined) {
|
|
this.showLogo = data.showLogo;
|
|
}
|
|
}
|
|
|
|
private handleStringOrTranslation(value: string | Translation): string {
|
|
if (typeof value === "string") {
|
|
// If it's a string, return it as is
|
|
return value;
|
|
}
|
|
|
|
// If it's a Translation object, translate it
|
|
return this.i18nService.t(value.key, ...(value.placeholders ?? []));
|
|
}
|
|
|
|
private resetPageData() {
|
|
this.pageTitle = null;
|
|
this.pageSubtitle = null;
|
|
this.pageIcon = null;
|
|
this.showReadonlyHostname = null;
|
|
this.showAcctSwitcher = null;
|
|
this.showBackButton = null;
|
|
this.showLogo = null;
|
|
this.maxWidth = null;
|
|
this.hideFooter = null;
|
|
this.hideCardWrapper = null;
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
this.destroy$.next();
|
|
this.destroy$.complete();
|
|
}
|
|
}
|