1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

Auth/PM-7077 - Browser Extension - UI Refresh - Account Switcher Component (#10268)

* PM-7077 - (1) Convert app-header to standalone (2) Convert account-switcher to standalone (3) Start wiring up extension refresh feature flag and fixing deps.

* PM-7077 WIP

* PM-7077 - Mostly get account switcher and account component converted to CL components.

* PM-7077 - Apply semibold classes to section headers to match storybook

* PM-7077 - AccountSwitcher - (1) Fix margin per design call (2) Add missing lockAll call

* PM-7077 - Remove test code.
This commit is contained in:
Jared Snider
2024-07-29 12:57:54 -04:00
committed by GitHub
parent 22bb1316cd
commit ea72552599
6 changed files with 326 additions and 131 deletions

View File

@@ -1,78 +1,177 @@
<app-header> <ng-container *ngIf="extensionRefreshFlag">
<div class="left"> <popup-page [loading]="loading">
<button type="button" (click)="back()">{{ "close" | i18n }}</button> <popup-header slot="header" pageTitle="{{ 'switchAccounts' | i18n }}" showBackButton>
</div> <ng-container slot="end">
<div class="center tw-font-bold">{{ "switchAccounts" | i18n }}</div> <app-pop-out></app-pop-out>
</app-header> <app-current-account></app-current-account>
</ng-container>
</popup-header>
<main <ng-container *ngIf="availableAccounts$ | async as availableAccounts">
*ngIf="loading" <bit-section>
class="tw-absolute tw-z-50 tw-box-border tw-flex tw-cursor-not-allowed tw-items-center tw-justify-center tw-bg-background tw-opacity-60"
>
<i class="bwi bwi-spinner bwi-2x bwi-spin" aria-hidden="true"></i>
</main>
<main>
<div class="tw-p-2">
<div *ngIf="availableAccounts$ | async as availableAccounts">
<ul class="tw-grid tw-list-none tw-gap-2" role="listbox">
<ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst"> <ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst">
<li *ngIf="availableAccount.isActive" class="tw-mb-4" role="option"> <div *ngIf="availableAccount.isActive" class="tw-mb-6">
<auth-account [account]="availableAccount" (loading)="loading = $event"></auth-account> <auth-account
</li> [account]="availableAccount"
<div *ngIf="isFirst" class="tw-uppercase tw-text-muted"> [extensionRefreshFlag]="extensionRefreshFlag"
{{ "availableAccounts" | i18n }} (loading)="loading = $event"
></auth-account>
</div>
<bit-section-header *ngIf="isFirst">
<h2 bitTypography="h6" class="tw-font-semibold">{{ "availableAccounts" | i18n }}</h2>
</bit-section-header>
<div *ngIf="!availableAccount.isActive">
<auth-account
[account]="availableAccount"
[extensionRefreshFlag]="extensionRefreshFlag"
(loading)="loading = $event"
></auth-account>
</div> </div>
<li *ngIf="!availableAccount.isActive" role="option">
<auth-account [account]="availableAccount" (loading)="loading = $event"></auth-account>
</li>
</ng-container> </ng-container>
</ul>
<!-- <!--
If the user has not reached the account limit, the last 'availableAccount' will have an 'id' of If the user has not reached the account limit, the last 'availableAccount' will have an 'id' of
'SPECIAL_ADD_ACCOUNT_ID'. Since we don't want to count this as one of the actual accounts, 'SPECIAL_ADD_ACCOUNT_ID'. Since we don't want to count this as one of the actual accounts,
we check to make sure the 'id' of the last 'availableAccount' is not equal to 'SPECIAL_ADD_ACCOUNT_ID' we check to make sure the 'id' of the last 'availableAccount' is not equal to 'SPECIAL_ADD_ACCOUNT_ID'
--> -->
<p <p
class="tw-text-sm tw-text-muted" class="tw-text-sm tw-text-muted"
*ngIf=" *ngIf="
availableAccounts.length >= accountLimit && availableAccounts.length >= accountLimit &&
availableAccounts[availableAccounts.length - 1].id !== specialAddAccountId availableAccounts[availableAccounts.length - 1].id !== specialAddAccountId
" "
> >
{{ "accountLimitReached" | i18n }} {{ "accountLimitReached" | i18n }}
</p> </p>
</div> </bit-section>
</ng-container>
<div class="tw-mt-8" *ngIf="currentAccount$ | async as currentAccount"> <div class="tw-mt-8" *ngIf="currentAccount$ | async as currentAccount">
<div class="tw-mb-2 tw-uppercase tw-text-muted">{{ "options" | i18n }}</div> <bit-section>
<div class="tw-grid tw-gap-2"> <bit-section-header>
<button <h2 bitTypography="h6" class="tw-font-semibold">
type="button" {{ "options" | i18n }}
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3 disabled:tw-cursor-not-allowed disabled:tw-border-text-muted/60 disabled:!tw-text-muted/60" </h2>
(click)="lock(currentAccount.id)" </bit-section-header>
[disabled]="currentAccount.status === lockedStatus || !activeUserCanLock"
[title]="!activeUserCanLock ? ('unlockMethodNeeded' | i18n) : ''" <bit-item>
<button
type="button"
bit-item-content
(click)="lock(currentAccount.id)"
[disabled]="currentAccount.status === lockedStatus || !activeUserCanLock"
[title]="!activeUserCanLock ? ('unlockMethodNeeded' | i18n) : ''"
>
<i slot="start" class="bwi bwi-lock tw-text-2xl tw-text-main" aria-hidden="true"></i>
{{ "lockNow" | i18n }}
</button>
</bit-item>
<bit-item>
<button type="button" bit-item-content (click)="logOut(currentAccount.id)">
<i
slot="start"
class="bwi bwi-sign-out tw-text-2xl tw-text-main"
aria-hidden="true"
></i>
{{ "logOut" | i18n }}
</button>
</bit-item>
<bit-item>
<button type="button" bit-item-content (click)="lockAll()">
<i slot="start" class="bwi bwi-lock tw-text-2xl tw-text-main" aria-hidden="true"></i>
{{ "lockAll" | i18n }}
</button>
</bit-item>
</bit-section>
</div>
</popup-page>
</ng-container>
<ng-container *ngIf="!extensionRefreshFlag">
<app-header>
<div class="left">
<button type="button" (click)="back()">{{ "close" | i18n }}</button>
</div>
<div class="center tw-font-bold">{{ "switchAccounts" | i18n }}</div>
</app-header>
<main
*ngIf="loading"
class="tw-absolute tw-z-50 tw-box-border tw-flex tw-cursor-not-allowed tw-items-center tw-justify-center tw-bg-background tw-opacity-60"
>
<i class="bwi bwi-spinner bwi-2x bwi-spin" aria-hidden="true"></i>
</main>
<main>
<div class="tw-p-2">
<div *ngIf="availableAccounts$ | async as availableAccounts">
<ul class="tw-grid tw-list-none tw-gap-2" role="listbox">
<ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst">
<li *ngIf="availableAccount.isActive" class="tw-mb-4" role="option">
<auth-account
[account]="availableAccount"
(loading)="loading = $event"
></auth-account>
</li>
<div *ngIf="isFirst" class="tw-uppercase tw-text-muted">
{{ "availableAccounts" | i18n }}
</div>
<li *ngIf="!availableAccount.isActive" role="option">
<auth-account
[account]="availableAccount"
(loading)="loading = $event"
></auth-account>
</li>
</ng-container>
</ul>
<!--
If the user has not reached the account limit, the last 'availableAccount' will have an 'id' of
'SPECIAL_ADD_ACCOUNT_ID'. Since we don't want to count this as one of the actual accounts,
we check to make sure the 'id' of the last 'availableAccount' is not equal to 'SPECIAL_ADD_ACCOUNT_ID'
-->
<p
class="tw-text-sm tw-text-muted"
*ngIf="
availableAccounts.length >= accountLimit &&
availableAccounts[availableAccounts.length - 1].id !== specialAddAccountId
"
> >
<i class="bwi bwi-lock tw-text-2xl" aria-hidden="true"></i> {{ "accountLimitReached" | i18n }}
{{ "lockNow" | i18n }} </p>
</button> </div>
<button
type="button" <div class="tw-mt-8" *ngIf="currentAccount$ | async as currentAccount">
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3" <div class="tw-mb-2 tw-uppercase tw-text-muted">{{ "options" | i18n }}</div>
(click)="logOut(currentAccount.id)" <div class="tw-grid tw-gap-2">
> <button
<i class="bwi bwi-sign-out tw-text-2xl" aria-hidden="true"></i> type="button"
{{ "logOut" | i18n }} class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3 disabled:tw-cursor-not-allowed disabled:tw-border-text-muted/60 disabled:!tw-text-muted/60"
</button> (click)="lock(currentAccount.id)"
<button [disabled]="currentAccount.status === lockedStatus || !activeUserCanLock"
type="button" [title]="!activeUserCanLock ? ('unlockMethodNeeded' | i18n) : ''"
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3" >
(click)="lockAll()" <i class="bwi bwi-lock tw-text-2xl" aria-hidden="true"></i>
> {{ "lockNow" | i18n }}
<i class="bwi bwi-lock tw-text-2xl" aria-hidden="true"></i> </button>
{{ "lockAll" | i18n }} <button
</button> type="button"
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3"
(click)="logOut(currentAccount.id)"
>
<i class="bwi bwi-sign-out tw-text-2xl" aria-hidden="true"></i>
{{ "logOut" | i18n }}
</button>
<button
type="button"
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3"
(click)="lockAll()"
>
<i class="bwi bwi-lock tw-text-2xl" aria-hidden="true"></i>
{{ "lockAll" | i18n }}
</button>
</div>
</div> </div>
</div> </div>
</div> </main>
</main> </ng-container>

View File

@@ -1,22 +1,54 @@
import { Location } from "@angular/common"; import { CommonModule, Location } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { Subject, firstValueFrom, map, of, switchMap, takeUntil } from "rxjs"; import { Subject, firstValueFrom, map, of, switchMap, takeUntil } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { DialogService } from "@bitwarden/components"; import {
AvatarModule,
ButtonModule,
DialogService,
ItemModule,
SectionComponent,
SectionHeaderComponent,
} from "@bitwarden/components";
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { HeaderComponent } from "../../../platform/popup/header.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { AccountComponent } from "./account.component";
import { CurrentAccountComponent } from "./current-account.component";
import { AccountSwitcherService } from "./services/account-switcher.service"; import { AccountSwitcherService } from "./services/account-switcher.service";
@Component({ @Component({
standalone: true,
templateUrl: "account-switcher.component.html", templateUrl: "account-switcher.component.html",
imports: [
CommonModule,
JslibModule,
ButtonModule,
ItemModule,
AvatarModule,
PopupPageComponent,
PopupHeaderComponent,
HeaderComponent,
PopOutComponent,
CurrentAccountComponent,
AccountComponent,
SectionComponent,
SectionHeaderComponent,
],
}) })
export class AccountSwitcherComponent implements OnInit, OnDestroy { export class AccountSwitcherComponent implements OnInit, OnDestroy {
readonly lockedStatus = AuthenticationStatus.Locked; readonly lockedStatus = AuthenticationStatus.Locked;
@@ -24,17 +56,18 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
loading = false; loading = false;
activeUserCanLock = false; activeUserCanLock = false;
extensionRefreshFlag = false;
constructor( constructor(
private accountSwitcherService: AccountSwitcherService, private accountSwitcherService: AccountSwitcherService,
private accountService: AccountService, private accountService: AccountService,
private vaultTimeoutService: VaultTimeoutService, private vaultTimeoutService: VaultTimeoutService,
private messagingService: MessagingService,
private dialogService: DialogService, private dialogService: DialogService,
private location: Location, private location: Location,
private router: Router, private router: Router,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService, private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private authService: AuthService, private authService: AuthService,
private configService: ConfigService,
) {} ) {}
get accountLimit() { get accountLimit() {
@@ -55,6 +88,10 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
); );
async ngOnInit() { async ngOnInit() {
this.extensionRefreshFlag = await this.configService.getFeatureFlag(
FeatureFlag.ExtensionRefresh,
);
const availableVaultTimeoutActions = await firstValueFrom( const availableVaultTimeoutActions = await firstValueFrom(
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(), this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
); );

View File

@@ -1,54 +1,109 @@
<button <ng-container *ngIf="extensionRefreshFlag">
*ngIf="account.id !== specialAccountAddId" <bit-item *ngIf="account.id !== specialAccountAddId">
type="button" <button bit-item-content type="button" (click)="selectAccount(account.id)">
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-border-none tw-p-3" <bit-avatar
(click)="selectAccount(account.id)" slot="start"
> [id]="account.id"
<div class="tw-flex-shrink-0"> [text]="account.name"
<bit-avatar [color]="account.avatarColor"
[id]="account.id" size="small"
[text]="account.name" aria-hidden="true"
[color]="account.avatarColor" ></bit-avatar>
size="small"
aria-hidden="true"
></bit-avatar>
</div>
<div class="tw-text-left">
<span class="tw-sr-only" *ngIf="status.text === 'active'"> {{ "activeAccount" | i18n }}: </span>
<span class="tw-sr-only" *ngIf="status.text !== 'active'">
{{ "switchToAccount" | i18n }}
</span>
<div class="tw-max-w-64 tw-truncate">
{{ account.email }}
</div>
<div class="account-switcher-row-details tw-max-w-64 tw-truncate tw-text-sm">
<span class="tw-sr-only">{{ "hostedAt" | i18n }}</span>
{{ account.server }}
</div>
<div
class="account-switcher-row-details tw-text-sm tw-italic"
[attr.aria-hidden]="status.text === 'active'"
>
<span class="tw-sr-only">(</span>
<span [ngClass]="status.text === 'active' ? 'tw-font-bold tw-text-success' : ''">{{
status.text
}}</span>
<span class="tw-sr-only">)</span>
</div>
</div>
<div class="tw-ml-auto tw-flex-shrink-0">
<i class="bwi tw-text-2xl" [ngClass]="status.icon" aria-hidden="true"></i>
</div>
</button>
<button <span class="tw-sr-only" *ngIf="status.text === 'active'">
*ngIf="account.id === specialAccountAddId" {{ "activeAccount" | i18n }}:
type="button" </span>
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-border-none tw-p-3" <span class="tw-sr-only" *ngIf="status.text !== 'active'">
(click)="selectAccount(account.id)" {{ "switchToAccount" | i18n }}
> </span>
<i class="bwi bwi-plus tw-text-2xl" aria-hidden="true"></i> <div class="tw-max-w-64 tw-truncate">
<div> {{ account.email }}
{{ account.name | i18n }} </div>
</div>
</button> <ng-container slot="secondary">
<div class="tw-max-w-64 tw-truncate tw-text-sm">
<span class="tw-sr-only">{{ "hostedAt" | i18n }}</span>
{{ account.server }}
</div>
<div class="tw-text-sm tw-italic" [attr.aria-hidden]="status.text === 'active'">
<span class="tw-sr-only">(</span>
<span [ngClass]="status.text === 'active' ? 'tw-font-bold tw-text-success' : ''">{{
status.text
}}</span>
<span class="tw-sr-only">)</span>
</div>
</ng-container>
<i slot="end" class="bwi tw-text-2xl" [ngClass]="status.icon" aria-hidden="true"></i>
</button>
</bit-item>
<bit-item *ngIf="account.id === specialAccountAddId">
<button type="button" bit-item-content (click)="selectAccount(account.id)">
<i slot="start" class="bwi bwi-plus tw-text-2xl tw-text-main" aria-hidden="true"></i>
{{ account.name | i18n }}
</button>
</bit-item>
</ng-container>
<ng-container *ngIf="!extensionRefreshFlag">
<button
*ngIf="account.id !== specialAccountAddId"
type="button"
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-border-none tw-p-3"
(click)="selectAccount(account.id)"
>
<div class="tw-flex-shrink-0">
<bit-avatar
[id]="account.id"
[text]="account.name"
[color]="account.avatarColor"
size="small"
aria-hidden="true"
></bit-avatar>
</div>
<div class="tw-text-left">
<span class="tw-sr-only" *ngIf="status.text === 'active'">
{{ "activeAccount" | i18n }}:
</span>
<span class="tw-sr-only" *ngIf="status.text !== 'active'">
{{ "switchToAccount" | i18n }}
</span>
<div class="tw-max-w-64 tw-truncate">
{{ account.email }}
</div>
<div class="account-switcher-row-details tw-max-w-64 tw-truncate tw-text-sm">
<span class="tw-sr-only">{{ "hostedAt" | i18n }}</span>
{{ account.server }}
</div>
<div
class="account-switcher-row-details tw-text-sm tw-italic"
[attr.aria-hidden]="status.text === 'active'"
>
<span class="tw-sr-only">(</span>
<span [ngClass]="status.text === 'active' ? 'tw-font-bold tw-text-success' : ''">{{
status.text
}}</span>
<span class="tw-sr-only">)</span>
</div>
</div>
<div class="tw-ml-auto tw-flex-shrink-0">
<i class="bwi tw-text-2xl" [ngClass]="status.icon" aria-hidden="true"></i>
</div>
</button>
<button
*ngIf="account.id === specialAccountAddId"
type="button"
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-border-none tw-p-3"
(click)="selectAccount(account.id)"
>
<i class="bwi bwi-plus tw-text-2xl" aria-hidden="true"></i>
<div>
{{ account.name | i18n }}
</div>
</button>
</ng-container>

View File

@@ -5,7 +5,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { AvatarModule } from "@bitwarden/components"; import { AvatarModule, ItemModule } from "@bitwarden/components";
import { AccountSwitcherService, AvailableAccount } from "./services/account-switcher.service"; import { AccountSwitcherService, AvailableAccount } from "./services/account-switcher.service";
@@ -13,10 +13,11 @@ import { AccountSwitcherService, AvailableAccount } from "./services/account-swi
standalone: true, standalone: true,
selector: "auth-account", selector: "auth-account",
templateUrl: "account.component.html", templateUrl: "account.component.html",
imports: [CommonModule, JslibModule, AvatarModule], imports: [CommonModule, JslibModule, AvatarModule, ItemModule],
}) })
export class AccountComponent { export class AccountComponent {
@Input() account: AvailableAccount; @Input() account: AvailableAccount;
@Input() extensionRefreshFlag: boolean = false;
@Output() loading = new EventEmitter<boolean>(); @Output() loading = new EventEmitter<boolean>();
constructor( constructor(

View File

@@ -1,14 +1,18 @@
import { CommonModule } from "@angular/common";
import { Component, Input } from "@angular/core"; import { Component, Input } from "@angular/core";
import { Observable, map, of, switchMap } from "rxjs"; import { Observable, map, of, switchMap } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CurrentAccountComponent } from "../../auth/popup/account-switching/current-account.component";
import { enableAccountSwitching } from "../flags"; import { enableAccountSwitching } from "../flags";
@Component({ @Component({
selector: "app-header", selector: "app-header",
templateUrl: "header.component.html", templateUrl: "header.component.html",
standalone: true,
imports: [CommonModule, CurrentAccountComponent],
}) })
export class HeaderComponent { export class HeaderComponent {
@Input() noTheme = false; @Input() noTheme = false;

View File

@@ -17,7 +17,6 @@ import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"
import { UserVerificationDialogComponent } from "@bitwarden/auth/angular"; import { UserVerificationDialogComponent } from "@bitwarden/auth/angular";
import { AvatarModule, ButtonModule, ToastModule } from "@bitwarden/components"; import { AvatarModule, ButtonModule, ToastModule } from "@bitwarden/components";
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
import { AccountComponent } from "../auth/popup/account-switching/account.component"; import { AccountComponent } from "../auth/popup/account-switching/account.component";
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component"; import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
import { EnvironmentComponent } from "../auth/popup/environment.component"; import { EnvironmentComponent } from "../auth/popup/environment.component";
@@ -123,6 +122,7 @@ import "../platform/popup/locales";
PopupTabNavigationComponent, PopupTabNavigationComponent,
PopupFooterComponent, PopupFooterComponent,
PopupHeaderComponent, PopupHeaderComponent,
HeaderComponent,
UserVerificationDialogComponent, UserVerificationDialogComponent,
CurrentAccountComponent, CurrentAccountComponent,
], ],
@@ -145,7 +145,6 @@ import "../platform/popup/locales";
FolderAddEditComponent, FolderAddEditComponent,
FoldersComponent, FoldersComponent,
VaultFilterComponent, VaultFilterComponent,
HeaderComponent,
HintComponent, HintComponent,
HomeComponent, HomeComponent,
LockComponent, LockComponent,
@@ -184,8 +183,8 @@ import "../platform/popup/locales";
Fido2Component, Fido2Component,
AutofillV1Component, AutofillV1Component,
EnvironmentSelectorComponent, EnvironmentSelectorComponent,
AccountSwitcherComponent,
], ],
exports: [],
providers: [CurrencyPipe, DatePipe], providers: [CurrencyPipe, DatePipe],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })