mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
[PM-8228] - Create new Premium Component (#10514)
* add new premium component * finish new premium component * revert change to config service * hide copy changes behind feature flag * revert keys back to original * remove stateService and translation key * add missing translation key * add missing key
This commit is contained in:
@@ -1082,6 +1082,9 @@
|
|||||||
"ppremiumSignUpStorage": {
|
"ppremiumSignUpStorage": {
|
||||||
"message": "1 GB encrypted storage for file attachments."
|
"message": "1 GB encrypted storage for file attachments."
|
||||||
},
|
},
|
||||||
|
"premiumSignUpEmergency": {
|
||||||
|
"message": "Emergency access"
|
||||||
|
},
|
||||||
"premiumSignUpTwoStepOptions": {
|
"premiumSignUpTwoStepOptions": {
|
||||||
"message": "Proprietary two-step login options such as YubiKey and Duo."
|
"message": "Proprietary two-step login options such as YubiKey and Duo."
|
||||||
},
|
},
|
||||||
@@ -1103,6 +1106,9 @@
|
|||||||
"premiumPurchaseAlert": {
|
"premiumPurchaseAlert": {
|
||||||
"message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?"
|
"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": {
|
"premiumCurrentMember": {
|
||||||
"message": "You are a Premium member!"
|
"message": "You are a Premium member!"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<popup-page>
|
||||||
|
<popup-header slot="header" pageTitle="{{ 'premiumMembership' | i18n }}" showBackButton>
|
||||||
|
<ng-container slot="end">
|
||||||
|
<app-pop-out></app-pop-out>
|
||||||
|
</ng-container>
|
||||||
|
</popup-header>
|
||||||
|
|
||||||
|
<div class="tw-flex tw-flex-col tw-p-2">
|
||||||
|
<h2 class="tw-font-bold">{{ "premiumFeatures" | i18n }}</h2>
|
||||||
|
<bit-section>
|
||||||
|
<bit-card>
|
||||||
|
<div class="tw-flex tw-flex-col tw-p-3">
|
||||||
|
<ul class="tw-list-disc tw-pl-5 tw-space-y-2 tw-break-words">
|
||||||
|
<li>
|
||||||
|
{{ "ppremiumSignUpStorage" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "ppremiumSignUpTwoStepOptions" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "premiumSignUpEmergency" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "ppremiumSignUpReports" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "ppremiumSignUpTotp" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "ppremiumSignUpSupport" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "ppremiumSignUpFuture" | i18n }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p class="tw-mt-5 tw-mb-0">{{ priceString }}</p>
|
||||||
|
</bit-card>
|
||||||
|
</bit-section>
|
||||||
|
<button bitButton type="submit" buttonType="primary" (click)="purchase()" class="tw-mb-3">
|
||||||
|
<b>{{ "premiumPurchase" | i18n }}</b>
|
||||||
|
<i class="bwi bwi-external-link" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
#refreshBtn
|
||||||
|
type="button"
|
||||||
|
(click)="refresh()"
|
||||||
|
[disabled]="$any(refreshBtn).loading"
|
||||||
|
[appApiAction]="refreshPromise"
|
||||||
|
bitButton
|
||||||
|
>
|
||||||
|
<span [hidden]="$any(refreshBtn).loading">{{ "premiumRefresh" | i18n }}</span>
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||||
|
[hidden]="!$any(refreshBtn).loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</popup-page>
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,11 @@ import { Component } from "@angular/core";
|
|||||||
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
|
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.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 { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.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 } from "@bitwarden/components";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -22,7 +22,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
|||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
stateService: StateService,
|
configService: ConfigService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
private location: Location,
|
private location: Location,
|
||||||
private currencyPipe: CurrencyPipe,
|
private currencyPipe: CurrencyPipe,
|
||||||
@@ -34,8 +34,8 @@ export class PremiumComponent extends BasePremiumComponent {
|
|||||||
i18nService,
|
i18nService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
apiService,
|
apiService,
|
||||||
|
configService,
|
||||||
logService,
|
logService,
|
||||||
stateService,
|
|
||||||
dialogService,
|
dialogService,
|
||||||
environmentService,
|
environmentService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import { ExcludedDomainsV1Component } from "../autofill/popup/settings/excluded-
|
|||||||
import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component";
|
import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component";
|
||||||
import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component";
|
import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component";
|
||||||
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.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 { PremiumComponent } from "../billing/popup/settings/premium.component";
|
||||||
import BrowserPopupUtils from "../platform/popup/browser-popup-utils";
|
import BrowserPopupUtils from "../platform/popup/browser-popup-utils";
|
||||||
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
||||||
@@ -337,12 +338,12 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { state: "excluded-domains" },
|
data: { state: "excluded-domains" },
|
||||||
}),
|
}),
|
||||||
{
|
...extensionRefreshSwap(PremiumComponent, PremiumV2Component, {
|
||||||
path: "premium",
|
path: "premium",
|
||||||
component: PremiumComponent,
|
component: PremiumComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { state: "premium" },
|
data: { state: "premium" },
|
||||||
},
|
}),
|
||||||
...extensionRefreshSwap(AppearanceComponent, AppearanceV2Component, {
|
...extensionRefreshSwap(AppearanceComponent, AppearanceV2Component, {
|
||||||
path: "appearance",
|
path: "appearance",
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
|
|||||||
@@ -1174,6 +1174,9 @@
|
|||||||
"premiumPurchaseAlert": {
|
"premiumPurchaseAlert": {
|
||||||
"message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?"
|
"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": {
|
"premiumCurrentMember": {
|
||||||
"message": "You are a premium member!"
|
"message": "You are a premium member!"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Component } from "@angular/core";
|
|||||||
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
|
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.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 { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@@ -19,6 +20,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
|||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
|
configService: ConfigService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
stateService: StateService,
|
stateService: StateService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
@@ -29,6 +31,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
|||||||
i18nService,
|
i18nService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
apiService,
|
apiService,
|
||||||
|
configService,
|
||||||
logService,
|
logService,
|
||||||
stateService,
|
stateService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ import { firstValueFrom, Observable } from "rxjs";
|
|||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.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 { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
|
||||||
import { DialogService } from "@bitwarden/components";
|
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class PremiumComponent implements OnInit {
|
export class PremiumComponent implements OnInit {
|
||||||
@@ -16,13 +17,14 @@ export class PremiumComponent implements OnInit {
|
|||||||
price = 10;
|
price = 10;
|
||||||
refreshPromise: Promise<any>;
|
refreshPromise: Promise<any>;
|
||||||
cloudWebVaultUrl: string;
|
cloudWebVaultUrl: string;
|
||||||
|
extensionRefreshFlagEnabled: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
protected apiService: ApiService,
|
protected apiService: ApiService,
|
||||||
|
protected configService: ConfigService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
protected stateService: StateService,
|
|
||||||
protected dialogService: DialogService,
|
protected dialogService: DialogService,
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
@@ -32,6 +34,9 @@ export class PremiumComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
||||||
|
this.extensionRefreshFlagEnabled = await this.configService.getFeatureFlag(
|
||||||
|
FeatureFlag.ExtensionRefresh,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
@@ -45,11 +50,20 @@ export class PremiumComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async purchase() {
|
async purchase() {
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
const dialogOpts: SimpleDialogOptions = {
|
||||||
title: { key: "premiumPurchase" },
|
title: { key: "continueToBitwardenDotCom" },
|
||||||
content: { key: "premiumPurchaseAlert" },
|
content: {
|
||||||
|
key: this.extensionRefreshFlagEnabled ? "premiumPurchaseAlertV2" : "premiumPurchaseAlert",
|
||||||
|
},
|
||||||
type: "info",
|
type: "info",
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (this.extensionRefreshFlagEnabled) {
|
||||||
|
dialogOpts.acceptButtonText = { key: "continue" };
|
||||||
|
dialogOpts.cancelButtonText = { key: "close" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmed = await this.dialogService.openSimpleDialog(dialogOpts);
|
||||||
|
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
this.platformUtilsService.launchUri(
|
this.platformUtilsService.launchUri(
|
||||||
|
|||||||
Reference in New Issue
Block a user