mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 11:43:51 +00:00
Merge branch 'main' into billing/pm-29602/update-cart-summary
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"devFlags": {},
|
||||
"flags": {
|
||||
"accountSwitching": false,
|
||||
"sdk": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,5 @@
|
||||
"base": "https://localhost:8080"
|
||||
},
|
||||
"skipWelcomeOnInstall": true
|
||||
},
|
||||
"flags": {
|
||||
"accountSwitching": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"flags": {
|
||||
"accountSwitching": true
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,16 @@
|
||||
</popup-header>
|
||||
|
||||
<ng-container *ngIf="availableAccounts$ | async as availableAccounts">
|
||||
<bit-section [disableMargin]="!enableAccountSwitching">
|
||||
<bit-section [disableMargin]="!(enableAccountSwitching$ | async)">
|
||||
<ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst">
|
||||
<div *ngIf="availableAccount.isActive" [ngClass]="{ 'tw-mb-6': enableAccountSwitching }">
|
||||
<div
|
||||
*ngIf="availableAccount.isActive"
|
||||
[ngClass]="{ 'tw-mb-6': enableAccountSwitching$ | async }"
|
||||
>
|
||||
<auth-account [account]="availableAccount" (loading)="loading = $event"></auth-account>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="enableAccountSwitching">
|
||||
<ng-container *ngIf="enableAccountSwitching$ | async">
|
||||
<bit-section-header *ngIf="isFirst">
|
||||
<h2 bitTypography="h6">{{ "availableAccounts" | i18n }}</h2>
|
||||
</bit-section-header>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CommonModule, Location } from "@angular/common";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, firstValueFrom, map, of, startWith, switchMap } from "rxjs";
|
||||
import { Observable, Subject, firstValueFrom, map, of, startWith, switchMap } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { LockService, LogoutService } from "@bitwarden/auth/common";
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { enableAccountSwitching } from "../../../platform/flags";
|
||||
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";
|
||||
@@ -59,7 +58,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
|
||||
loading = false;
|
||||
activeUserCanLock = false;
|
||||
enableAccountSwitching = true;
|
||||
enableAccountSwitching$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
private accountSwitcherService: AccountSwitcherService,
|
||||
@@ -72,7 +71,9 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
private authService: AuthService,
|
||||
private lockService: LockService,
|
||||
private logoutService: LogoutService,
|
||||
) {}
|
||||
) {
|
||||
this.enableAccountSwitching$ = this.accountSwitcherService.accountSwitchingEnabled$();
|
||||
}
|
||||
|
||||
get accountLimit() {
|
||||
return this.accountSwitcherService.ACCOUNT_LIMIT;
|
||||
@@ -97,19 +98,21 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
switchMap((accounts) => {
|
||||
// If account switching is disabled, don't show the lock all button
|
||||
// as only one account should be shown.
|
||||
if (!enableAccountSwitching()) {
|
||||
return of(false);
|
||||
}
|
||||
return this.accountSwitcherService.accountSwitchingEnabled$().pipe(
|
||||
switchMap((enabled) => {
|
||||
if (!enabled) {
|
||||
return of(false);
|
||||
}
|
||||
|
||||
// When there are an inactive accounts provide the option to lock all accounts
|
||||
// Note: "Add account" is counted as an inactive account, so check for more than one account
|
||||
return of(accounts.length > 1);
|
||||
// When there are inactive accounts provide the option to lock all accounts
|
||||
// Note: "Add account" is counted as an inactive account, so check for more than one account
|
||||
return of(accounts.length > 1);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
async ngOnInit() {
|
||||
this.enableAccountSwitching = enableAccountSwitching();
|
||||
|
||||
const availableVaultTimeoutActions = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import {
|
||||
Environment,
|
||||
EnvironmentService,
|
||||
@@ -37,6 +38,7 @@ describe("AccountSwitcherService", () => {
|
||||
const environmentService = mock<EnvironmentService>();
|
||||
const logService = mock<LogService>();
|
||||
const authService = mock<AuthService>();
|
||||
const configService = mock<ConfigService>();
|
||||
|
||||
let accountSwitcherService: AccountSwitcherService;
|
||||
|
||||
@@ -60,6 +62,7 @@ describe("AccountSwitcherService", () => {
|
||||
messagingService,
|
||||
environmentService,
|
||||
logService,
|
||||
configService,
|
||||
authService,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
filter,
|
||||
firstValueFrom,
|
||||
map,
|
||||
of,
|
||||
switchMap,
|
||||
throwError,
|
||||
timeout,
|
||||
@@ -17,11 +18,14 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { BrowserApi } from "../../../../platform/browser/browser-api";
|
||||
import { fromChromeEvent } from "../../../../platform/browser/from-chrome-event";
|
||||
|
||||
export type AvailableAccount = {
|
||||
@@ -52,6 +56,7 @@ export class AccountSwitcherService {
|
||||
private messagingService: MessagingService,
|
||||
private environmentService: EnvironmentService,
|
||||
private logService: LogService,
|
||||
private configService: ConfigService,
|
||||
authService: AuthService,
|
||||
) {
|
||||
this.availableAccounts$ = combineLatest([
|
||||
@@ -123,6 +128,19 @@ export class AccountSwitcherService {
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* PM-5594: This was a compile-time flag (default true) which made an exception for Safari in platform/flags.
|
||||
* The truthiness of AccountSwitching has been enshrined at this point, so those compile-time flags have been removed
|
||||
* in favor of this method to allow easier access to the config service for controlling Safari. Unwinding the Safari
|
||||
* flag should be more straightforward from this consolidation.
|
||||
*/
|
||||
accountSwitchingEnabled$(): Observable<boolean> {
|
||||
if (BrowserApi.isSafariApi) {
|
||||
return this.configService.getFeatureFlag$(FeatureFlag.SafariAccountSwitching);
|
||||
}
|
||||
return of(true);
|
||||
}
|
||||
|
||||
get specialAccountAddId() {
|
||||
return this.SPECIAL_ADD_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
<div class="tw-bg-background-alt">
|
||||
<p>
|
||||
{{
|
||||
accountSwitcherEnabled ? ("excludedDomainsDescAlt" | i18n) : ("excludedDomainsDesc" | i18n)
|
||||
(accountSwitcherEnabled$ | async)
|
||||
? ("excludedDomainsDescAlt" | i18n)
|
||||
: ("excludedDomainsDesc" | i18n)
|
||||
}}
|
||||
</p>
|
||||
<bit-section *ngIf="!isLoading">
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
FormArray,
|
||||
} from "@angular/forms";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { Observable, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { enableAccountSwitching } from "../../../platform/flags";
|
||||
import { AccountSwitcherService } from "../../../auth/popup/account-switching/services/account-switcher.service";
|
||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
|
||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||
@@ -74,7 +74,8 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
@ViewChildren("uriInput") uriInputElements: QueryList<ElementRef<HTMLInputElement>> =
|
||||
new QueryList();
|
||||
|
||||
accountSwitcherEnabled = false;
|
||||
readonly accountSwitcherEnabled$: Observable<boolean> =
|
||||
this.accountSwitcherService.accountSwitchingEnabled$();
|
||||
dataIsPristine = true;
|
||||
isLoading = false;
|
||||
excludedDomainsState: string[] = [];
|
||||
@@ -95,9 +96,8 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
private toastService: ToastService,
|
||||
private formBuilder: FormBuilder,
|
||||
private popupRouterCacheService: PopupRouterCacheService,
|
||||
) {
|
||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
||||
}
|
||||
private accountSwitcherService: AccountSwitcherService,
|
||||
) {}
|
||||
|
||||
get domainForms() {
|
||||
return this.domainListForm.get("domains") as FormArray;
|
||||
|
||||
@@ -8,12 +8,8 @@ import {
|
||||
|
||||
import { GroupPolicyEnvironment } from "../admin-console/types/group-policy-environment";
|
||||
|
||||
import { BrowserApi } from "./browser/browser-api";
|
||||
|
||||
// required to avoid linting errors when there are no flags
|
||||
export type Flags = {
|
||||
accountSwitching?: boolean;
|
||||
} & SharedFlags;
|
||||
export type Flags = SharedFlags;
|
||||
|
||||
// required to avoid linting errors when there are no flags
|
||||
export type DevFlags = {
|
||||
@@ -31,14 +27,3 @@ export function devFlagEnabled(flag: keyof DevFlags) {
|
||||
export function devFlagValue(flag: keyof DevFlags) {
|
||||
return baseDevFlagValue(flag);
|
||||
}
|
||||
|
||||
/** Helper method to sync flag specifically for account switching, which as platform-based values.
|
||||
* If this pattern needs to be repeated, it's better handled by increasing complexity of webpack configurations
|
||||
* Not by expanding these flag getters.
|
||||
*/
|
||||
export function enableAccountSwitching(): boolean {
|
||||
if (BrowserApi.isSafariApi) {
|
||||
return false;
|
||||
}
|
||||
return flagEnabled("accountSwitching");
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
[showRefresh]="showRefresh"
|
||||
(onRefresh)="refreshCurrentTab()"
|
||||
[description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : undefined"
|
||||
showAutofillButton
|
||||
isAutofillList
|
||||
[disableDescriptionMargin]="showEmptyAutofillTip$ | async"
|
||||
[primaryActionAutofill]="clickItemsToAutofillVaultView$ | async"
|
||||
[groupByType]="groupByType()"
|
||||
></app-vault-list-items-container>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { toSignal } from "@angular/core/rxjs-interop";
|
||||
import { combineLatest, map, Observable, startWith } from "rxjs";
|
||||
import { combineLatest, map, Observable } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
@@ -42,12 +42,6 @@ export class AutofillVaultListItemsComponent {
|
||||
*/
|
||||
protected showRefresh: boolean = BrowserPopupUtils.inSidebar(window);
|
||||
|
||||
/** Flag indicating whether the login item should automatically autofill when clicked */
|
||||
protected clickItemsToAutofillVaultView$: Observable<boolean> =
|
||||
this.vaultSettingsService.clickItemsToAutofillVaultView$.pipe(
|
||||
startWith(true), // Start with true to avoid flashing the fill button on first load
|
||||
);
|
||||
|
||||
protected readonly groupByType = toSignal(
|
||||
this.vaultPopupItemsService.hasFilterApplied$.pipe(map((hasFilter) => !hasFilter)),
|
||||
);
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
></button>
|
||||
<bit-menu #moreOptions>
|
||||
@if (!decryptionFailure) {
|
||||
<ng-container *ngIf="canAutofill && !hideAutofillOptions">
|
||||
<ng-container *ngIf="canAutofill && showAutofill()">
|
||||
<ng-container *ngIf="autofillAllowed$ | async">
|
||||
<button type="button" bitMenuItem (click)="doAutofill()">
|
||||
{{ "autofill" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showViewOption">
|
||||
<ng-container>
|
||||
<button type="button" bitMenuItem (click)="onView()">
|
||||
{{ "view" | i18n }}
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { booleanAttribute, Component, Input } from "@angular/core";
|
||||
import { booleanAttribute, Component, input, Input } from "@angular/core";
|
||||
import { Router, RouterModule } from "@angular/router";
|
||||
import { BehaviorSubject, combineLatest, firstValueFrom, map, Observable, switchMap } from "rxjs";
|
||||
import { filter } from "rxjs/operators";
|
||||
@@ -76,22 +76,10 @@ export class ItemMoreOptionsComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to show view item menu option. Used when something else is
|
||||
* assigned as the primary action for the item, such as autofill.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input({ transform: booleanAttribute })
|
||||
showViewOption = false;
|
||||
|
||||
/**
|
||||
* Flag to hide the autofill menu options. Used for items that are
|
||||
* Flag to show the autofill menu options. Used for items that are
|
||||
* already in the autofill list suggestion.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input({ transform: booleanAttribute })
|
||||
hideAutofillOptions = false;
|
||||
readonly showAutofill = input(false, { transform: booleanAttribute });
|
||||
|
||||
protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$;
|
||||
|
||||
|
||||
@@ -90,11 +90,11 @@
|
||||
</ng-container>
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="itemHeight$ | async" bitScrollLayout>
|
||||
<bit-item *cdkVirtualFor="let cipher of group.ciphers">
|
||||
<bit-item *cdkVirtualFor="let cipher of group.ciphers" class="tw-group/vault-item">
|
||||
<button
|
||||
bit-item-content
|
||||
type="button"
|
||||
(click)="primaryActionOnSelect(cipher)"
|
||||
(click)="onCipherSelect(cipher)"
|
||||
(dblclick)="launchCipher(cipher)"
|
||||
[appA11yTitle]="
|
||||
cipherItemTitleKey()(cipher)
|
||||
@@ -125,19 +125,14 @@
|
||||
</button>
|
||||
|
||||
<ng-container slot="end">
|
||||
<bit-item-action *ngIf="!hideAutofillButton()">
|
||||
<button
|
||||
type="button"
|
||||
bitBadge
|
||||
variant="primary"
|
||||
(click)="doAutofill(cipher)"
|
||||
[title]="autofillShortcutTooltip() ?? ('autofillTitle' | i18n: cipher.name)"
|
||||
[attr.aria-label]="'autofillTitle' | i18n: cipher.name"
|
||||
<bit-item-action *ngIf="isAutofillList()">
|
||||
<span
|
||||
class="tw-opacity-0 tw-text-sm tw-text-primary-600 tw-px-2 group-hover/vault-item:tw-opacity-100 group-focus-within/vault-item:tw-opacity-100"
|
||||
>
|
||||
{{ "fill" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</bit-item-action>
|
||||
<bit-item-action *ngIf="!showAutofillButton() && CipherViewLikeUtils.canLaunch(cipher)">
|
||||
<bit-item-action *ngIf="!isAutofillList() && CipherViewLikeUtils.canLaunch(cipher)">
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-external-link"
|
||||
@@ -149,8 +144,7 @@
|
||||
<app-item-copy-actions [cipher]="cipher"></app-item-copy-actions>
|
||||
<app-item-more-options
|
||||
[cipher]="cipher"
|
||||
[hideAutofillOptions]="hideAutofillMenuOptions()"
|
||||
[showViewOption]="primaryActionAutofill()"
|
||||
[showAutofill]="!isAutofillList()"
|
||||
></app-item-more-options>
|
||||
</ng-container>
|
||||
</bit-item>
|
||||
|
||||
@@ -136,24 +136,18 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
*/
|
||||
private viewCipherTimeout?: number;
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
ciphers = input<PopupCipherViewLike[]>([]);
|
||||
readonly ciphers = input<PopupCipherViewLike[]>([]);
|
||||
|
||||
/**
|
||||
* If true, we will group ciphers by type (Login, Card, Identity)
|
||||
* within subheadings in a single container, converted to a WritableSignal.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
groupByType = input<boolean | undefined>(false);
|
||||
readonly groupByType = input<boolean | undefined>(false);
|
||||
|
||||
/**
|
||||
* Computed signal for a grouped list of ciphers with an optional header
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
cipherGroups = computed<
|
||||
readonly cipherGroups = computed<
|
||||
{
|
||||
subHeaderKey?: string;
|
||||
ciphers: PopupCipherViewLike[];
|
||||
@@ -195,9 +189,7 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
/**
|
||||
* Title for the vault list item section.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
title = input<string | undefined>(undefined);
|
||||
readonly title = input<string | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Optionally allow the items to be collapsed.
|
||||
@@ -205,24 +197,20 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
* The key must be added to the state definition in `vault-popup-section.service.ts` since the
|
||||
* collapsed state is stored locally.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
collapsibleKey = input<keyof PopupSectionOpen | undefined>(undefined);
|
||||
readonly collapsibleKey = input<keyof PopupSectionOpen | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Optional description for the vault list item section. Will be shown below the title even when
|
||||
* no ciphers are available.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
description = input<string | undefined>(undefined);
|
||||
|
||||
readonly description = input<string | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Option to show a refresh button in the section header.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
showRefresh = input(false, { transform: booleanAttribute });
|
||||
|
||||
readonly showRefresh = input(false, { transform: booleanAttribute });
|
||||
|
||||
/**
|
||||
* Event emitted when the refresh button is clicked.
|
||||
@@ -235,23 +223,16 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
/**
|
||||
* Flag indicating that the current tab location is blocked
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
currentURIIsBlocked = toSignal(this.vaultPopupAutofillService.currentTabIsOnBlocklist$);
|
||||
readonly currentUriIsBlocked = toSignal(this.vaultPopupAutofillService.currentTabIsOnBlocklist$);
|
||||
|
||||
/**
|
||||
* Resolved i18n key to use for suggested cipher items
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
cipherItemTitleKey = computed(() => {
|
||||
readonly cipherItemTitleKey = computed(() => {
|
||||
return (cipher: CipherViewLike) => {
|
||||
const login = CipherViewLikeUtils.getLogin(cipher);
|
||||
const hasUsername = login?.username != null;
|
||||
const key =
|
||||
this.primaryActionAutofill() && !this.currentURIIsBlocked()
|
||||
? "autofillTitle"
|
||||
: "viewItemTitle";
|
||||
const key = !this.currentUriIsBlocked() ? "autofillTitle" : "viewItemTitle";
|
||||
return hasUsername ? `${key}WithField` : key;
|
||||
};
|
||||
});
|
||||
@@ -259,47 +240,25 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
/**
|
||||
* Option to show the autofill button for each item.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
showAutofillButton = input(false, { transform: booleanAttribute });
|
||||
readonly isAutofillList = input(false, { transform: booleanAttribute });
|
||||
|
||||
/**
|
||||
* Flag indicating whether the suggested cipher item autofill button should be shown or not
|
||||
* Computed property whether the cipher select action should perform autofill
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
hideAutofillButton = computed(
|
||||
() => !this.showAutofillButton() || this.currentURIIsBlocked() || this.primaryActionAutofill(),
|
||||
readonly shouldAutofillOnSelect = computed(
|
||||
() => this.isAutofillList() && !this.currentUriIsBlocked(),
|
||||
);
|
||||
|
||||
/**
|
||||
* Flag indicating whether the cipher item autofill menu options should be shown or not
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
hideAutofillMenuOptions = computed(() => this.currentURIIsBlocked() || this.showAutofillButton());
|
||||
|
||||
/**
|
||||
* Option to perform autofill operation as the primary action for autofill suggestions.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
primaryActionAutofill = input(false, { transform: booleanAttribute });
|
||||
|
||||
/**
|
||||
* Remove the bottom margin from the bit-section in this component
|
||||
* (used for containers at the end of the page where bottom margin is not needed)
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
disableSectionMargin = input(false, { transform: booleanAttribute });
|
||||
readonly disableSectionMargin = input(false, { transform: booleanAttribute });
|
||||
|
||||
/**
|
||||
* Remove the description margin
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
disableDescriptionMargin = input(false, { transform: booleanAttribute });
|
||||
readonly disableDescriptionMargin = input(false, { transform: booleanAttribute });
|
||||
|
||||
/**
|
||||
* The tooltip text for the organization icon for ciphers that belong to an organization.
|
||||
@@ -313,9 +272,7 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
return collections[0]?.name;
|
||||
}
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
protected autofillShortcutTooltip = signal<string | undefined>(undefined);
|
||||
protected readonly autofillShortcutTooltip = signal<string | undefined>(undefined);
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
@@ -340,10 +297,8 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
primaryActionOnSelect(cipher: PopupCipherViewLike) {
|
||||
return this.primaryActionAutofill() && !this.currentURIIsBlocked()
|
||||
? this.doAutofill(cipher)
|
||||
: this.onViewCipher(cipher);
|
||||
onCipherSelect(cipher: PopupCipherViewLike) {
|
||||
return this.shouldAutofillOnSelect() ? this.doAutofill(cipher) : this.onViewCipher(cipher);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,16 +50,10 @@
|
||||
<vault-permit-cipher-details-popover></vault-permit-cipher-details-popover>
|
||||
</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<bit-form-control disableMargin>
|
||||
<input bitCheckbox formControlName="showQuickCopyActions" type="checkbox" />
|
||||
<bit-label>{{ "showQuickCopyActions" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control disableMargin>
|
||||
<input bitCheckbox formControlName="clickItemsToAutofillVaultView" type="checkbox" />
|
||||
<bit-label>
|
||||
{{ "clickToAutofill" | i18n }}
|
||||
</bit-label>
|
||||
</bit-form-control>
|
||||
</bit-card>
|
||||
</form>
|
||||
</popup-page>
|
||||
|
||||
@@ -59,14 +59,12 @@ describe("AppearanceV2Component", () => {
|
||||
const enableRoutingAnimation$ = new BehaviorSubject<boolean>(true);
|
||||
const enableCompactMode$ = new BehaviorSubject<boolean>(false);
|
||||
const showQuickCopyActions$ = new BehaviorSubject<boolean>(false);
|
||||
const clickItemsToAutofillVaultView$ = new BehaviorSubject<boolean>(false);
|
||||
const setSelectedTheme = jest.fn().mockResolvedValue(undefined);
|
||||
const setShowFavicons = jest.fn().mockResolvedValue(undefined);
|
||||
const setEnableBadgeCounter = jest.fn().mockResolvedValue(undefined);
|
||||
const setEnableRoutingAnimation = jest.fn().mockResolvedValue(undefined);
|
||||
const setEnableCompactMode = jest.fn().mockResolvedValue(undefined);
|
||||
const setShowQuickCopyActions = jest.fn().mockResolvedValue(undefined);
|
||||
const setClickItemsToAutofillVaultView = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
const mockWidthService: Partial<PopupSizeService> = {
|
||||
width$: new BehaviorSubject("default"),
|
||||
@@ -113,10 +111,7 @@ describe("AppearanceV2Component", () => {
|
||||
},
|
||||
{
|
||||
provide: VaultSettingsService,
|
||||
useValue: {
|
||||
clickItemsToAutofillVaultView$,
|
||||
setClickItemsToAutofillVaultView,
|
||||
},
|
||||
useValue: mock<VaultSettingsService>(),
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -147,7 +142,6 @@ describe("AppearanceV2Component", () => {
|
||||
enableCompactMode: false,
|
||||
showQuickCopyActions: false,
|
||||
width: "default",
|
||||
clickItemsToAutofillVaultView: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -193,11 +187,5 @@ describe("AppearanceV2Component", () => {
|
||||
|
||||
expect(mockWidthService.setWidth).toHaveBeenCalledWith("wide");
|
||||
});
|
||||
|
||||
it("updates the click items to autofill vault view setting", () => {
|
||||
component.appearanceForm.controls.clickItemsToAutofillVaultView.setValue(true);
|
||||
|
||||
expect(setClickItemsToAutofillVaultView).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,7 +66,6 @@ export class AppearanceV2Component implements OnInit {
|
||||
enableCompactMode: false,
|
||||
showQuickCopyActions: false,
|
||||
width: "default" as PopupWidthOption,
|
||||
clickItemsToAutofillVaultView: false,
|
||||
});
|
||||
|
||||
/** To avoid flashes of inaccurate values, only show the form after the entire form is populated. */
|
||||
@@ -112,9 +111,6 @@ export class AppearanceV2Component implements OnInit {
|
||||
this.copyButtonsService.showQuickCopyActions$,
|
||||
);
|
||||
const width = await firstValueFrom(this.popupSizeService.width$);
|
||||
const clickItemsToAutofillVaultView = await firstValueFrom(
|
||||
this.vaultSettingsService.clickItemsToAutofillVaultView$,
|
||||
);
|
||||
|
||||
// Set initial values for the form
|
||||
this.appearanceForm.setValue({
|
||||
@@ -125,7 +121,6 @@ export class AppearanceV2Component implements OnInit {
|
||||
enableCompactMode,
|
||||
showQuickCopyActions,
|
||||
width,
|
||||
clickItemsToAutofillVaultView,
|
||||
});
|
||||
|
||||
this.formLoading = false;
|
||||
@@ -171,16 +166,6 @@ export class AppearanceV2Component implements OnInit {
|
||||
.subscribe((width) => {
|
||||
void this.updateWidth(width);
|
||||
});
|
||||
|
||||
this.appearanceForm.controls.clickItemsToAutofillVaultView.valueChanges
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((clickItemsToAutofillVaultView) => {
|
||||
void this.updateClickItemsToAutofillVaultView(clickItemsToAutofillVaultView);
|
||||
});
|
||||
}
|
||||
|
||||
async updateClickItemsToAutofillVaultView(clickItemsToAutofillVaultView: boolean) {
|
||||
await this.vaultSettingsService.setClickItemsToAutofillVaultView(clickItemsToAutofillVaultView);
|
||||
}
|
||||
|
||||
async updateFavicon(enableFavicon: boolean) {
|
||||
|
||||
@@ -514,7 +514,7 @@ export class vNextMembersComponent {
|
||||
if (result.error != null) {
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
message: this.i18nService.t(result.error),
|
||||
message: result.error,
|
||||
});
|
||||
this.logService.error(result.error);
|
||||
return;
|
||||
|
||||
@@ -57,12 +57,8 @@
|
||||
<ng-container *ngIf="subscription">
|
||||
<ng-container *ngIf="enableDiscountDisplay$ | async as enableDiscount; else noDiscount">
|
||||
<div class="tw-flex tw-items-center tw-gap-2 tw-flex-wrap tw-justify-end">
|
||||
<span [attr.aria-label]="'nextChargeDateAndAmount' | i18n">
|
||||
{{
|
||||
(sub.subscription.periodEndDate | date: "MMM d, y") +
|
||||
", " +
|
||||
(discountedSubscriptionAmount | currency: "$")
|
||||
}}
|
||||
<span [attr.aria-label]="'nextChargeDate' | i18n">
|
||||
{{ sub.subscription.periodEndDate | date: "MMM d, y" }}
|
||||
</span>
|
||||
<billing-discount-badge
|
||||
[discount]="getDiscount(sub?.customerDiscount)"
|
||||
@@ -71,12 +67,8 @@
|
||||
</ng-container>
|
||||
<ng-template #noDiscount>
|
||||
<div class="tw-flex tw-items-center tw-gap-2 tw-flex-wrap tw-justify-end">
|
||||
<span [attr.aria-label]="'nextChargeDateAndAmount' | i18n">
|
||||
{{
|
||||
(sub.subscription.periodEndDate | date: "MMM d, y") +
|
||||
", " +
|
||||
(subscriptionAmount | currency: "$")
|
||||
}}
|
||||
<span [attr.aria-label]="'nextChargeDate' | i18n">
|
||||
{{ sub.subscription.periodEndDate | date: "MMM d, y" }}
|
||||
</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -3281,6 +3281,9 @@
|
||||
"nextChargeHeader": {
|
||||
"message": "Next Charge"
|
||||
},
|
||||
"nextChargeDate": {
|
||||
"message": "Next charge date"
|
||||
},
|
||||
"plan": {
|
||||
"message": "Plan"
|
||||
},
|
||||
|
||||
@@ -94,7 +94,12 @@
|
||||
[bitAction]="loadMoreEvents"
|
||||
*ngIf="continuationToken"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<i
|
||||
*ngIf="loading"
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{{ "loadMore" | i18n }}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
@@ -18,6 +18,7 @@ export enum FeatureFlag {
|
||||
|
||||
/* Auth */
|
||||
PM23801_PrefetchPasswordPrelogin = "pm-23801-prefetch-password-prelogin",
|
||||
SafariAccountSwitching = "pm-5594-safari-account-switching",
|
||||
|
||||
/* Autofill */
|
||||
MacOsNativeCredentialSync = "macos-native-credential-sync",
|
||||
@@ -134,6 +135,7 @@ export const DefaultFeatureFlagValue = {
|
||||
|
||||
/* Auth */
|
||||
[FeatureFlag.PM23801_PrefetchPasswordPrelogin]: FALSE,
|
||||
[FeatureFlag.SafariAccountSwitching]: FALSE,
|
||||
|
||||
/* Billing */
|
||||
[FeatureFlag.TrialPaymentOptional]: FALSE,
|
||||
|
||||
@@ -16,11 +16,6 @@ export abstract class VaultSettingsService {
|
||||
* An observable monitoring the state of the show identities on the current tab.
|
||||
*/
|
||||
abstract showIdentitiesCurrentTab$: Observable<boolean>;
|
||||
/**
|
||||
* An observable monitoring the state of the click items on the Vault view
|
||||
* for Autofill suggestions.
|
||||
*/
|
||||
abstract clickItemsToAutofillVaultView$: Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Saves the enable passkeys setting to disk.
|
||||
@@ -37,10 +32,4 @@ export abstract class VaultSettingsService {
|
||||
* @param value The new value for the show identities on tab page setting.
|
||||
*/
|
||||
abstract setShowIdentitiesCurrentTab(value: boolean): Promise<void>;
|
||||
/**
|
||||
* Saves the click items on vault View for Autofill suggestions to disk.
|
||||
* @param value The new value for the click items on vault View for
|
||||
* Autofill suggestions setting.
|
||||
*/
|
||||
abstract setClickItemsToAutofillVaultView(value: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,3 @@ export const SHOW_IDENTITIES_CURRENT_TAB = new UserKeyDefinition<boolean>(
|
||||
clearOn: [], // do not clear user settings
|
||||
},
|
||||
);
|
||||
|
||||
export const CLICK_ITEMS_AUTOFILL_VAULT_VIEW = new UserKeyDefinition<boolean>(
|
||||
VAULT_SETTINGS_DISK,
|
||||
"clickItemsToAutofillOnVaultView",
|
||||
{
|
||||
deserializer: (obj) => obj,
|
||||
clearOn: [], // do not clear user settings
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Observable, combineLatest, map, shareReplay } from "rxjs";
|
||||
import { Observable, combineLatest, map } from "rxjs";
|
||||
|
||||
import { ActiveUserState, GlobalState, StateProvider } from "../../../platform/state";
|
||||
import { VaultSettingsService as VaultSettingsServiceAbstraction } from "../../abstractions/vault-settings/vault-settings.service";
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
SHOW_CARDS_CURRENT_TAB,
|
||||
SHOW_IDENTITIES_CURRENT_TAB,
|
||||
USER_ENABLE_PASSKEYS,
|
||||
CLICK_ITEMS_AUTOFILL_VAULT_VIEW,
|
||||
} from "../key-state/vault-settings.state";
|
||||
import { RestrictedItemTypesService } from "../restricted-item-types.service";
|
||||
|
||||
@@ -49,17 +48,6 @@ export class VaultSettingsService implements VaultSettingsServiceAbstraction {
|
||||
readonly showIdentitiesCurrentTab$: Observable<boolean> =
|
||||
this.showIdentitiesCurrentTabState.state$.pipe(map((x) => x ?? true));
|
||||
|
||||
private clickItemsToAutofillVaultViewState: ActiveUserState<boolean> =
|
||||
this.stateProvider.getActive(CLICK_ITEMS_AUTOFILL_VAULT_VIEW);
|
||||
/**
|
||||
* {@link VaultSettingsServiceAbstraction.clickItemsToAutofillVaultView$$}
|
||||
*/
|
||||
readonly clickItemsToAutofillVaultView$: Observable<boolean> =
|
||||
this.clickItemsToAutofillVaultViewState.state$.pipe(
|
||||
map((x) => x ?? false),
|
||||
shareReplay({ bufferSize: 1, refCount: false }),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private stateProvider: StateProvider,
|
||||
private restrictedItemTypesService: RestrictedItemTypesService,
|
||||
@@ -79,13 +67,6 @@ export class VaultSettingsService implements VaultSettingsServiceAbstraction {
|
||||
await this.showIdentitiesCurrentTabState.update(() => value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link VaultSettingsServiceAbstraction.setClickItemsToAutofillVaultView}
|
||||
*/
|
||||
async setClickItemsToAutofillVaultView(value: boolean): Promise<void> {
|
||||
await this.clickItemsToAutofillVaultViewState.update(() => value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link VaultSettingsServiceAbstraction.setEnablePasskeys}
|
||||
*/
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -106,7 +106,7 @@
|
||||
"@types/koa__multer": "2.0.7",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/koa-bodyparser": "4.3.7",
|
||||
"@types/koa-json": "2.0.23",
|
||||
"@types/koa-json": "2.0.24",
|
||||
"@types/lowdb": "1.0.15",
|
||||
"@types/lunr": "2.3.7",
|
||||
"@types/node": "22.19.7",
|
||||
@@ -15768,9 +15768,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/koa-json": {
|
||||
"version": "2.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa-json/-/koa-json-2.0.23.tgz",
|
||||
"integrity": "sha512-LJKLFouztosawgU5xrtanK4neLCQKXl+vuVN96YMeVdKTYObLq2Qybggm9V426Jwam8Gi/zOrPw1g+QH0VaEHw==",
|
||||
"version": "2.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa-json/-/koa-json-2.0.24.tgz",
|
||||
"integrity": "sha512-FF+nQil6YO8vXMuLnOgGHYspSZVVpi+W79m9/s7LBSOQhlX7QY02X3Evk/g1GgWNLbO674AQaziX6OCCKzQ6Aw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"@types/koa__multer": "2.0.7",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/koa-bodyparser": "4.3.7",
|
||||
"@types/koa-json": "2.0.23",
|
||||
"@types/koa-json": "2.0.24",
|
||||
"@types/lowdb": "1.0.15",
|
||||
"@types/lunr": "2.3.7",
|
||||
"@types/node": "22.19.7",
|
||||
|
||||
Reference in New Issue
Block a user