diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index f24d1db484..b360c46bf7 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1082,6 +1082,9 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpEmergency": { + "message": "Emergency access" + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1103,6 +1106,9 @@ "premiumPurchaseAlert": { "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" }, + "premiumPurchaseAlertV2": { + "message": "You can purchase Premium from your account settings on the Bitwarden web app." + }, "premiumCurrentMember": { "message": "You are a Premium member!" }, diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.html b/apps/browser/src/billing/popup/settings/premium-v2.component.html new file mode 100644 index 0000000000..2a98cffb0e --- /dev/null +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.html @@ -0,0 +1,60 @@ + + + + + + + +
+

{{ "premiumFeatures" | i18n }}

+ + +
+
    +
  • + {{ "ppremiumSignUpStorage" | i18n }} +
  • +
  • + {{ "ppremiumSignUpTwoStepOptions" | i18n }} +
  • +
  • + {{ "premiumSignUpEmergency" | i18n }} +
  • +
  • + {{ "ppremiumSignUpReports" | i18n }} +
  • +
  • + {{ "ppremiumSignUpTotp" | i18n }} +
  • +
  • + {{ "ppremiumSignUpSupport" | i18n }} +
  • +
  • + {{ "ppremiumSignUpFuture" | i18n }} +
  • +
+
+

{{ priceString }}

+
+
+ + +
+
diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.ts b/apps/browser/src/billing/popup/settings/premium-v2.component.ts new file mode 100644 index 0000000000..456aa6dc9a --- /dev/null +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.ts @@ -0,0 +1,86 @@ +import { CommonModule, CurrencyPipe, Location } from "@angular/common"; +import { Component } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +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"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + ButtonModule, + CardComponent, + DialogService, + ItemModule, + SectionComponent, +} from "@bitwarden/components"; + +import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component"; +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"; + +@Component({ + selector: "app-premium", + templateUrl: "premium-v2.component.html", + standalone: true, + imports: [ + ButtonModule, + CardComponent, + CommonModule, + CurrentAccountComponent, + ItemModule, + JslibModule, + PopupPageComponent, + PopupHeaderComponent, + PopOutComponent, + RouterModule, + SectionComponent, + ], +}) +export class PremiumV2Component extends BasePremiumComponent { + priceString: string; + + constructor( + i18nService: I18nService, + platformUtilsService: PlatformUtilsService, + apiService: ApiService, + configService: ConfigService, + logService: LogService, + private location: Location, + private currencyPipe: CurrencyPipe, + dialogService: DialogService, + environmentService: EnvironmentService, + billingAccountProfileStateService: BillingAccountProfileStateService, + ) { + super( + i18nService, + platformUtilsService, + apiService, + configService, + logService, + dialogService, + environmentService, + billingAccountProfileStateService, + ); + + // Support old price string. Can be removed in future once all translations are properly updated. + const thePrice = this.currencyPipe.transform(this.price, "$"); + // Safari extension crashes due to $1 appearing in the price string ($10.00). Escape the $ to fix. + const formattedPrice = this.platformUtilsService.isSafari() + ? thePrice.replace("$", "$$$") + : thePrice; + this.priceString = i18nService.t("premiumPrice", formattedPrice); + if (this.priceString.indexOf("%price%") > -1) { + this.priceString = this.priceString.replace("%price%", thePrice); + } + } + + goBack() { + this.location.back(); + } +} diff --git a/apps/browser/src/billing/popup/settings/premium.component.ts b/apps/browser/src/billing/popup/settings/premium.component.ts index dcfbc84aec..ed64574d17 100644 --- a/apps/browser/src/billing/popup/settings/premium.component.ts +++ b/apps/browser/src/billing/popup/settings/premium.component.ts @@ -4,11 +4,11 @@ import { Component } from "@angular/core"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +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"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { DialogService } from "@bitwarden/components"; @Component({ @@ -22,7 +22,7 @@ export class PremiumComponent extends BasePremiumComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, apiService: ApiService, - stateService: StateService, + configService: ConfigService, logService: LogService, private location: Location, private currencyPipe: CurrencyPipe, @@ -34,8 +34,8 @@ export class PremiumComponent extends BasePremiumComponent { i18nService, platformUtilsService, apiService, + configService, logService, - stateService, dialogService, environmentService, billingAccountProfileStateService, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index af85057550..688e7e72a0 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -46,6 +46,7 @@ import { ExcludedDomainsV1Component } from "../autofill/popup/settings/excluded- import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component"; import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component"; import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; +import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component"; import { PremiumComponent } from "../billing/popup/settings/premium.component"; import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; @@ -337,12 +338,12 @@ const routes: Routes = [ canActivate: [authGuard], data: { state: "excluded-domains" }, }), - { + ...extensionRefreshSwap(PremiumComponent, PremiumV2Component, { path: "premium", component: PremiumComponent, canActivate: [authGuard], data: { state: "premium" }, - }, + }), ...extensionRefreshSwap(AppearanceComponent, AppearanceV2Component, { path: "appearance", canActivate: [authGuard], diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 14bd61a092..c0f98f1753 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1174,6 +1174,9 @@ "premiumPurchaseAlert": { "message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?" }, + "premiumPurchaseAlertV2": { + "message": "You can purchase Premium from your account settings on the Bitwarden web app." + }, "premiumCurrentMember": { "message": "You are a premium member!" }, diff --git a/apps/desktop/src/vault/app/accounts/premium.component.ts b/apps/desktop/src/vault/app/accounts/premium.component.ts index 8b3d9e11f0..43c36ad901 100644 --- a/apps/desktop/src/vault/app/accounts/premium.component.ts +++ b/apps/desktop/src/vault/app/accounts/premium.component.ts @@ -3,6 +3,7 @@ import { Component } from "@angular/core"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +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"; @@ -19,6 +20,7 @@ export class PremiumComponent extends BasePremiumComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, apiService: ApiService, + configService: ConfigService, logService: LogService, stateService: StateService, dialogService: DialogService, @@ -29,6 +31,7 @@ export class PremiumComponent extends BasePremiumComponent { i18nService, platformUtilsService, apiService, + configService, logService, stateService, dialogService, diff --git a/libs/angular/src/vault/components/premium.component.ts b/libs/angular/src/vault/components/premium.component.ts index ea38be66e4..cfe3132789 100644 --- a/libs/angular/src/vault/components/premium.component.ts +++ b/libs/angular/src/vault/components/premium.component.ts @@ -3,12 +3,13 @@ import { firstValueFrom, Observable } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +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"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, SimpleDialogOptions } from "@bitwarden/components"; @Directive() export class PremiumComponent implements OnInit { @@ -16,13 +17,14 @@ export class PremiumComponent implements OnInit { price = 10; refreshPromise: Promise; cloudWebVaultUrl: string; + extensionRefreshFlagEnabled: boolean; constructor( protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService, protected apiService: ApiService, + protected configService: ConfigService, private logService: LogService, - protected stateService: StateService, protected dialogService: DialogService, private environmentService: EnvironmentService, billingAccountProfileStateService: BillingAccountProfileStateService, @@ -32,6 +34,9 @@ export class PremiumComponent implements OnInit { async ngOnInit() { this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$); + this.extensionRefreshFlagEnabled = await this.configService.getFeatureFlag( + FeatureFlag.ExtensionRefresh, + ); } async refresh() { @@ -45,11 +50,20 @@ export class PremiumComponent implements OnInit { } async purchase() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "premiumPurchase" }, - content: { key: "premiumPurchaseAlert" }, + const dialogOpts: SimpleDialogOptions = { + title: { key: "continueToBitwardenDotCom" }, + content: { + key: this.extensionRefreshFlagEnabled ? "premiumPurchaseAlertV2" : "premiumPurchaseAlert", + }, type: "info", - }); + }; + + if (this.extensionRefreshFlagEnabled) { + dialogOpts.acceptButtonText = { key: "continue" }; + dialogOpts.cancelButtonText = { key: "close" }; + } + + const confirmed = await this.dialogService.openSimpleDialog(dialogOpts); if (confirmed) { this.platformUtilsService.launchUri(