diff --git a/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.spec.ts b/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.spec.ts index f7f9c2482281..e5e832ad459c 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.spec.ts @@ -91,7 +91,12 @@ describe("AddEditComponent", () => { providers: [ provideNoopAnimations(), { provide: PlatformUtilsService, useValue: mock() }, - { provide: ConfigService, useValue: mock() }, + { + provide: ConfigService, + useValue: mock({ + getFeatureFlag$: jest.fn().mockReturnValue(of(false)), + }), + }, { provide: PopupRouterCacheService, useValue: { back, setHistory } }, { provide: PopupCloseWarningService, useValue: { disable } }, { provide: Router, useValue: { navigate } }, diff --git a/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.ts b/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.ts index cb26afabafa3..c36745716ca0 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.ts +++ b/apps/browser/src/vault/popup/components/vault/add-edit/add-edit.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, OnInit, OnDestroy, viewChild } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Params, Router } from "@angular/router"; import { firstValueFrom, map, Observable, switchMap } from "rxjs"; @@ -192,6 +192,11 @@ export type AddEditQueryParams = Partial>; ], }) export class AddEditComponent implements OnInit, OnDestroy { + protected readonly btnTextAddCreateFeatureFlag = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.PM32380_BtnTextAddCreate), + { initialValue: false }, + ); + readonly cipherFormComponent = viewChild(CipherFormComponent); headerText: string; @@ -513,18 +518,54 @@ export class AddEditComponent implements OnInit, OnDestroy { setHeader(mode: CipherFormMode, type: CipherType) { const isEditMode = mode === "edit" || mode === "partial-edit"; const translation = { - [CipherType.Login]: isEditMode ? "editItemHeaderLogin" : "newItemHeaderLogin", - [CipherType.Card]: isEditMode ? "editItemHeaderCard" : "newItemHeaderCard", - [CipherType.Identity]: isEditMode ? "editItemHeaderIdentity" : "newItemHeaderIdentity", - [CipherType.SecureNote]: isEditMode ? "editItemHeaderNote" : "newItemHeaderNote", - [CipherType.SshKey]: isEditMode ? "editItemHeaderSshKey" : "newItemHeaderSshKey", + [CipherType.Login]: isEditMode + ? this.btnTextAddCreateFeatureFlag() + ? "editItemHeaderLoginSentenceCase" + : "editItemHeaderLogin" + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderLogin" + : "newItemHeaderLogin", + [CipherType.Card]: isEditMode + ? this.btnTextAddCreateFeatureFlag() + ? "editItemHeaderCardSentenceCase" + : "editItemHeaderCard" + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderCard" + : "newItemHeaderCard", + [CipherType.Identity]: isEditMode + ? this.btnTextAddCreateFeatureFlag() + ? "editItemHeaderIdentitySentenceCase" + : "editItemHeaderIdentity" + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderIdentity" + : "newItemHeaderIdentity", + [CipherType.SecureNote]: isEditMode + ? this.btnTextAddCreateFeatureFlag() + ? "editItemHeaderNoteSentenceCase" + : "editItemHeaderNote" + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderNote" + : "newItemHeaderNote", + [CipherType.SshKey]: isEditMode + ? "editItemHeaderSshKey" + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderSshKey" + : "newItemHeaderSshKey", [CipherType.BankAccount]: isEditMode ? "editItemHeaderBankAccount" - : "newItemHeaderBankAccount", + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderBankAccount" + : "newItemHeaderBankAccount", [CipherType.DriversLicense]: isEditMode ? "editItemHeaderLicense" - : "newItemHeaderDriversLicense", - [CipherType.Passport]: isEditMode ? "editItemHeaderPassport" : "newItemHeaderPassport", + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderDriversLicense" + : "newItemHeaderDriversLicense", + [CipherType.Passport]: isEditMode + ? "editItemHeaderPassport" + : this.btnTextAddCreateFeatureFlag() + ? "addItemHeaderPassport" + : "newItemHeaderPassport", }; return this.i18nService.t(translation[type]); } diff --git a/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.html b/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.html index 6e1a9ff9aded..e4fa0eca2ea2 100644 --- a/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.html +++ b/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.html @@ -6,7 +6,7 @@ (click)="navigateToNewItemPage()" startIcon="bwi-plus" > - {{ "new" | i18n }} + {{ (btnTextAddCreateFeatureFlag() ? "add" : "new") | i18n }} } @else { @for (menuItem of cipherMenuItems$ | async; track menuItem.type) { diff --git a/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts b/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts index 3ce733d625d9..d03c22849be2 100644 --- a/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts +++ b/apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts @@ -61,6 +61,11 @@ export class NewItemDropdownComponent implements OnInit { { initialValue: false }, ); + protected readonly btnTextAddCreateFeatureFlag = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.PM32380_BtnTextAddCreate), + { initialValue: false }, + ); + constructor( private dialogService: DialogService, private restrictedItemTypeService: RestrictedItemTypesService, diff --git a/apps/browser/src/vault/popup/components/vault/view/view.component.ts b/apps/browser/src/vault/popup/components/vault/view/view.component.ts index c1c1a7bd7374..1d47e7a8df5d 100644 --- a/apps/browser/src/vault/popup/components/vault/view/view.component.ts +++ b/apps/browser/src/vault/popup/components/vault/view/view.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { Component } from "@angular/core"; +import { Component, inject } from "@angular/core"; import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; @@ -23,6 +23,8 @@ import { } from "@bitwarden/common/autofill/constants"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { EventCollectionService, EventType } from "@bitwarden/common/dirt/event-logs"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { UserId } from "@bitwarden/common/types/guid"; @@ -111,6 +113,12 @@ type LoadAction = ], }) export class ViewComponent { + private readonly configService = inject(ConfigService); + protected readonly btnTextAddCreateFeatureFlag = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.PM32380_BtnTextAddCreate), + { initialValue: false }, + ); + private activeUserId: UserId; headerText: string; @@ -216,10 +224,18 @@ export class ViewComponent { setHeader(type: CipherType) { const translation = { - [CipherType.Login]: "viewItemHeaderLogin", - [CipherType.Card]: "viewItemHeaderCard", - [CipherType.Identity]: "viewItemHeaderIdentity", - [CipherType.SecureNote]: "viewItemHeaderNote", + [CipherType.Login]: this.btnTextAddCreateFeatureFlag() + ? "viewItemHeaderLoginSentenceCase" + : "viewItemHeaderLogin", + [CipherType.Card]: this.btnTextAddCreateFeatureFlag() + ? "viewItemHeaderCardSentenceCase" + : "viewItemHeaderCard", + [CipherType.Identity]: this.btnTextAddCreateFeatureFlag() + ? "viewItemHeaderIdentitySentenceCase" + : "viewItemHeaderIdentity", + [CipherType.SecureNote]: this.btnTextAddCreateFeatureFlag() + ? "viewItemHeaderNoteSentenceCase" + : "viewItemHeaderNote", [CipherType.SshKey]: "viewItemHeaderSshKey", [CipherType.BankAccount]: "viewItemHeaderBankAccount", [CipherType.DriversLicense]: "viewItemHeaderLicense", diff --git a/apps/desktop/src/app/tools/send/send.component.html b/apps/desktop/src/app/tools/send/send.component.html index 39c92dcb9ad4..2ad124fc7a82 100644 --- a/apps/desktop/src/app/tools/send/send.component.html +++ b/apps/desktop/src/app/tools/send/send.component.html @@ -19,7 +19,7 @@ diff --git a/apps/desktop/src/app/tools/send/send.component.ts b/apps/desktop/src/app/tools/send/send.component.ts index 0c08ffc241f4..7088bdb449b3 100644 --- a/apps/desktop/src/app/tools/send/send.component.ts +++ b/apps/desktop/src/app/tools/send/send.component.ts @@ -4,6 +4,8 @@ import { Component, DestroyRef, inject } from "@angular/core"; import { toSignal } from "@angular/core/rxjs-interop"; import { combineLatest, lastValueFrom, map } from "rxjs"; +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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -64,6 +66,12 @@ export class SendComponent { private logService = inject(LogService); private sendFormService = inject(SendFormService); private destroyRef = inject(DestroyRef); + private readonly configService = inject(ConfigService); + + protected readonly btnTextAddCreateFeatureFlag = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.PM32380_BtnTextAddCreate), + { initialValue: false }, + ); private activeDrawerRef?: DialogRef; diff --git a/apps/desktop/src/vault/app/vault-v3/vault-list.component.html b/apps/desktop/src/vault/app/vault-v3/vault-list.component.html index 4983b71541b9..a73b30ffbc8e 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-list.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-list.component.html @@ -92,6 +92,8 @@ [canCreateCipher]="true" [canCreateFolder]="true" [canCreateSshKey]="true" + buttonType="secondary" + [icon]="undefined" (cipherAdded)="addCipher($event)" (folderAdded)="addFolder()" (onAddItemDialog)="onAddItemDialog.emit()" diff --git a/apps/desktop/src/vault/app/vault-v3/vault-list.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-list.component.ts index 610e799543ef..6b0b24c169a3 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-list.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-list.component.ts @@ -3,12 +3,14 @@ import { ScrollingModule } from "@angular/cdk/scrolling"; import { AsyncPipe } from "@angular/common"; import { Component, input, output, effect, inject, computed } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; import { Observable, of, switchMap } from "rxjs"; import { BitSvg } from "@bitwarden/assets/svg"; import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; @@ -92,6 +94,12 @@ export class VaultListComponent { protected cipherAuthorizationService = inject(CipherAuthorizationService); protected restrictedItemTypesService = inject(RestrictedItemTypesService); private premiumUpgradePromptService = inject(PremiumUpgradePromptService); + private readonly configService = inject(ConfigService); + + protected readonly btnTextAddCreateFeatureFlag = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.PM32380_BtnTextAddCreate), + { initialValue: false }, + ); protected dataSource = new TableDataSource>(); private restrictedTypes: RestrictedCipherType[] = []; diff --git a/libs/tools/send/send-ui/src/new-send-dropdown-v2/new-send-dropdown-v2.component.html b/libs/tools/send/send-ui/src/new-send-dropdown-v2/new-send-dropdown-v2.component.html index 18592a5f6d6f..7802d1c59b02 100644 --- a/libs/tools/send/send-ui/src/new-send-dropdown-v2/new-send-dropdown-v2.component.html +++ b/libs/tools/send/send-ui/src/new-send-dropdown-v2/new-send-dropdown-v2.component.html @@ -5,7 +5,16 @@ type="button" [startIcon]="!hideIcon() ? 'bwi-plus' : undefined" > - {{ (hideIcon() ? "createSend" : "new") | i18n }} + {{ + (hideIcon() + ? btnTextAddCreateFeatureFlag() + ? "createSendV2" + : "createSend" + : btnTextAddCreateFeatureFlag() + ? "create" + : "new" + ) | i18n + }} diff --git a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts index b5cbeced209c..b076fee7b691 100644 --- a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts +++ b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts @@ -1,5 +1,6 @@ import { CommonModule } from "@angular/common"; -import { Component, Input, OnInit } from "@angular/core"; +import { Component, inject, Input, OnInit } from "@angular/core"; +import { toSignal } from "@angular/core/rxjs-interop"; import { Router, RouterLink } from "@angular/router"; import { firstValueFrom } from "rxjs"; @@ -7,6 +8,8 @@ import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/pre import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ButtonModule, ButtonType, MenuModule } from "@bitwarden/components"; @@ -30,6 +33,13 @@ export class NewSendDropdownComponent implements OnInit { hasNoPremium = false; + private readonly configService = inject(ConfigService); + + protected readonly btnTextAddCreateFeatureFlag = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.PM32380_BtnTextAddCreate), + { initialValue: false }, + ); + constructor( private billingAccountProfileStateService: BillingAccountProfileStateService, private accountService: AccountService, diff --git a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html index 6db93dd841a4..41e298995c93 100644 --- a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html +++ b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html @@ -1,7 +1,7 @@ @if (useNewItemDialog() && (canCreateCipher() || canCreateCollection() || canCreateFolder())) { @@ -18,7 +18,7 @@
diff --git a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts index 2b1ce51d5fe6..ee50ca5cab84 100644 --- a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts +++ b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts @@ -10,7 +10,9 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; import { + BitwardenIcon, ButtonModule, + ButtonType, MenuModule, PopoverComponent, PopoverModule, @@ -30,6 +32,8 @@ export class NewCipherMenuComponent { readonly canCreateFolder = input(false); readonly canCreateCollection = input(false); readonly canCreateSshKey = input(false); + readonly icon = input("bwi-plus"); + readonly buttonType = input("primary"); /** Optional popover to anchor to the "New" button for coachmark tours */ readonly coachmarkPopover = input(); @@ -102,7 +106,11 @@ export class NewCipherMenuComponent { } if (btnTextAddCreateFeatureFlag) { - return "add"; + if (this.buttonType() === "secondary") { + return "addItem"; + } else { + return "add"; + } } else { return "new"; }