diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index bb61769de90..a37c038d822 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -5,11 +5,13 @@ import { combineLatest, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { NoResults, NoSendsIcon } from "@bitwarden/assets/svg"; +import { BrowserPremiumUpgradePromptService } from "@bitwarden/browser/vault/popup/services/browser-premium-upgrade-prompt.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ButtonModule, CalloutModule, @@ -39,6 +41,12 @@ export enum SendState { @Component({ templateUrl: "send-v2.component.html", + providers: [ + { + provide: PremiumUpgradePromptService, + useClass: BrowserPremiumUpgradePromptService, + }, + ], imports: [ CalloutModule, PopupPageComponent, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index a1e0a43343f..463819b96e4 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -17,6 +17,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; @@ -50,6 +51,7 @@ import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-p import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service"; import { PopupCloseWarningService } from "../../../../../popup/services/popup-close-warning.service"; import { BrowserCipherFormGenerationService } from "../../../services/browser-cipher-form-generation.service"; +import { BrowserPremiumUpgradePromptService } from "../../../services/browser-premium-upgrade-prompt.service"; import { BrowserTotpCaptureService } from "../../../services/browser-totp-capture.service"; import { fido2PopoutSessionData$, @@ -136,6 +138,7 @@ export type AddEditQueryParams = Partial>; { provide: CipherFormConfigService, useClass: DefaultCipherFormConfigService }, { provide: TotpCaptureService, useClass: BrowserTotpCaptureService }, { provide: CipherFormGenerationService, useClass: BrowserCipherFormGenerationService }, + { provide: PremiumUpgradePromptService, useClass: BrowserPremiumUpgradePromptService }, ], imports: [ CommonModule, diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html index 2650345e94b..0fbe1c55b0a 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html @@ -5,10 +5,10 @@ (click)="openAttachments()" [disabled]="parentFormDisabled" > - {{ "attachments" | i18n }} - - {{ "premium" | i18n }} - +
+ {{ "attachments" | i18n }} + +
diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts index 8f1729f0ab6..a2045736ce2 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts @@ -13,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { ToastService } from "@bitwarden/components"; @@ -108,6 +109,12 @@ describe("OpenAttachmentsComponent", () => { provide: AccountService, useValue: accountService, }, + { + provide: PremiumUpgradePromptService, + useValue: { + promptForPremium: jest.fn().mockResolvedValue(null), + }, + }, ], }).compileComponents(); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts index 493d632dd64..26410a46187 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts @@ -6,6 +6,7 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; import { firstValueFrom, map, switchMap } from "rxjs"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { getOrganizationById, @@ -27,7 +28,14 @@ import { FilePopoutUtilsService } from "../../../../../../tools/popup/services/f @Component({ selector: "app-open-attachments", templateUrl: "./open-attachments.component.html", - imports: [BadgeModule, CommonModule, ItemModule, JslibModule, TypographyModule], + imports: [ + BadgeModule, + CommonModule, + ItemModule, + JslibModule, + TypographyModule, + PremiumBadgeComponent, + ], }) export class OpenAttachmentsComponent implements OnInit { /** Cipher `id` */ diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html index 4af12903a24..18f7f67abc2 100644 --- a/apps/desktop/src/app/accounts/settings.component.html +++ b/apps/desktop/src/app/accounts/settings.component.html @@ -339,16 +339,10 @@ formControlName="enableAutotype" (change)="saveEnableAutotype()" /> - {{ "enableAutotypeTransitionKey" | i18n }} - +
+ {{ "enableAutotypeTransitionKey" | i18n }} + +
diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 3c21b3cf4b7..0ec77419d02 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -7,6 +7,7 @@ import { RouterModule } from "@angular/router"; import { BehaviorSubject, Observable, Subject, combineLatest, firstValueFrom, of } from "rxjs"; import { concatMap, map, pairwise, startWith, switchMap, takeUntil, timeout } from "rxjs/operators"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { VaultTimeoutInputComponent } from "@bitwarden/auth/angular"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -39,6 +40,7 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.e import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { UserId } from "@bitwarden/common/types/guid"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CheckboxModule, DialogService, @@ -51,7 +53,6 @@ import { SelectModule, ToastService, TypographyModule, - BadgeComponent, } from "@bitwarden/components"; import { KeyService, BiometricStateService, BiometricsStatus } from "@bitwarden/key-management"; import { PermitCipherDetailsPopoverComponent } from "@bitwarden/vault"; @@ -60,17 +61,22 @@ import { SetPinComponent } from "../../auth/components/set-pin.component"; import { SshAgentPromptType } from "../../autofill/models/ssh-agent-setting"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutotypeService } from "../../autofill/services/desktop-autotype.service"; -import { PremiumComponent } from "../../billing/app/accounts/premium.component"; import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; +import { DesktopPremiumUpgradePromptService } from "../../services/desktop-premium-upgrade-prompt.service"; import { NativeMessagingManifestService } from "../services/native-messaging-manifest.service"; @Component({ selector: "app-settings", templateUrl: "settings.component.html", standalone: true, + providers: [ + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], imports: [ - BadgeComponent, CheckboxModule, CommonModule, FormFieldModule, @@ -87,6 +93,7 @@ import { NativeMessagingManifestService } from "../services/native-messaging-man TypographyModule, VaultTimeoutInputComponent, PermitCipherDetailsPopoverComponent, + PremiumBadgeComponent, ], }) export class SettingsComponent implements OnInit, OnDestroy { @@ -134,8 +141,6 @@ export class SettingsComponent implements OnInit, OnDestroy { pinEnabled$: Observable = of(true); - hasPremium: boolean = false; - form = this.formBuilder.group({ // Security vaultTimeout: [null as VaultTimeout | null], @@ -421,9 +426,7 @@ export class SettingsComponent implements OnInit, OnDestroy { .hasPremiumFromAnySource$(activeAccount.id) .pipe(takeUntil(this.destroy$)) .subscribe((hasPremium) => { - this.hasPremium = hasPremium; - - if (this.hasPremium) { + if (hasPremium) { this.form.controls.enableAutotype.enable(); } }); @@ -892,10 +895,6 @@ export class SettingsComponent implements OnInit, OnDestroy { } } - async openPremiumDialog() { - await this.dialogService.open(PremiumComponent); - } - async saveEnableAutotype() { await this.desktopAutotypeService.setAutotypeEnabledState(this.form.value.enableAutotype); } diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 7cdb45a6ca7..1e7ef8e0000 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -295,7 +295,7 @@ export class AppComponent implements OnInit, OnDestroy { await this.openModal(SettingsComponent, this.settingsRef); break; case "openPremium": - await this.openModal(PremiumComponent, this.premiumRef); + this.dialogService.open(PremiumComponent); break; case "showFingerprintPhrase": { const activeUserId = await firstValueFrom( diff --git a/apps/desktop/src/billing/app/accounts/premium.component.html b/apps/desktop/src/billing/app/accounts/premium.component.html index c8179c6e486..d88602bed1e 100644 --- a/apps/desktop/src/billing/app/accounts/premium.component.html +++ b/apps/desktop/src/billing/app/accounts/premium.component.html @@ -61,7 +61,7 @@ > {{ "premiumPurchase" | i18n }} - +
diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index badc9e8db40..5a6683ed904 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -13,6 +13,7 @@ import { firstValueFrom, Subject, takeUntil, switchMap, lastValueFrom, Observabl import { filter, map, take } from "rxjs/operators"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; @@ -101,6 +102,7 @@ const BroadcasterSubscriptionId = "VaultComponent"; I18nPipe, ItemModule, ButtonModule, + PremiumBadgeComponent, NavComponent, VaultFilterModule, VaultItemsV2Component, @@ -455,7 +457,6 @@ export class VaultV2Component async openAttachmentsDialog() { if (!this.userHasPremiumAccess) { - await this.premiumUpgradePromptService.promptForPremium(); return; } const dialogRef = AttachmentsV2Component.open(this.dialogService, { diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 88e2a40ec6a..b1c591ef5ef 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -452,7 +452,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { const canAccessAttachments = await firstValueFrom(this.canAccessAttachments$); if (!canAccessAttachments) { - await this.premiumUpgradeService.promptForPremium(); + await this.premiumUpgradeService.promptForPremium(this.cipher?.organizationId); return; } diff --git a/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts b/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts index d2783781c6d..a4a1d76d1d6 100644 --- a/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts +++ b/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts @@ -1,7 +1,7 @@ -import { Component, input, output } from "@angular/core"; +import { Component, input } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { BadgeModule } from "@bitwarden/components"; @Component({ @@ -15,17 +15,11 @@ import { BadgeModule } from "@bitwarden/components"; imports: [BadgeModule, JslibModule], }) export class PremiumBadgeComponent { - /** Skip sending the premiumRequired message (default: false). */ - skipMessaging = input(false); - onClick = output(); + organizationId = input(); - constructor(private messagingService: MessagingService) {} + constructor(private premiumUpgradePromptService: PremiumUpgradePromptService) {} async promptForPremium() { - this.onClick.emit(); - if (this.skipMessaging()) { - return; - } - this.messagingService.send("premiumRequired"); + await this.premiumUpgradePromptService.promptForPremium(this.organizationId()); } } diff --git a/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts b/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts index 79efa6f772c..08259358f30 100644 --- a/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts +++ b/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts @@ -6,6 +6,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessageSender } from "@bitwarden/common/platform/messaging"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { BadgeModule, I18nMockService } from "@bitwarden/components"; import { PremiumBadgeComponent } from "./premium-badge.component"; @@ -51,6 +52,12 @@ export default { hasPremiumFromAnySource$: () => of(false), }, }, + { + provide: PremiumUpgradePromptService, + useValue: { + promptForPremium: (orgId?: string) => {}, + }, + }, ], }), ], diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html index d31f20f771b..02b8be552bd 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html @@ -124,7 +124,7 @@
{{ "verificationCodeTotp" | i18n }} - +