mirror of
https://github.com/bitwarden/desktop.git
synced 2024-11-02 08:30:14 +01:00
premium membership page
This commit is contained in:
parent
1c693a45b3
commit
3a8f149008
@ -762,5 +762,73 @@
|
|||||||
"updateKey": {
|
"updateKey": {
|
||||||
"message": "You cannot use this feature until you update your encryption key.",
|
"message": "You cannot use this feature until you update your encryption key.",
|
||||||
"description": "You cannot use this feature until you update your encryption key."
|
"description": "You cannot use this feature until you update your encryption key."
|
||||||
|
},
|
||||||
|
"premiumMembership": {
|
||||||
|
"message": "Premium Membership",
|
||||||
|
"description": "Premium Membership"
|
||||||
|
},
|
||||||
|
"premiumManage": {
|
||||||
|
"message": "Manage Membership",
|
||||||
|
"description": "Manage Membership"
|
||||||
|
},
|
||||||
|
"premiumManageAlert": {
|
||||||
|
"message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?",
|
||||||
|
"description": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?"
|
||||||
|
},
|
||||||
|
"premiumRefresh": {
|
||||||
|
"message": "Refresh Membership",
|
||||||
|
"description": "Refresh Membership"
|
||||||
|
},
|
||||||
|
"premiumNotCurrentMember": {
|
||||||
|
"message": "You are not currently a premium member.",
|
||||||
|
"description": "You are not currently a premium member."
|
||||||
|
},
|
||||||
|
"premiumSignUpAndGet": {
|
||||||
|
"message": "Sign up for a premium membership and get:",
|
||||||
|
"description": "Sign up for a premium membership and get:"
|
||||||
|
},
|
||||||
|
"ppremiumSignUpStorage": {
|
||||||
|
"message": "1 GB of encrypted file storage.",
|
||||||
|
"description": "1 GB of encrypted file storage."
|
||||||
|
},
|
||||||
|
"ppremiumSignUpTwoStep": {
|
||||||
|
"message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo.",
|
||||||
|
"description": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo."
|
||||||
|
},
|
||||||
|
"ppremiumSignUpTotp": {
|
||||||
|
"message": "TOTP verification code (2FA) generator for logins in your vault.",
|
||||||
|
"description": "TOTP verification code (2FA) generator for logins in your vault."
|
||||||
|
},
|
||||||
|
"ppremiumSignUpSupport": {
|
||||||
|
"message": "Priority customer support.",
|
||||||
|
"description": "Priority customer support."
|
||||||
|
},
|
||||||
|
"ppremiumSignUpFuture": {
|
||||||
|
"message": "All future premium features. More coming soon!",
|
||||||
|
"description": "All future premium features. More coming soon!"
|
||||||
|
},
|
||||||
|
"premiumPurchase": {
|
||||||
|
"message": "Purchase Premium",
|
||||||
|
"description": "Purchase Premium"
|
||||||
|
},
|
||||||
|
"premiumPurchaseAlert": {
|
||||||
|
"message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?",
|
||||||
|
"description": "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!",
|
||||||
|
"description": "You are a premium member!"
|
||||||
|
},
|
||||||
|
"premiumCurrentMemberThanks": {
|
||||||
|
"message": "Thank you for supporting bitwarden.",
|
||||||
|
"description": "Thank you for supporting bitwarden."
|
||||||
|
},
|
||||||
|
"premiumPrice": {
|
||||||
|
"message": "All for just %price% /year!",
|
||||||
|
"description": "All for just %price% /year!"
|
||||||
|
},
|
||||||
|
"refreshComplete": {
|
||||||
|
"message": "Refresh complete",
|
||||||
|
"description": "Refresh complete"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,13 @@
|
|||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
|
.state('premium', {
|
||||||
|
url: '/premium',
|
||||||
|
templateUrl: 'app/settings/views/settingsPremium.html',
|
||||||
|
controller: 'settingsPremiumController',
|
||||||
|
data: { authorize: true },
|
||||||
|
params: { animation: null }
|
||||||
|
})
|
||||||
|
|
||||||
.state('folders', {
|
.state('folders', {
|
||||||
url: '/folders',
|
url: '/folders',
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="home-page">
|
<div class="home-page">
|
||||||
<img src="../../../../images/logo@3x.png" alt="bitwarden" />
|
<img src="../../../../images/logo@3x.png" alt="bitwarden" />
|
||||||
<p>{{i18n.loginOrCreateNewAccount}}</p>
|
<p>{{i18n.loginOrCreateNewAccount}}</p>
|
||||||
<div class="buttons">
|
<div class="bottom-buttons">
|
||||||
<a class="btn btn-lg btn-primary btn-block" ui-sref="register({animation: 'in-slide-up'})"
|
<a class="btn btn-lg btn-primary btn-block" ui-sref="register({animation: 'in-slide-up'})"
|
||||||
analytics-on="click" analytics-event="Clicked Create Account">
|
analytics-on="click" analytics-event="Clicked Create Account">
|
||||||
<b>{{i18n.createAccount}}</b>
|
<b>{{i18n.createAccount}}</b>
|
||||||
|
50
src/popup/app/settings/settingsPremiumController.js
Normal file
50
src/popup/app/settings/settingsPremiumController.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
angular
|
||||||
|
.module('bit.settings')
|
||||||
|
|
||||||
|
.controller('settingsPremiumController', function ($scope, i18nService, tokenService, apiService, toastr, SweetAlert,
|
||||||
|
$analytics, $timeout) {
|
||||||
|
$scope.i18n = i18nService;
|
||||||
|
$scope.isPremium = tokenService.getPremium();
|
||||||
|
$scope.price = '$10';
|
||||||
|
|
||||||
|
$scope.refresh = function () {
|
||||||
|
apiService.refreshIdentityToken(function () {
|
||||||
|
toastr.success(i18nService.refreshComplete);
|
||||||
|
$timeout(function () {
|
||||||
|
$scope.isPremium = tokenService.getPremium();
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
toastr.error(i18nService.errorsOccurred);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.purchase = function () {
|
||||||
|
SweetAlert.swal({
|
||||||
|
title: i18nService.premiumPurchase,
|
||||||
|
text: i18nService.premiumPurchaseAlert,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: i18nService.yes,
|
||||||
|
cancelButtonText: i18nService.cancel
|
||||||
|
}, function (confirmed) {
|
||||||
|
$analytics.eventTrack('Clicked Purchase Premium');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.manage = function () {
|
||||||
|
SweetAlert.swal({
|
||||||
|
title: i18nService.premiumManage,
|
||||||
|
text: i18nService.premiumManageAlert,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: i18nService.yes,
|
||||||
|
cancelButtonText: i18nService.cancel
|
||||||
|
}, function (confirmed) {
|
||||||
|
$analytics.eventTrack('Clicked Manage Membership');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=manage' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -38,6 +38,10 @@
|
|||||||
{{i18n.account}}
|
{{i18n.account}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
|
<a class="list-section-item text-primary" ui-sref="premium({animation: 'in-slide-left'})">
|
||||||
|
<i class="fa fa-star fa-fw"></i> <b>{{i18n.premiumMembership}}</b>
|
||||||
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
|
</a>
|
||||||
<a class="list-section-item" href="" ng-click="changePassword()">
|
<a class="list-section-item" href="" ng-click="changePassword()">
|
||||||
{{i18n.changeMasterPassword}}
|
{{i18n.changeMasterPassword}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
|
54
src/popup/app/settings/views/settingsPremium.html
Normal file
54
src/popup/app/settings/views/settingsPremium.html
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<div class="header">
|
||||||
|
<div class="left">
|
||||||
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.settings}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{i18n.premiumMembership}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="premium-page">
|
||||||
|
<div ng-if="!isPremium">
|
||||||
|
<p class="text-center lead">{{i18n.premiumNotCurrentMember}}</p>
|
||||||
|
<p>{{i18n.premiumSignUpAndGet}}</p>
|
||||||
|
<ul class="fa-ul">
|
||||||
|
<li>
|
||||||
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
|
{{i18n.ppremiumSignUpStorage}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
|
{{i18n.ppremiumSignUpTwoStep}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
|
T{{i18n.ppremiumSignUpTotp}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
|
{{i18n.ppremiumSignUpSupport}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
|
{{i18n.ppremiumSignUpFuture}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="text-center lead">{{i18n.premiumPrice.replace('%price%', price)}}</p>
|
||||||
|
<div class="bottom-buttons">
|
||||||
|
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="purchase()">
|
||||||
|
<b>{{i18n.premiumPurchase}}</b>
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-lg btn-link btn-block" href="#" stop-click ng-click="refresh()">
|
||||||
|
{{i18n.premiumRefresh}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ng-if="isPremium">
|
||||||
|
<p class="text-center lead">{{i18n.premiumCurrentMember}}</p>
|
||||||
|
<p class="text-center">{{i18n.premiumCurrentMemberThanks}}</p>
|
||||||
|
<div class="bottom-buttons">
|
||||||
|
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="manage()">
|
||||||
|
<b>{{i18n.premiumManage}}</b>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -84,6 +84,7 @@
|
|||||||
<script src="app/settings/settingsFoldersController.js"></script>
|
<script src="app/settings/settingsFoldersController.js"></script>
|
||||||
<script src="app/settings/settingsAddFolderController.js"></script>
|
<script src="app/settings/settingsAddFolderController.js"></script>
|
||||||
<script src="app/settings/settingsEditFolderController.js"></script>
|
<script src="app/settings/settingsEditFolderController.js"></script>
|
||||||
|
<script src="app/settings/settingsPremiumController.js"></script>
|
||||||
|
|
||||||
<script src="app/tools/toolsModule.js"></script>
|
<script src="app/tools/toolsModule.js"></script>
|
||||||
<script src="app/tools/toolsController.js"></script>
|
<script src="app/tools/toolsController.js"></script>
|
||||||
|
@ -37,22 +37,6 @@
|
|||||||
p {
|
p {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
font-size: @font-size-base;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-link {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.splash-page {
|
.splash-page {
|
||||||
@ -74,4 +58,36 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom-buttons {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
font-size: @font-size-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.premium-page {
|
||||||
|
padding: 60px 20px 20px;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
p.lead {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -56,6 +56,18 @@ function initApiService() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ApiService.prototype.refreshIdentityToken = function (success, error) {
|
||||||
|
refreshToken(this, function () {
|
||||||
|
success();
|
||||||
|
}, function (jqXHR) {
|
||||||
|
if (jqXHR) {
|
||||||
|
handleError(error, jqXHR, false, self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
error();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Two Factor APIs
|
// Two Factor APIs
|
||||||
|
|
||||||
ApiService.prototype.postTwoFactorEmail = function (request, success, error) {
|
ApiService.prototype.postTwoFactorEmail = function (request, success, error) {
|
||||||
@ -511,23 +523,10 @@ function initApiService() {
|
|||||||
});
|
});
|
||||||
} // handle token refresh
|
} // handle token refresh
|
||||||
else if (self.tokenService.tokenNeedsRefresh()) {
|
else if (self.tokenService.tokenNeedsRefresh()) {
|
||||||
self.tokenService.getRefreshToken(function (refreshToken) {
|
refreshToken(self, function (accessToken) {
|
||||||
if (!refreshToken || refreshToken === '') {
|
resolveTokenQs(accessToken, self, deferred);
|
||||||
deferred.reject();
|
}, function (err) {
|
||||||
return;
|
deferred.reject(err);
|
||||||
}
|
|
||||||
|
|
||||||
postConnectToken(self, {
|
|
||||||
grant_type: 'refresh_token',
|
|
||||||
client_id: 'browser',
|
|
||||||
refresh_token: refreshToken
|
|
||||||
}, function (token) {
|
|
||||||
self.tokenService.setTokens(token.accessToken, token.refreshToken, function () {
|
|
||||||
resolveTokenQs(token.accessToken, self, deferred);
|
|
||||||
});
|
|
||||||
}, function (jqXHR) {
|
|
||||||
deferred.reject(jqXHR);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -543,6 +542,27 @@ function initApiService() {
|
|||||||
return deferred.promise
|
return deferred.promise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshToken(self, success, error) {
|
||||||
|
self.tokenService.getRefreshToken(function (refreshToken) {
|
||||||
|
if (!refreshToken || refreshToken === '') {
|
||||||
|
error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postConnectToken(self, {
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
client_id: 'browser',
|
||||||
|
refresh_token: refreshToken
|
||||||
|
}, function (token) {
|
||||||
|
self.tokenService.setTokens(token.accessToken, token.refreshToken, function () {
|
||||||
|
success(token.accessToken);
|
||||||
|
});
|
||||||
|
}, function (jqXHR) {
|
||||||
|
error(jqXHR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function resolveTokenQs(token, self, deferred) {
|
function resolveTokenQs(token, self, deferred) {
|
||||||
var issuer = self.tokenService.getIssuer();
|
var issuer = self.tokenService.getIssuer();
|
||||||
if (issuer === self.baseUrl) {
|
if (issuer === self.baseUrl) {
|
||||||
|
Loading…
Reference in New Issue
Block a user