mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-28 17:27:50 +01:00
premium membership component
This commit is contained in:
parent
54d2742604
commit
830a437f1e
62
src/app/accounts/premium.component.html
Normal file
62
src/app/accounts/premium.component.html
Normal file
@ -0,0 +1,62 @@
|
||||
<div class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'premiumMembership' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{'premiumNotCurrentMember' | i18n}}</p>
|
||||
<p>{{'premiumSignUpAndGet' | i18n}}</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success"></i>
|
||||
{{'premiumSignUpStorage' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success"></i>
|
||||
{{'premiumSignUpTwoStep' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success"></i>
|
||||
{{'premiumSignUpTotp' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success"></i>
|
||||
{{'premiumSignUpSupport' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success"></i>
|
||||
{{'premiumSignUpFuture' | i18n}}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead no-margin">{{'premiumPrice' | i18n : price}}</p>
|
||||
</div>
|
||||
<div *ngIf="isPremium">
|
||||
<p class="text-center lead">{{'premiumCurrentMember' | i18n}}</p>
|
||||
<p class="text-center">{{'premiumCurrentMemberThanks' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
||||
<b>{{'premiumManage' | i18n}}</b>
|
||||
</button>
|
||||
<button type="button" class="primary" appBlurClick (click)="purchase()" *ngIf="!isPremium">
|
||||
<b>{{'premiumPurchase' | i18n}}</b>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="right" *ngIf="!isPremium">
|
||||
<button #refreshBtn type="button" appBlurClick (click)="refresh()" [disabled]="refreshBtn.loading"
|
||||
title="{{'premiumRefresh' | i18n}}" [appApiAction]="refreshPromise">
|
||||
<i class="fa fa-refresh fa-lg fa-fw" [hidden]="refreshBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!refreshBtn.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
59
src/app/accounts/premium.component.ts
Normal file
59
src/app/accounts/premium.component.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import * as template from './premium.component.html';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { TokenService } from 'jslib/abstractions/token.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-premium',
|
||||
template: template,
|
||||
})
|
||||
export class PremiumComponent implements OnInit {
|
||||
isPremium: boolean = false;
|
||||
price: string = '$10';
|
||||
refreshPromise: Promise<any>;
|
||||
|
||||
constructor(private analytics: Angulartics2, private toasterService: ToasterService,
|
||||
private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
||||
private tokenService: TokenService, private apiService: ApiService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.isPremium = !this.tokenService.getPremium();
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
try {
|
||||
this.refreshPromise = this.apiService.refreshIdentityToken();
|
||||
await this.refreshPromise;
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('refreshComplete'));
|
||||
this.isPremium = this.tokenService.getPremium();
|
||||
} catch { }
|
||||
}
|
||||
|
||||
async purchase() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(this.i18nService.t('premiumPurchaseAlert'),
|
||||
this.i18nService.t('premiumPurchase'), this.i18nService.t('yes'), this.i18nService.t('cancel'));
|
||||
if (confirmed) {
|
||||
this.analytics.eventTrack.next({ action: 'Clicked Purchase Premium' });
|
||||
this.platformUtilsService.launchUri('https://vault.bitwarden.com/#/?premium=purchase');
|
||||
}
|
||||
}
|
||||
|
||||
async manage() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(this.i18nService.t('premiumManageAlert'),
|
||||
this.i18nService.t('premiumManage'), this.i18nService.t('yes'), this.i18nService.t('cancel'));
|
||||
if (confirmed) {
|
||||
this.analytics.eventTrack.next({ action: 'Clicked Manage Membership' });
|
||||
this.platformUtilsService.launchUri('https://vault.bitwarden.com/#/?premium=manage');
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import { Router } from '@angular/router';
|
||||
|
||||
import { ModalComponent } from './modal.component';
|
||||
|
||||
import { PremiumComponent } from './accounts/premium.component';
|
||||
import { SettingsComponent } from './accounts/settings.component';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
@ -49,10 +50,12 @@ const BroadcasterSubscriptionId = 'AppComponent';
|
||||
template: `
|
||||
<toaster-container [toasterconfig]="toasterConfig"></toaster-container>
|
||||
<ng-template #settings></ng-template>
|
||||
<ng-template #premium></ng-template>
|
||||
<router-outlet></router-outlet>`,
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
@ViewChild('settings', { read: ViewContainerRef }) settingsRef: ViewContainerRef;
|
||||
@ViewChild('premium', { read: ViewContainerRef }) premiumRef: ViewContainerRef;
|
||||
|
||||
toasterConfig: ToasterConfig = new ToasterConfig({
|
||||
showCloseButton: true,
|
||||
@ -113,6 +116,9 @@ export class AppComponent implements OnInit {
|
||||
case 'openSettings':
|
||||
this.openSettings();
|
||||
break;
|
||||
case 'openPremium':
|
||||
this.openPremium();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
@ -177,4 +183,18 @@ export class AppComponent implements OnInit {
|
||||
this.modal = null;
|
||||
});
|
||||
}
|
||||
|
||||
private openPremium() {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.premiumRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<PremiumComponent>(PremiumComponent, this.premiumRef);
|
||||
|
||||
this.modal.onClosed.subscribe(() => {
|
||||
this.modal = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import { EnvironmentComponent } from './accounts/environment.component';
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { PremiumComponent } from './accounts/premium.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { SettingsComponent } from './accounts/settings.component';
|
||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||
@ -80,6 +81,7 @@ import { ViewComponent } from './vault/view.component';
|
||||
LoginComponent,
|
||||
ModalComponent,
|
||||
PasswordGeneratorComponent,
|
||||
PremiumComponent,
|
||||
RegisterComponent,
|
||||
SearchCiphersPipe,
|
||||
SettingsComponent,
|
||||
@ -96,6 +98,7 @@ import { ViewComponent } from './vault/view.component';
|
||||
FolderAddEditComponent,
|
||||
ModalComponent,
|
||||
PasswordGeneratorComponent,
|
||||
PremiumComponent,
|
||||
SettingsComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
],
|
||||
|
@ -650,9 +650,6 @@
|
||||
"syncVault": {
|
||||
"message": "Sync Vault"
|
||||
},
|
||||
"premiumMembership": {
|
||||
"message": "Premium Membership"
|
||||
},
|
||||
"changeMasterPass": {
|
||||
"message": "Change Master Password"
|
||||
},
|
||||
@ -814,5 +811,62 @@
|
||||
"copySecurityCode": {
|
||||
"message": "Copy Security Code",
|
||||
"description": "Copy credit card security code (CVV)"
|
||||
},
|
||||
"premiumMembership": {
|
||||
"message": "Premium Membership"
|
||||
},
|
||||
"premiumManage": {
|
||||
"message": "Manage Membership"
|
||||
},
|
||||
"premiumManageAlert": {
|
||||
"message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?"
|
||||
},
|
||||
"premiumRefresh": {
|
||||
"message": "Refresh Membership"
|
||||
},
|
||||
"premiumNotCurrentMember": {
|
||||
"message": "You are not currently a premium member."
|
||||
},
|
||||
"premiumSignUpAndGet": {
|
||||
"message": "Sign up for a premium membership and get:"
|
||||
},
|
||||
"premiumSignUpStorage": {
|
||||
"message": "1 GB of encrypted file storage."
|
||||
},
|
||||
"premiumSignUpTwoStep": {
|
||||
"message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo."
|
||||
},
|
||||
"premiumSignUpTotp": {
|
||||
"message": "TOTP verification code (2FA) generator for logins in your vault."
|
||||
},
|
||||
"premiumSignUpSupport": {
|
||||
"message": "Priority customer support."
|
||||
},
|
||||
"premiumSignUpFuture": {
|
||||
"message": "All future premium features. More coming soon!"
|
||||
},
|
||||
"premiumPurchase": {
|
||||
"message": "Purchase Premium"
|
||||
},
|
||||
"premiumPurchaseAlert": {
|
||||
"message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?"
|
||||
},
|
||||
"premiumCurrentMember": {
|
||||
"message": "You are a premium member!"
|
||||
},
|
||||
"premiumCurrentMemberThanks": {
|
||||
"message": "Thank you for supporting bitwarden."
|
||||
},
|
||||
"premiumPrice": {
|
||||
"message": "All for just $PRICE$ /year!",
|
||||
"placeholders": {
|
||||
"price": {
|
||||
"content": "$1",
|
||||
"example": "$10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"refreshComplete": {
|
||||
"message": "Refresh complete"
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ export class MenuMain {
|
||||
submenu: [
|
||||
{
|
||||
label: this.main.i18nService.t('premiumMembership'),
|
||||
click: () => this.main.messagingService.send('premiumMembership'),
|
||||
click: () => this.main.messagingService.send('openPremium'),
|
||||
id: 'premiumMembership',
|
||||
},
|
||||
{
|
||||
|
@ -24,6 +24,10 @@ p {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ small {
|
||||
color: $brand-primary !important;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: $brand-success !important;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: $text-muted !important;
|
||||
}
|
||||
@ -20,6 +24,16 @@ small {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
font-size: $font-size-large;
|
||||
margin-bottom: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user