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(