From 3a8f14900859e6db032898dcb4e32d63f1059389 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 13 Jul 2017 22:33:34 -0400 Subject: [PATCH] premium membership page --- src/_locales/en/messages.json | 68 +++++++++++++++++++ src/popup/app/config.js | 7 ++ src/popup/app/global/home.html | 2 +- .../app/settings/settingsPremiumController.js | 50 ++++++++++++++ src/popup/app/settings/views/settings.html | 4 ++ .../app/settings/views/settingsPremium.html | 54 +++++++++++++++ src/popup/index.html | 1 + src/popup/less/pages.less | 50 +++++++++----- src/services/apiService.js | 54 ++++++++++----- 9 files changed, 255 insertions(+), 35 deletions(-) create mode 100644 src/popup/app/settings/settingsPremiumController.js create mode 100644 src/popup/app/settings/views/settingsPremium.html diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 739fbb2d1df..905dba6522e 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -762,5 +762,73 @@ "updateKey": { "message": "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" } } diff --git a/src/popup/app/config.js b/src/popup/app/config.js index 906a67d84bf..7fbabf693b0 100644 --- a/src/popup/app/config.js +++ b/src/popup/app/config.js @@ -198,6 +198,13 @@ data: { authorize: true }, params: { animation: null } }) + .state('premium', { + url: '/premium', + templateUrl: 'app/settings/views/settingsPremium.html', + controller: 'settingsPremiumController', + data: { authorize: true }, + params: { animation: null } + }) .state('folders', { url: '/folders', diff --git a/src/popup/app/global/home.html b/src/popup/app/global/home.html index 644dbfc8c6b..d9436ccc463 100644 --- a/src/popup/app/global/home.html +++ b/src/popup/app/global/home.html @@ -2,7 +2,7 @@
bitwarden

{{i18n.loginOrCreateNewAccount}}

-
+
{{i18n.createAccount}} diff --git a/src/popup/app/settings/settingsPremiumController.js b/src/popup/app/settings/settingsPremiumController.js new file mode 100644 index 00000000000..71702963053 --- /dev/null +++ b/src/popup/app/settings/settingsPremiumController.js @@ -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' }); + } + }); + }; + }); diff --git a/src/popup/app/settings/views/settings.html b/src/popup/app/settings/views/settings.html index a90bfe09416..c9d348ca8af 100644 --- a/src/popup/app/settings/views/settings.html +++ b/src/popup/app/settings/views/settings.html @@ -38,6 +38,10 @@ {{i18n.account}}
+ + {{i18n.premiumMembership}} + + {{i18n.changeMasterPassword}} diff --git a/src/popup/app/settings/views/settingsPremium.html b/src/popup/app/settings/views/settingsPremium.html new file mode 100644 index 00000000000..6296c91d0b7 --- /dev/null +++ b/src/popup/app/settings/views/settingsPremium.html @@ -0,0 +1,54 @@ +
+ +
{{i18n.premiumMembership}}
+
+
+
+
+

{{i18n.premiumNotCurrentMember}}

+

{{i18n.premiumSignUpAndGet}}

+
    +
  • + + {{i18n.ppremiumSignUpStorage}} +
  • +
  • + + {{i18n.ppremiumSignUpTwoStep}} +
  • +
  • + + T{{i18n.ppremiumSignUpTotp}} +
  • +
  • + + {{i18n.ppremiumSignUpSupport}} +
  • +
  • + + {{i18n.ppremiumSignUpFuture}} +
  • +
+

{{i18n.premiumPrice.replace('%price%', price)}}

+ +
+
+

{{i18n.premiumCurrentMember}}

+

{{i18n.premiumCurrentMemberThanks}}

+ +
+
+
diff --git a/src/popup/index.html b/src/popup/index.html index 6669c5c85da..46c44cc3c85 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -84,6 +84,7 @@ + diff --git a/src/popup/less/pages.less b/src/popup/less/pages.less index e5ca95ec57b..8aa0dde267b 100644 --- a/src/popup/less/pages.less +++ b/src/popup/less/pages.less @@ -37,22 +37,6 @@ p { 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 { @@ -74,4 +58,36 @@ margin-left: auto; margin-right: auto; } -} \ No newline at end of file +} + +.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; + } +} diff --git a/src/services/apiService.js b/src/services/apiService.js index c21ad0ad0da..84acb009a4c 100644 --- a/src/services/apiService.js +++ b/src/services/apiService.js @@ -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 ApiService.prototype.postTwoFactorEmail = function (request, success, error) { @@ -511,23 +523,10 @@ function initApiService() { }); } // handle token refresh else if (self.tokenService.tokenNeedsRefresh()) { - self.tokenService.getRefreshToken(function (refreshToken) { - if (!refreshToken || refreshToken === '') { - deferred.reject(); - return; - } - - 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); - }); + refreshToken(self, function (accessToken) { + resolveTokenQs(accessToken, self, deferred); + }, function (err) { + deferred.reject(err); }); } else { @@ -543,6 +542,27 @@ function initApiService() { 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) { var issuer = self.tokenService.getIssuer(); if (issuer === self.baseUrl) {