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 { ModalComponent } from './modal.component';
|
||||||
|
|
||||||
|
import { PremiumComponent } from './accounts/premium.component';
|
||||||
import { SettingsComponent } from './accounts/settings.component';
|
import { SettingsComponent } from './accounts/settings.component';
|
||||||
|
|
||||||
import { ToasterService } from 'angular2-toaster';
|
import { ToasterService } from 'angular2-toaster';
|
||||||
@ -49,10 +50,12 @@ const BroadcasterSubscriptionId = 'AppComponent';
|
|||||||
template: `
|
template: `
|
||||||
<toaster-container [toasterconfig]="toasterConfig"></toaster-container>
|
<toaster-container [toasterconfig]="toasterConfig"></toaster-container>
|
||||||
<ng-template #settings></ng-template>
|
<ng-template #settings></ng-template>
|
||||||
|
<ng-template #premium></ng-template>
|
||||||
<router-outlet></router-outlet>`,
|
<router-outlet></router-outlet>`,
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
@ViewChild('settings', { read: ViewContainerRef }) settingsRef: ViewContainerRef;
|
@ViewChild('settings', { read: ViewContainerRef }) settingsRef: ViewContainerRef;
|
||||||
|
@ViewChild('premium', { read: ViewContainerRef }) premiumRef: ViewContainerRef;
|
||||||
|
|
||||||
toasterConfig: ToasterConfig = new ToasterConfig({
|
toasterConfig: ToasterConfig = new ToasterConfig({
|
||||||
showCloseButton: true,
|
showCloseButton: true,
|
||||||
@ -113,6 +116,9 @@ export class AppComponent implements OnInit {
|
|||||||
case 'openSettings':
|
case 'openSettings':
|
||||||
this.openSettings();
|
this.openSettings();
|
||||||
break;
|
break;
|
||||||
|
case 'openPremium':
|
||||||
|
this.openPremium();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -177,4 +183,18 @@ export class AppComponent implements OnInit {
|
|||||||
this.modal = null;
|
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 { HintComponent } from './accounts/hint.component';
|
||||||
import { LockComponent } from './accounts/lock.component';
|
import { LockComponent } from './accounts/lock.component';
|
||||||
import { LoginComponent } from './accounts/login.component';
|
import { LoginComponent } from './accounts/login.component';
|
||||||
|
import { PremiumComponent } from './accounts/premium.component';
|
||||||
import { RegisterComponent } from './accounts/register.component';
|
import { RegisterComponent } from './accounts/register.component';
|
||||||
import { SettingsComponent } from './accounts/settings.component';
|
import { SettingsComponent } from './accounts/settings.component';
|
||||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||||
@ -80,6 +81,7 @@ import { ViewComponent } from './vault/view.component';
|
|||||||
LoginComponent,
|
LoginComponent,
|
||||||
ModalComponent,
|
ModalComponent,
|
||||||
PasswordGeneratorComponent,
|
PasswordGeneratorComponent,
|
||||||
|
PremiumComponent,
|
||||||
RegisterComponent,
|
RegisterComponent,
|
||||||
SearchCiphersPipe,
|
SearchCiphersPipe,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
@ -96,6 +98,7 @@ import { ViewComponent } from './vault/view.component';
|
|||||||
FolderAddEditComponent,
|
FolderAddEditComponent,
|
||||||
ModalComponent,
|
ModalComponent,
|
||||||
PasswordGeneratorComponent,
|
PasswordGeneratorComponent,
|
||||||
|
PremiumComponent,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
TwoFactorOptionsComponent,
|
TwoFactorOptionsComponent,
|
||||||
],
|
],
|
||||||
|
@ -650,9 +650,6 @@
|
|||||||
"syncVault": {
|
"syncVault": {
|
||||||
"message": "Sync Vault"
|
"message": "Sync Vault"
|
||||||
},
|
},
|
||||||
"premiumMembership": {
|
|
||||||
"message": "Premium Membership"
|
|
||||||
},
|
|
||||||
"changeMasterPass": {
|
"changeMasterPass": {
|
||||||
"message": "Change Master Password"
|
"message": "Change Master Password"
|
||||||
},
|
},
|
||||||
@ -814,5 +811,62 @@
|
|||||||
"copySecurityCode": {
|
"copySecurityCode": {
|
||||||
"message": "Copy Security Code",
|
"message": "Copy Security Code",
|
||||||
"description": "Copy credit card security code (CVV)"
|
"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: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: this.main.i18nService.t('premiumMembership'),
|
label: this.main.i18nService.t('premiumMembership'),
|
||||||
click: () => this.main.messagingService.send('premiumMembership'),
|
click: () => this.main.messagingService.send('openPremium'),
|
||||||
id: 'premiumMembership',
|
id: 'premiumMembership',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,10 @@ p {
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,10 @@ small {
|
|||||||
color: $brand-primary !important;
|
color: $brand-primary !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: $brand-success !important;
|
||||||
|
}
|
||||||
|
|
||||||
.text-muted {
|
.text-muted {
|
||||||
color: $text-muted !important;
|
color: $text-muted !important;
|
||||||
}
|
}
|
||||||
@ -20,6 +24,16 @@ small {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-margin {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.lead {
|
||||||
|
font-size: $font-size-large;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user