From a2b4d07c7059e4667d96674458e8e336cc3f06ed Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 4 Dec 2015 16:27:21 +0000 Subject: [PATCH 01/72] feat: remove beta invite system --- Gruntfile.coffee | 120 +------- app/admin.jade | 271 ------------------ app/admin.js | 204 ------------- app/index.jade | 6 +- app/partials/signup.jade | 7 +- assets/js/controllers/feedback.controller.js | 2 +- .../js/controllers/recoverFunds.controller.js | 1 - assets/js/controllers/signup.controller.js | 22 -- assets/js/services/wallet.service.js | 74 ++--- betakeys-template.MDF | Bin 7168 -> 0 bytes package.json | 13 +- server.js | 251 +--------------- 12 files changed, 32 insertions(+), 939 deletions(-) delete mode 100644 app/admin.jade delete mode 100644 app/admin.js delete mode 100644 betakeys-template.MDF diff --git a/Gruntfile.coffee b/Gruntfile.coffee index b04974dc88..7d3dde9559 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -25,22 +25,11 @@ module.exports = (grunt) -> options: { context : { PRODUCTION: true - BETA: false } }, expand: true src: ['build/index.html'] dest: '' - beta: - options: { - context : { - PRODUCTION: true - BETA: true - } - }, - expand: true - src: ['build/admin.html', 'build/index-beta.html'] - dest: '' concat: options: @@ -96,16 +85,6 @@ module.exports = (grunt) -> dest: "dist/application.min.js" - beta: - src: [ - "build/bower_components/jquery/dist/jquery.js" - "build/bower_components/angular/angular.min.js" - "build/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js" - "build/bower_components/bootstrap/dist/js/bootstrap.min.js" - "app/admin.js" - ] - dest: "dist/beta-admin.js" - coffee: coffee_to_js: options: @@ -140,14 +119,7 @@ module.exports = (grunt) -> "build/css/**/*.css" ], dest: "dist/application.css" - }, - beta: { - src: [ - "build/css/blockchain.css" - "build/css/navigation.css" - ], - dest: "dist/beta-admin.css" - }, + } }, autoprefixer: { @@ -201,8 +173,7 @@ module.exports = (grunt) -> main: files: [ {src: ["beep.wav"], dest: "dist/"} - {src: ["index.html", "index-beta.html"], dest: "dist/", cwd: "build", expand: true} - {src: ["admin.html"], dest: "dist/", cwd: "build", expand: true} + {src: ["index.html"], dest: "dist/", cwd: "build", expand: true} {src: ["img/*"], dest: "dist/", cwd: "build", expand: true} {src: ["locales/*"], dest: "dist/", cwd: "build", expand: true} {src: ["**/*"], dest: "dist/fonts", cwd: "build/fonts", expand: true} @@ -228,15 +199,6 @@ module.exports = (grunt) -> {src: ["*"], dest: "build/fonts", cwd: "assets/fonts/themify", expand: true} ] - beta: - files: [ - {src: ["beta/betaAdminServer.js"], dest: "dist/", cwd: "app", expand: true} - {src: ["beta/package.json"], dest: "dist/", cwd: "app", expand: true} - ] - - beta_index: - src: "build/index.html" - dest: "build/index-beta.html" images: files: [ @@ -280,7 +242,6 @@ module.exports = (grunt) -> options: client: false files: - "build/admin.html": "app/admin.jade" "build/index.html": "app/index.jade" babel: @@ -295,7 +256,7 @@ module.exports = (grunt) -> }] rename: - assets: # Renames all images, fonts, etc and updates application.min.js, application.css and admin.html with their new names. + assets: # Renames all images, fonts, etc and updates application.min.js and application.css with their new names. options: skipIfHashed: true startSymbol: "{{" @@ -322,7 +283,7 @@ module.exports = (grunt) -> publicdir = require("fs").realpathSync("dist") path = require("path") - for referring_file_path in ["dist/application.min.js", "dist/beta-admin.js", "dist/application.css", "dist/beta-admin.css", "dist/admin.html", "dist/index.html", "dist/index-beta.html"] + for referring_file_path in ["dist/application.min.js", "dist/application.css", "dist/index.html"] contents = grunt.file.read(referring_file_path) before = undefined after = undefined @@ -347,7 +308,7 @@ module.exports = (grunt) -> 'dist/beep.wav' ] - html: # Renames application/beta.min.js/css and updates index/admin.html + html: # Renames application.min.js/css and updates index.html options: skipIfHashed: true startSymbol: "{{" @@ -359,7 +320,7 @@ module.exports = (grunt) -> publicdir = require("fs").realpathSync("dist") path = require("path") - for referring_file_path in ["dist/index.html", "dist/index-beta.html", "dist/admin.html"] + for referring_file_path in ["dist/index.html"] contents = grunt.file.read(referring_file_path) before = undefined after = undefined @@ -377,8 +338,6 @@ module.exports = (grunt) -> src: [ 'dist/application.min.js' 'dist/application.css' - 'dist/beta-admin.js' - 'dist/beta-admin.css' ] shell: @@ -390,10 +349,6 @@ module.exports = (grunt) -> command: () -> 'rsync -rz --delete server.js hd-dev@server:' - deploy_beta_to_dev: - command: () -> - 'rsync -rz --delete node_modules/my-wallet-v3-beta-module hd-dev@server:node_modules/' - deploy_static_to_staging: command: () -> 'rsync -rz --delete dist hd-staging@server:' @@ -402,22 +357,6 @@ module.exports = (grunt) -> command: () -> 'rsync -rz --delete server.js hd-staging@server:' - deploy_beta_to_staging: - command: () -> - 'rsync -rz --delete node_modules/my-wallet-v3-beta-module hd-staging@server:node_modules/' - - deploy_static_to_alpha: - command: () -> - 'rsync -rz --delete dist hd-alpha@server:' - - deploy_server_to_alpha: - command: () -> - 'rsync -rz --delete server.js hd-alpha@server:' - - deploy_beta_to_alpha: - command: () -> - 'rsync -rz --delete node_modules/my-wallet-v3-beta-module hd-alpha@server:node_modules/' - deploy_start_dev: command: () -> 'ssh hd-dev@server "./start.sh"' @@ -513,11 +452,6 @@ module.exports = (grunt) -> "watch" ] - grunt.registerTask "dist_beta", [ - "concat:beta" - "concat_css:beta" - ] - # Default task(s). grunt.registerTask "dist", [ "clean" @@ -531,11 +465,8 @@ module.exports = (grunt) -> "concat:application" "concat_css:app" "jade" - "copy:beta_index" "preprocess" "copy:main" - "copy:beta" - "dist_beta" # We don't check beta dependencies against a whitelist "rename:assets" "rename:html" "git_changelog" @@ -550,11 +481,8 @@ module.exports = (grunt) -> "concat:application" "concat_css:app" "jade" - "copy:beta_index" "preprocess" "copy:main" - "copy:beta" - "dist_beta" "rename:assets" "rename:html" ] @@ -570,15 +498,9 @@ module.exports = (grunt) -> "shell:deploy_start_dev" ] - grunt.registerTask "deploy_beta_to_dev", [ - "shell:deploy_beta_to_dev" - "shell:deploy_start_dev" - ] - grunt.registerTask "deploy_to_dev", [ "dist" "shell:deploy_static_to_dev" - "shell:deploy_beta_to_dev" "shell:deploy_server_to_dev" "shell:deploy_start_dev" ] @@ -594,43 +516,13 @@ module.exports = (grunt) -> "shell:deploy_start_staging" ] - grunt.registerTask "deploy_beta_to_staging", [ - "shell:deploy_beta_to_staging" - "shell:deploy_start_staging" - ] - grunt.registerTask "deploy_to_staging", [ "dist" "shell:deploy_static_to_staging" - "shell:deploy_beta_to_staging" "shell:deploy_server_to_staging" "shell:deploy_start_staging" ] - grunt.registerTask "deploy_static_to_alpha", [ - "dist" - "shell:deploy_static_to_alpha" - "shell:deploy_start_alpha" - ] - - grunt.registerTask "deploy_server_to_alpha", [ - "shell:deploy_server_to_alpha" - "shell:deploy_start_alpha" - ] - - grunt.registerTask "deploy_beta_to_alpha", [ - "shell:deploy_beta_to_alpha" - "shell:deploy_start_alpha" - ] - - grunt.registerTask "deploy_to_alpha", [ - "dist" - "shell:deploy_static_to_alpha" - "shell:deploy_beta_to_alpha" - "shell:deploy_server_to_alpha" - "shell:deploy_start_alpha" - ] - grunt.registerTask "check_translations", [ "shell:check_translations" ] diff --git a/app/admin.jade b/app/admin.jade deleted file mode 100644 index ee5a33aec0..0000000000 --- a/app/admin.jade +++ /dev/null @@ -1,271 +0,0 @@ -doctype -html.overflow-scroll(ng-app="AdminInterface" lang='en') - head - meta(charset='utf-8') - title Beta Admin - - - script(src='../bower_components/jquery/dist/jquery.js') - script(src='../bower_components/angular/angular.js') - script(src="../bower_components/bootstrap/dist/js/bootstrap.min.js") - script(src='../bower_components/angular-bootstrap/ui-bootstrap-tpls.js') - - link(rel='stylesheet', href='../build/css/blockchain.css') - link(rel='stylesheet', href='../build/css/navigation.css') - - script(src='../app/admin.js') - - - - - body.overflow-scroll(ng-controller="AdminCtrl") - .navbar.navbar-default.navbar-inverse.navbar-unauth(role='navigation') - .container-fluid - .navbar-header - a.navbar-brand - img#logo(src="../img/logo-updated.png" alt="Blockchain") - .navbar-collapse.collapse - ul.nav.navbar-nav.navbar-right - li.item - a(href="/") Home - li.item - a(href="/") About - li.item.active - a(href="/") Wallet - li.item - a(href="/") Explorer - li.item - a(href="/") Merchant - li.item - a(href="/") Support - - div.container.login-form.mbl - h1 V3 Beta Admin Interface - .form-group.col-xs-6 - form.form-inline - button.btn.btn-default(ng-click="openModal('assignKeyModal.html', 'AssignKeyCtrl')") + New Key - button.btn.btn-default(ng-click="openModal('capturePageModal.html', 'CapturePageCtrl')") Capture Page - button.btn.btn-default(ng-click="openModal('activationModal.html', 'ActivateKeysCtrl')") - span.glyphicon.glyphicon-flash - span  Activate Keys - button.btn.btn-default(ng-click="retrieveCSV()") CSV - .form-group.col-xs-6 - form.form-inline.pull-right(name="searchForm" ng-submit="load()" novalidate) - input.form-control(ng-model="search.text" type="text" placeholder="Search" required) - select.form-control(ng-model="search.filter" ng-options="f for f in filters" required) - button.btn.btn-default(ng-disabled="searchForm.$invalid" type="submit") - span(class="glyphicon glyphicon-arrow-right") - .form-group.col-xs-6.ng-cloak - label {{ walletCount }} Wallets Created - .table-div.ng-cloak - table.table.table-hover.table-condensed - tr - th.pointer(ng-repeat="th in headers" ng-click="sort(th)") {{ th }} - th ios - th android - th edit - th revoke - tr(ng-repeat="item in tableData") - td {{ item.rowid }} - td {{ item.key }} - td {{ item.name }} - td {{ item.email }} - td {{ getDateFromTime(item.lastseen) }} - td {{ getDateFromTime(item.email_opened_at) }} - td {{ getDateFromTime(item.email_link_followed_at) }} - td {{ item.guid }} - td {{ (item.activated) ? 'Activated' : 'Pending' }} - td {{ displayIOSStatus(item) }} - td {{ displayAndroidStatus(item) }} - td - a(ng-click="openModal('editModal.html', 'EditKeyCtrl', item)") edit - td - a(ng-click="revokeKey(item.rowid)") revoke - .flex.flex-justify.pal - button.btn.btn-primary.mrl(ng-click="load(-1)") - | Prev Page - button.btn.btn-primary.mll(ng-click="load(1)") - | Next Page - - // Assign new key modal - script(type="text/ng-template" id="assignKeyModal.html"). - - - - // Edit/Activate key modal - script(type="text/ng-template" id="editModal.html"). - - - - // Capture page settings modal - script(type="text/ng-template" id="capturePageModal.html"). - - - - // Activate many keys modal - script(type="text/ng-template" id="activationModal.html"). - - diff --git a/app/admin.js b/app/admin.js deleted file mode 100644 index 31049c3670..0000000000 --- a/app/admin.js +++ /dev/null @@ -1,204 +0,0 @@ -var admin = angular.module('AdminInterface', ['ui.bootstrap']); - -// Main Controller -admin.controller('AdminCtrl', function ($rootScope, $scope, $http, $modal, InterfaceHelper) { - - $rootScope.percentRequested = 0; - - // Declare scope variables - $scope.tableData = []; - $scope.headers = ['rowid', 'key', 'name', 'email', 'lastseen', 'email opened','link followed', 'guid', 'status']; - $scope.filters = ['name', 'email', 'key', 'guid']; - $scope.search = { text: '', filter: '', sort: 'rowid', order: 'Z' }; - $scope.offset = 0; - - // Convert timestamp to readable - $scope.getDateFromTime = function (time) { - return (time) ? new Date(time).toDateString() : 'Never'; - }; - - $scope.displayIOSStatus = function (item) { - if (item.ios_sent) return 'Sent'; - if (item.ios_approved) return 'Approved'; - if (item.ios) return 'Requested'; - return 'Not Requested'; - }; - - $scope.displayAndroidStatus = function (item) { - if (item.android_sent) return 'Sent'; - if (item.android_approved) return 'Approved'; - if (item.android) return 'Requested'; - return 'Not Requested'; - }; - - $scope.sort = function (header) { - if ($scope.search.sort === header) { - $scope.search.order = ($scope.search.order === 'A') ? 'Z' : 'A'; - } else { - $scope.search.sort = header; - $scope.search.order = 'A'; - } - $scope.load(); - }; - - // API functions - - $scope.revokeKey = function (id) { - InterfaceHelper.callApi('/delete-key', { rowid: id }) - .success($scope.load); - }; - - $scope.getPercent = function () { - $http.get('/percent_requested') - .success(function(res){ $rootScope.percentRequested = res.width; }) - .error(InterfaceHelper.error); - }; - - $scope.setPercent = function (value) { - InterfaceHelper.callApi('/set-percent-requested', {percent:value}) - .success($scope.getPercent); - }; - - $scope.getNumWalletsCreated = function () { - InterfaceHelper.callApi('/wallets-created') - .success(function (data) { - $scope.walletCount = data.count - }); - }; - - $scope.retrieveCSV = function () { - location.assign(InterfaceHelper.getRootUrl() + '/get-csv'); - }; - - $scope.load = function (offset) { - var filter = {}; - $scope.offset = $scope.offset + (offset|0); - if ($scope.offset < 0) $scope.offset = 0; - filter[$scope.search.filter] = $scope.search.text; - InterfaceHelper.callApi('/get-sorted-keys', { - sort: $scope.search.sort, - order: $scope.search.order, - filter: filter, - offset: 100 * $scope.offset - }).success(function (response) { - $scope.tableData = response.data; - }); - }; - - // Modal opening - $scope.openModal = function (tmpl, ctrl, entry) { - $modal.open({ - templateUrl: tmpl, - controller: ctrl, - resolve: { - getPercent: function () { return $scope.getPercent; }, - setPercent: function () { return $scope.setPercent; }, - load: function () { return $scope.load; }, - entry: function () { return entry; } - } - }); - }; - - // Initial data load - $scope.load(); - $scope.getNumWalletsCreated(); -}); - -// Modal Controllers -admin.controller('AssignKeyCtrl', function ($scope, $uibModalInstance, InterfaceHelper, load) { - $scope.fields = { name: '', email: '', guid: '' }; - $scope.assignKey = function (name, email, guid) { - if (guid === '') guid = null; - InterfaceHelper.callApi('/assign-key', {name:name,email:email,guid:guid}) - .success(load); - $uibModalInstance.dismiss(); - }; -}); - -admin.controller('CapturePageCtrl', function ($scope, getPercent, setPercent) { - $scope.setPercent = setPercent; - getPercent(); -}); - -admin.controller('EditKeyCtrl', function ($scope, $uibModalInstance, InterfaceHelper, load, entry) { - $scope.fields = angular.copy(entry); - $scope.submitEdit = function (doActivate) { - var endpoint = (doActivate) ? '/activate-key' : '/update-key'; - var selection = { rowid: entry.rowid }; - var update = InterfaceHelper.compareProperties($scope.fields, entry); - update.activated = doActivate; - InterfaceHelper.callApi(endpoint, {selection: selection, update: update}) - .success(function () { - load(); - $uibModalInstance.dismiss(); - }); - }; - $scope.resendText = 'Resend Invitation Email' - $scope.resending = false; - $scope.resendActivationEmail = function () { - $scope.resending = true; $scope.resendText = 'Sending...'; - InterfaceHelper.callApi('/resend-activation', {key: $scope.fields.key}) - .success(function(res){ - if (res.error) console.error(res.error); - $scope.resendText = res.error ? 'Error' : 'Sent!'; - }); - }; -}); - -admin.controller('ActivateKeysCtrl', function ($scope, InterfaceHelper, load) { - $scope.step = 0; - $scope.numKeys = $scope.numEmails = 0; - $scope.activate = function (min, max) { - $scope.step = 1; - InterfaceHelper.callApi('/activate-all', {min:min||null,max:max||null}) - .success(function (res) { - load(); - if (res.error) $scope.error = res.error; - if (typeof res.data === 'object') { - $scope.numKeys = res.data.count; - $scope.numEmails = res.data.successful; - } - $scope.step = 2; - }); - }; - $scope.resendText = 'Resend Invitation Emails' - $scope.resend = function (min, max) { - $scope.step = 1; - InterfaceHelper.callApi('/resend-many', {min:min||null,max:max||null}) - .success(function(res){ - if (res.error) $scope.error = res.error; - if (typeof res.data === 'object') { - $scope.numKeys = res.data.count; - $scope.numEmails = res.data.successful; - } - $scope.step = 3; - }); - }; -}); - -// Helper Service -admin.factory('InterfaceHelper', function ($http, $httpParamSerializerJQLike) { - var helper = {}; - var rootUrl = '/admin/api'; - helper.error = function (response) { - if (!response || !response.error) return; - console.error(response.error) - }; - helper.callApi = function (endpoint, data) { - return $http.get(rootUrl + endpoint, { - params: data, - paramSerializer: $httpParamSerializerJQLike - }).error(helper.error); - }; - helper.getRootUrl = function () { - return rootUrl; - }; - helper.compareProperties = function (o1, o2) { - var object = {}; - for (p in o1) { - if (o1[p] !== o2[p]) object[p] = o1[p]; - } - return object; - }; - return helper; -}); diff --git a/app/index.jade b/app/index.jade index 16a4154fcc..28d63daf1e 100644 --- a/app/index.jade +++ b/app/index.jade @@ -1,7 +1,6 @@ doctype - - - + + head meta(charset='utf-8') meta(name="viewport",content="width=device-width, initial-scale=1.0, maximum-scale=1") @@ -69,7 +68,6 @@ head script(src='build/js/controllers/claimModal.controller.js') script(src='build/js/controllers/confirmRecoveryPhrase.controller.js') script(src='build/js/controllers/home.controller.js') - script(src='build/js/controllers/feedback.controller.js') script(src='build/js/controllers/firstTime.controller.js') script(src='build/js/controllers/login.controller.js') script(src='build/js/controllers/navigation.controller.js') diff --git a/app/partials/signup.jade b/app/partials/signup.jade index c6050c17c5..67ff731350 100644 --- a/app/partials/signup.jade +++ b/app/partials/signup.jade @@ -1,12 +1,7 @@ div header h2.em-300(translate="NEW_ACCT_WELCOME", ng-show="currentStep == 1") - form.form-horizontal(ng-show="betaCheckError") - .form-group - p - p - p {{ betaCheckError }} - form.form-horizontal(role="form",name="form",novalidate, ng-show="betaCheckSuccess") + form.form-horizontal(role="form",name="form",novalidate) div(ng-switch="currentStep") div(ng-switch-when="1") .security-red.mbl.em-400.flex-center diff --git a/assets/js/controllers/feedback.controller.js b/assets/js/controllers/feedback.controller.js index a142ca687f..6684bfb307 100644 --- a/assets/js/controllers/feedback.controller.js +++ b/assets/js/controllers/feedback.controller.js @@ -15,7 +15,7 @@ function FeedbackCtrl($scope, $log, $state, $http) { 'fullname': $scope.fullname, 'email': $scope.email }; - $http.post('/feedback', form).success(data => { + $http.post('https://blockchain.info/v3-feedback', form).success(data => { $scope.formStage = (data.success) ? 2 : 3; }).error(() => { $scope.formStage = 3; diff --git a/assets/js/controllers/recoverFunds.controller.js b/assets/js/controllers/recoverFunds.controller.js index 12cc2480ee..fb08dba205 100644 --- a/assets/js/controllers/recoverFunds.controller.js +++ b/assets/js/controllers/recoverFunds.controller.js @@ -17,7 +17,6 @@ function RecoverFundsCtrl($scope, $rootScope, $state, $timeout, $translate, Wall $scope.working = true; const success = (wallet) => { - $rootScope.beta = false; $scope.working = false; $scope.nextStep(); $rootScope.$safeApply(); diff --git a/assets/js/controllers/signup.controller.js b/assets/js/controllers/signup.controller.js index f9a7a7a434..1ee3f988ae 100644 --- a/assets/js/controllers/signup.controller.js +++ b/assets/js/controllers/signup.controller.js @@ -32,28 +32,6 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa acceptedAgreement: false }; - $scope.betaCheckSuccess = false; - - - // If BETA=1 is set in .env then in index.html/jade $rootScope.beta is set. - // The following checks are not ideal as they can be bypassed with some creative Javascript commands. - if ($rootScope.beta) { - // Check if we allow signups at the moment: - $http.post("/check_beta", {}).success((data) => { - if (data.open) { - $scope.betaCheckSuccess = true; - } else { - if (data.error && data.error.message) { - $scope.betaCheckError = data.error.message; - } - } - }).error(() => { - $scope.betaCheckError = "There is a problem with our alpha wallet access control system, please try again later."; - }); - } else { - $scope.betaCheckSuccess = true; - } - $scope.showAgreement = () => { const modalInstance = $uibModal.open({ templateUrl: "partials/alpha-agreement.jade", diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 86d79c92fe..12b0d9fea3 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -178,45 +178,22 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.applyIfNeeded(); }; - let betaCheckFinished = () => { - $window.root = 'https://blockchain.info/'; - wallet.my.login( - uid, - null, // sharedKey - password, - two_factor_code, - didLogin, - needsTwoFactorCode, - wrongTwoFactorCode, - authorizationRequired, - loginError, - () => {}, // fetchSuccess - () => {}, // decryptSucces - () => {} // buildHDSucces - ); - currency.fetchExchangeRate(); - }; - - // If BETA=1 is set in .env then in index.html/jade $rootScope.beta is set. - if ($rootScope.beta) { - $http.post('/check_guid_for_beta_key', { - guid: uid - }).success((data) => { - if (data.verified) { - betaCheckFinished(); - } else { - if (data.error && data.error.message) { - Alerts.displayError(data.error.message); - } - errorCallback(); - } - }).error(() => { - Alerts.displayError('Unable to verify your wallet UID.'); - errorCallback(); - }); - } else { - betaCheckFinished(); - } + $window.root = 'https://blockchain.info/'; + wallet.my.login( + uid, + null, // sharedKey + password, + two_factor_code, + didLogin, + needsTwoFactorCode, + wrongTwoFactorCode, + authorizationRequired, + loginError, + () => {}, // fetchSuccess + () => {}, // decryptSucces + () => {} // buildHDSucces + ); + currency.fetchExchangeRate(); }; wallet.upgrade = (successCallback, cancelSecondPasswordCallback) => { @@ -308,24 +285,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB Alerts.displayError('Unable to login to new wallet'); }; - if ($rootScope.beta) { - $http.post('/register_guid', { - guid: uid, - email: email - }).success((data) => { - if (data.success) { - wallet.login(uid, password, null, null, loginSuccess, loginError); - } else { - if (data.error && data.error.message) { - Alerts.displayError(data.error.message); - } - } - }).error(() => { - Alerts.displayWarning('Unable to associate your new wallet with your invite code. Please try to login using your UID ' + uid + ' or register again.', true); - }); - } else { - wallet.login(uid, password, null, null, loginSuccess, loginError); - } + wallet.login(uid, password, null, null, loginSuccess, loginError); }; let error = (error) => { diff --git a/betakeys-template.MDF b/betakeys-template.MDF deleted file mode 100644 index 407d5614da0a97021966bb055a362e63e10b9765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7168 zcmeHML1+|L7@jvT+0Cv-eJBLMz~IG%Brvndb~YDvwbKxiour$UC7{f{nRycjCo{>+ zBvCwxMQHVq9!j9l-n>=NLkn#$J$Ug@dMv$(h%H_`gn&2w-)2{LlZYM+O=Jch@4fH; z-~a#f?fco~+nFyX-Iz&E5YE$B8WuVPgoLw_BnYV~K@if7fnh!cqXS0sLCwXxXNWQM zEY*!(2pA0sXbAlvej#e__TS?cUrhH544@lFV`_SAg?b)~Bf|M!#SEA689a!O>1)xQugMXB zMn~lls^mlUg#;PyF@E&!n3lR1|YKXH!&q zAc`-^L4a(>&Z`CX87kd>Wh{~d06}os8Kw?E&*&=20%WR+GMrZ#Ku`YpiDWorS_)Mh z6QJKuPmnHvm~7_?dB|A0KmG4rk_ITRLNHlYP-(3;ktHdBsFSzU5eqV|U0mHDodBuS zDHKK(6`-noiF9yC&KG1>fFi#A_48u{^MjG?@Bz@h>~9$n3!Sk1{^=hXw7L9l;+y3A ze{Wyuq`HKG-DH6GKf>z*UdNAiGfxsbVIW~(cNtK-Hg%u(=s5@Ar$<*)gW>(()aKBO zeXPyxF14G{_rL!4)gch-KED3vv37~C_CXi1mgs+FuJ`?)`d_1MM3_@B5+$%f$R|1_ R{jb$qF46z%|F&fNUjrZW3(^1p diff --git a/package.json b/package.json index 6a4b2df320..7e653c26d8 100644 --- a/package.json +++ b/package.json @@ -4,16 +4,11 @@ "version": "0.0.0", "description": "AngularJS front-end to My-Wallet-V3.", "dependencies": { - "basic-auth": "^1.0.3", - "body-parser": "^1.14.1", - "compression": "^1.6.0", - "errorhandler": "^1.4.2", - "express": "^4.13.3", - "method-override": "^2.3.5", - "morgan": "^1.6.1", - "request": "^2.55.0" }, "devDependencies": { + "errorhandler": "^1.4.2", + "express": "^4.13.3", + "request": "^2.55.0", "bower": "^1.3.1", "chalk": "^0.5.1", "coffee-script": "^1.9.1", @@ -85,7 +80,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/blockchain/My-Wallet-HD-Frontend" + "url": "https://github.com/blockchain/My-Wallet-V3-Frontend" }, "keywords": [ "blockchain" diff --git a/server.js b/server.js index 1eae668bc5..683996fcd5 100644 --- a/server.js +++ b/server.js @@ -2,30 +2,21 @@ var express = require('express') , compression = require('compression') - , bodyParser = require('body-parser') - , morgan = require('morgan') , errorhandler = require('errorhandler') - , methodOverride = require('method-override') , ejs = require('ejs') - , auth = require('basic-auth') , request = require('request') , path = require('path') - , fs = require('fs'); loadEnv('.env'); var port = process.env.PORT || 8080 , dist = parseInt(process.env.DIST) === 1 - , beta = parseInt(process.env.BETA) === 1 , origins = (process.env.BLOCKCHAIN || '').split(' ') , whitelist = (process.env.IP_WHITELIST || '').split(' ') - , admin = { user: 'blockchain', pass: process.env.ADMIN_PASSWORD }; // App configuration var app = express(); -app.use(morgan('combined')); -app.use(bodyParser.json()); app.use(compression({ threshold: 512 })); @@ -54,8 +45,6 @@ app.use(function (req, res, next) { if (whitelist != '' && whitelist.indexOf(ip.split(', ')[0]) < 0) { console.log(ip); res.status(403).send('I\'m sorry Dave, I can\'t let you do that.'); - } else if (dist && beta) { - res.render('index-beta.html'); } else if (dist) { res.render('index.html'); } else { @@ -64,9 +53,7 @@ app.use(function (req, res, next) { return; } - if (req.url.indexOf('beta_key')) { - res.setHeader('Cache-Control', 'public, max-age=0, no-cache'); - } else if (dist) { + if (dist) { res.setHeader('Cache-Control', 'public, max-age=31557600'); } else { res.setHeader('Cache-Control', 'public, max-age=0, no-cache'); @@ -81,221 +68,11 @@ if (dist) { app.set('views', path.join(__dirname, 'dist')); } else { console.log('Development mode: multiple javascript files, not cached'); - app.use(methodOverride()); app.use(express.static(__dirname)); app.set('view engine', 'jade'); app.set('views', __dirname); } -// Routing -if (beta) { - // Beta system enabled - console.log('Enabling beta invite system'); - var v3Beta = require('my-wallet-v3-beta-module')(path.join(__dirname, process.env.BETA_DATABASE_PATH || '')); - - app.get(/^\/key-.{8}$/, function (req, res) { - var key = req.path.split(path.sep)[1].split('-')[1]; - v3Beta.emailLinkFollowed({key:key}); - res.cookie('key', '"' + key + '"'); - res.redirect('/'); - }); - - // *.blockchain.info/logo-key-{key} redirects to image on Amazon - app.get(/^\/key-logo-.{8}$/, function (req, res) { - var key = req.path.split(path.sep)[1].split('-')[2]; - v3Beta.emailOpened({key:key}); - res.redirect('https://s3.amazonaws.com/blockchainwallet/bc-logo-family.png'); - }); - - app.post('/check_beta', function (req, res) { - v3Beta.verifyLimit(function (err, open) { - if (err) res.json({ open: false, error: {message: err} }); - else res.json({ open: open }); - }); - }); - - app.post('/verify_wallet_created', function (req, res) { - v3Beta.verifyLimit(function (err, open) { - if (err) res.json({ success: false, error: {message: err} }); - else res.json({ success: open }); - }); - }); - - app.post('/check_guid_for_beta_key', function (req, res) { - v3Beta.isGuidAssociatedWithBetaKey(req.body.guid, function (err, verified) { - if (err) { - res.json({ - verified: false, - error: { - message: 'There was a problem verifying your access permission. Please try again later.', - err: err - } - }); - } else if (verified) { - res.json({ verified: true }); - } else { - res.json({ - verified: false, - error: { message: 'Please create a new Alpha wallet first.' } - }); - } - }); - }); - - app.post('/register_guid', function (req, res) { - v3Beta.verifyLimit(function (err, open) { - if (err) { - res.json({ success: false, error: {message: err} }); - } else { - v3Beta.assignKey('', req.body.email, req.body.guid, function (key) { - v3Beta.newWalletCreated(key, function () { - res.json({ success: true }); - }); - }); - } - }); - }); - - app.post('/whitelist_guid', function (req, res) { - if (req.body == null) { - res.json({ error: 'no request body' }); - } else if (req.body.secret !== process.env.WHITELIST_SECRET) { - res.json({ error: 'incorrect secret' }); - } else if (req.body.guid == null) { - res.json({ error: 'missing request body guid parameter' }); - } else { - var name = req.body.name || 'Mobile Tester'; - v3Beta.assignKey(name, req.body.email, req.body.guid, function (err, key) { - res.json({ - error: err, - key: key - }); - }); - } - }); - - app.get('/admin/?', authenticate(admin), function (req, res) { - var adminIndex = dist ? 'admin.html' : 'app/admin.jade'; - res.render(adminIndex); - }); - - app.get("/admin/api/:method", authenticate(admin), function (req, res) { - switch (req.params.method) { - case 'get-all-keys': - v3Beta.getKeys(function (err, data) { - res.send(JSON.stringify(data)); - }); - break; - case 'get-sorted-keys': - v3Beta.getKeys(req.query, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'assign-key': - v3Beta.assignKey(req.query.name, req.query.email, req.query.guid, function (err, key) { - res.json({ - error: err, - key: key - }); - }); - break; - case 'delete-key': - v3Beta.deleteKey(req.query, function (err) { - res.json({ error: err }); - }); - break; - case 'update-key': - v3Beta.updateKey(req.query.selection, req.query.update, function (err) { - res.json({ error: err }); - }); - break; - case 'activate-key': - v3Beta.activateKey(req.query.selection, req.query.update, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'remind-email': - v3Beta.remindEmail(req.query.key, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'activate-all': - var range = [req.query.min || 0, req.query.max || 100000]; - v3Beta.activateAll(range, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'remind-all': - var range = [req.query.min || 0, req.query.max || 100000]; - v3Beta.remindAll(range, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'resend-activation': - v3Beta.resendActivationEmail(req.query.key, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'resend-many': - var range = [req.query.min || 0, req.query.max || 100000]; - v3Beta.resendMany(range, function (err, data) { - res.json({ - error: err, - data: data - }); - }); - break; - case 'wallets-created': - v3Beta.fetchNumWalletsCreated(function (err, count) { - res.json({ - error: err, - count: count - }); - }); - break; - case 'get-csv': - v3Beta.fetchCSV({}, function (err, csv) { - fs.writeFileSync('tmp.csv', csv); - res.download('tmp.csv', 'emails.csv', function () { - fs.unlink('tmp.csv'); - }); - }); - break; - case 'set-percent-requested': - var percent = parseInt(req.query.percent) - , isNumber = !isNaN(percent); - if (isNumber) process.env.PERCENT_REQUESTED = percent; - res.json({ - success: Boolean(isNumber) - }); - break; - default: - res.status(400).json({ - error: { message: 'Unknown request method' }, - success: false - }); - } - }); -} - app.get('/verify-email', function (req, res) { request.get('https://blockchain.info/wallet' + req.originalUrl); res.cookie('email-verified', true); @@ -324,18 +101,6 @@ app.get('/authorize-approve', function (req, res) { res.send(approveHTML); }); -app.post('/feedback', function (req, res) { - var jira = 'https://blockchain.atlassian.net/rest/collectors/1.0/template/feedback/e6ce4d72' - , headers = { 'X-Atlassian-Token': 'nocheck' }; - request.post({ - url: jira, - headers: headers, - form: req.body - }, function (err, httpResponse, body) { - res.json({ success: !(err != null) }); - }); -}); - app.get('/unsubscribe', function (req, res) { request.get('https://blockchain.info/wallet' + req.originalUrl); res.redirect('/'); @@ -366,20 +131,6 @@ function allowOrigins(origins) { }; } -function authenticate(options) { - return function (req, res, next) { - var credentials = auth(req) - , authorized = credentials && - credentials.name === options.user && - credentials.pass === options.pass; - authorized ? next() : issueChallenge(); - function issueChallenge() { - res.setHeader('WWW-Authenticate', 'Basic realm="blockchain-wallet-v3"'); - res.sendStatus(401); - } - }; -} - // Helper functions function loadEnv(envFile) { try { From 4e227192e597a7ef18b8d92387e08c9361fb2597 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 11 Dec 2015 16:30:50 +0100 Subject: [PATCH 02/72] refactor(Dependencies): removed unused npm dependencies. Removed unmaintained E2E tests. --- e2e-tests/config.js | 46 ----- e2e-tests/global/login_spec.js | 170 ------------------ e2e-tests/security-center/block_tor_spec.js | 48 ----- e2e-tests/settings/accounts_spec.js | 35 ---- e2e-tests/settings/advanced_spec.js | 35 ---- e2e-tests/settings/my_addresses_spec.js | 35 ---- e2e-tests/settings/my_details_spec.js | 97 ---------- e2e-tests/settings/wallet_recovery_spec.js | 35 ---- e2e-tests/settings/wallet_settings_spec.js | 42 ----- e2e-tests/transactions/transactions_spec.js | 188 -------------------- e2e-tests/util.js | 115 ------------ karma.conf.js | 5 - package.json | 21 +-- server.js | 50 ------ 14 files changed, 1 insertion(+), 921 deletions(-) delete mode 100755 e2e-tests/config.js delete mode 100755 e2e-tests/global/login_spec.js delete mode 100644 e2e-tests/security-center/block_tor_spec.js delete mode 100755 e2e-tests/settings/accounts_spec.js delete mode 100755 e2e-tests/settings/advanced_spec.js delete mode 100755 e2e-tests/settings/my_addresses_spec.js delete mode 100755 e2e-tests/settings/my_details_spec.js delete mode 100755 e2e-tests/settings/wallet_recovery_spec.js delete mode 100755 e2e-tests/settings/wallet_settings_spec.js delete mode 100755 e2e-tests/transactions/transactions_spec.js delete mode 100644 e2e-tests/util.js diff --git a/e2e-tests/config.js b/e2e-tests/config.js deleted file mode 100755 index b5118db996..0000000000 --- a/e2e-tests/config.js +++ /dev/null @@ -1,46 +0,0 @@ -exports.config = { - - // The address of a running selenium server - seleniumAddress: 'http://localhost:4444/wd/hub', - - // Spec patterns are relative to the location of this config - specs: ['**/*_spec.js'], - - suites: { - trans: '**/transactions/*_spec.js', - global: '**/global/*_spec.js', - settings: '**/settings/*_spec.js', - securityCenter: '**/security-center/*_spec.js' - }, - - params: { - login: { - uidfake: 'c5825g04-8ke3-25r1-p103-3g000wr4-123', - pwweak: 'asdf', - pwregular: 'asdfgh123456', - pwnormal: 'asdf!@#$', - email: 'example@example.com', - nums: '1234567890', - chars: '$^*%(^*#$&@', - } - }, - - jasmineNodeOpts: { - showColors: true, - isVerbose: true, - includeStackTrace: true - }, - - onPrepare: function() { - browser.driver.manage().window().setSize(1400, 1200); - }, - - // Capabilities to be passed to the webdriver instance - multiCapabilities: [{ - 'browserName': 'chrome' - // }, { - // 'browserName': 'firefox' - }] - -} - diff --git a/e2e-tests/global/login_spec.js b/e2e-tests/global/login_spec.js deleted file mode 100755 index b8dad79f12..0000000000 --- a/e2e-tests/global/login_spec.js +++ /dev/null @@ -1,170 +0,0 @@ -describe('login-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - // These are log in fields - var loginUIDField = browser.element(by.model('uid')); - var loginPasswordField = browser.element(by.model('password')); - - // These are create login fields - var passwordField = element(by.model('fields.password')); - var confField = element(by.model('fields.confirmation')); - var emailField = element(by.model('fields.email')); - - // These are log in buttons - var loginButton = element(by.id('login')); - - // Clear log in test fields - var clearLoginFields = function (){ - loginUIDField.clear(); - loginPasswordField.clear(); - } - - // Click field, see invalid error, clear email field - var validateInvalid = function(){ - passwordField.click(); - util.shouldContainCSS('p.ng-binding', 'Invalid'); - emailField.clear(); - } - - beforeEach(function() { - - util.getURL(); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - xit('should validate top navigation items', function() { - - browser.findElement(by.id('logo')); - browser.findElement(by.linkText('Home')); - browser.findElement(by.linkText('About')); - browser.findElement(by.linkText('Wallet')); - browser.findElement(by.linkText('Explorer')); - browser.findElement(by.linkText('Merchant')); - browser.findElement(by.linkText('Support')); - - }); - - it('should have login page elements', function() { - - // Find UID label and text field - browser.findElement(by.css('[translate="UID"]')); - browser.findElement(by.model('uid')); - - // Find password label and text field - browser.findElement(by.css('[translate="PASSWORD"]')); - browser.findElement(by.model('password')); - - // Find login and create wallet buttons - browser.findElement(by.id('login')); - browser.findElement(by.css('[ng-click="register()"]')); - - }); - - it('should test password strength', function() { - - util.submitBetaKey(); - - // Test weak password - passwordField.sendKeys(browser.params.login.pwweak); - browser.findElement(by.css('.progress-bar-danger')); - passwordField.clear(); - - // Test regular password - passwordField.sendKeys(browser.params.login.pwregular); - browser.findElement(by.css('.progress-bar-warning')); - passwordField.clear(); - - // Test normal password - passwordField.sendKeys(browser.params.login.pwnormal); - browser.findElement(by.css('.progress-bar-info')); - passwordField.clear(); - - // Test strong password - passwordField.sendKeys(login.pw); - browser.findElement(by.css('.progress-bar-success')); - passwordField.clear(); - - }); - - it('should test password matching', function() { - - // Enter key and click Create Wallet button - util.submitBetaKey(); - - // Enter non-matching passwords - passwordField.sendKeys(login.pw); - confField.sendKeys(browser.params.login.pwweak); - - // Submit and validate error messaging - passwordField.click(); - util.shouldContainCSS('p.ng-binding', 'Does not match'); - - // Clear both fields - passwordField.clear(); - confField.clear(); - - }); - - // Invite key pre-fills email address and disables editing, this test case is disabled - xit('should test email address validation', function() { - - // Enter beta key and click Create Wallet button - util.submitBetaKey(); - - // Test email address with only letters - emailField.sendKeys(browser.params.login.pwweak); - validateInvalid(); - - // Test email address with only numbers - emailField.sendKeys(browser.params.login.nums); - validateInvalid(); - - // Test email address with only special characters - emailField.sendKeys(browser.params.login.chars); - validateInvalid(); - - }); - - it('should test unsuccessful login, invalid UID', function() { - - // Clear log in text fields - clearLoginFields(); - - // Enter bogus wallet identifier and password - loginUIDField.sendKeys(browser.params.login.uidfake); - loginPasswordField.sendKeys(login.pw); - - // Submit and validate error messaging - loginButton.click(); - // TODO This element isn't being found - //util.shouldContainCSS('alert.ng-isolate-scope.alert-danger.alert-dismissable', 'This wallet is not associated with a beta invite key.'); - - }); - - it('should test unsuccessful login, invalid password', function() { - - // Clear log in text fields - clearLoginFields(); - - // Enter valid wallet identifier and incorrect password - loginUIDField.sendKeys(login.uid); - loginPasswordField.sendKeys(browser.params.login.pwweak); - - // Submit and validate error messaging - loginButton.click(); - browser.sleep(3000); - util.shouldContainCSS('.help-block', 'Error Decrypting Wallet. Please check your password is correct.'); - - }); - -}); diff --git a/e2e-tests/security-center/block_tor_spec.js b/e2e-tests/security-center/block_tor_spec.js deleted file mode 100644 index 6ca9065923..0000000000 --- a/e2e-tests/security-center/block_tor_spec.js +++ /dev/null @@ -1,48 +0,0 @@ -describe('block-tor', function() { - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - - beforeEach(function() { - util.getURL(); - util.logIn(); - util.navigateTo("SECURITY"); - browser.sleep(4000); - browser.findElement(by.cssContainingText('.ng-binding', 'Tor Blocked')).isDisplayed().then(function(result) { - if (result) { - util.navigateTo("SETTINGS"); - util.navigateTo("ADVANCED"); - browser.sleep(1000);//wait for animation - browser.findElement(by.css('[translate="DISABLE_BLOCK_TOR"]')).click(); - browser.findElement(by.css('[translate="HOME"]')).click(); - util.navigateTo("SECURITY"); - } - else { - util.navigateTo("SECURITY"); - } - }); - }); - - afterEach(function() { - - browser.refresh(); - - }); - - it('should be able to block tor', function() { - - - browser.findElement(by.css('[translate="ADD_BLOCK_TOR"]')).click(); - browser.findElement(by.css('[translate="BLOCK_TOR_EXPLAIN"]')) - - //enable the button - browser.findElement(by.css('[ng-click="enableBlockTOR()"]')).click(); - - //wait for animation to complete - browser.sleep(2000); - browser.findElement(by.cssContainingText('.ng-binding', 'Tor Blocked')) - }); - -}); - diff --git a/e2e-tests/settings/accounts_spec.js b/e2e-tests/settings/accounts_spec.js deleted file mode 100755 index ae6888f15a..0000000000 --- a/e2e-tests/settings/accounts_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -describe('accounts-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - // Navigate to Settings and validate page - browser.findElement(by.css('[translate="SETTINGS"]')).click(); - browser.findElement(by.css('[translate="WALLET_SETTINGS_EXPLAIN"]')); - - // Navigate to Wallet Settings and validate page - browser.findElement(by.css('[translate="MY_ACCOUNTS"]')).click(); - browser.findElement(by.css('[translate="ACCOUNT_MANAGEMENT_EXPLAIN"]')); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - xit('should have page elements', function() { - - }); - -}); \ No newline at end of file diff --git a/e2e-tests/settings/advanced_spec.js b/e2e-tests/settings/advanced_spec.js deleted file mode 100755 index 31446fa5e7..0000000000 --- a/e2e-tests/settings/advanced_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -describe('advanced-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - // Navigate to Settings and validate page - browser.findElement(by.css('[translate="SETTINGS"]')).click(); - browser.findElement(by.css('[translate="WALLET_SETTINGS_EXPLAIN"]')); - - // Navigate to Wallet Settings and validate page - browser.findElement(by.css('[translate="ADVANCED"]')).click(); - browser.findElement(by.css('[translate="ADVANCED_EXPLAIN"]')); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - xit('should have page elements', function() { - - }); - -}); \ No newline at end of file diff --git a/e2e-tests/settings/my_addresses_spec.js b/e2e-tests/settings/my_addresses_spec.js deleted file mode 100755 index af08e6e9cd..0000000000 --- a/e2e-tests/settings/my_addresses_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -describe('my-addresses-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - // Navigate to Settings and validate page - browser.findElement(by.css('[translate="SETTINGS"]')).click(); - browser.findElement(by.css('[translate="WALLET_SETTINGS_EXPLAIN"]')); - - // Navigate to Wallet Settings and validate page - browser.findElement(by.css('[translate="MY_ADDRESSES"]')).click(); - browser.findElement(by.css('[translate="MY_ADDRESSES_EXPLAIN"]')); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - xit('should have page elements', function() { - - }); - -}); diff --git a/e2e-tests/settings/my_details_spec.js b/e2e-tests/settings/my_details_spec.js deleted file mode 100755 index 03075e0e5d..0000000000 --- a/e2e-tests/settings/my_details_spec.js +++ /dev/null @@ -1,97 +0,0 @@ -describe('my-details-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - // My Details specific variables - var passwordHintCSS = '[translate="PASSWORD_HINT"]'; - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - // Navigate to Settings and validate page - browser.findElement(by.css('[translate="SETTINGS"]')).click(); - browser.findElement(by.css('[translate="WALLET_SETTINGS_EXPLAIN"]')); - - // Navigate to My Details and validate page - browser.findElement(by.css('[translate="PREFERENCES"]')).click(); - - // Click is to bring focus to scrollable portion of page - browser.findElement(by.css('[translate="PREFERENCES_EXPLAIN"]')).click(); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - - it('should have wallet password elements', function() { - - // Scroll to password hint section - util.scrollTo(passwordHintCSS); - - - // Validate password elements - browser.findElement(by.css('[translate="WALLET_PASSWORD"]')); - browser.findElement(by.css('[translate="WALLET_PASSWORD_EXPLAIN"]')); - browser.findElement(by.css('[translate="PASSWORD_SET"]')); - - }); - - it('should have a wallet password modal', function() { - - // Scroll to password hint section - util.scrollTo(passwordHintCSS); - - // Click Change Button - browser.findElement(by.css('[translate="CHANGE"]')).click(); - - // Validate modal open - browser.findElement(by.css('.modal-content')); - - // Validate modal elements - browser.findElement(by.css('[translate="CURRENT_PASSWORD"]')); - browser.findElement(by.css('[translate="NEW_PASSWORD"]')); - browser.findElement(by.css('[translate="CONFIRM_PASSWORD"]')); - - }); - - it('should change the wallet password hint', function() { - - // Bring focus to scrollable portion - browser.findElement(by.css('[translate="UID"]')).click(); - - // Validate password hint elements - browser.findElement(by.css(passwordHintCSS)); - browser.findElement(by.css('[translate="PASSWORD_HINT_EXPLAIN"]')); - - // Scroll to password hint section - util.scrollTo(passwordHintCSS); - - // Set new password hint - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[ng-click="edit()"]')).click(); - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[ng-model="form.newValue"]')).clear(); - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[ng-model="form.newValue"]')).sendKeys('new password hint'); - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[type="submit"]')).click(); - browser.sleep(1000); - util.shouldContainCSS('h2.status', 'new password hint'); - - // Reset old password hint - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[ng-click="edit()"]')).click(); - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[ng-model="form.newValue"]')).clear(); - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[ng-model="form.newValue"]')).sendKeys('original password hint'); - browser.element(by.css('[ng-model="user.passwordHint"]')).element(by.css('[type="submit"]')).click(); - browser.sleep(1000); - util.shouldContainCSS('h2.status', 'original password hint'); - - }); - -}); diff --git a/e2e-tests/settings/wallet_recovery_spec.js b/e2e-tests/settings/wallet_recovery_spec.js deleted file mode 100755 index e472b14a31..0000000000 --- a/e2e-tests/settings/wallet_recovery_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -describe('wallet-recovery-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - // Navigate to Settings and validate page - browser.findElement(by.css('[translate="SETTINGS"]')).click(); - browser.findElement(by.css('[translate="WALLET_SETTINGS_EXPLAIN"]')); - - // Navigate to Wallet Settings and validate page - browser.findElement(by.css('[translate="WALLET_RECOVERY"]')).click(); - browser.findElement(by.css('[translate="WALLET_RECOVERY_EXPLAIN"]')); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - xit('should have page elements', function() { - - }); - -}); \ No newline at end of file diff --git a/e2e-tests/settings/wallet_settings_spec.js b/e2e-tests/settings/wallet_settings_spec.js deleted file mode 100755 index 32e1e077e9..0000000000 --- a/e2e-tests/settings/wallet_settings_spec.js +++ /dev/null @@ -1,42 +0,0 @@ -describe('wallet-settings-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - // Navigate to Settings and validate page - browser.findElement(by.css('[translate="SETTINGS"]')).click(); - browser.findElement(by.css('[translate="WALLET_SETTINGS_EXPLAIN"]')).click(); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - it('should validate wallet language elements', function() { - - browser.findElement(by.css('[translate="LANGUAGE"]')); - browser.findElement(by.css('[translate="LANGUAGE_EXPLAIN"]')); - browser.findElement(by.css('[language="settings.language"]')); - - }); - - it('should change language settings', function() { - - browser.findElement(by.css('[language="settings.language"]')).findElement(by.css('.ui-select-toggle')).click(); - - - }); - -}); diff --git a/e2e-tests/transactions/transactions_spec.js b/e2e-tests/transactions/transactions_spec.js deleted file mode 100755 index 2da8880c27..0000000000 --- a/e2e-tests/transactions/transactions_spec.js +++ /dev/null @@ -1,188 +0,0 @@ -describe('transactions-page', function() { - - // Required JS files - var login = require('../ignore.js'); - var util = require('../util.js'); - - // Account name variables - var account1Name = 'DONT EDIT 1'; - var account2Name = 'DONT EDIT 2'; - var account3Name = 'DONT EDIT 3'; - var account4Name = 'Spending'; - - // Account transaction value variables - var account1TransValue = '0.00087847 BTC'; - var account1TransValueTx = '0.00077847 BTC'; - var account3TransValue = '0.00042116 BTC'; - var account3TransLocationPage = 'h1.ng-binding'; - - // Account date variables - var account1TransDate = 'April 7 @ 03:51 PM'; - var account3TransDate = 'February 18 @ 06:56 PM'; - - - beforeEach(function() { - - util.getURL(); - util.logIn(); - util.validateHome(); - - }); - - afterEach(function() { - - // Refresh to begin test on login page - browser.refresh(); - - }); - - it('should log out via the "Log Out" button', function() { - - // Open Profile drop down menu, click Logout, and dismiss alert - util.logOut(); - - // Protractor waits 5 seconds until 'Logged Out' message dismisses - - // Find UID label and text field - browser.sleep(4000); //Required wait for UID field - browser.findElement(by.css('[translate="UID"]')); - browser.findElement(by.model('uid')); - - // Find password label and text field - browser.findElement(by.css('[translate="PASSWORD"]')); - browser.findElement(by.model('password')); - - // Find login and create wallet buttons - browser.findElement(by.id('login')); - browser.findElement(by.css('[ng-click="register()"]')); - - }); - - it('should validate account names and balances', function() { - - // Open Transactions drawer in left navigation - browser.findElement(by.css('[translate="MY_TRANSACTIONS"]')).click(); - - // Validate all account names - browser.findElement(by.css('[translate="ALL_ACCOUNTS"]')); - util.shouldContainCSS('[ng-repeat="account in accounts()"]', account1Name); - util.shouldContainCSS('[ng-repeat="account in accounts()"]', account2Name); - util.shouldContainCSS('[ng-repeat="account in accounts()"]', account3Name); - util.shouldContainCSS('[ng-repeat="account in accounts()"]', account4Name); - - // Validate two account balances - util.shouldContainCSS('[ng-repeat="account in accounts()"]', account1TransValue); - util.shouldContainCSS('[ng-repeat="account in accounts()"]', account3TransValue); - - }); - - it('should show account balance and transaction history when selecting account name', function() { - - // Open Transactions drawer in left navigation - browser.findElement(by.css('[translate="MY_TRANSACTIONS"]')).click(); - - // Click on 4th account and validate value and date - browser.element.all(by.repeater('account in accounts()')).get(3).click(); - util.shouldContainCSS(account3TransLocationPage, account3TransValue); - util.shouldContainCSS('date', account3TransDate); - - }); - - it('should show transaction details when clicking on date/time of transaction', function() { - - // Open Transactions drawer in left navigation - browser.findElement(by.css('[translate="MY_TRANSACTIONS"]')).click(); - - // Click on 4th account and validate value and date - browser.element.all(by.repeater('account in accounts()')).get(3).click() - browser.sleep(500); // Resolves test from failing intermittently - util.shouldContainCSS(account3TransLocationPage, account3TransValue); - browser.findElement(by.cssContainingText('date', account3TransDate)).click(); - - // Validate transaction details page - browser.sleep(500); // Required for next steps to pass in Chrome - browser.findElement(by.css('[translate="BACK_TO_FEED"]')); - browser.findElement(by.css('[translate="TRANSACTION_DETAILS"]')); - browser.findElement(by.css('[translate="FROM"]')); - browser.findElement(by.css('[translate="TO"]')); - browser.findElement(by.css('[translate="NOTES"]')); - browser.findElement(by.css('[translate="TRANSACTION_COMPLETE"]')); - browser.findElement(by.css('[translate="VALUE_AT_SEND"]')); - util.shouldContainCSS('[btc="transaction.result"]', '$0.10'); - browser.findElement(by.css('[translate="VALUE_NOW"]')); - browser.findElement(by.css('[translate="VERIFY_ON_BCI"]')); - - }); - - it('should return to account balance and transaction history from transaction details', function() { - - // Open Transactions drawer in left navigation - browser.findElement(by.css('[translate="MY_TRANSACTIONS"]')).click(); - - // Click on 4th account in list and view transaction details - browser.element.all(by.repeater('account in accounts()')).get(3).click(); - browser.element.all(by.repeater("transaction in transactions | filter:transactionFilter | orderBy:'-txTime'")).get(0).click(); - - // Return to account details and validate Request/Send buttons - browser.findElement(by.css('h4.back')).click(); - browser.findElement(by.css('[translate="REQUEST"]')); - browser.findElement(by.css('[translate="SEND"]')); - - }); - - it('should show transacted bitcoin address for each listed transaction', function() { - - // Open Transactions drawer in left navigation - browser.findElement(by.css('[translate="MY_TRANSACTIONS"]')).click(); - - // Click on account 'DONT EDIT 1' and validate transaction date - browser.element.all(by.repeater('account in accounts()')).get(1).click(); - browser.sleep(500); - util.shouldContainCSS('date.ng-binding', account1TransDate); - - // Validate address labels within account details - util.shouldContainCSS('.ng-binding', '1FV6M8WSJLRKECvGzXwVZyfRgdVHxZmjrX'); - util.shouldContainCSS('.ng-binding', '1DPrsgPctXtgN1GQcshPJhwEYr7xykWpVJ'); - util.shouldContainCSS('.ng-binding', '1H5Me956D9N39tbq8hFBJiBc6dJbRaAmxe'); - - }); - - it('should auto populate account name on Send/Receive modal', function() { - - // Open Transactions drawer in left navigation - browser.findElement(by.css('[translate="MY_TRANSACTIONS"]')).click(); - - // Click on account 'DONT EDIT 1' and validate transaction date - browser.element.all(by.repeater('account in accounts()')).get(1).click(); - browser.findElement(by.css('[translate="SEND"]')).click(); - - // Validate Send modal details - browser.findElement(by.css('[translate="FROM:"]')); - util.shouldContainCSS('span.ng-binding.ng-scope', account1TransValueTx); - - // Close Send modal, wait for and click Request button - browser.findElement(by.css('[ng-click="close()"]')).click(); - expect(element(by.css('[ng-click="request()"]')).isPresent()).toBe(true); - browser.findElement(by.css('[translate="REQUEST"]')).click(); - - // Validate Receive modal details - browser.findElement(by.css('[translate="RECEIVE_TO"]')); - util.shouldContainCSS('span.ng-binding.ng-scope', account1Name); - browser.findElement(by.css('[ng-click="close()"]')).click(); - - }); - - it('should filter by transaction type', function() { - - util.navigateTo("MY_TRANSACTIONS"); - element.all(by.css('.filter-bar')).all(by.css('[translate="SENT"]')).click(); - browser.sleep(1000); - expect(element.all(by.css('.transaction-feed')).all(by.css('[translate="RECEIVED_BITCOIN_FROM"]')).count()).toEqual(0); - - - element.all(by.css('.filter-bar')).all(by.css('[translate="RECEIVED_BITCOIN_FROM"]')).click(); - expect(element.all(by.css('.transaction-feed')).all(by.css('[translate="MOVE_BITCOIN_TO"]')).count()).toEqual(0); - - }); - -}); diff --git a/e2e-tests/util.js b/e2e-tests/util.js deleted file mode 100644 index bb76ad4403..0000000000 --- a/e2e-tests/util.js +++ /dev/null @@ -1,115 +0,0 @@ -var login = require('./ignore.js'); - -module.exports = { - - getURL: function() { - - // Visit URL and validate page title - // Use ONE of the following two lines to enable tests on staging versus localhost. - browser.driver.get('https://dev.blockchain.info/#/login'); // Dev server - //browser.driver.get('https://staging.blockchain.info/#/login'); // Staging server - //browser.driver.get('https://alpha.blockchain.info/#/login'); // Alpha server - //browser.driver.get('http://local.blockchain.com:8080/#/login'); // Localhost - - expect(browser.getTitle()).toEqual('Blockchain Wallet HD'); - - }, - - logIn: function() { - - // Clear login text fields - element(by.model('uid')).clear(); - element(by.model('password')).clear(); - - // Fill text fields with login credentials, submit - element(by.model('uid')).sendKeys(login.uid); - element(by.model('password')).sendKeys(login.pw); - element(by.id('login')).click(); - - }, - - logOut: function () { - - // Open Profile drop down menu, click Logout, and dismiss alert - element.all(by.css('[ng-click="logout()"]')).first().click(); - browser.sleep(200); - browser.switchTo().alert().accept(); - - }, - - validateHome: function () { - - // Validate account homepage details - browser.sleep(3000); // Required wait for Request and Send button validation - browser.findElement(by.css('[translate="REQUEST"]')); - browser.findElement(by.css('[translate="SEND"]')); - browser.findElement(by.css('.bc-well')); - - }, - - navigateTo: function(section) { - browser.sleep(2000); - browser.findElement(by.css('[translate="' + section + '"]')).click(); - }, - - submitBetaKey: function () { - - var promise = browser.getCurrentUrl(); - promise.then(function(currentURL) { - - // Is this still valid for localhost? - if (currentURL == 'http://local.blockchain.com:8080/#/login') { - console.log('URL is local.blockchain.com:8080'); - - // Click Create Wallet button - browser.findElement(by.css('[translate="CREATE_WALLET"]')).click(); - - } - - else if (currentURL == 'https://dev.blockchain.info/#/login') { - console.log('URL is dev.blockchain.info'); - - // Click invite key link - browser.findElement(by.css('[ng-click="prepareRegister()"]')).click(); - - // Enter beta key and click Create Wallet button - element(by.css('[ng-model="key"]')).sendKeys(login.betaKey); - browser.findElement(by.css('[ng-click="register()"]')).click(); - - } - - else if (currentURL == 'https://alpha.blockchain.info/#/login') { - console.log('URL is dev.blockchain.info'); - - // Click invite key link - browser.findElement(by.css('[ng-click="status.enterkey = !status.enterkey"]')).click(); - - // Enter beta key and click Create Wallet button - element(by.css('[ng-model="key"]')).sendKeys(login.betaKey); - browser.findElement(by.css('[ng-click="register()"]')).click(); - - } - - else { - console.log('Unable to determine server instance') - - } - }) - - }, - - shouldContainCSS: function (selector, text) { - - browser.findElement(by.cssContainingText(selector, text)); - - }, - - scrollTo: function (selector) { - var filter = browser.findElement(by.css(selector)); - var scrollIntoView = function () { - arguments[0].scrollIntoView(); - }; - browser.executeScript(scrollIntoView, filter); - } - -} diff --git a/karma.conf.js b/karma.conf.js index e1e760cda9..fa3d3646b4 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -112,11 +112,6 @@ module.exports = function(karma){ browsers : ['PhantomJS'], - junitReporter : { - outputFile: 'test_out/unit.xml', - suite: 'unit' - }, - reporters: ['progress','osx', 'coverage'], coverageReporter: { diff --git a/package.json b/package.json index 7e653c26d8..41e1c9a189 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,10 @@ "dependencies": { }, "devDependencies": { - "errorhandler": "^1.4.2", "express": "^4.13.3", - "request": "^2.55.0", "bower": "^1.3.1", - "chalk": "^0.5.1", "coffee-script": "^1.9.1", - "compression": "^1.3.0", "ejs": "~0.8.4", - "get-stdin": "^3.0.0", "git-changelog": "^0.1.8", "grunt": "^0.4.5", "grunt-autoprefixer": "^3.0.0", @@ -43,29 +38,15 @@ "jade": "*", "karma": "^0.12.16", "karma-babel-preprocessor": "^5.2.2", - "karma-chrome-launcher": "^0.2.0", "karma-coffee-preprocessor": "^0.2.1", "karma-coverage": "^0.5.1", "karma-jade-preprocessor": "0.0.11", "karma-jasmine": "^0.2.0", - "karma-junit-reporter": "^0.2.2", "karma-ng-jade2js-preprocessor": "^0.1.5", "karma-osx-reporter": "^0.1.0", "karma-phantomjs-launcher": "^0.1.4", - "mkdir": "0.0.2", - "mkdirp": "^0.5.0", - "mocha": "^1.21.5", - "nan": "^1.3.0", "node-env-file": "^0.1.4", - "node-watch": "^0.3.4", - "nodemon": "^1.8.1", - "object-assign": "^1.0.0", - "protractor": "~1.0.0", - "shelljs": "^0.3.0", - "sinon": "^1.10.3", - "spies": "^0.1.6", - "tmp": "0.0.23", - "yargs": "^1.3.2" + "shelljs": "^0.3.0" }, "scripts": { "postinstall": "node_modules/bower/bin/bower install", diff --git a/server.js b/server.js index 683996fcd5..b32da1d740 100644 --- a/server.js +++ b/server.js @@ -1,10 +1,7 @@ 'use strict'; var express = require('express') - , compression = require('compression') - , errorhandler = require('errorhandler') , ejs = require('ejs') - , request = require('request') , path = require('path') loadEnv('.env'); @@ -17,15 +14,6 @@ var port = process.env.PORT || 8080 // App configuration var app = express(); -app.use(compression({ - threshold: 512 -})); - -app.use(errorhandler({ - dumpExceptions: true, - showStack: true -})); - app.use(function (req, res, next) { if (req.url === '/') { var cspHeader = ([ @@ -73,44 +61,6 @@ if (dist) { app.set('views', __dirname); } -app.get('/verify-email', function (req, res) { - request.get('https://blockchain.info/wallet' + req.originalUrl); - res.cookie('email-verified', true); - res.redirect('/'); -}); - -app.get('/authorize-approve', function (req, res) { - var approveHTML = '\ - \n\ - \n\ - \n\ - \n\ - Verifying authorization request\n\ - \n\ - \n\ - '; - res.send(approveHTML); -}); - -app.get('/unsubscribe', function (req, res) { - request.get('https://blockchain.info/wallet' + req.originalUrl); - res.redirect('/'); -}); - -app.get(/^\/.{8}-.{4}-.{4}-.{4}-.{12}$/, function (req, res) { - res.cookie('uid', '"' + req.path.split(path.sep)[1] + '"'); - res.redirect('/'); -}); - app.use(function (req, res) { res.send('

404 Not Found

'); }); From 463482500c01c104e59256505568b61f95c1574f Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 15 Dec 2015 12:34:37 +0000 Subject: [PATCH 03/72] fix(Feedback): use new endpoint --- assets/js/controllers/feedback.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/controllers/feedback.controller.js b/assets/js/controllers/feedback.controller.js index 6684bfb307..a142ca687f 100644 --- a/assets/js/controllers/feedback.controller.js +++ b/assets/js/controllers/feedback.controller.js @@ -15,7 +15,7 @@ function FeedbackCtrl($scope, $log, $state, $http) { 'fullname': $scope.fullname, 'email': $scope.email }; - $http.post('https://blockchain.info/v3-feedback', form).success(data => { + $http.post('/feedback', form).success(data => { $scope.formStage = (data.success) ? 2 : 3; }).error(() => { $scope.formStage = 3; From 950f3824946f3f2ce231098345058d51c6e63612 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 15 Dec 2015 16:54:31 +0000 Subject: [PATCH 04/72] fix(Feedback): missing controller file --- app/index.jade | 1 + 1 file changed, 1 insertion(+) diff --git a/app/index.jade b/app/index.jade index 28d63daf1e..1021c6c4f6 100644 --- a/app/index.jade +++ b/app/index.jade @@ -84,6 +84,7 @@ head script(src='build/js/controllers/modalNotification.controller.js') script(src='build/js/controllers/recoverFunds.controller.js') script(src='build/js/controllers/lostGuid.controller.js') + script(src='build/js/controllers/feedback.controller.js') script(src='build/js/controllers/walletNavigation.controller.js') script(src='build/js/controllers/settings/settings.controller.js') script(src='build/js/controllers/settings/info.controller.js') From a0e377e0db41614e086eea4ad4d3c294bbfa0496 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 15 Dec 2015 17:35:45 +0000 Subject: [PATCH 05/72] fix(Deploy): move CSS and JS to /js so that relative font path works --- Gruntfile.coffee | 10 +++++----- app/index.jade | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 7d3dde9559..26dd0e6b37 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -83,7 +83,7 @@ module.exports = (grunt) -> 'build/application-dependencies.min.js' ] - dest: "dist/application.min.js" + dest: "dist/js/application.min.js" coffee: coffee_to_js: @@ -118,7 +118,7 @@ module.exports = (grunt) -> "build/css/blockchain.css" # Needs to be loaded first "build/css/**/*.css" ], - dest: "dist/application.css" + dest: "dist/css/application.css" } }, @@ -283,7 +283,7 @@ module.exports = (grunt) -> publicdir = require("fs").realpathSync("dist") path = require("path") - for referring_file_path in ["dist/application.min.js", "dist/application.css", "dist/index.html"] + for referring_file_path in ["dist/js/application.min.js", "dist/css/application.css", "dist/index.html"] contents = grunt.file.read(referring_file_path) before = undefined after = undefined @@ -336,8 +336,8 @@ module.exports = (grunt) -> files: src: [ - 'dist/application.min.js' - 'dist/application.css' + 'dist/js/application.min.js' + 'dist/css/application.css' ] shell: diff --git a/app/index.jade b/app/index.jade index 1021c6c4f6..e063528778 100644 --- a/app/index.jade +++ b/app/index.jade @@ -164,8 +164,8 @@ head //- Whitelist by copying script from browser and putting it in a file From 32e65e7305859734630369bb07fc8e901473c808 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 14 Dec 2015 14:51:41 +0000 Subject: [PATCH 06/72] refactor(Signup): cleaned up controller and improved tests --- app/partials/signup.jade | 60 +++--- assets/js/controllers/signup.controller.js | 67 +++--- .../controllers/signup_controller_spec.coffee | 194 +++++++++++------- 3 files changed, 177 insertions(+), 144 deletions(-) diff --git a/app/partials/signup.jade b/app/partials/signup.jade index 67ff731350..462dbce4bc 100644 --- a/app/partials/signup.jade +++ b/app/partials/signup.jade @@ -1,36 +1,34 @@ div header - h2.em-300(translate="NEW_ACCT_WELCOME", ng-show="currentStep == 1") + h2.em-300(translate="NEW_ACCT_WELCOME") form.form-horizontal(role="form",name="form",novalidate) - div(ng-switch="currentStep") - div(ng-switch-when="1") - .security-red.mbl.em-400.flex-center - i.ti-hand-stop.mrm.h3.mvn.hidden-xs - span(translate="ALPHA_WARNING") - .form-group(ng-class="{'has-error': errors.email, 'has-success': success.email}") - label.col-sm-4.control-label(translate="EMAIL") - .col-sm-8 - input.form-control(type="email",ng-model="fields.email",autofocus,ng-blur="validate()", ng-focus="errors.email = null") - span.help-block - p {{ errors.email }} - .form-group(ng-class="{'has-error': errors.password, 'has-success': success.password}") - label.col-sm-4.control-label(translate="NEW_PASSWORD") - .col-sm-8 - input.form-control(type="password", name="password",ng-model="fields.password",autofocus,ng-blur="validate()", ng-focus="errors.password = null", ng-maxlength="255", min-entropy="25" required) - password-entropy(password="fields.password").help-block - span.help-block {{ errors.password }} - .form-group(ng-class="{'has-error': errors.confirmation, 'has-success': success.confirmation}") - label.col-sm-4.control-label(translate="CONFIRM_PASSWORD") - .col-sm-8 - input.form-control(type="password",ng-model="fields.confirmation",on-enter="tryNextStep()",autofocus,ng-blur="validate()", ng-focus="errors.confirmation = null") - span.help-block - p {{ errors.confirmation }} - .form-group.flex-center.mtm - .col-sm-4 - input#agreement_accept.pull-right(ng-model="fields.acceptedAgreement" type="checkbox" name="agreement_accept" ng-change="validate()") - label.em-300.col-sm-8 - | I have read and agree to the - a.em-500(ng-click="showAgreement()") Alpha Program Participation Agreement + .security-red.mbl.em-400.flex-center + i.ti-hand-stop.mrm.h3.mvn.hidden-xs + span(translate="ALPHA_WARNING") + .form-group(ng-class="{'has-error': errors.email, 'has-success': success.email}") + label.col-sm-4.control-label(translate="EMAIL") + .col-sm-8 + input.form-control(type="email",ng-model="fields.email",autofocus,ng-blur="validate()", ng-focus="errors.email = null") + span.help-block + p {{ errors.email }} + .form-group(ng-class="{'has-error': errors.password, 'has-success': success.password}") + label.col-sm-4.control-label(translate="NEW_PASSWORD") + .col-sm-8 + input.form-control(type="password", name="password",ng-model="fields.password",autofocus,ng-blur="validate()", ng-focus="errors.password = null", ng-maxlength="255", min-entropy="25" required) + password-entropy(password="fields.password").help-block + span.help-block {{ errors.password }} + .form-group(ng-class="{'has-error': errors.confirmation, 'has-success': success.confirmation}") + label.col-sm-4.control-label(translate="CONFIRM_PASSWORD") + .col-sm-8 + input.form-control(type="password",ng-model="fields.confirmation",on-enter="trySignup()",autofocus,ng-blur="validate()", ng-focus="errors.confirmation = null") + span.help-block + p {{ errors.confirmation }} + .form-group.flex-center.mtm + .col-sm-4 + input#agreement_accept.pull-right(ng-model="fields.acceptedAgreement" type="checkbox" name="agreement_accept" ng-change="validate()") + label.em-300.col-sm-8 + | I have read and agree to the + a.em-500(ng-click="showAgreement()") Alpha Program Participation Agreement .flex-center.flex-end.mbl - button.button-primary(ng-click="nextStep()",ng-disabled="!form.$valid || !isValid[0] || !fields.acceptedAgreement", translate="CONTINUE", ng-show="currentStep == 1 && !working") + button.button-primary(ng-click="signup()",ng-disabled="!form.$valid || !isValid || !fields.acceptedAgreement", translate="CONTINUE", ng-show="!working") img(ng-show="working" src="img/spinner.gif") diff --git a/assets/js/controllers/signup.controller.js b/assets/js/controllers/signup.controller.js index 1ee3f988ae..1ebe6671c7 100644 --- a/assets/js/controllers/signup.controller.js +++ b/assets/js/controllers/signup.controller.js @@ -3,10 +3,7 @@ angular .controller("SignupCtrl", SignupCtrl); function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModal, $translate, $cookieStore, $filter, $state, $http, languages) { - $scope.currentStep = 1; $scope.working = false; - $scope.languages = languages; - $scope.currencies = currency.currencies; $scope.alerts = Alerts.alerts; $scope.status = Wallet.status; @@ -17,18 +14,17 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa } }); - $scope.isValid = [true, true]; + $scope.isValid = true; let language_guess = $filter("getByProperty")("code", $translate.use(), languages); if (language_guess == null) { - language_guess = $filter("getByProperty")("code", "en", languages); + $scope.language_guess = $filter("getByProperty")("code", "en", languages); } - const currency_guess = $filter("getByProperty")("code", "USD", currency.currencies); + $scope.currency_guess = $filter("getByProperty")("code", "USD", currency.currencies); + $scope.fields = { email: "", password: "", confirmation: "", - language: language_guess, - currency: currency_guess, acceptedAgreement: false }; @@ -45,33 +41,29 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa $state.go("wallet.common.home"); }; - $scope.tryNextStep = () => { - if ($scope.isValid[0]) $scope.nextStep(); + $scope.trySignup = () => { + if ($scope.isValid) $scope.signup(); }; - $scope.nextStep = () => { + $scope.signup = () => { $scope.validate(); - if ($scope.isValid[$scope.currentStep - 1]) { - if ($scope.currentStep === 1) { - $scope.working = true; - $scope.createWallet( uid => { - $scope.working = false; - if (uid != null) { - $cookieStore.put("uid", uid); - } - if ($scope.savePassword) { - $cookieStore.put("password", $scope.fields.password); - } - $scope.currentStep++; - $scope.close(""); - }); - } + if ($scope.isValid) { + $scope.working = true; + $scope.createWallet( uid => { + $scope.working = false; + if (uid != null) { + $cookieStore.put("uid", uid); + } + if ($scope.savePassword) { + $cookieStore.put("password", $scope.fields.password); + } + $scope.close(""); + }); } }; $scope.createWallet = successCallback => { Wallet.create($scope.fields.password, $scope.fields.email, $scope.fields.language, $scope.fields.currency, uid => { - $cookieStore.put("uid", uid); successCallback(uid); }); }; @@ -86,8 +78,7 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa if (visual == null) { visual = true; } - $scope.isValid[0] = true; - $scope.isValid[1] = true; + $scope.isValid = true; $scope.errors = { email: null, password: null, @@ -99,12 +90,12 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa confirmation: false }; if ($scope.fields.email === "") { - $scope.isValid[0] = false; + $scope.isValid = false; $translate("EMAIL_ADDRESS_REQUIRED").then( translation => { $scope.errors.email = translation; }); } else if ($scope.form && $scope.form.$error.email) { - $scope.isValid[0] = false; + $scope.isValid = false; $translate("EMAIL_ADDRESS_INVALID").then( translation => { $scope.errors.email = translation; }); @@ -113,25 +104,25 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa } if ($scope.form && $scope.form.$error) { if ($scope.form.$error.minEntropy) { - $scope.isValid[0] = false; + $scope.isValid = false; $translate("TOO_WEAK").then( translation => { $scope.errors.password = translation; }); } if ($scope.form.$error.maxlength) { - $scope.isValid[0] = false; + $scope.isValid = false; $translate("TOO_LONG").then( translation => { $scope.errors.password = translation; }); } } if ($scope.fields.confirmation === "") { - $scope.isValid[0] = false; + $scope.isValid = false; } else { if ($scope.fields.confirmation === $scope.fields.password) { $scope.success.confirmation = true; } else { - $scope.isValid[0] = false; + $scope.isValid = false; if (visual) { $translate("NO_MATCH").then( translation => { $scope.errors.confirmation = translation; @@ -140,19 +131,19 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa } } if (!$scope.fields.acceptedAgreement) { - $scope.isValid[0] = false; + $scope.isValid = false; } }; $scope.validate(); - $scope.$watch("fields.language", (newVal, oldVal) => { + $scope.$watch("language_guess", (newVal, oldVal) => { if (newVal != null) { $translate.use(newVal.code); Wallet.changeLanguage(newVal); } }); - $scope.$watch("fields.currency", (newVal, oldVal) => { + $scope.$watch("currency_guess", (newVal, oldVal) => { if (newVal != null) { Wallet.changeCurrency(newVal); } diff --git a/tests/controllers/signup_controller_spec.coffee b/tests/controllers/signup_controller_spec.coffee index 572777ea27..93b0ac4e87 100644 --- a/tests/controllers/signup_controller_spec.coffee +++ b/tests/controllers/signup_controller_spec.coffee @@ -9,15 +9,14 @@ describe "SignupCtrl", -> beforeEach -> angular.mock.inject ($injector, $rootScope, $controller) -> Wallet = $injector.get("Wallet") - MyWallet = $injector.get("MyWallet") - - MyWallet.createNewWallet = (email, pass, translation, lang, currency, success, error) -> success() - MyWallet.isValidateBIP39Mnemonic = () -> true Wallet.login = (uid, pass, code, twoFactor, success, error) -> success() + Wallet.create = (password, email, currency, language, success) -> success("new_guid") + Wallet.settings_api = + change_language: (code, success) -> success() + change_local_currency: () -> + Wallet.changeCurrency = () -> - Wallet.settings_api.change_language = (-> ) - Wallet.settings_api.change_local_currency = (-> ) scope = $rootScope.$new() @@ -26,12 +25,6 @@ describe "SignupCtrl", -> $stateParams: {}, $uibModalInstance: modalInstance - scope.isValid = [false, false] - scope.fields.email = "a@b.com" - scope.fields.password = "testing" - scope.fields.confirmation = "testing" - scope.fields.acceptedAgreement = true - scope.form = {$error: {email: null}} scope.validate() return @@ -44,101 +37,152 @@ describe "SignupCtrl", -> expect(Alerts.clear).toHaveBeenCalled() ) - describe "first step", -> - it "should be step 1", -> - expect(scope.currentStep).toBe(1) + it "should have initial values", -> + expect(scope.fields.email).toBeDefined() + expect(scope.fields.password).toBeDefined() + expect(scope.fields.confirmation).toBeDefined() + expect(scope.fields.acceptedAgreement).toBe(false) + + describe "password", -> + beforeEach -> + scope.fields.acceptedAgreement = true + scope.fields.email = "a@b.com" + + it "should not display an error if password confirmation matches", -> + scope.fields.password = "testing" + scope.fields.confirmation = "testing" + + scope.validate() - it "should go to second step", -> - scope.nextStep() - expect(scope.currentStep).toBe(2) + expect(scope.isValid).toBe(true) + expect(scope.errors.confirmation).toBeNull() it "should not display an error if password is still empty", -> - scope.fields.currentPassword = "test" scope.fields.password = "" scope.validate() - expect(scope.isValid[0]).toBe(false) + expect(scope.isValid).toBe(false) expect(scope.errors.password).toBeNull() - # it "should display an error if password is too short", -> - # scope.fields.currentPassword = "test" - # scope.fields.password = "1" - # scope.validate() - # expect(scope.isValid[0]).toBe(false) - # expect(scope.errors.password).not.toBeNull() - it "should not display an error if password confirmation is still empty", -> - scope.fields.currentPassword = "test" scope.fields.password = "testing" scope.fields.confirmation = "" scope.validate() - expect(scope.isValid[0]).toBe(false) + expect(scope.isValid).toBe(false) expect(scope.errors.confirmation).toBeNull() - it "should not display an error if password confirmation matches", -> - scope.fields.currentPassword = "test" + it "should display an error if password confirmation does not match", -> scope.fields.password = "testing" - scope.fields.confirmation = "testing" + scope.fields.confirmation = "wrong" scope.validate() - expect(scope.isValid[0]).toBe(true) - expect(scope.errors.confirmation).toBeNull() + expect(scope.isValid).toBe(false) + expect(scope.errors.confirmation).not.toBeNull() - it "should display an error if password confirmation does not match", -> - scope.fields.currentPassword = "test" - scope.fields.password = "testing" - scope.fields.confirmation = "wrong" + describe "agreement", -> + beforeEach -> + scope.fields.email = "a@b.com" + scope.fields.password = "1234" + scope.fields.confirmation = "1234" + + it "should not be signed by default", -> + expect(scope.fields.acceptedAgreement).toBe(false) + + it "should be signed by the user to register", -> + expect(scope.isValid).toBe(false) + scope.fields.acceptedAgreement = true scope.validate() + expect(scope.isValid).toBe(true) - expect(scope.isValid[0]).toBe(false) - expect(scope.errors.confirmation).not.toBeNull() + it "should not register when invalid", -> + scope.fields.password = "" # invalid + scope.validate() + spyOn(scope, "signup") + scope.trySignup() + expect(scope.signup).not.toHaveBeenCalled() - it "should not go to second step is invalid", -> - scope.fields.password = "" # invalid - scope.nextStep() - expect(scope.currentStep).toBe(1) - - describe "wallet creation", -> - - it "should create a new wallet", (done) -> - inject((MyWallet) -> - spyOn(MyWallet, 'createNewWallet') - scope.createWallet (-> ) - expect(MyWallet.createNewWallet).toHaveBeenCalled() - done() - ) - - it "should add uid to cookieStore", (done) -> - inject(($cookieStore) -> - spyOn($cookieStore, 'put') - scope.createWallet (uid) -> - expect($cookieStore.put).toHaveBeenCalledWith('uid', uid) - done() - ) - - describe "second step", -> + describe "signup()", -> + shouldBeValid = true beforeEach -> - scope.currentStep = 2 + spyOn(scope, "validate").and.callFake(() -> + scope.isValid = shouldBeValid # Side-effect + shouldBeValid + ) + - it "should have a list of languages", -> - expect(scope.languages.length).toBeGreaterThan(1) + it "should validate once more", -> + scope.signup() + expect(scope.validate).toHaveBeenCalled() - it "should have a list of currencies", -> - expect(scope.currencies.length).toBeGreaterThan(1) + # Check the test is configured correctly: + expect(scope.isValid).toBe(true) + it "should call createWallet()", -> + spyOn(scope, "createWallet") + scope.signup() + expect(scope.createWallet).toHaveBeenCalled() + + it "should not call createWallet() if validation failed", -> + spyOn(scope, "createWallet") + shouldBeValid = false + scope.signup() + expect(scope.createWallet).not.toHaveBeenCalled() + shouldBeValid = true # Sorry... + + + it "should create a new wallet", inject((Wallet) -> + spyOn(Wallet, 'create') + scope.createWallet (-> ) + expect(Wallet.create).toHaveBeenCalled() + ) + + it "should add uid to cookieStore", inject(($cookieStore) -> + spyOn($cookieStore, 'put') + scope.signup() + expect($cookieStore.put).toHaveBeenCalledWith('uid', "new_guid") + ) + + it "should add password to cookieStore in dev mode", inject(($cookieStore) -> + spyOn($cookieStore, 'put') + scope.savePassword = true + scope.fields.password = "testing" + + scope.signup() + expect($cookieStore.put).toHaveBeenCalledWith('password', "testing") + ) + + it "should not add password to cookieStore in production mode", inject(($cookieStore) -> + spyOn($cookieStore, 'put') + scope.savePassword = false + scope.fields.password = "testing" + + scope.signup() + expect($cookieStore.put).not.toHaveBeenCalledWith('password', "testing") + ) + + describe "language", -> it "should guess the correct language", -> - expect(scope.fields.language.code).toBe("en") + expect(scope.language_guess.code).toBe("en") - it "should switch interface language when new language is selected", inject(($translate) -> + it "should switch interface language to guessed language", inject(($translate, languages) -> spyOn($translate, "use") - expect(scope.fields.language.code).not.toBe(scope.languages[0].code) - scope.fields.language = scope.languages[0] + expect(scope.language_guess.code).not.toBe(languages[0].code) + scope.language_guess = languages[0] scope.$digest() - expect($translate.use).toHaveBeenCalledWith(scope.languages[0].code) + expect($translate.use).toHaveBeenCalledWith(languages[0].code) ) + describe "currency", -> it "should guess the correct currency", -> - expect(scope.fields.currency.code).toBe("USD") + expect(scope.currency_guess.code).toBe("USD") + + it "should switch to the guessed currency", inject((currency, Wallet) -> + spyOn(Wallet, "changeCurrency") + expect(scope.currency_guess.code).not.toBe(currency.currencies[1].code) + scope.currency_guess = currency.currencies[1] + scope.$digest() + expect(Wallet.changeCurrency).toHaveBeenCalledWith(currency.currencies[1]) + ) From 85fbbd2d908a4fa1a7e29e5e3f20032e02e8b026 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 16 Dec 2015 10:16:04 +0000 Subject: [PATCH 07/72] feat(Release): use beta version of my-wallet-v3 bower --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 401481e58a..05e80bb849 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,7 @@ "browserdetection": "0.3.*", "bc-qr-reader": "0.2.*", "bootstrap-sass": "3.3.*", - "blockchain-wallet": "3.3.*", + "blockchain-wallet": "blockchain/my-wallet-v3-bower#beta", "bc-phone-number": "5.0.*" }, "devDependencies": { From f6349813fa6f433a903362d3fbdf37e5f3245dfb Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 16 Dec 2015 11:38:37 +0000 Subject: [PATCH 08/72] fix(Deploy): clean bower and npm cache in dist task --- Gruntfile.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 26dd0e6b37..67b3f647da 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -341,6 +341,10 @@ module.exports = (grunt) -> ] shell: + clean_bower_and_npm_cache: + command: () -> + 'bower cache clean && npm cache clean' + deploy_static_to_dev: command: () -> 'rsync -rz --delete dist hd-dev@server:' @@ -454,6 +458,7 @@ module.exports = (grunt) -> # Default task(s). grunt.registerTask "dist", [ + "shell:clean_bower_and_npm_cache" "clean" "build" "shell:check_dependencies" @@ -473,6 +478,7 @@ module.exports = (grunt) -> ] grunt.registerTask "dist_unsafe", [ + "shell:clean_bower_and_npm_cache" "clean" "build" "shell:skip_check_dependencies" From fa1d4906ba7d9198eff28527b691116848851a29 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 16 Dec 2015 12:23:10 +0000 Subject: [PATCH 09/72] fix(Deploy): dmore aggresive cleaning in dist task --- Gruntfile.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 67b3f647da..08104c5eee 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -4,7 +4,7 @@ module.exports = (grunt) -> pkg: grunt.file.readJSON("package.json") clean: { build: ["build"] - dist: ["dist"] + dist: ["dist", "bower_components", "node_modules"] test: ["coverage"] sass: [".sass-cache"] } @@ -73,8 +73,7 @@ module.exports = (grunt) -> application: # All components should first be minimized. Only trusted sources should be imported as minified.. src: [ - 'bower_components/blockchain-wallet/dist/my-wallet.min.js' - "build/bower_components/jquery/dist/jquery.js" # Duplicate; also included in my-wallet a.t.m. Minified version causes problems. + 'build/bower_components/blockchain-wallet/dist/my-wallet.min.js' 'build/bower_components/angular/angular.min.js' 'build/bower_components/angular-sanitize/angular-sanitize.min.js' 'build/bower_components/angular-cookies/angular-cookies.min.js' @@ -187,7 +186,6 @@ module.exports = (grunt) -> css: files: [ - {src: ["intlTelInput.css"], dest: "build/css", cwd: "bower_components/intl-tel-input/build/css", expand: true } {src: ["font-awesome.min.css"], dest: "build/css", cwd: "bower_components/fontawesome/css", expand: true } {src: ["*.css"], dest: "build/css", cwd: "assets/css", expand: true } ] @@ -383,7 +381,7 @@ module.exports = (grunt) -> npm_install_dependencies: command: () -> - 'cd build && npm install' + 'npm install' bower_install_dependencies: command: () -> @@ -460,6 +458,7 @@ module.exports = (grunt) -> grunt.registerTask "dist", [ "shell:clean_bower_and_npm_cache" "clean" + "shell:npm_install_dependencies" "build" "shell:check_dependencies" "shell:npm_install_dependencies" @@ -480,6 +479,7 @@ module.exports = (grunt) -> grunt.registerTask "dist_unsafe", [ "shell:clean_bower_and_npm_cache" "clean" + "shell:npm_install_dependencies" "build" "shell:skip_check_dependencies" "concat:application_dependencies" From 206d60b51c58e07bbc43a41db46816112cfcfebb Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Thu, 17 Dec 2015 18:00:55 +0000 Subject: [PATCH 10/72] feat(Routes): login and verify email routes and endpoint support --- app/index.jade | 2 ++ app/partials/help.jade | 2 +- app/partials/lost-guid.jade | 2 +- assets/js/controllers/app.controller.js | 4 ++-- assets/js/controllers/claim.controller.js | 2 +- assets/js/controllers/login.controller.js | 10 ++++----- assets/js/controllers/openLink.controller.js | 2 +- .../js/controllers/verifyEmail.controller.js | 16 ++++++++++++++ .../js/core/walletTokenEndpoints.service.js | 7 ++++++ assets/js/routes.js | 22 ++++++++++++++++++- assets/js/services/wallet.service.js | 21 ++++++++++++++++-- server.js | 2 +- tests/controllers/app_ctrl_spec.coffee | 2 +- tests/controllers/claim_ctrl_spec.coffee | 2 +- 14 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 assets/js/controllers/verifyEmail.controller.js create mode 100644 assets/js/core/walletTokenEndpoints.service.js diff --git a/app/index.jade b/app/index.jade index e063528778..00411d2bcb 100644 --- a/app/index.jade +++ b/app/index.jade @@ -55,6 +55,7 @@ head script(src='build/js/core/store.service.js') script(src='build/js/core/myWallet.service.js') script(src='build/js/core/payment.service.js') + script(src='build/js/core/walletTokenEndpoints.service.js') script(src='build/js/browser-polyfill.js') script(src='build/js/app.js') @@ -86,6 +87,7 @@ head script(src='build/js/controllers/lostGuid.controller.js') script(src='build/js/controllers/feedback.controller.js') script(src='build/js/controllers/walletNavigation.controller.js') + script(src='build/js/controllers/verifyEmail.controller.js') script(src='build/js/controllers/settings/settings.controller.js') script(src='build/js/controllers/settings/info.controller.js') script(src='build/js/controllers/settings/preferences.controller.js') diff --git a/app/partials/help.jade b/app/partials/help.jade index aeb7a4d768..9dd792b273 100644 --- a/app/partials/help.jade +++ b/app/partials/help.jade @@ -25,5 +25,5 @@ div .flex-1.flex-end a.button-default.button-nowrap(translate="CONTACT_SUPPORT" href="https://blockchain.zendesk.com/anonymous_requests/new" target="_blank") .flex-end - a.button-muted.mtm(ui-sref="public.login") + a.button-muted.mtm(ui-sref="public.login-no-uid") span(translate="GO_BACK") diff --git a/app/partials/lost-guid.jade b/app/partials/lost-guid.jade index a2adcf076e..053e691f02 100644 --- a/app/partials/lost-guid.jade +++ b/app/partials/lost-guid.jade @@ -41,5 +41,5 @@ .flex-end button.button-muted.mrm( type="button" - ui-sref="public.login" + ui-sref="public.login-no-uid" translate="CONTINUE_TO_LOGIN") diff --git a/assets/js/controllers/app.controller.js b/assets/js/controllers/app.controller.js index 0b96959b20..a8eee65b6d 100644 --- a/assets/js/controllers/app.controller.js +++ b/assets/js/controllers/app.controller.js @@ -59,9 +59,9 @@ function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $cookieS }; $scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => { - let loggedOutStates = ['public', 'public.login', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open']; + let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email']; if (loggedOutStates.every(s => toState.name !== s) && $scope.status.isLoggedIn === false) { - $state.go("public.login"); + $state.go("public.login-no-uid"); } if (Wallet.status.isLoggedIn && (Wallet.store.resetLogoutTimeout != null)) { Wallet.store.resetLogoutTimeout(); diff --git a/assets/js/controllers/claim.controller.js b/assets/js/controllers/claim.controller.js index 40947acad6..d80861f465 100644 --- a/assets/js/controllers/claim.controller.js +++ b/assets/js/controllers/claim.controller.js @@ -7,6 +7,6 @@ function ClaimCtrl($scope, Wallet, $translate, $stateParams, $state, Alerts) { Wallet.goal.claim = {code: $stateParams.code, balance: balance}; if (!Wallet.status.isLoggedIn) { Alerts.displayInfo("Please login to your wallet or create a new one to proceed.", true); - $state.go("public.login"); + $state.go("public.login-no-uid"); } } diff --git a/assets/js/controllers/login.controller.js b/assets/js/controllers/login.controller.js index fb1826a682..81c74e6f40 100644 --- a/assets/js/controllers/login.controller.js +++ b/assets/js/controllers/login.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("LoginCtrl", LoginCtrl); -function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore, $uibModal, $state, $timeout, $translate, filterFilter) { +function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore, $uibModal, $state, $stateParams, $timeout, $translate, filterFilter) { $scope.status = Wallet.status; $scope.settings = Wallet.settings; $scope.disableLogin = null; @@ -13,7 +13,7 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore password: null, twoFactor: null }; - $scope.uidAvailable = $cookieStore.get('uid') != null; + $scope.uidAvailable = $cookieStore.get('uid') != null || $stateParams.uid; $scope.user = Wallet.user; // Browser compatibility warnings: @@ -88,7 +88,7 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore if (Wallet.guid != null) { $scope.uid = Wallet.guid; } else { - $scope.uid = $cookieStore.get("uid"); + $scope.uid = $stateParams.uid || $cookieStore.get("uid"); } if ($scope.key != null) { $scope.status.enterkey = true; @@ -99,8 +99,8 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore $scope.$emit('showNotification', { type: 'verified-email', icon: 'ti-email', - heading: translations.SUCCESS, - msg: $scope.uidAvailable ? translations.EMAIL_VERIFIED_SUCCESS : translations.EMAIL_VERIFIED_SUCCESS_NO_UID + heading: translations.SUCCESS + // msg: $scope.uidAvailable ? translations.EMAIL_VERIFIED_SUCCESS : translations.EMAIL_VERIFIED_SUCCESS_NO_UID }); }); } diff --git a/assets/js/controllers/openLink.controller.js b/assets/js/controllers/openLink.controller.js index 5cbd69bc28..59417eb399 100644 --- a/assets/js/controllers/openLink.controller.js +++ b/assets/js/controllers/openLink.controller.js @@ -9,6 +9,6 @@ function OpenLinkController($scope, Wallet, $translate, $stateParams, $state, Al $translate("PLEASE_LOGIN_FIRST").then(translation => { Alerts.displayInfo(translation, true); }); - $state.go("public.login"); + $state.go("public.login-no-uid"); } } diff --git a/assets/js/controllers/verifyEmail.controller.js b/assets/js/controllers/verifyEmail.controller.js new file mode 100644 index 0000000000..0f50931a86 --- /dev/null +++ b/assets/js/controllers/verifyEmail.controller.js @@ -0,0 +1,16 @@ +angular + .module('walletApp') + .controller("VerifyEmailCtrl", VerifyEmailCtrl); + +function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts) { + const success = (uid) => { + $state.go("public.login-uid", {uid: uid}) + } + + const error = (message) => { + $state.go("public.login-no-uid"); + Alerts.displayError(error); + } + + Wallet.verifyEmail($stateParams.token, success, error) +} diff --git a/assets/js/core/walletTokenEndpoints.service.js b/assets/js/core/walletTokenEndpoints.service.js new file mode 100644 index 0000000000..53032c362b --- /dev/null +++ b/assets/js/core/walletTokenEndpoints.service.js @@ -0,0 +1,7 @@ +angular + .module('walletApp.core') + .factory('MyWalletTokenEndpoints', MyWalletTokenEndpoints); + +function MyWalletTokenEndpoints() { + return Blockchain.WalletTokenEndpoints +} diff --git a/assets/js/routes.js b/assets/js/routes.js index 5c32532a44..51142f5733 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -70,7 +70,7 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) - .state('public.login', { + .state('public.login-no-uid', { url: '/login', views: { alerts: commonViews.alerts, @@ -80,6 +80,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('public.login-uid', { + url: '/login/:uid', + views: { + alerts: commonViews.alerts, + contents: { + templateUrl: 'partials/login.jade', + controller: 'LoginCtrl' + } + } + }) .state('public.signup', { url: '/signup', views: { @@ -223,6 +233,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('wallet.common.verify-email', { + url: '/verify-email/{token:.*}', + views: { + top: top, + left: walletNav, + right: { + controller: 'VerifyEmailCtrl' + } + } + }) .state('wallet.common.settings', { url: '/settings', views: { diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 12b0d9fea3..ee49ddf7c9 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -9,9 +9,9 @@ angular .module('walletServices', []) .factory('Wallet', Wallet); -Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', '$rootScope', 'ngAudio', '$cookieStore', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; +Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', 'MyWalletTokenEndpoints', '$rootScope', 'ngAudio', '$cookieStore', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; -function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, $rootScope, ngAudio, $cookieStore, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { +function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, MyWalletTokenEndpoints, $rootScope, ngAudio, $cookieStore, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { const wallet = { goal: { auth: false @@ -58,6 +58,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.store = MyWalletStore; wallet.api = MyBlockchainApi; wallet.payment = MyWalletPayment; + wallet.tokenEndpoints = MyWalletTokenEndpoints; wallet.transactions = []; wallet.api_code = '1770d5d9-bcea-4d28-ad21-6cbd5be018a8'; @@ -1197,6 +1198,22 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my.wallet.encrypt(password, success, error, encrypting, syncing); }; + wallet.verifyEmail = (token, successCallback, errorCallback) => { + const success = (result) => { + // TODO: process result in My-Wallet-V3 + + wallet.user.isEmailVerified = true; + successCallback(result.guid); + wallet.applyIfNeeded(); + } + + const error = (message) => { + errorCallback(message); + } + + wallet.tokenEndpoints.verifyEmail(token, success, error); + } + // Testing: only works on mock MyWallet wallet.refresh = () => { diff --git a/server.js b/server.js index b32da1d740..f39f4f2898 100644 --- a/server.js +++ b/server.js @@ -21,7 +21,7 @@ app.use(function (req, res, next) { "style-src 'self' 'sha256-vv5i1tRAGZ/gOQeRpI3CEWtvnCpu5FCixlD2ZPu7h84=' 'sha256-47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU='", "child-src 'none'", "script-src 'self' 'sha256-mBeSvdVuQxRa2pGoL8lzKX14b2vKgssqQoW36iRlU9g=' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='", - "connect-src 'self' *.blockchain.info *.blockchain.com wss://*.blockchain.info https://blockchain.info https://api.sharedcoin.com", + "connect-src 'self' http://mats.blockchain.com:8080 wss://*.blockchain.info https://blockchain.info", "object-src 'none'", "media-src 'self' data: mediastream: blob:", "font-src 'self'", '' diff --git a/tests/controllers/app_ctrl_spec.coffee b/tests/controllers/app_ctrl_spec.coffee index c2b44ed472..1477dca8f4 100644 --- a/tests/controllers/app_ctrl_spec.coffee +++ b/tests/controllers/app_ctrl_spec.coffee @@ -37,7 +37,7 @@ describe "AppCtrl", -> expect(scope.status.isLoggedIn).toBe(false) spyOn($state, "go") scope.$broadcast("$stateChangeSuccess", {name: "home"}) - expect($state.go).toHaveBeenCalledWith("public.login") + expect($state.go).toHaveBeenCalledWith("public.login-no-uid") ) it "should not redirect to login if logged in", inject((Wallet, $state) -> diff --git a/tests/controllers/claim_ctrl_spec.coffee b/tests/controllers/claim_ctrl_spec.coffee index 6c67e30fe2..a2f3231dd3 100644 --- a/tests/controllers/claim_ctrl_spec.coffee +++ b/tests/controllers/claim_ctrl_spec.coffee @@ -23,7 +23,7 @@ describe "ClaimCtrl", -> return it "should redirect to login", inject(($state) -> - expect($state.go).toHaveBeenCalledWith("public.login") + expect($state.go).toHaveBeenCalledWith("public.login-no-uid") ) it "should store the goal", inject((Wallet) -> From b88070a8e0607db0a2774916d617b86ba2b0b45e Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 13:34:01 +0100 Subject: [PATCH 11/72] dev(Server): allow other backends --- Gruntfile.coffee | 2 +- README.md | 46 +++------------------------- app/index.jade | 2 +- assets/js/services/wallet.service.js | 6 ++++ server.js | 5 +-- 5 files changed, 15 insertions(+), 46 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 08104c5eee..bf5e2a6256 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -403,7 +403,7 @@ module.exports = (grunt) -> app_name : 'Blockchain HD Frontend', # logo : 'https://raw.githubusercontent.com/blockchain/My-Wallet-HD-Frontend/changelog/assets/icons/png/logo.png', intro : 'Recent changes' - grep_commits: '^fix|^feat|^ui|^copy|^docs|^dep|^refactor|^chore|^test|BREAKING' + grep_commits: '^fix|^feat|^ui|^copy|^docs|^dep|^refactor|^chore|^test|^dev|BREAKING' repo_url: 'https://github.com/blockchain/My-Wallet-HD-Frontend' coveralls: diff --git a/README.md b/README.md index 5ae6127390..55acd19ab5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MyWallet V3 Frontend [![Build Status](https://travis-ci.org/blockchain/My-Wallet-V3-Frontend.png?branch=master)](https://travis-ci.org/blockchain/My-Wallet-V3-Frontend) [![Coverage Status](https://coveralls.io/repos/blockchain/My-Wallet-V3-Frontend/badge.svg?branch=master&service=github)](https://coveralls.io/github/blockchain/My-Wallet-V3-Frontend?branch=master) -An AngularJS bitcoin web wallet powered by [My-Wallet-V3](https://github.com/blockchain/My-Wallet-V3). +An AngularJS bitcoin web wallet powered by [My-Wallet-V3](https://github.com/blockchain/My-Wallet-V3). This is the new and improved wallet. You can see it at [alpha.blockchain.info](https://alpha.blockchain.info/). For the original wallet at [blockchain.info](https://blockchain.info/) please see [this repository](https://github.com/blockchain/My-Wallet) or [contact support](http://blockchain.zendesk.com/). @@ -49,7 +49,7 @@ npm start Visit [local.blockchain.com:8080](http://local.blockchain.com:8080/). Do not use `localhost:8080`. You will need to modify your "hosts" file (`/etc/hosts` on OSX and most UNIX systems) because this is no longer registered at the DNS level for application security reasons. Add this line to `/etc/hosts`: 127.0.0.1 local.blockchain.com - + ## Developing My-Wallet-V3 If you are making changes to [My-Wallet-V3](https://github.com/blockchain/My-Wallet-V3) that you want to try out in the frontend, create a symlink: @@ -58,21 +58,6 @@ rm My-Wallet-V3-Frontend/bower_components/blockchain-wallet/dist/my-wallet.js ln -s ../../../../My-Wallet-V3/dist/my-wallet.js My-Wallet-V3-Frontend/bower_components/blockchain-wallet/dist/my-wallet.js ``` -## Use Beta Invites Locally - -To enable the beta invite functionality, create a file called `.env` and add the following to it: -`INVITE=1` -`BETA_DATABASE_PATH=betakeys.MDF` -`ADMIN_PASSWORD=...` - -Copy the database file template (`betakeys.MDF` is ignored by git): -```sh -cp betakeys-template.MDF betakeys.MDF -``` - -You should see a number of example users at: -http://local.blockchain.com:8080/betaadmin/ - ## Usage You can open any wallet registered with your email address. It will ask you to upgrade to HD if needed. You can also sign up for a new wallet. @@ -90,31 +75,6 @@ If you enable "handle bitcoin links" in your wallet settings, you can open bitco bitcoin:?address=1FeerpCgswvGRLVKme759C96DUBtf7SvA2?amount=0.01 -## UI Tests - -Protractor UI tests are currently running on https://dev.blockchain.info/ or http://local.blockchain.com. Choose instance in util.js. File with login credentials (ignore.js) will be distributed separately and placed in the `e2e-tests` folder. - -Install Protractor globally: - - npm install -g protractor - -This installs both protractor and webdriver-manager. Update webdriver-manager: - - webdriver-manager update - -Start up a server: - - webdriver-manager start - -Open a new Terminal tab, navigate to the e2e test folder, and begin the tests: - - cd e2e-tests/ - protractor config.js - -To test specific files - - protractor config.js --specs [folder-name]/[file_name]_spec.js - ## Contribute Did you know you can [sign your commits](https://git-scm.com/book/tr/v2/Git-Tools-Signing-Your-Work) using a PGP key? @@ -139,6 +99,8 @@ You can test the resulting files by setting `DIST=1` in `.env` and restarting th `index.html` should be cached using `If-Modified-Since` or `etag`. All other files contain a hash of their content and should be cached forever. +If you're using a different backend from `https://blockchain.info/` (e.g. for development), set `ROOT_URL` in `.env`. + ## Security Security issues can be reported to us in the following venues: diff --git a/app/index.jade b/app/index.jade index 00411d2bcb..a97a0f611e 100644 --- a/app/index.jade +++ b/app/index.jade @@ -1,5 +1,5 @@ doctype - + head meta(charset='utf-8') diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index ee49ddf7c9..09ea2719c0 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -56,7 +56,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my = MyWallet; wallet.settings_api = MyBlockchainSettings; wallet.store = MyWalletStore; + wallet.api = MyBlockchainApi; + const customRootURL = $rootScope.rootURL; + if(customRootURL) { + wallet.api.setRootURL(customRootURL); + } + wallet.payment = MyWalletPayment; wallet.tokenEndpoints = MyWalletTokenEndpoints; wallet.transactions = []; diff --git a/server.js b/server.js index f39f4f2898..e8321476b3 100644 --- a/server.js +++ b/server.js @@ -10,6 +10,7 @@ var port = process.env.PORT || 8080 , dist = parseInt(process.env.DIST) === 1 , origins = (process.env.BLOCKCHAIN || '').split(' ') , whitelist = (process.env.IP_WHITELIST || '').split(' ') + , rootURL = process.env.ROOT_URL || 'https://blockchain.info/' // App configuration var app = express(); @@ -17,11 +18,11 @@ var app = express(); app.use(function (req, res, next) { if (req.url === '/') { var cspHeader = ([ - "img-src 'self' blockchain.info data:", + "img-src 'self' " + rootURL + " data:", "style-src 'self' 'sha256-vv5i1tRAGZ/gOQeRpI3CEWtvnCpu5FCixlD2ZPu7h84=' 'sha256-47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU='", "child-src 'none'", "script-src 'self' 'sha256-mBeSvdVuQxRa2pGoL8lzKX14b2vKgssqQoW36iRlU9g=' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='", - "connect-src 'self' http://mats.blockchain.com:8080 wss://*.blockchain.info https://blockchain.info", + "connect-src 'self' " + rootURL + " wss://*.blockchain.info", "object-src 'none'", "media-src 'self' data: mediastream: blob:", "font-src 'self'", '' From 05de6206cb6439da3799f5a1710488c43db1b272 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 13:34:37 +0100 Subject: [PATCH 12/72] test(Mocks): added MyWalletTokenEndpoints mock --- tests/mocks/my_wallet/my_wallet_token_endpoints.coffee | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/mocks/my_wallet/my_wallet_token_endpoints.coffee diff --git a/tests/mocks/my_wallet/my_wallet_token_endpoints.coffee b/tests/mocks/my_wallet/my_wallet_token_endpoints.coffee new file mode 100644 index 0000000000..04c2070a7b --- /dev/null +++ b/tests/mocks/my_wallet/my_wallet_token_endpoints.coffee @@ -0,0 +1,2 @@ +angular.module('walletApp.core').factory 'MyWalletTokenEndpoints', () -> + this From 4d1bdfaaf19be2e8486ae578ba76dd849be76aa9 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 15:03:08 +0100 Subject: [PATCH 13/72] feat(Routes): show modal after verifying email --- assets/js/controllers/login.controller.js | 11 ------- .../js/controllers/verifyEmail.controller.js | 32 +++++++++++++++++-- assets/js/services/wallet.service.js | 7 ++-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/assets/js/controllers/login.controller.js b/assets/js/controllers/login.controller.js index 81c74e6f40..cb6fd82894 100644 --- a/assets/js/controllers/login.controller.js +++ b/assets/js/controllers/login.controller.js @@ -93,17 +93,6 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore if ($scope.key != null) { $scope.status.enterkey = true; } - if ($cookieStore.get('email-verified')) { - $cookieStore.remove('email-verified'); - $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS', 'EMAIL_VERIFIED_SUCCESS_NO_UID']).then(translations => { - $scope.$emit('showNotification', { - type: 'verified-email', - icon: 'ti-email', - heading: translations.SUCCESS - // msg: $scope.uidAvailable ? translations.EMAIL_VERIFIED_SUCCESS : translations.EMAIL_VERIFIED_SUCCESS_NO_UID - }); - }); - } $scope.twoFactorCode = ""; $scope.busy = false; $scope.isValid = false; diff --git a/assets/js/controllers/verifyEmail.controller.js b/assets/js/controllers/verifyEmail.controller.js index 0f50931a86..912c82e62a 100644 --- a/assets/js/controllers/verifyEmail.controller.js +++ b/assets/js/controllers/verifyEmail.controller.js @@ -2,14 +2,40 @@ angular .module('walletApp') .controller("VerifyEmailCtrl", VerifyEmailCtrl); -function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts) { +function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate, $rootScope) { const success = (uid) => { - $state.go("public.login-uid", {uid: uid}) + uid = null; + + if(uid) { + $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS']).then(translations => { + $state.go("public.login-uid", {uid: uid}).then(() =>{ + $rootScope.$emit('showNotification', { + type: 'verified-email', + icon: 'ti-email', + heading: translations.SUCCESS, + msg: translations.EMAIL_VERIFIED_SUCCESS + }); + }); + }); + } else { + $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS_NO_UID']).then(translations => { + $state.go("public.login-no-uid").then(() => { + $rootScope.$emit('showNotification', { + type: 'verified-email', + icon: 'ti-email', + heading: translations.SUCCESS, + msg: translations.EMAIL_VERIFIED_SUCCESS_NO_UID + }); + }); + }); + } + + } const error = (message) => { $state.go("public.login-no-uid"); - Alerts.displayError(error); + Alerts.displayError(message, true); } Wallet.verifyEmail($stateParams.token, success, error) diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 09ea2719c0..22f6a67e02 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -1205,15 +1205,14 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB }; wallet.verifyEmail = (token, successCallback, errorCallback) => { - const success = (result) => { - // TODO: process result in My-Wallet-V3 - + const success = (guid) => { wallet.user.isEmailVerified = true; - successCallback(result.guid); + successCallback(guid); wallet.applyIfNeeded(); } const error = (message) => { + console.log(message); errorCallback(message); } From 4baac6ea6b2699861ba3dad8b08ee09ec31878c2 Mon Sep 17 00:00:00 2001 From: Justin Tormey Date: Thu, 17 Dec 2015 16:01:42 -0500 Subject: [PATCH 14/72] fix(Signup): improved validation --- app/partials/alpha-agreement.jade | 2 +- app/partials/signup.jade | 64 ++++++-- assets/js/controllers/signup.controller.js | 101 ++----------- .../controllers/signup_controller_spec.coffee | 139 ++++++++---------- 4 files changed, 126 insertions(+), 180 deletions(-) diff --git a/app/partials/alpha-agreement.jade b/app/partials/alpha-agreement.jade index f2dfe127b9..9844fdd9d5 100644 --- a/app/partials/alpha-agreement.jade +++ b/app/partials/alpha-agreement.jade @@ -109,4 +109,4 @@ | 8.1. Except for those limited rights expressly granted in Section 2.1, Blockchain and its licensors retain all right, title and interest in and to the Alpha Services and the Alpha Materials, including all related intellectual property rights. The parties are independent contractors with respect to each other, and nothing in this Agreement shall be construed as creating an employer-employee relationship, a partnership, agency relationship or a joint venture between the parties. This Agreement further controls the actions of all party representatives, officers, agents, employees and associated individuals. The terms of this Agreement shall be binding on the parties, and all successors to the foregoing who take their rights hereunder. Neither party will assign, transfer or delegate its rights or obligations under this Agreement (in whole or in part) without the other party’s prior | written consent except that Blockchain may assign and delegate this Agreement pursuant to a transfer of all or substantially all of Blockchain’s business and assets, whether by merger, sale of assets, sale of stock, or otherwise. Any attempted assignment, transfer or delegation in violation of the foregoing shall be null and void. All modifications to and waivers of any terms of this Agreement must be in a writing that is signed by the parties hereto and expressly references this Agreement. This Agreement shall be governed by the laws of the State of New York, without regard to New York conflict of laws rules. The exclusive venue and jurisdiction for any and all disputes, claims and controversies arising from or relating to this Agreement shall be the state or federal courts located in New York City. In the event that any provision of this Agreement conflicts with governing law or if any provision is held to be null, void or otherwise ineffective or invalid by a court of competent jurisdiction, (a) such provision shall be deemed to be restated to reflect as nearly as possible the original intentions of the parties in accordance with applicable law, and (b) the remaining terms, provisions, covenants and restrictions of this Agreement shall remain in full force and effect. No waiver of any breach of any provision of this Agreement shall constitute a waiver of any prior, concurrent or subsequent breach of the same or any other provisions hereof, and no waiver shall be effective unless made in writing and signed by an authorized representative of the waiving party. This Agreement and all expressly referenced documents constitute the entire agreement between the parties with respect to the subject matter hereof and supersedes all prior and contemporaneous agreements or communications, including any reseller or similar agreements previously executed by the parties. All notices, consents and approvals under this Agreement must be delivered in writing by email to the email address supplied by one party to the other during the account signup process, or posted to the Blockchain website. .modal-footer.flex-end.pal - button.button-success.button-lg(ng-click="acceptAgreement(); $dismiss()" translate="OK") + button.button-success.button-lg(ng-click="$close()" translate="OK") diff --git a/app/partials/signup.jade b/app/partials/signup.jade index 462dbce4bc..e1c73e7e89 100644 --- a/app/partials/signup.jade +++ b/app/partials/signup.jade @@ -1,34 +1,66 @@ div header h2.em-300(translate="NEW_ACCT_WELCOME") - form.form-horizontal(role="form",name="form",novalidate) - .security-red.mbl.em-400.flex-center + form.form-horizontal( + role="form" + name="signupForm" + ng-submit="signup()" + autocomplete="off" + novalidate) + .security-red.mbl.mtl.em-400.flex-center i.ti-hand-stop.mrm.h3.mvn.hidden-xs span(translate="ALPHA_WARNING") - .form-group(ng-class="{'has-error': errors.email, 'has-success': success.email}") + .form-group(ng-class="{'has-error': signupForm.email.$invalid && signupForm.email.$touched}") label.col-sm-4.control-label(translate="EMAIL") .col-sm-8 - input.form-control(type="email",ng-model="fields.email",autofocus,ng-blur="validate()", ng-focus="errors.email = null") - span.help-block - p {{ errors.email }} - .form-group(ng-class="{'has-error': errors.password, 'has-success': success.password}") + input.form-control( + name="email" + type="email" + ng-model="fields.email" + required + autofocus) + span.help-block(ng-show="signupForm.email.$touched") + p(ng-show="signupForm.email.$error.required" translate="EMAIL_ADDRESS_REQUIRED") + p(ng-show="signupForm.email.$error.email" translate="EMAIL_ADDRESS_INVALID") + .form-group(ng-class="{'has-error': signupForm.password.$invalid && signupForm.password.$touched}") label.col-sm-4.control-label(translate="NEW_PASSWORD") .col-sm-8 - input.form-control(type="password", name="password",ng-model="fields.password",autofocus,ng-blur="validate()", ng-focus="errors.password = null", ng-maxlength="255", min-entropy="25" required) - password-entropy(password="fields.password").help-block - span.help-block {{ errors.password }} - .form-group(ng-class="{'has-error': errors.confirmation, 'has-success': success.confirmation}") + input.form-control( + name="password" + type="password" + ng-model="fields.password" + ng-maxlength="255" + min-entropy="25" + required) + password-entropy.help-block(password="fields.password") + span.help-block(ng-show="signupForm.password.$touched") + p(ng-show="signupForm.password.$error.minEntropy && fields.password != undefined" translate="TOO_WEAK") + p(ng-show="signupForm.password.$error.maxlength" translate="TOO_LONG") + .form-group(ng-class="{'has-error': signupForm.confirmation.$invalid && signupForm.confirmation.$touched}") label.col-sm-4.control-label(translate="CONFIRM_PASSWORD") .col-sm-8 - input.form-control(type="password",ng-model="fields.confirmation",on-enter="trySignup()",autofocus,ng-blur="validate()", ng-focus="errors.confirmation = null") - span.help-block - p {{ errors.confirmation }} + input.form-control( + name="confirmation" + type="password" + ng-model="fields.confirmation" + is-valid="fields.confirmation == fields.password" + required) + span.help-block(ng-show="signupForm.confirmation.$touched") + p(ng-show="signupForm.confirmation.$error.isValid" translate="NO_MATCH") .form-group.flex-center.mtm .col-sm-4 - input#agreement_accept.pull-right(ng-model="fields.acceptedAgreement" type="checkbox" name="agreement_accept" ng-change="validate()") + input#agreement_accept.pull-right( + name="agreement" + type="checkbox" + ng-model="fields.acceptedAgreement" + required) label.em-300.col-sm-8 | I have read and agree to the a.em-500(ng-click="showAgreement()") Alpha Program Participation Agreement .flex-center.flex-end.mbl - button.button-primary(ng-click="signup()",ng-disabled="!form.$valid || !isValid || !fields.acceptedAgreement", translate="CONTINUE", ng-show="!working") + button.button-primary( + type="submit" + ng-disabled="signupForm.$invalid" + translate="CONTINUE" + ng-show="!working") img(ng-show="working" src="img/spinner.gif") diff --git a/assets/js/controllers/signup.controller.js b/assets/js/controllers/signup.controller.js index 1ebe6671c7..3f60ace587 100644 --- a/assets/js/controllers/signup.controller.js +++ b/assets/js/controllers/signup.controller.js @@ -2,7 +2,9 @@ angular .module('walletApp') .controller("SignupCtrl", SignupCtrl); -function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModal, $translate, $cookieStore, $filter, $state, $http, languages) { +SignupCtrl.$inject = ['$scope', '$state', '$cookies', '$filter', '$translate', '$uibModal', 'Wallet', 'Alerts', 'currency', 'languages']; + +function SignupCtrl($scope, $state, $cookies, $filter, $translate, $uibModal, Wallet, Alerts, currency, languages) { $scope.working = false; $scope.alerts = Alerts.alerts; $scope.status = Wallet.status; @@ -14,7 +16,6 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa } }); - $scope.isValid = true; let language_guess = $filter("getByProperty")("code", $translate.use(), languages); if (language_guess == null) { $scope.language_guess = $filter("getByProperty")("code", "en", languages); @@ -31,9 +32,10 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa $scope.showAgreement = () => { const modalInstance = $uibModal.open({ templateUrl: "partials/alpha-agreement.jade", - controller: 'SignupCtrl', + controller: function () {}, windowClass: "bc-modal terms-modal" }); + modalInstance.result.then(() => $scope.fields.acceptedAgreement = true); }; $scope.close = () => { @@ -41,21 +43,16 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa $state.go("wallet.common.home"); }; - $scope.trySignup = () => { - if ($scope.isValid) $scope.signup(); - }; - $scope.signup = () => { - $scope.validate(); - if ($scope.isValid) { + if ($scope.signupForm.$valid) { $scope.working = true; - $scope.createWallet( uid => { + $scope.createWallet((uid) => { $scope.working = false; if (uid != null) { - $cookieStore.put("uid", uid); + $cookies.put("uid", uid); } if ($scope.savePassword) { - $cookieStore.put("password", $scope.fields.password); + $cookies.put("password", $scope.fields.password); } $scope.close(""); }); @@ -63,93 +60,19 @@ function SignupCtrl($scope, $rootScope, $log, Wallet, Alerts, currency, $uibModa }; $scope.createWallet = successCallback => { - Wallet.create($scope.fields.password, $scope.fields.email, $scope.fields.language, $scope.fields.currency, uid => { + Wallet.create($scope.fields.password, $scope.fields.email, $scope.fields.language, $scope.fields.currency, (uid) => { successCallback(uid); }); }; - $scope.$watch("fields.confirmation", newVal => { - if ((newVal != null) && $scope.fields.password !== "") { - $scope.validate(false); - } - }); - - $scope.validate = visual => { - if (visual == null) { - visual = true; - } - $scope.isValid = true; - $scope.errors = { - email: null, - password: null, - confirmation: null - }; - $scope.success = { - email: false, - password: false, - confirmation: false - }; - if ($scope.fields.email === "") { - $scope.isValid = false; - $translate("EMAIL_ADDRESS_REQUIRED").then( translation => { - $scope.errors.email = translation; - }); - } else if ($scope.form && $scope.form.$error.email) { - $scope.isValid = false; - $translate("EMAIL_ADDRESS_INVALID").then( translation => { - $scope.errors.email = translation; - }); - } else { - $scope.success.email = true; - } - if ($scope.form && $scope.form.$error) { - if ($scope.form.$error.minEntropy) { - $scope.isValid = false; - $translate("TOO_WEAK").then( translation => { - $scope.errors.password = translation; - }); - } - if ($scope.form.$error.maxlength) { - $scope.isValid = false; - $translate("TOO_LONG").then( translation => { - $scope.errors.password = translation; - }); - } - } - if ($scope.fields.confirmation === "") { - $scope.isValid = false; - } else { - if ($scope.fields.confirmation === $scope.fields.password) { - $scope.success.confirmation = true; - } else { - $scope.isValid = false; - if (visual) { - $translate("NO_MATCH").then( translation => { - $scope.errors.confirmation = translation; - }); - } - } - } - if (!$scope.fields.acceptedAgreement) { - $scope.isValid = false; - } - }; - $scope.validate(); - $scope.$watch("language_guess", (newVal, oldVal) => { - if (newVal != null) { + if (newVal) { $translate.use(newVal.code); Wallet.changeLanguage(newVal); } }); $scope.$watch("currency_guess", (newVal, oldVal) => { - if (newVal != null) { - Wallet.changeCurrency(newVal); - } - }); - - $scope.$on('signed_agreement', () => { - $scope.fields.acceptedAgreement = true; + if (newVal) Wallet.changeCurrency(newVal); }); } diff --git a/tests/controllers/signup_controller_spec.coffee b/tests/controllers/signup_controller_spec.coffee index 93b0ac4e87..fe28c0d63b 100644 --- a/tests/controllers/signup_controller_spec.coffee +++ b/tests/controllers/signup_controller_spec.coffee @@ -7,7 +7,7 @@ describe "SignupCtrl", -> beforeEach angular.mock.module("walletApp") beforeEach -> - angular.mock.inject ($injector, $rootScope, $controller) -> + angular.mock.inject ($injector, $rootScope, $controller, $compile) -> Wallet = $injector.get("Wallet") Wallet.login = (uid, pass, code, twoFactor, success, error) -> success() @@ -25,7 +25,18 @@ describe "SignupCtrl", -> $stateParams: {}, $uibModalInstance: modalInstance - scope.validate() + element = angular.element( + '
' + + '' + + '' + + '' + + '' + + '
' + ) + scope.model = { fields: { email: '', password: '', confirmation: '', acceptedAgreement: false } } + $compile(element)(scope) + + scope.$digest() return @@ -43,82 +54,61 @@ describe "SignupCtrl", -> expect(scope.fields.confirmation).toBeDefined() expect(scope.fields.acceptedAgreement).toBe(false) - describe "password", -> - beforeEach -> - scope.fields.acceptedAgreement = true - scope.fields.email = "a@b.com" - - it "should not display an error if password confirmation matches", -> - scope.fields.password = "testing" - scope.fields.confirmation = "testing" - - scope.validate() - - expect(scope.isValid).toBe(true) - expect(scope.errors.confirmation).toBeNull() - - it "should not display an error if password is still empty", -> - scope.fields.password = "" - scope.validate() - expect(scope.isValid).toBe(false) - expect(scope.errors.password).toBeNull() - - it "should not display an error if password confirmation is still empty", -> - scope.fields.password = "testing" - scope.fields.confirmation = "" + it "should not register when invalid", -> + spyOn(scope, 'createWallet') + scope.signupForm.password.$setViewValue('') + scope.$digest() - scope.validate() + scope.signup() + expect(scope.createWallet).not.toHaveBeenCalled() - expect(scope.isValid).toBe(false) - expect(scope.errors.confirmation).toBeNull() + describe "password", -> - it "should display an error if password confirmation does not match", -> - scope.fields.password = "testing" - scope.fields.confirmation = "wrong" + beforeEach -> + form = scope.signupForm + form.email.$setViewValue('a@b.com') + form.agreement.$setViewValue(true) + scope.$digest() - scope.validate() + it "should not have an error if password confirmation matches", -> + scope.signupForm.password.$setViewValue('testing') + scope.signupForm.confirmation.$setViewValue('testing') + scope.$digest() + expect(scope.signupForm.confirmation.$valid).toBe(true) - expect(scope.isValid).toBe(false) - expect(scope.errors.confirmation).not.toBeNull() + it "should have an error if password confirmation does not match", -> + scope.signupForm.password.$setViewValue('testing') + scope.signupForm.confirmation.$setViewValue('wrong') + scope.$digest() + expect(scope.signupForm.confirmation.$valid).toBe(false) describe "agreement", -> + beforeEach -> - scope.fields.email = "a@b.com" - scope.fields.password = "1234" - scope.fields.confirmation = "1234" + form = scope.signupForm + form.email.$setViewValue('a@b.com') + form.password.$setViewValue('my_password12345') + form.confirmation.$setViewValue('my_password12345') + scope.$digest() it "should not be signed by default", -> expect(scope.fields.acceptedAgreement).toBe(false) it "should be signed by the user to register", -> - expect(scope.isValid).toBe(false) - scope.fields.acceptedAgreement = true - - scope.validate() - expect(scope.isValid).toBe(true) - - it "should not register when invalid", -> - scope.fields.password = "" # invalid - scope.validate() - spyOn(scope, "signup") - scope.trySignup() - expect(scope.signup).not.toHaveBeenCalled() + expect(scope.signupForm.$valid).toBe(false) + scope.signupForm.agreement.$setViewValue(true) + scope.$digest() + expect(scope.signupForm.$valid).toBe(true) describe "signup()", -> - shouldBeValid = true - beforeEach -> - spyOn(scope, "validate").and.callFake(() -> - scope.isValid = shouldBeValid # Side-effect - shouldBeValid - ) - - it "should validate once more", -> - scope.signup() - expect(scope.validate).toHaveBeenCalled() - - # Check the test is configured correctly: - expect(scope.isValid).toBe(true) + beforeEach -> + form = scope.signupForm + form.email.$setViewValue('a@b.com') + form.password.$setViewValue('my_password12345') + form.confirmation.$setViewValue('my_password12345') + form.agreement.$setViewValue(true) + scope.$digest() it "should call createWallet()", -> spyOn(scope, "createWallet") @@ -127,11 +117,12 @@ describe "SignupCtrl", -> it "should not call createWallet() if validation failed", -> spyOn(scope, "createWallet") - shouldBeValid = false + + scope.signupForm.password.$setViewValue('weak') + scope.$digest() + scope.signup() expect(scope.createWallet).not.toHaveBeenCalled() - shouldBeValid = true # Sorry... - it "should create a new wallet", inject((Wallet) -> spyOn(Wallet, 'create') @@ -139,28 +130,28 @@ describe "SignupCtrl", -> expect(Wallet.create).toHaveBeenCalled() ) - it "should add uid to cookieStore", inject(($cookieStore) -> - spyOn($cookieStore, 'put') + it "should add uid to cookies", inject(($cookies) -> + spyOn($cookies, 'put') scope.signup() - expect($cookieStore.put).toHaveBeenCalledWith('uid', "new_guid") + expect($cookies.put).toHaveBeenCalledWith('uid', "new_guid") ) - it "should add password to cookieStore in dev mode", inject(($cookieStore) -> - spyOn($cookieStore, 'put') + it "should add password to cookies in dev mode", inject(($cookies) -> + spyOn($cookies, 'put') scope.savePassword = true scope.fields.password = "testing" scope.signup() - expect($cookieStore.put).toHaveBeenCalledWith('password', "testing") + expect($cookies.put).toHaveBeenCalledWith('password', "testing") ) - it "should not add password to cookieStore in production mode", inject(($cookieStore) -> - spyOn($cookieStore, 'put') + it "should not add password to cookies in production mode", inject(($cookies) -> + spyOn($cookies, 'put') scope.savePassword = false scope.fields.password = "testing" scope.signup() - expect($cookieStore.put).not.toHaveBeenCalledWith('password', "testing") + expect($cookies.put).not.toHaveBeenCalledWith('password', "testing") ) describe "language", -> From 7d26fa7d06d0bcb3b8e92279ee5d385a1aca4944 Mon Sep 17 00:00:00 2001 From: Justin Tormey Date: Thu, 17 Dec 2015 16:07:11 -0500 Subject: [PATCH 15/72] refactor(Cookies): replace references to deprecated with --- assets/js/controllers/app.controller.js | 2 +- assets/js/controllers/login.controller.js | 16 ++++++++-------- assets/js/controllers/navigation.controller.js | 6 +++--- .../controllers/settings/settings.controller.js | 2 +- assets/js/controllers/transaction.controller.js | 2 +- assets/js/services/wallet.service.js | 16 ++++++++-------- tests/mocks/my_wallet/my_wallet_mock.coffee | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/assets/js/controllers/app.controller.js b/assets/js/controllers/app.controller.js index a8eee65b6d..64c4e09c12 100644 --- a/assets/js/controllers/app.controller.js +++ b/assets/js/controllers/app.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("AppCtrl", AppCtrl); -function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $cookieStore, $timeout, $uibModal, $window, $translate) { +function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout, $uibModal, $window, $translate) { $scope.status = Wallet.status; $scope.settings = Wallet.settings; $rootScope.isMock = Wallet.isMock; diff --git a/assets/js/controllers/login.controller.js b/assets/js/controllers/login.controller.js index cb6fd82894..be08d2c25c 100644 --- a/assets/js/controllers/login.controller.js +++ b/assets/js/controllers/login.controller.js @@ -2,18 +2,18 @@ angular .module('walletApp') .controller("LoginCtrl", LoginCtrl); -function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore, $uibModal, $state, $stateParams, $timeout, $translate, filterFilter) { +function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $uibModal, $state, $stateParams, $timeout, $translate, filterFilter) { $scope.status = Wallet.status; $scope.settings = Wallet.settings; $scope.disableLogin = null; $scope.status.enterkey = false; - $scope.key = $cookieStore.get("key"); + $scope.key = $cookies.get("key"); $scope.errors = { uid: null, password: null, twoFactor: null }; - $scope.uidAvailable = $cookieStore.get('uid') != null || $stateParams.uid; + $scope.uidAvailable = $cookies.get('uid') != null || $stateParams.uid; $scope.user = Wallet.user; // Browser compatibility warnings: @@ -88,7 +88,7 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore if (Wallet.guid != null) { $scope.uid = Wallet.guid; } else { - $scope.uid = $stateParams.uid || $cookieStore.get("uid"); + $scope.uid = $stateParams.uid || $cookies.get("uid"); } if ($scope.key != null) { $scope.status.enterkey = true; @@ -96,8 +96,8 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore $scope.twoFactorCode = ""; $scope.busy = false; $scope.isValid = false; - if (!!$cookieStore.get("password")) { - $scope.password = $cookieStore.get("password"); + if (!!$cookies.get("password")) { + $scope.password = $cookies.get("password"); } $scope.login = () => { if ($scope.busy) return; @@ -126,10 +126,10 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookieStore Wallet.login($scope.uid, $scope.password, null, needs2FA, success, error); } if ($scope.uid != null && $scope.uid !== "") { - $cookieStore.put("uid", $scope.uid); + $cookies.put("uid", $scope.uid); } if ($scope.savePassword && $scope.password != null && $scope.password !== "") { - $cookieStore.put("password", $scope.password); + $cookies.put("password", $scope.password); } }; diff --git a/assets/js/controllers/navigation.controller.js b/assets/js/controllers/navigation.controller.js index bf238b38a1..74bc17e086 100644 --- a/assets/js/controllers/navigation.controller.js +++ b/assets/js/controllers/navigation.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("NavigationCtrl", NavigationCtrl); -function NavigationCtrl($scope, Wallet, currency, SecurityCenter, $translate, $cookieStore, $state, filterFilter, $interval) { +function NavigationCtrl($scope, Wallet, currency, SecurityCenter, $translate, $cookies, $state, filterFilter, $interval) { $scope.status = Wallet.status; $scope.security = SecurityCenter.security; $scope.settings = Wallet.settings; @@ -36,8 +36,8 @@ function NavigationCtrl($scope, Wallet, currency, SecurityCenter, $translate, $c if (confirm(translation)) { $scope.uid = null; $scope.password = null; - $cookieStore.remove("password"); -// $cookieStore.remove("uid") // Pending a "Forget Me feature" + $cookies.remove("password"); +// $cookies.remove("uid") // Pending a "Forget Me feature" $state.go("wallet.common.transactions", { accountIndex: "" diff --git a/assets/js/controllers/settings/settings.controller.js b/assets/js/controllers/settings/settings.controller.js index 24d63437d8..8ac8feb871 100644 --- a/assets/js/controllers/settings/settings.controller.js +++ b/assets/js/controllers/settings/settings.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("SettingsCtrl", SettingsCtrl); -function SettingsCtrl($scope, Wallet, Alerts, $cookieStore, $state) { +function SettingsCtrl($scope, Wallet, Alerts, $state) { if ($state.current.name === "wallet.common.settings") { $state.go("wallet.common.settings.info"); } diff --git a/assets/js/controllers/transaction.controller.js b/assets/js/controllers/transaction.controller.js index 9225cf743e..6bd8fe4f63 100644 --- a/assets/js/controllers/transaction.controller.js +++ b/assets/js/controllers/transaction.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller('TransactionCtrl', TransactionCtrl); -function TransactionCtrl($scope, Wallet, $log, $state, $stateParams, $filter, $cookieStore, $sce) { +function TransactionCtrl($scope, Wallet, $log, $state, $stateParams, $filter, $sce) { $scope.addressBook = Wallet.addressBook; $scope.status = Wallet.status; $scope.settings = Wallet.settings; diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 22f6a67e02..c41817a994 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -9,9 +9,9 @@ angular .module('walletServices', []) .factory('Wallet', Wallet); -Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', 'MyWalletTokenEndpoints', '$rootScope', 'ngAudio', '$cookieStore', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; +Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', 'MyWalletTokenEndpoints', '$rootScope', 'ngAudio', '$cookies', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; -function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, MyWalletTokenEndpoints, $rootScope, ngAudio, $cookieStore, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { +function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, MyWalletTokenEndpoints, $rootScope, ngAudio, $cookies, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { const wallet = { goal: { auth: false @@ -814,11 +814,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } else if (event === 'logging_out') { if (wallet.didLogoutByChoice) { $translate('LOGGED_OUT').then((translation) => { - $cookieStore.put('alert-success', translation); + $cookies.put('alert-success', translation); }); } else { $translate('LOGGED_OUT_AUTOMATICALLY').then((translation) => { - $cookieStore.put('alert-warning', translation); + $cookies.put('alert-warning', translation); wallet.applyIfNeeded(); }); } @@ -852,15 +852,15 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.monitor(event, data); }); - let message = $cookieStore.get('alert-warning'); + let message = $cookies.get('alert-warning'); if (message !== void 0 && message !== null) { Alerts.displayWarning(message, true); - $cookieStore.remove('alert-warning'); + $cookies.remove('alert-warning'); } - message = $cookieStore.get('alert-success'); + message = $cookies.get('alert-success'); if (message !== void 0 && message !== null) { Alerts.displaySuccess(message); - $cookieStore.remove('alert-success'); + $cookies.remove('alert-success'); } wallet.setNote = (tx, text) => { diff --git a/tests/mocks/my_wallet/my_wallet_mock.coffee b/tests/mocks/my_wallet/my_wallet_mock.coffee index bd5ac47747..280bb2bae2 100644 --- a/tests/mocks/my_wallet/my_wallet_mock.coffee +++ b/tests/mocks/my_wallet/my_wallet_mock.coffee @@ -1,4 +1,4 @@ angular .module('walletApp.core') - .factory 'MyWallet', ($window, $timeout, $log, $cookieStore, MyWalletStore) -> + .factory 'MyWallet', ($window, $timeout, $log, MyWalletStore) -> return {} From 632db4c634ac6b2792cc0f27310740b2c73ed8e4 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 16:05:14 +0100 Subject: [PATCH 16/72] feat(Unsubscribe): unsubscribe route --- app/index.jade | 1 + assets/js/controllers/app.controller.js | 2 +- .../js/controllers/unsubscribe.controller.js | 31 +++++++++++++++++++ assets/js/routes.js | 10 ++++++ assets/js/services/wallet.service.js | 14 +++++++++ locales/en-human.json | 3 +- 6 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 assets/js/controllers/unsubscribe.controller.js diff --git a/app/index.jade b/app/index.jade index a97a0f611e..6b54fddeeb 100644 --- a/app/index.jade +++ b/app/index.jade @@ -88,6 +88,7 @@ head script(src='build/js/controllers/feedback.controller.js') script(src='build/js/controllers/walletNavigation.controller.js') script(src='build/js/controllers/verifyEmail.controller.js') + script(src='build/js/controllers/unsubscribe.controller.js') script(src='build/js/controllers/settings/settings.controller.js') script(src='build/js/controllers/settings/info.controller.js') script(src='build/js/controllers/settings/preferences.controller.js') diff --git a/assets/js/controllers/app.controller.js b/assets/js/controllers/app.controller.js index 64c4e09c12..43310981f8 100644 --- a/assets/js/controllers/app.controller.js +++ b/assets/js/controllers/app.controller.js @@ -59,7 +59,7 @@ function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout }; $scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => { - let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email']; + let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe']; if (loggedOutStates.every(s => toState.name !== s) && $scope.status.isLoggedIn === false) { $state.go("public.login-no-uid"); } diff --git a/assets/js/controllers/unsubscribe.controller.js b/assets/js/controllers/unsubscribe.controller.js new file mode 100644 index 0000000000..a85e300c2b --- /dev/null +++ b/assets/js/controllers/unsubscribe.controller.js @@ -0,0 +1,31 @@ +angular + .module('walletApp') + .controller("UnsubscribeCtrl", UnsubscribeCtrl); + +function UnsubscribeCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate) { + const success = (uid) => { + + if(uid) { + $translate('UNSUBSCRIBE_SUCCESS').then(translation => { + $state.go("public.login-uid", {uid: uid}).then(() => { + Alerts.displaySuccess(translation) + }); + }); + } else { + $translate('UNSUBSCRIBE_SUCCESS').then(translation => { + $state.go("public.login-no-uid").then(() => { + Alerts.displaySuccess(translation) + }); + }); + } + + + } + + const error = (message) => { + $state.go("public.login-no-uid"); + Alerts.displayError(message, true); + } + + Wallet.unsubscribe($stateParams.token, success, error) +} diff --git a/assets/js/routes.js b/assets/js/routes.js index 51142f5733..dacdd48ec5 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -243,6 +243,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('wallet.common.unsubscribe', { + url: '/unsubscribe/{token:.*}', + views: { + top: top, + left: walletNav, + right: { + controller: 'UnsubscribeCtrl' + } + } + }) .state('wallet.common.settings', { url: '/settings', views: { diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index c41817a994..08aef4d7b5 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -1219,6 +1219,20 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.tokenEndpoints.verifyEmail(token, success, error); } + wallet.unsubscribe = (token, successCallback, errorCallback) => { + const success = (guid) => { + successCallback(guid); + wallet.applyIfNeeded(); + } + + const error = (message) => { + console.log(message); + errorCallback(message); + } + + wallet.tokenEndpoints.unsubscribe(token, success, error); + } + // Testing: only works on mock MyWallet wallet.refresh = () => { diff --git a/locales/en-human.json b/locales/en-human.json index 4d66d6136e..d3a5be0705 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -503,5 +503,6 @@ "TAKES_A_WHILE": "This may take a while. If your browser asks if you want to cancel the script, please press continue.", "NOTIFICATIONS" : "Email Notifications", "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses in your accounts and imported addresses are eligible to trigger notifications.", - "VERIFY_EMAIL_FIRST" : "Please verify your email address first." + "VERIFY_EMAIL_FIRST" : "Please verify your email address first.", + "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems." } From 58fc7d26166367abb846cb33057c8df5cedc3fb5 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 16:41:26 +0100 Subject: [PATCH 17/72] feat(Login): new browser approval - assuming user is in the same browser --- app/index.jade | 1 + .../authorizeApprove.controller.js | 33 +++++++++++++++++++ assets/js/routes.js | 10 ++++++ assets/js/services/wallet.service.js | 14 ++++++++ locales/en-human.json | 3 +- 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 assets/js/controllers/authorizeApprove.controller.js diff --git a/app/index.jade b/app/index.jade index 6b54fddeeb..ab73b4504c 100644 --- a/app/index.jade +++ b/app/index.jade @@ -89,6 +89,7 @@ head script(src='build/js/controllers/walletNavigation.controller.js') script(src='build/js/controllers/verifyEmail.controller.js') script(src='build/js/controllers/unsubscribe.controller.js') + script(src='build/js/controllers/authorizeApprove.controller.js') script(src='build/js/controllers/settings/settings.controller.js') script(src='build/js/controllers/settings/info.controller.js') script(src='build/js/controllers/settings/preferences.controller.js') diff --git a/assets/js/controllers/authorizeApprove.controller.js b/assets/js/controllers/authorizeApprove.controller.js new file mode 100644 index 0000000000..f74d516376 --- /dev/null +++ b/assets/js/controllers/authorizeApprove.controller.js @@ -0,0 +1,33 @@ +angular + .module('walletApp') + .controller("AuthorizeApproveCtrl", AuthorizeApproveCtrl); + +function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Alerts, $translate) { + const success = (uid) => { + + $window.close(); // This is sometimes ignored, hence the code below: + + if(uid) { + $translate('AUTHORIZE_APPROVE_SUCCESS').then(translation => { + $state.go("public.login-uid", {uid: uid}).then(() => { + Alerts.displaySuccess(translation) + }); + }); + } else { + $translate('AUTHORIZE_APPROVE_SUCCESS').then(translation => { + $state.go("public.login-no-uid").then(() => { + Alerts.displaySuccess(translation) + }); + }); + } + + + } + + const error = (message) => { + $state.go("public.login-no-uid"); + Alerts.displayError(message, true); + } + + Wallet.authorizeApprove($stateParams.token, success, error) +} diff --git a/assets/js/routes.js b/assets/js/routes.js index dacdd48ec5..1636ccea66 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -253,6 +253,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('wallet.common.authorize-approve', { + url: '/authorize-approve/{token:.*}', + views: { + top: top, + left: walletNav, + right: { + controller: 'AuthorizeApproveCtrl' + } + } + }) .state('wallet.common.settings', { url: '/settings', views: { diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 08aef4d7b5..321bbdbcb6 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -1233,6 +1233,20 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.tokenEndpoints.unsubscribe(token, success, error); } + wallet.authorizeApprove = (token, successCallback, errorCallback) => { + const success = (guid) => { + successCallback(guid); + wallet.applyIfNeeded(); + } + + const error = (message) => { + console.log(message); + errorCallback(message); + } + + wallet.tokenEndpoints.authorizeApprove(token, success, error); + } + // Testing: only works on mock MyWallet wallet.refresh = () => { diff --git a/locales/en-human.json b/locales/en-human.json index d3a5be0705..21cf1b8967 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -504,5 +504,6 @@ "NOTIFICATIONS" : "Email Notifications", "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses in your accounts and imported addresses are eligible to trigger notifications.", "VERIFY_EMAIL_FIRST" : "Please verify your email address first.", - "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems." + "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems.", + "AUTHORIZE_APPROVE_SUCCESS" : "Login approved! Please return to your previous browser / tab to see your wallet." } From d53490d140265861218fc1f208869c1929cb39af Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 18:37:06 +0100 Subject: [PATCH 18/72] feat(Login): new browser approval - different browser --- app/partials/authorize-approve.jade | 60 +++++++++++++++++++ assets/js/controllers/app.controller.js | 2 +- .../authorizeApprove.controller.js | 42 ++++++++++++- assets/js/routes.js | 20 +++---- assets/js/services/wallet.service.js | 12 +++- locales/en-human.json | 13 +++- 6 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 app/partials/authorize-approve.jade diff --git a/app/partials/authorize-approve.jade b/app/partials/authorize-approve.jade new file mode 100644 index 0000000000..cbfb0e8ee8 --- /dev/null +++ b/app/partials/authorize-approve.jade @@ -0,0 +1,60 @@ +div(ng-show="checkingToken") + header + hgroup + .flex-between.flex-center.flex-wrap + h2.em-300.mtn(translate="CHECKING_LOGIN_ATTEMPT") + hgroup + img(src="img/spinner.gif") +div(ng-show="differentBrowser") + header + hgroup + .flex-between.flex-center.flex-wrap + h2.em-300.mtn(translate="AUTHORIZE_APPROVE_OTHER_BROWSER") + p.em-300(translate="AUTHORIZE_APPROVE_OTHER_BROWSER_EXPLAIN") + hgroup + p.em-300 {{ details.device_change_reason }} + + .row + .col-xs-6 + h4(translate="AUTHORIZE_APPROVE_REQUESTING_DEVICE") + p + b + span(translate="BROWSER") + span : + | {{ details.requester_device_description }} + br + b + span(translate="IP_ADDRESS") + span : + | {{ details.requester_ip }} + br + b + span(translate="COUNTRY_OF_ORIGIN") + span : + | {{ details.requester_country }} + br + .col-xs-6 + h4(translate="AUTHORIZE_APPROVE_THIS_DEVICE") + p + b + span(translate="BROWSER") + span : + | {{ details.approver_device_description }} + br + b + span(translate="IP_ADDRESS") + span : + | {{ details.approver_ip }} + br + b + span(translate="COUNTRY_OF_ORIGIN") + span : + | {{ details.approver_country }} + br + + form.ptl.form-horizontal.clearfix(role="form") + .flex-between.flex-center.pal + .flex-center + button.button-danger(ui-ladda="busyApproving", ng-click="approve()", data-style="expand-left", ladda-translate="ACCEPT") + .flex-center.flex-end + button.button-primary(ui-ladda="busyRejecting", ng-click="reject()", data-style="expand-left", ladda-translate="REJECT") diff --git a/assets/js/controllers/app.controller.js b/assets/js/controllers/app.controller.js index 43310981f8..9b4f918068 100644 --- a/assets/js/controllers/app.controller.js +++ b/assets/js/controllers/app.controller.js @@ -59,7 +59,7 @@ function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout }; $scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => { - let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe']; + let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve']; if (loggedOutStates.every(s => toState.name !== s) && $scope.status.isLoggedIn === false) { $state.go("public.login-no-uid"); } diff --git a/assets/js/controllers/authorizeApprove.controller.js b/assets/js/controllers/authorizeApprove.controller.js index f74d516376..fb3a11541a 100644 --- a/assets/js/controllers/authorizeApprove.controller.js +++ b/assets/js/controllers/authorizeApprove.controller.js @@ -4,6 +4,10 @@ angular function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Alerts, $translate) { const success = (uid) => { + $scope.checkingToken = false; + $scope.busyApproving = false; + $scope.busyRejecting = false; + $window.close(); // This is sometimes ignored, hence the code below: @@ -20,14 +24,46 @@ function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Ale }); }); } - - } const error = (message) => { + $scope.checkingToken = false; + $scope.busyApproving = false; + $scope.busyRejecting = false; + $state.go("public.login-no-uid"); Alerts.displayError(message, true); } - Wallet.authorizeApprove($stateParams.token, success, error) + const differentBrowser = (details) => { + $scope.checkingToken = false; + + $scope.differentBrowser = true; + $scope.details = details; + } + + $scope.checkingToken = true; + + Wallet.authorizeApprove($stateParams.token, success, differentBrowser, null, error); + + $scope.approve = () => { + $scope.busyApproving = true; + Wallet.authorizeApprove($stateParams.token, success, () => {}, true, error); + } + + $scope.reject = () => { + $scope.busyRejecting = true; + + const rejected = () => { + $scope.busyRejecting = false; + + $translate('AUTHORIZE_REJECT_SUCCESS').then(translation => { + $state.go("public.login-no-uid").then(() => { + Alerts.displaySuccess(translation) + }); + }); + }; + + Wallet.authorizeApprove($stateParams.token, rejected, () => {}, false, error); + } } diff --git a/assets/js/routes.js b/assets/js/routes.js index 1636ccea66..9f5c99809d 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -129,6 +129,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('public.authorize-approve', { + url: '/authorize-approve/{token:.*}', + views: { + alerts: commonViews.alerts, + contents: { + templateUrl: 'partials/authorize-approve.jade', + controller: 'AuthorizeApproveCtrl' + } + } + }) .state('signup.finish', { url: '/signup/finish', views: commonViews @@ -253,16 +263,6 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) - .state('wallet.common.authorize-approve', { - url: '/authorize-approve/{token:.*}', - views: { - top: top, - left: walletNav, - right: { - controller: 'AuthorizeApproveCtrl' - } - } - }) .state('wallet.common.settings', { url: '/settings', views: { diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 321bbdbcb6..11914a0b8f 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -1214,6 +1214,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const error = (message) => { console.log(message); errorCallback(message); + wallet.applyIfNeeded(); } wallet.tokenEndpoints.verifyEmail(token, success, error); @@ -1228,12 +1229,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const error = (message) => { console.log(message); errorCallback(message); + wallet.applyIfNeeded(); } wallet.tokenEndpoints.unsubscribe(token, success, error); } - wallet.authorizeApprove = (token, successCallback, errorCallback) => { + wallet.authorizeApprove = (token, successCallback, differentBrowserCallback, differentBrowserApproved, errorCallback) => { const success = (guid) => { successCallback(guid); wallet.applyIfNeeded(); @@ -1242,9 +1244,15 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const error = (message) => { console.log(message); errorCallback(message); + wallet.applyIfNeeded(); + } + + const differentBrowser = (details) => { + differentBrowserCallback(details); + wallet.applyIfNeeded(); } - wallet.tokenEndpoints.authorizeApprove(token, success, error); + wallet.tokenEndpoints.authorizeApprove(token, success, differentBrowser, differentBrowserApproved, error); } // Testing: only works on mock MyWallet diff --git a/locales/en-human.json b/locales/en-human.json index 21cf1b8967..dcdb2d9b3b 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -505,5 +505,16 @@ "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses in your accounts and imported addresses are eligible to trigger notifications.", "VERIFY_EMAIL_FIRST" : "Please verify your email address first.", "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems.", - "AUTHORIZE_APPROVE_SUCCESS" : "Login approved! Please return to your previous browser / tab to see your wallet." + "AUTHORIZE_APPROVE_SUCCESS" : "Login approved! Please return to your previous browser / tab to see your wallet.", + "AUTHORIZE_APPROVE_OTHER_BROWSER" : "Login attempt from other browser", + "AUTHORIZE_APPROVE_OTHER_BROWSER_EXPLAIN" : "Someone, hopefully you, is attempting to login to your wallet from a different browser.", + "AUTHORIZE_APPROVE_REQUESTING_DEVICE" : "Requesting device", + "AUTHORIZE_APPROVE_THIS_DEVICE" : "This device", + "ACCEPT" : "Accept", + "REJECT" : "Reject", + "BROWSER" : "Browser", + "IP_ADDRESS" : "IP Address", + "COUNTRY_OF_ORIGIN" : "Country Of Origin", + "AUTHORIZE_REJECT_SUCCESS" : "Login attempt rejected! Please contact support if this was not you.", + "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt" } From 465c694af5082dc61667659141cffd23184a3314 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 20:17:18 +0100 Subject: [PATCH 19/72] fix(Deploy): ROOT_URL now required for dev, defaults to / in production --- README.md | 8 ++++++-- assets/js/services/wallet.service.js | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 55acd19ab5..6c836627a1 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ Install dependencies: npm install ``` +Create a file called `.env` in the root of the project. Put the following in it: + +``` +ROOT_URL=https://blockchain.info/ +``` + ## Build Grunt watches and compiles the Jade view templates and CSS. Keep it running: @@ -99,8 +105,6 @@ You can test the resulting files by setting `DIST=1` in `.env` and restarting th `index.html` should be cached using `If-Modified-Since` or `etag`. All other files contain a hash of their content and should be cached forever. -If you're using a different backend from `https://blockchain.info/` (e.g. for development), set `ROOT_URL` in `.env`. - ## Security Security issues can be reported to us in the following venues: diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 11914a0b8f..68e0fc9fd0 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -61,6 +61,8 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const customRootURL = $rootScope.rootURL; if(customRootURL) { wallet.api.setRootURL(customRootURL); + } else { + wallet.api.setRootURL("/"); } wallet.payment = MyWalletPayment; From 99c1e2d29244e14dba50b977c2540d9c24b64988 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 18 Dec 2015 20:24:07 +0100 Subject: [PATCH 20/72] test: fixed --- tests/mocks/my_wallet/my_blockchain_api_mock.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/mocks/my_wallet/my_blockchain_api_mock.coffee b/tests/mocks/my_wallet/my_blockchain_api_mock.coffee index 07cfed5eb2..709cb2caa3 100644 --- a/tests/mocks/my_wallet/my_blockchain_api_mock.coffee +++ b/tests/mocks/my_wallet/my_blockchain_api_mock.coffee @@ -8,4 +8,5 @@ angular $q.resolve(result) getFiatAtTime: (time, amount, currency) -> $q.resolve(amount.toFixed(2)) + setRootURL: () -> }) From bfe927f81b6ccbe7cd96d34b728410fdb3b0d49d Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 21 Dec 2015 14:04:01 +0100 Subject: [PATCH 21/72] ui(Accounts): different wording and don't confirm creation --- app/partials/home.jade | 2 +- app/partials/send.jade | 5 ++- app/partials/settings/import-address.jade | 2 +- app/partials/transaction.jade | 4 +- app/partials/transactions.jade | 2 +- app/templates/destination-input.jade | 4 +- .../js/controllers/accountForm.controller.js | 8 ---- assets/js/controllers/request.controller.js | 2 +- assets/js/controllers/send.controller.js | 4 +- assets/js/routes.js | 2 +- locales/en-human.json | 43 +++++++++---------- .../controllers/account_form_ctrl_spec.coffee | 7 --- tests/controllers/send_ctrl_spec.coffee | 2 +- 13 files changed, 35 insertions(+), 52 deletions(-) diff --git a/app/partials/home.jade b/app/partials/home.jade index 79531f04f3..7c00d49561 100644 --- a/app/partials/home.jade +++ b/app/partials/home.jade @@ -3,7 +3,7 @@ activity-feed .col-md-6.col-sm-12.col-xs-12.col-home.flex-column .section - h5.mtn.type-h5.em-400.mbl Account Balances + h5.mtn.type-h5.em-400.mbl(translate="BALANCES") ul.pln.account-balances li.flex-between.border-bottom-light(ng-repeat="account in activeAccounts") span.em-500 {{::account.label}} diff --git a/app/partials/send.jade b/app/partials/send.jade index ce48cf4d1c..edc23b9216 100644 --- a/app/partials/send.jade +++ b/app/partials/send.jade @@ -34,12 +34,13 @@ ui-select-choices(repeat="origin in origins | filter: getFilter($select.search)" group-by="'type'" ui-disable-choice="hasZeroBalance(origin)") span(ng-class="{aaa: hasZeroBalance(origin)}") label-origin(origin='origin' highlight="$select.search") - span.help-block(ng-show="sendForm.from.$invalid && sendForm.destinations0.$touched") Must select an account to send from + span.help-block(ng-show="sendForm.from.$invalid && sendForm.destinations0.$touched", translate="MUST_SELECT_ORIGIN") //- Advanced Send .form-group.bc-modal-fade.mvl.advanced-recipient.row //- To label.pts.col-sm-2.col-xs-12 - span(translate="TO:") + span(translate="TO") + |: .col-sm-10.col-xs-12 .flex-column(ng-class="{'advanced-recipient-row': advanced}" ng-repeat="item in transaction.destinations track by $index") p.form-control-static(ng-hide="originsLoaded") diff --git a/app/partials/settings/import-address.jade b/app/partials/settings/import-address.jade index 37f0d471bf..ff6115b995 100644 --- a/app/partials/settings/import-address.jade +++ b/app/partials/settings/import-address.jade @@ -66,7 +66,7 @@ p.form-control-static {{ address.address }} .form-group label.col-sm-3.control-label - span(translate="TO_ACCOUNT") + span(translate="TO") |: .col-sm-7 ui-select(ng-model="fields.account") diff --git a/app/partials/transaction.jade b/app/partials/transaction.jade index 3b1b950097..db13e386c8 100644 --- a/app/partials/transaction.jade +++ b/app/partials/transaction.jade @@ -14,7 +14,9 @@ label(translate="FROM:") p.text.mbs {{::from}} .tx-receiver - label(translate="TO:") + label + span(translate="TO") + |: ul.pln.flex-column.flex.type-sm li.flex-center.mbm(ng-repeat="destination in destinations") span.text-overflow-hidden.flex-1.flex.text diff --git a/app/partials/transactions.jade b/app/partials/transactions.jade index 4d93ac7e3e..2ab53f327e 100644 --- a/app/partials/transactions.jade +++ b/app/partials/transactions.jade @@ -8,7 +8,7 @@ translate="{{ f }}" ) .filter-search - input(type="text" placeholder="Search by Account name or Address" ng-model="searchText") + input(type="text" placeholder="{{ 'SEARCH' | translate }}" ng-model="searchText") i.ti-search .transaction-feed .flex-center.flex-justify.flex-column.mtvl(ng-hide="loading || getTotal(accountIndex) > 0 || (transactions | filter:transactionFilter).length > 0 || selectedAccountIndex == 'imported'") diff --git a/app/templates/destination-input.jade b/app/templates/destination-input.jade index a59684eea0..8328fc2e9e 100644 --- a/app/templates/destination-input.jade +++ b/app/templates/destination-input.jade @@ -10,7 +10,7 @@ type="text" tabindex="1" autocomplete="off" - placeholder="Paste or scan an address or select an account" + placeholder="Paste or scan an address or select a destination" ng-model="model.address" ng-hide="model.type === 'Accounts'" ng-blur="blur()" @@ -29,8 +29,6 @@ span.caret span.sr-only Toggle Dropdown ul.uib-dropdown-menu.dropdown-menu-right.drop-menu - li.dropdown-header - | Accounts li(ng-repeat="account in accounts") a(ng-click="setModel(account)" ng-disabled="true") | {{ account.label }} diff --git a/assets/js/controllers/accountForm.controller.js b/assets/js/controllers/accountForm.controller.js index 1fea89249c..9c6e7881ff 100644 --- a/assets/js/controllers/accountForm.controller.js +++ b/assets/js/controllers/accountForm.controller.js @@ -29,14 +29,6 @@ function AccountFormCtrl($scope, Wallet, $uibModalInstance, $log, $translate, ac $scope.status.busy = false; $uibModalInstance.dismiss(""); Wallet.saveActivity(3); - $translate(['SUCCESS', 'ACCOUNT_CREATED']).then(translations => { - $scope.$emit('showNotification', { - type: 'created-account', - icon: 'ti-layout-list-post', - heading: translations.SUCCESS, - msg: translations.ACCOUNT_CREATED - }); - }); }; const error = () => {$scope.status.busy = false;} diff --git a/assets/js/controllers/request.controller.js b/assets/js/controllers/request.controller.js index da25d460ec..a88fe3f485 100644 --- a/assets/js/controllers/request.controller.js +++ b/assets/js/controllers/request.controller.js @@ -22,7 +22,7 @@ function RequestCtrl($scope, Wallet, Alerts, currency, $uibModalInstance, $log, for(const account of $scope.accounts()) { if (account.index != null && !account.archived) { let acct = angular.copy(account); - acct.type = "Accounts"; + acct.type = ""; $scope.destinations.push(acct); if ((destination != null) && (destination.index != null) && destination.index === acct.index) { $scope.fields.to = acct; diff --git a/assets/js/controllers/send.controller.js b/assets/js/controllers/send.controller.js index 28f33c30c1..19462dec1d 100644 --- a/assets/js/controllers/send.controller.js +++ b/assets/js/controllers/send.controller.js @@ -228,7 +228,7 @@ function SendCtrl($scope, $log, Wallet, Alerts, currency, $uibModalInstance, $ti } else { let dest = destinations[0]; $scope.toLabel = dest.index == null ? - dest.label || dest.address : `${dest.label} Account`; + dest.label || dest.address : `${dest.label}`; } }; @@ -271,7 +271,7 @@ function SendCtrl($scope, $log, Wallet, Alerts, currency, $uibModalInstance, $ti balance: origin.balance, archived: origin.archived }; - formatted.type = origin.index != null ? 'Accounts' : 'Imported Addresses'; + formatted.type = origin.index != null ? '' : 'Imported Addresses'; if (origin.index == null) formatted.isWatchOnly = origin.isWatchOnly; return formatted; }; diff --git a/assets/js/routes.js b/assets/js/routes.js index 9f5c99809d..3f2b3837b1 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -307,7 +307,7 @@ function AppRouter($stateProvider, $urlRouterProvider) { } }) .state('wallet.common.settings.accounts_index', { - url: '/accounts', + url: '/addresses', views: { settings: { templateUrl: 'partials/settings/accounts.jade', diff --git a/locales/en-human.json b/locales/en-human.json index dcdb2d9b3b..b4e5c51a1e 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -7,11 +7,9 @@ "LOGIN_HELP": "Login Help", "LOGIN_HELP_TEXT": "Need help accessing your wallet?", "CREATE_WALLET": "Create Wallet", - "ACCOUNTS": "Accounts", - "NEW_ACCOUNT": "Add Account +", + "NEW_ACCOUNT": "Add +", "MY_TRANSACTIONS": "Transactions", "ALL": "All", - "ACCOUNT": "Account", "SEND_FEEDBACK": "Send Feedback", "BACK": "Back", "CONTACT_SUPPORT": "Contact Support", @@ -68,7 +66,7 @@ "LANGUAGE" : "Wallet Language", "PREFERENCES" : "Preferences", "PREFERENCES_EXPLAIN" : "Customize your wallet experience.", - "MENU_ACCOUNTS_AND_ADDRESSES" : "Accounts & Addresses", + "MENU_ACCOUNTS_AND_ADDRESSES" : "Addresses", "WALLET_INFO" : "Wallet Information", "WALLET_INFO_EXPLAIN" : "Use the Wallet ID to log in via our web client or scan the code below with your iPhone or Android Blockchain Wallet App to access your wallet on your mobile.", "PAIRING_CODE" : "Mobile App Pairing Code", @@ -119,20 +117,20 @@ "CANCEL_EDIT_NOTE" : "Cancel", "WALLET_RECOVERY_PHRASE" : "Wallet Recovery Phrase", "RECOVERY_PHRASE" : "Recovery Phrase", - "RECOVERY_PHRASE_EXPLAIN" : "Your recovery phrase can be used to restore all your funds in the case of a lost password or a loss of service at Blockchain. Note, that the recovery phrase never changes and recovers all of your existing bitcoins as well as newly received funds in this wallet. Please note that imported addresses are not backed up by the wallet recovery phrase. We strongly recommend to transfer funds from imported addresses into an account in this wallet.", + "RECOVERY_PHRASE_EXPLAIN" : "Your recovery phrase can be used to restore all your funds in the case of a lost password or a loss of service at Blockchain. Note, that the recovery phrase never changes and recovers all of your existing bitcoins as well as newly received funds in this wallet. Please note that imported addresses are not backed up by the wallet recovery phrase. We strongly recommend to transfer funds from imported addresses into this wallet.", "CONFIRM_RECOVERY_PHRASE" : "Backup Phrase", "RECOVERY_ERROR": "Unable to import now, please try again.", "NEXT_STEP" : "Next Step", "NEXT" : "Next", "EMAIL" : "Email", "NEW_ACCT_WELCOME" : "Create A New Blockchain Wallet", - "CREATE_NEW_ACCOUNT" : "Create New Account", - "CREATE_ACCOUNT" : "Create Account", + "CREATE_NEW_ACCOUNT" : "Create New", + "CREATE_ACCOUNT" : "Create New", "CREATE_ADDRESS" : "New Address", - "ACCOUNT_NAME" : "Account Name:", + "ACCOUNT_NAME" : "Name:", "REVEAL_XPUB" : "Show xPub", "MANAGE_ADDRESSES" : "Manage Addresses", - "ACCOUNT_XPUB_MODAL_TITLE" : "Account xPub", + "ACCOUNT_XPUB_MODAL_TITLE" : "Extended Public Key", "PENDING" : "Pending", "COMPLETE" : "Complete", "TRANSACTION_COMPLETE" : "Transaction Complete", @@ -143,10 +141,8 @@ "FROM" : "From", "FROM:" : "From:", "TO" : "To", - "TO:" : "To:", "FEE" : "Fee", "TOTAL" : "Total", - "TO_ACCOUNT" : "To Account", "INTERNAL" : "Internal", "GOOGLE_AUTH_CODE" : "Google Authenticator", "SMS_CODE" : "SMS Code", @@ -176,8 +172,7 @@ "HANDLE_BITCOIN_LINKS_EXPLAIN" : "Enable this to allow your Blockchain Wallet to handle bitcoin payment links in the web browser. This will make your experience more convenient when transacting online.", "SET_HANDLE_BITCOIN_LINKS" : "Enable", "HANDLE_BITCOIN_LINKS_STATUS_UNKNOWN" : "We can't detect whether or not handling of bitcoin links has been enabled. If it has already been enabled, nothing will happen.", - "ACCOUNT_MANAGEMENT" : "Account Management", - "ACCOUNT_MANAGEMENT_EXPLAIN" : "Your Blockchain Wallet can create separate accounts within your single wallet. You may find this feature useful when you would like to separate your funds between a savings account and a spending account, or between a personal account and a business account.", + "ACCOUNT_MANAGEMENT_EXPLAIN" : "You can customize the way you organize your bitcoins in your Blockchain Wallet. You may find this useful when you would like to separate your funds between personal and business expenses, or put your savings aside.", "BALANCE" : "Balance", "MAKE_DEFAULT" : "Make default", "RENAME" : "Rename", @@ -208,7 +203,7 @@ "DISABLE_BLOCK_TOR" : "Allow", "ALLOWED" : "Allowed", "BLOCKED" : "Blocked", - "RENAME_ACCOUNT" : "Rename Account", + "RENAME_ACCOUNT" : "Name", "RENAME" : "Rename", "TRANSACTION_WILL_COMPLETE_IN" : "This transaction will complete in approximately {{ minutes }} minutes.", "SPENDABLE_ADDRESSES" : "Spendable Addresses", @@ -228,7 +223,7 @@ "IMPORT_BITCOIN_ADDRESS" : "Import Existing Bitcoin Address", "IMPORT_BITCOIN_ADDRESS_SWEEP" : "Transfer Funds from Imported Address", "IMPORT_BITCOIN_ADDRESS_EXPLAIN" : "Your wallet automatically creates new bitcoin addresses as it needs them. You can optionally import an existing address and transfer the funds to your wallet if you have the corresponding Private Key. This is an advanced functionality and only suggested for advanced users.", - "CONFIRM_NOT_SWEEP" : "Are you sure? We recommend that you sweep these funds into an account.", + "CONFIRM_NOT_SWEEP" : "Are you sure? We recommend that you sweep these funds.", "YOUR_PRIVATE_KEY" : "Show Private Key", "PRIVATE_KEY_VALID" : "The Private Key appears to be Valid", "ADDRESS_VALID" : "The Bitcoin address is valid", @@ -334,7 +329,6 @@ "BITCOIN_CURRENCY_EXPLAIN": "Adjust the precision you would prefer bitcoin values to be displayed in.", "JUST_RECEIVED_BITCOIN" : "You've just received Bitcoin!", "SUCCESS" : "Success!", - "ACCOUNT_CREATED" : "You've successfully created an account", "BITCOIN_SENT" : "You've successfully sent bitcoin", "NO_TRANSACTIONS_YET" : "Your Transactions", "SORRY_ZERO_TXS" : "Sorry, we couldn't find any transactions!", @@ -415,10 +409,10 @@ "EMAIL_VERIFIED" : "Email Verified", "LAUNCHED" : "We've launched!", "GET_STARTED" : "Get Started", - "WELCOME_TEXT" : "We've launched our wallet! And we're excited to show you all the great features we've added. A brand new Security Center, account management, revamped algorithms and a shiny new interface to name a few - go ahead, get started!", + "WELCOME_TEXT" : "We've launched our wallet! And we're excited to show you all the great features we've added. A brand new Security Center, revamped algorithms and a shiny new interface to name a few - go ahead, get started!", "TOS" : "ToS", "PRIVACY" : "Privacy Policy", - "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet into multiple accounts to better allocate, track and manage your funds. Some common account names include Savings, Spending, and Business Expenses.", + "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet to better allocate, track and manage your bitcoins. Some common names include Savings, Spending, and Business Expenses.", "TRANSACTION_DETAIL_STATUS" : "Your transaction has been submitted to the bitcoin network and is waiting for miners to validate the transaction. A transaction is considered to be confirmed when there are 3 network confirmations.", "SEND_BITOIN_STEP2B" : "Add a note to remind yourself of what this transaction relates to. This note will be private and only seen by you, unless you select the Make Public option. If a note is public, anyone viewing the transaction on Blockchain will be able to read your note.", "ADVANCED_SEND_STEP_1" : "A normal miners fee is recommended for most transactions. You can override this recommendation but be advised that lower fees may take several days to confirm.", @@ -427,7 +421,7 @@ "VALUE_NOW" : "Value Now", "VERIFY_ON_BCI" : "Verify on Blockchain.info", "ARE_YOU_SURE" : "We just want to double check! Are you sure you wish to proceed? Remember you cannot go back to your old wallet :)", - "ACCOUNT_NAME_TAKEN" : "That account name is already in use", + "ACCOUNT_NAME_TAKEN" : "That name is already in use", "EMAIL_ADDRESS_TOOLTIP" : "Your verified email address is used to send login codes when unusual activity is detected and to send bitcoin payment alerts when you receive funds.", "RECOVERY_PHRASE_TOOLTIP" : "Your recovery phrase allows you to restore your wallet in case of loss of password or extended downtime on our servers.", "PASSWORD_HINT_TOOLTIP" : "Allows us to send your password hint to your verified email address in case you forget your password.", @@ -462,7 +456,7 @@ "LOGGING" : "Activity Logging", "LOG" : "Log", "LOGGING_EXPLAIN" : "Record wallet activity and display it in your activity feed.", - "ACTIVITY_1": "Accounts", + "ACTIVITY_1": "Addresses", "ACTIVITY_1_DESC": "Created My Bitcoin Wallet", "ACTIVITY_2_DESC": "Set password", "ACTIVITY_3_DESC": "Created wallet!", @@ -475,7 +469,7 @@ "EDIT_NOTE": "Edit", "DELETE_NOTE": "Delete", "ALPHA_WARNING": "Please note, this is an Alpha of a new product. Please do not test this wallet with more funds than you are willing to lose.", - "XPUB_WARNING": "You should only give this Public Key to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", + "XPUB_WARNING": "You should only give this Extended Public Key (xPub) to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", "LOST_WALLET_ID": "I've lost my Wallet ID", "LOST_WALLET_PWD": "I've lost my Wallet Password", "LOST_2FA": "I've lost my 2FA Device", @@ -502,7 +496,7 @@ "REDIRECTING": "We're redirecting you to your wallet now!", "TAKES_A_WHILE": "This may take a while. If your browser asks if you want to cancel the script, please press continue.", "NOTIFICATIONS" : "Email Notifications", - "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses in your accounts and imported addresses are eligible to trigger notifications.", + "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses and imported addresses are eligible to trigger notifications.", "VERIFY_EMAIL_FIRST" : "Please verify your email address first.", "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems.", "AUTHORIZE_APPROVE_SUCCESS" : "Login approved! Please return to your previous browser / tab to see your wallet.", @@ -516,5 +510,8 @@ "IP_ADDRESS" : "IP Address", "COUNTRY_OF_ORIGIN" : "Country Of Origin", "AUTHORIZE_REJECT_SUCCESS" : "Login attempt rejected! Please contact support if this was not you.", - "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt" + "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt", + "SEARCH" : "Search", + "BALANCES" : "Balances", + "MUST_SELECT_ORIGIN" :"Must select where to send from" } diff --git a/tests/controllers/account_form_ctrl_spec.coffee b/tests/controllers/account_form_ctrl_spec.coffee index f3685fa29c..e7ec18f3ad 100644 --- a/tests/controllers/account_form_ctrl_spec.coffee +++ b/tests/controllers/account_form_ctrl_spec.coffee @@ -76,13 +76,6 @@ describe "AccountFormCtrl", -> expect(Wallet.accounts()[Wallet.accounts().length - 1].label).toBe("New Account") ) - it "should show a confirmation modal", inject(($uibModal)-> - spyOn($uibModal, "open").and.callThrough() - scope.createAccount() - expect($uibModal.open).toHaveBeenCalled() - expect($uibModal.open.calls.argsFor(0)[0].windowClass).toEqual("notification-modal") - ) - describe "rename", -> it "original name should be shown", -> diff --git a/tests/controllers/send_ctrl_spec.coffee b/tests/controllers/send_ctrl_spec.coffee index cc43c98485..da08354e0f 100644 --- a/tests/controllers/send_ctrl_spec.coffee +++ b/tests/controllers/send_ctrl_spec.coffee @@ -517,7 +517,7 @@ describe "SendCtrl", -> it "should set the label to an account", -> scope.transaction.destinations[0] = scope.accounts()[0] scope.updateToLabel() - expect(scope.toLabel).toEqual('Checking Account') + expect(scope.toLabel).toEqual('Checking') it "should set the label when advanced", -> scope.advanced = true From 52f1eddd1a9985069debf737dc98e182f119d864 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 21 Dec 2015 14:27:02 +0100 Subject: [PATCH 22/72] chore(Changelog): update for beta branch --- Changelog.md | 55 +++++++++++++++++++++++++++++++++++++++++++----- Gruntfile.coffee | 2 ++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2b8d998ec0..473d33243f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ __Blockchain HD Frontend__ _Recent changes_ -# (2015-12-29) +# (2015-12-21) @@ -10,10 +10,55 @@ _Recent changes_ ## Bug Fixes -- **Currency:** correct decimal places for bits and mBTC - ([e5d3c6c4](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/e5d3c6c4f2cb2b377b54b259be6561d129f6da18)) -- **Login:** disable browser validation and autocomplete - ([c0ecd11d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/c0ecd11da6262a42662cc79e18f0f83648424184)) +- **Deploy:** + - ROOT_URL now required for dev, defaults to / in production + ([93dd3c2d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/93dd3c2df8dcf7127c0e98e927d9fda734a54e09)) + - dmore aggresive cleaning in dist task + ([32ef6349](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/32ef6349804aacadc57c1c1ea61b5ec34e450396)) + - clean bower and npm cache in dist task + ([38be7640](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/38be7640dea9e3dbda21a2526c6814f953ed627f)) + - move CSS and JS to /js so that relative font path works + ([bef9a15e](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/bef9a15ea18120da85e9c694abafb421e3134f40)) +- **Feedback:** + - missing controller file + ([41b74a85](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/41b74a858117489b1ffbf93ed916af824cb8a9d3)) + - use new endpoint + ([7857e2b2](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/7857e2b2542cabf01baa12511c1372d7e62127fc)) +- **Signup:** improved validation + ([fb9966fa](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/fb9966face09ae4037c5ae0f8d2645e069fba7bc)) + + +## Features + +- remove beta invite system + ([788b8aa5](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/788b8aa503aca65b20819a730531b2b7b9062b07)) +- **Login:** + - new browser approval - different browser + ([c3195ad6](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/c3195ad6e3b99830cfb71b4c5eef39a40be2b0fc)) + - new browser approval - assuming user is in the same browser + ([4ad6e26c](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/4ad6e26cd9e91056589e53e97a856d17dbbb8dc5)) +- **Release:** use beta version of my-wallet-v3 bower + ([0d828172](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/0d82817246118dd6d7488388154e341f556fae40)) +- **Routes:** + - show modal after verifying email + ([d1a8991d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/d1a8991df43236d29dbab783716eecbb3ecf447d)) + - login and verify email routes and endpoint support + ([ff315a76](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/ff315a76844d6f2ee5c079227bdb5b1230f76c07)) +- **Unsubscribe:** unsubscribe route + ([9ea51ccb](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/9ea51ccb2c74d57bc658f338c62a24ef93eb45a3)) + + +## Refactor + +- **Cookies:** replace references to deprecated with +- **Dependencies:** removed unused npm dependencies. Removed unmaintained E2E tests. +- **Signup:** cleaned up controller and improved tests + + +## Test + +- fixed +- **Mocks:** added MyWalletTokenEndpoints mock diff --git a/Gruntfile.coffee b/Gruntfile.coffee index bf5e2a6256..67d718af60 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -405,6 +405,8 @@ module.exports = (grunt) -> intro : 'Recent changes' grep_commits: '^fix|^feat|^ui|^copy|^docs|^dep|^refactor|^chore|^test|^dev|BREAKING' repo_url: 'https://github.com/blockchain/My-Wallet-HD-Frontend' + branch_name: 'beta' + tag: '1.2.17' coveralls: options: From e0ac8d638d3b01e38622a45987f7e110e3a90c10 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 21 Dec 2015 14:43:56 +0100 Subject: [PATCH 23/72] Revert "Different account wording, don't confirm creation" --- app/partials/home.jade | 2 +- app/partials/send.jade | 5 +-- app/partials/settings/import-address.jade | 2 +- app/partials/transaction.jade | 4 +- app/partials/transactions.jade | 2 +- app/templates/destination-input.jade | 4 +- .../js/controllers/accountForm.controller.js | 8 ++++ assets/js/controllers/request.controller.js | 2 +- assets/js/controllers/send.controller.js | 4 +- assets/js/routes.js | 2 +- locales/en-human.json | 43 ++++++++++--------- .../controllers/account_form_ctrl_spec.coffee | 7 +++ tests/controllers/send_ctrl_spec.coffee | 2 +- 13 files changed, 52 insertions(+), 35 deletions(-) diff --git a/app/partials/home.jade b/app/partials/home.jade index 7c00d49561..79531f04f3 100644 --- a/app/partials/home.jade +++ b/app/partials/home.jade @@ -3,7 +3,7 @@ activity-feed .col-md-6.col-sm-12.col-xs-12.col-home.flex-column .section - h5.mtn.type-h5.em-400.mbl(translate="BALANCES") + h5.mtn.type-h5.em-400.mbl Account Balances ul.pln.account-balances li.flex-between.border-bottom-light(ng-repeat="account in activeAccounts") span.em-500 {{::account.label}} diff --git a/app/partials/send.jade b/app/partials/send.jade index edc23b9216..ce48cf4d1c 100644 --- a/app/partials/send.jade +++ b/app/partials/send.jade @@ -34,13 +34,12 @@ ui-select-choices(repeat="origin in origins | filter: getFilter($select.search)" group-by="'type'" ui-disable-choice="hasZeroBalance(origin)") span(ng-class="{aaa: hasZeroBalance(origin)}") label-origin(origin='origin' highlight="$select.search") - span.help-block(ng-show="sendForm.from.$invalid && sendForm.destinations0.$touched", translate="MUST_SELECT_ORIGIN") + span.help-block(ng-show="sendForm.from.$invalid && sendForm.destinations0.$touched") Must select an account to send from //- Advanced Send .form-group.bc-modal-fade.mvl.advanced-recipient.row //- To label.pts.col-sm-2.col-xs-12 - span(translate="TO") - |: + span(translate="TO:") .col-sm-10.col-xs-12 .flex-column(ng-class="{'advanced-recipient-row': advanced}" ng-repeat="item in transaction.destinations track by $index") p.form-control-static(ng-hide="originsLoaded") diff --git a/app/partials/settings/import-address.jade b/app/partials/settings/import-address.jade index ff6115b995..37f0d471bf 100644 --- a/app/partials/settings/import-address.jade +++ b/app/partials/settings/import-address.jade @@ -66,7 +66,7 @@ p.form-control-static {{ address.address }} .form-group label.col-sm-3.control-label - span(translate="TO") + span(translate="TO_ACCOUNT") |: .col-sm-7 ui-select(ng-model="fields.account") diff --git a/app/partials/transaction.jade b/app/partials/transaction.jade index db13e386c8..3b1b950097 100644 --- a/app/partials/transaction.jade +++ b/app/partials/transaction.jade @@ -14,9 +14,7 @@ label(translate="FROM:") p.text.mbs {{::from}} .tx-receiver - label - span(translate="TO") - |: + label(translate="TO:") ul.pln.flex-column.flex.type-sm li.flex-center.mbm(ng-repeat="destination in destinations") span.text-overflow-hidden.flex-1.flex.text diff --git a/app/partials/transactions.jade b/app/partials/transactions.jade index 2ab53f327e..4d93ac7e3e 100644 --- a/app/partials/transactions.jade +++ b/app/partials/transactions.jade @@ -8,7 +8,7 @@ translate="{{ f }}" ) .filter-search - input(type="text" placeholder="{{ 'SEARCH' | translate }}" ng-model="searchText") + input(type="text" placeholder="Search by Account name or Address" ng-model="searchText") i.ti-search .transaction-feed .flex-center.flex-justify.flex-column.mtvl(ng-hide="loading || getTotal(accountIndex) > 0 || (transactions | filter:transactionFilter).length > 0 || selectedAccountIndex == 'imported'") diff --git a/app/templates/destination-input.jade b/app/templates/destination-input.jade index 8328fc2e9e..a59684eea0 100644 --- a/app/templates/destination-input.jade +++ b/app/templates/destination-input.jade @@ -10,7 +10,7 @@ type="text" tabindex="1" autocomplete="off" - placeholder="Paste or scan an address or select a destination" + placeholder="Paste or scan an address or select an account" ng-model="model.address" ng-hide="model.type === 'Accounts'" ng-blur="blur()" @@ -29,6 +29,8 @@ span.caret span.sr-only Toggle Dropdown ul.uib-dropdown-menu.dropdown-menu-right.drop-menu + li.dropdown-header + | Accounts li(ng-repeat="account in accounts") a(ng-click="setModel(account)" ng-disabled="true") | {{ account.label }} diff --git a/assets/js/controllers/accountForm.controller.js b/assets/js/controllers/accountForm.controller.js index 9c6e7881ff..1fea89249c 100644 --- a/assets/js/controllers/accountForm.controller.js +++ b/assets/js/controllers/accountForm.controller.js @@ -29,6 +29,14 @@ function AccountFormCtrl($scope, Wallet, $uibModalInstance, $log, $translate, ac $scope.status.busy = false; $uibModalInstance.dismiss(""); Wallet.saveActivity(3); + $translate(['SUCCESS', 'ACCOUNT_CREATED']).then(translations => { + $scope.$emit('showNotification', { + type: 'created-account', + icon: 'ti-layout-list-post', + heading: translations.SUCCESS, + msg: translations.ACCOUNT_CREATED + }); + }); }; const error = () => {$scope.status.busy = false;} diff --git a/assets/js/controllers/request.controller.js b/assets/js/controllers/request.controller.js index a88fe3f485..da25d460ec 100644 --- a/assets/js/controllers/request.controller.js +++ b/assets/js/controllers/request.controller.js @@ -22,7 +22,7 @@ function RequestCtrl($scope, Wallet, Alerts, currency, $uibModalInstance, $log, for(const account of $scope.accounts()) { if (account.index != null && !account.archived) { let acct = angular.copy(account); - acct.type = ""; + acct.type = "Accounts"; $scope.destinations.push(acct); if ((destination != null) && (destination.index != null) && destination.index === acct.index) { $scope.fields.to = acct; diff --git a/assets/js/controllers/send.controller.js b/assets/js/controllers/send.controller.js index 19462dec1d..28f33c30c1 100644 --- a/assets/js/controllers/send.controller.js +++ b/assets/js/controllers/send.controller.js @@ -228,7 +228,7 @@ function SendCtrl($scope, $log, Wallet, Alerts, currency, $uibModalInstance, $ti } else { let dest = destinations[0]; $scope.toLabel = dest.index == null ? - dest.label || dest.address : `${dest.label}`; + dest.label || dest.address : `${dest.label} Account`; } }; @@ -271,7 +271,7 @@ function SendCtrl($scope, $log, Wallet, Alerts, currency, $uibModalInstance, $ti balance: origin.balance, archived: origin.archived }; - formatted.type = origin.index != null ? '' : 'Imported Addresses'; + formatted.type = origin.index != null ? 'Accounts' : 'Imported Addresses'; if (origin.index == null) formatted.isWatchOnly = origin.isWatchOnly; return formatted; }; diff --git a/assets/js/routes.js b/assets/js/routes.js index 3f2b3837b1..9f5c99809d 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -307,7 +307,7 @@ function AppRouter($stateProvider, $urlRouterProvider) { } }) .state('wallet.common.settings.accounts_index', { - url: '/addresses', + url: '/accounts', views: { settings: { templateUrl: 'partials/settings/accounts.jade', diff --git a/locales/en-human.json b/locales/en-human.json index b4e5c51a1e..dcdb2d9b3b 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -7,9 +7,11 @@ "LOGIN_HELP": "Login Help", "LOGIN_HELP_TEXT": "Need help accessing your wallet?", "CREATE_WALLET": "Create Wallet", - "NEW_ACCOUNT": "Add +", + "ACCOUNTS": "Accounts", + "NEW_ACCOUNT": "Add Account +", "MY_TRANSACTIONS": "Transactions", "ALL": "All", + "ACCOUNT": "Account", "SEND_FEEDBACK": "Send Feedback", "BACK": "Back", "CONTACT_SUPPORT": "Contact Support", @@ -66,7 +68,7 @@ "LANGUAGE" : "Wallet Language", "PREFERENCES" : "Preferences", "PREFERENCES_EXPLAIN" : "Customize your wallet experience.", - "MENU_ACCOUNTS_AND_ADDRESSES" : "Addresses", + "MENU_ACCOUNTS_AND_ADDRESSES" : "Accounts & Addresses", "WALLET_INFO" : "Wallet Information", "WALLET_INFO_EXPLAIN" : "Use the Wallet ID to log in via our web client or scan the code below with your iPhone or Android Blockchain Wallet App to access your wallet on your mobile.", "PAIRING_CODE" : "Mobile App Pairing Code", @@ -117,20 +119,20 @@ "CANCEL_EDIT_NOTE" : "Cancel", "WALLET_RECOVERY_PHRASE" : "Wallet Recovery Phrase", "RECOVERY_PHRASE" : "Recovery Phrase", - "RECOVERY_PHRASE_EXPLAIN" : "Your recovery phrase can be used to restore all your funds in the case of a lost password or a loss of service at Blockchain. Note, that the recovery phrase never changes and recovers all of your existing bitcoins as well as newly received funds in this wallet. Please note that imported addresses are not backed up by the wallet recovery phrase. We strongly recommend to transfer funds from imported addresses into this wallet.", + "RECOVERY_PHRASE_EXPLAIN" : "Your recovery phrase can be used to restore all your funds in the case of a lost password or a loss of service at Blockchain. Note, that the recovery phrase never changes and recovers all of your existing bitcoins as well as newly received funds in this wallet. Please note that imported addresses are not backed up by the wallet recovery phrase. We strongly recommend to transfer funds from imported addresses into an account in this wallet.", "CONFIRM_RECOVERY_PHRASE" : "Backup Phrase", "RECOVERY_ERROR": "Unable to import now, please try again.", "NEXT_STEP" : "Next Step", "NEXT" : "Next", "EMAIL" : "Email", "NEW_ACCT_WELCOME" : "Create A New Blockchain Wallet", - "CREATE_NEW_ACCOUNT" : "Create New", - "CREATE_ACCOUNT" : "Create New", + "CREATE_NEW_ACCOUNT" : "Create New Account", + "CREATE_ACCOUNT" : "Create Account", "CREATE_ADDRESS" : "New Address", - "ACCOUNT_NAME" : "Name:", + "ACCOUNT_NAME" : "Account Name:", "REVEAL_XPUB" : "Show xPub", "MANAGE_ADDRESSES" : "Manage Addresses", - "ACCOUNT_XPUB_MODAL_TITLE" : "Extended Public Key", + "ACCOUNT_XPUB_MODAL_TITLE" : "Account xPub", "PENDING" : "Pending", "COMPLETE" : "Complete", "TRANSACTION_COMPLETE" : "Transaction Complete", @@ -141,8 +143,10 @@ "FROM" : "From", "FROM:" : "From:", "TO" : "To", + "TO:" : "To:", "FEE" : "Fee", "TOTAL" : "Total", + "TO_ACCOUNT" : "To Account", "INTERNAL" : "Internal", "GOOGLE_AUTH_CODE" : "Google Authenticator", "SMS_CODE" : "SMS Code", @@ -172,7 +176,8 @@ "HANDLE_BITCOIN_LINKS_EXPLAIN" : "Enable this to allow your Blockchain Wallet to handle bitcoin payment links in the web browser. This will make your experience more convenient when transacting online.", "SET_HANDLE_BITCOIN_LINKS" : "Enable", "HANDLE_BITCOIN_LINKS_STATUS_UNKNOWN" : "We can't detect whether or not handling of bitcoin links has been enabled. If it has already been enabled, nothing will happen.", - "ACCOUNT_MANAGEMENT_EXPLAIN" : "You can customize the way you organize your bitcoins in your Blockchain Wallet. You may find this useful when you would like to separate your funds between personal and business expenses, or put your savings aside.", + "ACCOUNT_MANAGEMENT" : "Account Management", + "ACCOUNT_MANAGEMENT_EXPLAIN" : "Your Blockchain Wallet can create separate accounts within your single wallet. You may find this feature useful when you would like to separate your funds between a savings account and a spending account, or between a personal account and a business account.", "BALANCE" : "Balance", "MAKE_DEFAULT" : "Make default", "RENAME" : "Rename", @@ -203,7 +208,7 @@ "DISABLE_BLOCK_TOR" : "Allow", "ALLOWED" : "Allowed", "BLOCKED" : "Blocked", - "RENAME_ACCOUNT" : "Name", + "RENAME_ACCOUNT" : "Rename Account", "RENAME" : "Rename", "TRANSACTION_WILL_COMPLETE_IN" : "This transaction will complete in approximately {{ minutes }} minutes.", "SPENDABLE_ADDRESSES" : "Spendable Addresses", @@ -223,7 +228,7 @@ "IMPORT_BITCOIN_ADDRESS" : "Import Existing Bitcoin Address", "IMPORT_BITCOIN_ADDRESS_SWEEP" : "Transfer Funds from Imported Address", "IMPORT_BITCOIN_ADDRESS_EXPLAIN" : "Your wallet automatically creates new bitcoin addresses as it needs them. You can optionally import an existing address and transfer the funds to your wallet if you have the corresponding Private Key. This is an advanced functionality and only suggested for advanced users.", - "CONFIRM_NOT_SWEEP" : "Are you sure? We recommend that you sweep these funds.", + "CONFIRM_NOT_SWEEP" : "Are you sure? We recommend that you sweep these funds into an account.", "YOUR_PRIVATE_KEY" : "Show Private Key", "PRIVATE_KEY_VALID" : "The Private Key appears to be Valid", "ADDRESS_VALID" : "The Bitcoin address is valid", @@ -329,6 +334,7 @@ "BITCOIN_CURRENCY_EXPLAIN": "Adjust the precision you would prefer bitcoin values to be displayed in.", "JUST_RECEIVED_BITCOIN" : "You've just received Bitcoin!", "SUCCESS" : "Success!", + "ACCOUNT_CREATED" : "You've successfully created an account", "BITCOIN_SENT" : "You've successfully sent bitcoin", "NO_TRANSACTIONS_YET" : "Your Transactions", "SORRY_ZERO_TXS" : "Sorry, we couldn't find any transactions!", @@ -409,10 +415,10 @@ "EMAIL_VERIFIED" : "Email Verified", "LAUNCHED" : "We've launched!", "GET_STARTED" : "Get Started", - "WELCOME_TEXT" : "We've launched our wallet! And we're excited to show you all the great features we've added. A brand new Security Center, revamped algorithms and a shiny new interface to name a few - go ahead, get started!", + "WELCOME_TEXT" : "We've launched our wallet! And we're excited to show you all the great features we've added. A brand new Security Center, account management, revamped algorithms and a shiny new interface to name a few - go ahead, get started!", "TOS" : "ToS", "PRIVACY" : "Privacy Policy", - "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet to better allocate, track and manage your bitcoins. Some common names include Savings, Spending, and Business Expenses.", + "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet into multiple accounts to better allocate, track and manage your funds. Some common account names include Savings, Spending, and Business Expenses.", "TRANSACTION_DETAIL_STATUS" : "Your transaction has been submitted to the bitcoin network and is waiting for miners to validate the transaction. A transaction is considered to be confirmed when there are 3 network confirmations.", "SEND_BITOIN_STEP2B" : "Add a note to remind yourself of what this transaction relates to. This note will be private and only seen by you, unless you select the Make Public option. If a note is public, anyone viewing the transaction on Blockchain will be able to read your note.", "ADVANCED_SEND_STEP_1" : "A normal miners fee is recommended for most transactions. You can override this recommendation but be advised that lower fees may take several days to confirm.", @@ -421,7 +427,7 @@ "VALUE_NOW" : "Value Now", "VERIFY_ON_BCI" : "Verify on Blockchain.info", "ARE_YOU_SURE" : "We just want to double check! Are you sure you wish to proceed? Remember you cannot go back to your old wallet :)", - "ACCOUNT_NAME_TAKEN" : "That name is already in use", + "ACCOUNT_NAME_TAKEN" : "That account name is already in use", "EMAIL_ADDRESS_TOOLTIP" : "Your verified email address is used to send login codes when unusual activity is detected and to send bitcoin payment alerts when you receive funds.", "RECOVERY_PHRASE_TOOLTIP" : "Your recovery phrase allows you to restore your wallet in case of loss of password or extended downtime on our servers.", "PASSWORD_HINT_TOOLTIP" : "Allows us to send your password hint to your verified email address in case you forget your password.", @@ -456,7 +462,7 @@ "LOGGING" : "Activity Logging", "LOG" : "Log", "LOGGING_EXPLAIN" : "Record wallet activity and display it in your activity feed.", - "ACTIVITY_1": "Addresses", + "ACTIVITY_1": "Accounts", "ACTIVITY_1_DESC": "Created My Bitcoin Wallet", "ACTIVITY_2_DESC": "Set password", "ACTIVITY_3_DESC": "Created wallet!", @@ -469,7 +475,7 @@ "EDIT_NOTE": "Edit", "DELETE_NOTE": "Delete", "ALPHA_WARNING": "Please note, this is an Alpha of a new product. Please do not test this wallet with more funds than you are willing to lose.", - "XPUB_WARNING": "You should only give this Extended Public Key (xPub) to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", + "XPUB_WARNING": "You should only give this Public Key to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", "LOST_WALLET_ID": "I've lost my Wallet ID", "LOST_WALLET_PWD": "I've lost my Wallet Password", "LOST_2FA": "I've lost my 2FA Device", @@ -496,7 +502,7 @@ "REDIRECTING": "We're redirecting you to your wallet now!", "TAKES_A_WHILE": "This may take a while. If your browser asks if you want to cancel the script, please press continue.", "NOTIFICATIONS" : "Email Notifications", - "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses and imported addresses are eligible to trigger notifications.", + "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses in your accounts and imported addresses are eligible to trigger notifications.", "VERIFY_EMAIL_FIRST" : "Please verify your email address first.", "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems.", "AUTHORIZE_APPROVE_SUCCESS" : "Login approved! Please return to your previous browser / tab to see your wallet.", @@ -510,8 +516,5 @@ "IP_ADDRESS" : "IP Address", "COUNTRY_OF_ORIGIN" : "Country Of Origin", "AUTHORIZE_REJECT_SUCCESS" : "Login attempt rejected! Please contact support if this was not you.", - "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt", - "SEARCH" : "Search", - "BALANCES" : "Balances", - "MUST_SELECT_ORIGIN" :"Must select where to send from" + "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt" } diff --git a/tests/controllers/account_form_ctrl_spec.coffee b/tests/controllers/account_form_ctrl_spec.coffee index e7ec18f3ad..f3685fa29c 100644 --- a/tests/controllers/account_form_ctrl_spec.coffee +++ b/tests/controllers/account_form_ctrl_spec.coffee @@ -76,6 +76,13 @@ describe "AccountFormCtrl", -> expect(Wallet.accounts()[Wallet.accounts().length - 1].label).toBe("New Account") ) + it "should show a confirmation modal", inject(($uibModal)-> + spyOn($uibModal, "open").and.callThrough() + scope.createAccount() + expect($uibModal.open).toHaveBeenCalled() + expect($uibModal.open.calls.argsFor(0)[0].windowClass).toEqual("notification-modal") + ) + describe "rename", -> it "original name should be shown", -> diff --git a/tests/controllers/send_ctrl_spec.coffee b/tests/controllers/send_ctrl_spec.coffee index da08354e0f..cc43c98485 100644 --- a/tests/controllers/send_ctrl_spec.coffee +++ b/tests/controllers/send_ctrl_spec.coffee @@ -517,7 +517,7 @@ describe "SendCtrl", -> it "should set the label to an account", -> scope.transaction.destinations[0] = scope.accounts()[0] scope.updateToLabel() - expect(scope.toLabel).toEqual('Checking') + expect(scope.toLabel).toEqual('Checking Account') it "should set the label when advanced", -> scope.advanced = true From eae05c42b0db8e838619285c63220e840ced0720 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 21 Dec 2015 17:11:57 +0100 Subject: [PATCH 24/72] feat(Deploy): pass backend URL to grunt dist --- Gruntfile.coffee | 110 +++++++++++++++++++++++++++++++---------------- bower.json | 2 +- package.json | 6 +-- 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 67d718af60..6fe404df38 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -416,6 +416,15 @@ module.exports = (grunt) -> force: true recursive: true + replace: + root_url: + src: ['build/js/services/wallet.service.js'], + overwrite: true, + replacements: [{ + from: 'setRootURL("/")' + to: () => + 'setRootURL("' + @rootUrl + '")' + }] grunt.loadNpmTasks "grunt-contrib-uglify" grunt.loadNpmTasks('grunt-contrib-concat') @@ -435,6 +444,7 @@ module.exports = (grunt) -> grunt.loadNpmTasks('git-changelog') grunt.loadNpmTasks('grunt-babel') grunt.loadNpmTasks('grunt-karma-coveralls') + grunt.loadNpmTasks('grunt-text-replace') grunt.registerTask "compile", ["coffee"] @@ -457,43 +467,69 @@ module.exports = (grunt) -> ] # Default task(s). - grunt.registerTask "dist", [ - "shell:clean_bower_and_npm_cache" - "clean" - "shell:npm_install_dependencies" - "build" - "shell:check_dependencies" - "shell:npm_install_dependencies" - "shell:bower_install_dependencies" - "shell:check_pgp_signatures" - "concat:application_dependencies" - "uglify:application_dependencies" - "concat:application" - "concat_css:app" - "jade" - "preprocess" - "copy:main" - "rename:assets" - "rename:html" - "git_changelog" - ] - - grunt.registerTask "dist_unsafe", [ - "shell:clean_bower_and_npm_cache" - "clean" - "shell:npm_install_dependencies" - "build" - "shell:skip_check_dependencies" - "concat:application_dependencies" - "uglify:application_dependencies" - "concat:application" - "concat_css:app" - "jade" - "preprocess" - "copy:main" - "rename:assets" - "rename:html" - ] + grunt.registerTask "dist", (rootUrl) => + grunt.task.run [ + "shell:clean_bower_and_npm_cache" + "clean" + "shell:npm_install_dependencies" + "build" + ] + + if rootUrl + @rootUrl = rootUrl + + console.log("Custom root URL: " + @rootUrl) + + grunt.task.run [ + "replace:root_url" + ] + + grunt.task.run [ + "shell:check_dependencies" + "shell:npm_install_dependencies" + "shell:bower_install_dependencies" + "shell:check_pgp_signatures" + "concat:application_dependencies" + "uglify:application_dependencies" + "concat:application" + "concat_css:app" + "jade" + "preprocess" + "copy:main" + "rename:assets" + "rename:html" + "git_changelog" + ] + + grunt.registerTask "dist_unsafe", (rootUrl) => + grunt.task.run [ + "shell:clean_bower_and_npm_cache" + "clean" + "shell:npm_install_dependencies" + "build" + ] + + if rootUrl + @rootUrl = rootUrl + + console.log("Custom root URL: " + @rootUrl) + + grunt.task.run [ + "replace:root_url" + ] + + grunt.task.run [ + "shell:skip_check_dependencies" + "concat:application_dependencies" + "uglify:application_dependencies" + "concat:application" + "concat_css:app" + "jade" + "preprocess" + "copy:main" + "rename:assets" + "rename:html" + ] grunt.registerTask "deploy_static_to_dev", [ "dist" diff --git a/bower.json b/bower.json index 05e80bb849..c5f6a6eee0 100644 --- a/bower.json +++ b/bower.json @@ -18,7 +18,7 @@ "angular-qr": "0.2.*", "angular-translate": "2.7.*", "angular-translate-loader-static-files": "2.7.*", - "angular-inview": "1.5.0", + "angular-inview": "1.5.*", "angular-password-entropy": "0.1.*", "browserdetection": "0.3.*", "bc-qr-reader": "0.2.*", diff --git a/package.json b/package.json index 41e1c9a189..c6613ff0d3 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,12 @@ "private": true, "version": "0.0.0", "description": "AngularJS front-end to My-Wallet-V3.", - "dependencies": { - }, + "dependencies": {}, "devDependencies": { - "express": "^4.13.3", "bower": "^1.3.1", "coffee-script": "^1.9.1", "ejs": "~0.8.4", + "express": "^4.13.3", "git-changelog": "^0.1.8", "grunt": "^0.4.5", "grunt-autoprefixer": "^3.0.0", @@ -33,6 +32,7 @@ "grunt-rename-assets": "0.0.1", "grunt-shell": "^1.1.1", "grunt-surround": "^0.1.0", + "grunt-text-replace": "^0.4.0", "isparta": "^3.0.4", "istanbul": "^0.3.19", "jade": "*", From 3aa6e159ee3233a186e42947b02cd504e24613db Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 21 Dec 2015 17:16:37 +0100 Subject: [PATCH 25/72] dep(Whitelist): bump angular-inview patch version --- dependency-whitelist.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependency-whitelist.json b/dependency-whitelist.json index 2fb25835be..e4dcfc6c91 100644 --- a/dependency-whitelist.json +++ b/dependency-whitelist.json @@ -43,8 +43,8 @@ "repo" : "angular-translate/bower-angular-translate-loader-static-files" }, "angular-inview" : { - "version": "1.5.0", - "commits" : ["a4a3ce66181979a34a004d996982199f5bf69d32"], + "version": "1.5.6", + "commits" : ["96cb2f24b62849632ffdcb62cbb679de594d7629"], "repo" : "thenikso/angular-inview" }, "angular-password-entropy" : { From e3ee30e5cb66184f6c25e79500b0f5af32481d55 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 21 Dec 2015 14:55:53 +0100 Subject: [PATCH 26/72] ui(Signup): update text for new users --- app/partials/first-login-modal.jade | 6 +++--- locales/en-human.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/partials/first-login-modal.jade b/app/partials/first-login-modal.jade index bc53b9874b..ff9243ca66 100644 --- a/app/partials/first-login-modal.jade +++ b/app/partials/first-login-modal.jade @@ -1,6 +1,6 @@ .modal-body.flex .flex-column.flex-between.rocket-text.flex-1 - h3.mtn.h2.em-300.center-align(translate="LAUNCHED") - p.center-align.mtm.width-100.h4.em-300(translate="WELCOME_TEXT") - button.button-success.button-lg.mtl(ng-click="ok()", translate="GET_STARTED") + h3.mtn.h2.em-300.center-align(translate="FIRST_LOGIN_TITLE") + p.center-align.mtm.width-100.h4.em-300(translate="FIRST_LOGIN_TEXT") + button.button-success.button-lg.mtl(ng-click="ok()", translate="FIRST_LOGIN_ACTION") img.rocket-gif.flex-1(src="img/rocket.gif") diff --git a/locales/en-human.json b/locales/en-human.json index dcdb2d9b3b..cbe3b3acd4 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -413,9 +413,9 @@ "NOT_STORED" : "Not Stored", "PHRASE_BACKED" : "Phrase Backed", "EMAIL_VERIFIED" : "Email Verified", - "LAUNCHED" : "We've launched!", - "GET_STARTED" : "Get Started", - "WELCOME_TEXT" : "We've launched our wallet! And we're excited to show you all the great features we've added. A brand new Security Center, account management, revamped algorithms and a shiny new interface to name a few - go ahead, get started!", + "FIRST_LOGIN_TITLE" : "Welcome!", + "FIRST_LOGIN_TEXT" : "Welcome to the new version of the Blockchain Wallet! A brand new Security Center, a Recovery phrase to back up your wallet, no address reuse for increased privacy, and a completely new user interface, just to name a few. ", + "FIRST_LOGIN_ACTION" : "Get Started", "TOS" : "ToS", "PRIVACY" : "Privacy Policy", "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet into multiple accounts to better allocate, track and manage your funds. Some common account names include Savings, Spending, and Business Expenses.", From e8dca1afc9fa0663bf67924a24e94a2bf374e865 Mon Sep 17 00:00:00 2001 From: Matt Tuzzolo Date: Mon, 21 Dec 2015 13:41:47 +0000 Subject: [PATCH 27/72] Redo #233 Different account wording, don't confirm creation --- app/partials/home.jade | 2 +- app/partials/send.jade | 5 ++- app/partials/settings/import-address.jade | 2 +- app/partials/transaction.jade | 4 +- app/partials/transactions.jade | 2 +- app/templates/destination-input.jade | 4 +- .../js/controllers/accountForm.controller.js | 8 ---- assets/js/controllers/request.controller.js | 2 +- assets/js/controllers/send.controller.js | 4 +- assets/js/routes.js | 2 +- locales/en-human.json | 41 +++++++++---------- .../controllers/account_form_ctrl_spec.coffee | 7 ---- tests/controllers/send_ctrl_spec.coffee | 2 +- 13 files changed, 34 insertions(+), 51 deletions(-) diff --git a/app/partials/home.jade b/app/partials/home.jade index 79531f04f3..7c00d49561 100644 --- a/app/partials/home.jade +++ b/app/partials/home.jade @@ -3,7 +3,7 @@ activity-feed .col-md-6.col-sm-12.col-xs-12.col-home.flex-column .section - h5.mtn.type-h5.em-400.mbl Account Balances + h5.mtn.type-h5.em-400.mbl(translate="BALANCES") ul.pln.account-balances li.flex-between.border-bottom-light(ng-repeat="account in activeAccounts") span.em-500 {{::account.label}} diff --git a/app/partials/send.jade b/app/partials/send.jade index ce48cf4d1c..edc23b9216 100644 --- a/app/partials/send.jade +++ b/app/partials/send.jade @@ -34,12 +34,13 @@ ui-select-choices(repeat="origin in origins | filter: getFilter($select.search)" group-by="'type'" ui-disable-choice="hasZeroBalance(origin)") span(ng-class="{aaa: hasZeroBalance(origin)}") label-origin(origin='origin' highlight="$select.search") - span.help-block(ng-show="sendForm.from.$invalid && sendForm.destinations0.$touched") Must select an account to send from + span.help-block(ng-show="sendForm.from.$invalid && sendForm.destinations0.$touched", translate="MUST_SELECT_ORIGIN") //- Advanced Send .form-group.bc-modal-fade.mvl.advanced-recipient.row //- To label.pts.col-sm-2.col-xs-12 - span(translate="TO:") + span(translate="TO") + |: .col-sm-10.col-xs-12 .flex-column(ng-class="{'advanced-recipient-row': advanced}" ng-repeat="item in transaction.destinations track by $index") p.form-control-static(ng-hide="originsLoaded") diff --git a/app/partials/settings/import-address.jade b/app/partials/settings/import-address.jade index 37f0d471bf..ff6115b995 100644 --- a/app/partials/settings/import-address.jade +++ b/app/partials/settings/import-address.jade @@ -66,7 +66,7 @@ p.form-control-static {{ address.address }} .form-group label.col-sm-3.control-label - span(translate="TO_ACCOUNT") + span(translate="TO") |: .col-sm-7 ui-select(ng-model="fields.account") diff --git a/app/partials/transaction.jade b/app/partials/transaction.jade index 3b1b950097..db13e386c8 100644 --- a/app/partials/transaction.jade +++ b/app/partials/transaction.jade @@ -14,7 +14,9 @@ label(translate="FROM:") p.text.mbs {{::from}} .tx-receiver - label(translate="TO:") + label + span(translate="TO") + |: ul.pln.flex-column.flex.type-sm li.flex-center.mbm(ng-repeat="destination in destinations") span.text-overflow-hidden.flex-1.flex.text diff --git a/app/partials/transactions.jade b/app/partials/transactions.jade index 4d93ac7e3e..2ab53f327e 100644 --- a/app/partials/transactions.jade +++ b/app/partials/transactions.jade @@ -8,7 +8,7 @@ translate="{{ f }}" ) .filter-search - input(type="text" placeholder="Search by Account name or Address" ng-model="searchText") + input(type="text" placeholder="{{ 'SEARCH' | translate }}" ng-model="searchText") i.ti-search .transaction-feed .flex-center.flex-justify.flex-column.mtvl(ng-hide="loading || getTotal(accountIndex) > 0 || (transactions | filter:transactionFilter).length > 0 || selectedAccountIndex == 'imported'") diff --git a/app/templates/destination-input.jade b/app/templates/destination-input.jade index a59684eea0..8328fc2e9e 100644 --- a/app/templates/destination-input.jade +++ b/app/templates/destination-input.jade @@ -10,7 +10,7 @@ type="text" tabindex="1" autocomplete="off" - placeholder="Paste or scan an address or select an account" + placeholder="Paste or scan an address or select a destination" ng-model="model.address" ng-hide="model.type === 'Accounts'" ng-blur="blur()" @@ -29,8 +29,6 @@ span.caret span.sr-only Toggle Dropdown ul.uib-dropdown-menu.dropdown-menu-right.drop-menu - li.dropdown-header - | Accounts li(ng-repeat="account in accounts") a(ng-click="setModel(account)" ng-disabled="true") | {{ account.label }} diff --git a/assets/js/controllers/accountForm.controller.js b/assets/js/controllers/accountForm.controller.js index 1fea89249c..9c6e7881ff 100644 --- a/assets/js/controllers/accountForm.controller.js +++ b/assets/js/controllers/accountForm.controller.js @@ -29,14 +29,6 @@ function AccountFormCtrl($scope, Wallet, $uibModalInstance, $log, $translate, ac $scope.status.busy = false; $uibModalInstance.dismiss(""); Wallet.saveActivity(3); - $translate(['SUCCESS', 'ACCOUNT_CREATED']).then(translations => { - $scope.$emit('showNotification', { - type: 'created-account', - icon: 'ti-layout-list-post', - heading: translations.SUCCESS, - msg: translations.ACCOUNT_CREATED - }); - }); }; const error = () => {$scope.status.busy = false;} diff --git a/assets/js/controllers/request.controller.js b/assets/js/controllers/request.controller.js index da25d460ec..a88fe3f485 100644 --- a/assets/js/controllers/request.controller.js +++ b/assets/js/controllers/request.controller.js @@ -22,7 +22,7 @@ function RequestCtrl($scope, Wallet, Alerts, currency, $uibModalInstance, $log, for(const account of $scope.accounts()) { if (account.index != null && !account.archived) { let acct = angular.copy(account); - acct.type = "Accounts"; + acct.type = ""; $scope.destinations.push(acct); if ((destination != null) && (destination.index != null) && destination.index === acct.index) { $scope.fields.to = acct; diff --git a/assets/js/controllers/send.controller.js b/assets/js/controllers/send.controller.js index 28f33c30c1..19462dec1d 100644 --- a/assets/js/controllers/send.controller.js +++ b/assets/js/controllers/send.controller.js @@ -228,7 +228,7 @@ function SendCtrl($scope, $log, Wallet, Alerts, currency, $uibModalInstance, $ti } else { let dest = destinations[0]; $scope.toLabel = dest.index == null ? - dest.label || dest.address : `${dest.label} Account`; + dest.label || dest.address : `${dest.label}`; } }; @@ -271,7 +271,7 @@ function SendCtrl($scope, $log, Wallet, Alerts, currency, $uibModalInstance, $ti balance: origin.balance, archived: origin.archived }; - formatted.type = origin.index != null ? 'Accounts' : 'Imported Addresses'; + formatted.type = origin.index != null ? '' : 'Imported Addresses'; if (origin.index == null) formatted.isWatchOnly = origin.isWatchOnly; return formatted; }; diff --git a/assets/js/routes.js b/assets/js/routes.js index 9f5c99809d..3f2b3837b1 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -307,7 +307,7 @@ function AppRouter($stateProvider, $urlRouterProvider) { } }) .state('wallet.common.settings.accounts_index', { - url: '/accounts', + url: '/addresses', views: { settings: { templateUrl: 'partials/settings/accounts.jade', diff --git a/locales/en-human.json b/locales/en-human.json index cbe3b3acd4..5ae4956d99 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -7,11 +7,9 @@ "LOGIN_HELP": "Login Help", "LOGIN_HELP_TEXT": "Need help accessing your wallet?", "CREATE_WALLET": "Create Wallet", - "ACCOUNTS": "Accounts", - "NEW_ACCOUNT": "Add Account +", + "NEW_ACCOUNT": "Add +", "MY_TRANSACTIONS": "Transactions", "ALL": "All", - "ACCOUNT": "Account", "SEND_FEEDBACK": "Send Feedback", "BACK": "Back", "CONTACT_SUPPORT": "Contact Support", @@ -68,7 +66,7 @@ "LANGUAGE" : "Wallet Language", "PREFERENCES" : "Preferences", "PREFERENCES_EXPLAIN" : "Customize your wallet experience.", - "MENU_ACCOUNTS_AND_ADDRESSES" : "Accounts & Addresses", + "MENU_ACCOUNTS_AND_ADDRESSES" : "Addresses", "WALLET_INFO" : "Wallet Information", "WALLET_INFO_EXPLAIN" : "Use the Wallet ID to log in via our web client or scan the code below with your iPhone or Android Blockchain Wallet App to access your wallet on your mobile.", "PAIRING_CODE" : "Mobile App Pairing Code", @@ -119,20 +117,20 @@ "CANCEL_EDIT_NOTE" : "Cancel", "WALLET_RECOVERY_PHRASE" : "Wallet Recovery Phrase", "RECOVERY_PHRASE" : "Recovery Phrase", - "RECOVERY_PHRASE_EXPLAIN" : "Your recovery phrase can be used to restore all your funds in the case of a lost password or a loss of service at Blockchain. Note, that the recovery phrase never changes and recovers all of your existing bitcoins as well as newly received funds in this wallet. Please note that imported addresses are not backed up by the wallet recovery phrase. We strongly recommend to transfer funds from imported addresses into an account in this wallet.", + "RECOVERY_PHRASE_EXPLAIN" : "Your recovery phrase can be used to restore all your funds in the case of a lost password or a loss of service at Blockchain. Note, that the recovery phrase never changes and recovers all of your existing bitcoins as well as newly received funds in this wallet. Please note that imported addresses are not backed up by the wallet recovery phrase. We strongly recommend to transfer funds from imported addresses into this wallet.", "CONFIRM_RECOVERY_PHRASE" : "Backup Phrase", "RECOVERY_ERROR": "Unable to import now, please try again.", "NEXT_STEP" : "Next Step", "NEXT" : "Next", "EMAIL" : "Email", "NEW_ACCT_WELCOME" : "Create A New Blockchain Wallet", - "CREATE_NEW_ACCOUNT" : "Create New Account", - "CREATE_ACCOUNT" : "Create Account", + "CREATE_NEW_ACCOUNT" : "Create New", + "CREATE_ACCOUNT" : "Create New", "CREATE_ADDRESS" : "New Address", - "ACCOUNT_NAME" : "Account Name:", + "ACCOUNT_NAME" : "Name:", "REVEAL_XPUB" : "Show xPub", "MANAGE_ADDRESSES" : "Manage Addresses", - "ACCOUNT_XPUB_MODAL_TITLE" : "Account xPub", + "ACCOUNT_XPUB_MODAL_TITLE" : "Extended Public Key", "PENDING" : "Pending", "COMPLETE" : "Complete", "TRANSACTION_COMPLETE" : "Transaction Complete", @@ -143,10 +141,8 @@ "FROM" : "From", "FROM:" : "From:", "TO" : "To", - "TO:" : "To:", "FEE" : "Fee", "TOTAL" : "Total", - "TO_ACCOUNT" : "To Account", "INTERNAL" : "Internal", "GOOGLE_AUTH_CODE" : "Google Authenticator", "SMS_CODE" : "SMS Code", @@ -176,8 +172,7 @@ "HANDLE_BITCOIN_LINKS_EXPLAIN" : "Enable this to allow your Blockchain Wallet to handle bitcoin payment links in the web browser. This will make your experience more convenient when transacting online.", "SET_HANDLE_BITCOIN_LINKS" : "Enable", "HANDLE_BITCOIN_LINKS_STATUS_UNKNOWN" : "We can't detect whether or not handling of bitcoin links has been enabled. If it has already been enabled, nothing will happen.", - "ACCOUNT_MANAGEMENT" : "Account Management", - "ACCOUNT_MANAGEMENT_EXPLAIN" : "Your Blockchain Wallet can create separate accounts within your single wallet. You may find this feature useful when you would like to separate your funds between a savings account and a spending account, or between a personal account and a business account.", + "ACCOUNT_MANAGEMENT_EXPLAIN" : "You can customize the way you organize your bitcoins in your Blockchain Wallet. You may find this useful when you would like to separate your funds between personal and business expenses, or put your savings aside.", "BALANCE" : "Balance", "MAKE_DEFAULT" : "Make default", "RENAME" : "Rename", @@ -208,7 +203,7 @@ "DISABLE_BLOCK_TOR" : "Allow", "ALLOWED" : "Allowed", "BLOCKED" : "Blocked", - "RENAME_ACCOUNT" : "Rename Account", + "RENAME_ACCOUNT" : "Name", "RENAME" : "Rename", "TRANSACTION_WILL_COMPLETE_IN" : "This transaction will complete in approximately {{ minutes }} minutes.", "SPENDABLE_ADDRESSES" : "Spendable Addresses", @@ -228,7 +223,7 @@ "IMPORT_BITCOIN_ADDRESS" : "Import Existing Bitcoin Address", "IMPORT_BITCOIN_ADDRESS_SWEEP" : "Transfer Funds from Imported Address", "IMPORT_BITCOIN_ADDRESS_EXPLAIN" : "Your wallet automatically creates new bitcoin addresses as it needs them. You can optionally import an existing address and transfer the funds to your wallet if you have the corresponding Private Key. This is an advanced functionality and only suggested for advanced users.", - "CONFIRM_NOT_SWEEP" : "Are you sure? We recommend that you sweep these funds into an account.", + "CONFIRM_NOT_SWEEP" : "Are you sure? We recommend that you sweep these funds.", "YOUR_PRIVATE_KEY" : "Show Private Key", "PRIVATE_KEY_VALID" : "The Private Key appears to be Valid", "ADDRESS_VALID" : "The Bitcoin address is valid", @@ -334,7 +329,6 @@ "BITCOIN_CURRENCY_EXPLAIN": "Adjust the precision you would prefer bitcoin values to be displayed in.", "JUST_RECEIVED_BITCOIN" : "You've just received Bitcoin!", "SUCCESS" : "Success!", - "ACCOUNT_CREATED" : "You've successfully created an account", "BITCOIN_SENT" : "You've successfully sent bitcoin", "NO_TRANSACTIONS_YET" : "Your Transactions", "SORRY_ZERO_TXS" : "Sorry, we couldn't find any transactions!", @@ -418,7 +412,7 @@ "FIRST_LOGIN_ACTION" : "Get Started", "TOS" : "ToS", "PRIVACY" : "Privacy Policy", - "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet into multiple accounts to better allocate, track and manage your funds. Some common account names include Savings, Spending, and Business Expenses.", + "CREATE_NEW_ACCOUNT_MODAL" : "Divide your wallet to better allocate, track and manage your bitcoins. Some common names include Savings, Spending, and Business Expenses.", "TRANSACTION_DETAIL_STATUS" : "Your transaction has been submitted to the bitcoin network and is waiting for miners to validate the transaction. A transaction is considered to be confirmed when there are 3 network confirmations.", "SEND_BITOIN_STEP2B" : "Add a note to remind yourself of what this transaction relates to. This note will be private and only seen by you, unless you select the Make Public option. If a note is public, anyone viewing the transaction on Blockchain will be able to read your note.", "ADVANCED_SEND_STEP_1" : "A normal miners fee is recommended for most transactions. You can override this recommendation but be advised that lower fees may take several days to confirm.", @@ -427,7 +421,7 @@ "VALUE_NOW" : "Value Now", "VERIFY_ON_BCI" : "Verify on Blockchain.info", "ARE_YOU_SURE" : "We just want to double check! Are you sure you wish to proceed? Remember you cannot go back to your old wallet :)", - "ACCOUNT_NAME_TAKEN" : "That account name is already in use", + "ACCOUNT_NAME_TAKEN" : "That name is already in use", "EMAIL_ADDRESS_TOOLTIP" : "Your verified email address is used to send login codes when unusual activity is detected and to send bitcoin payment alerts when you receive funds.", "RECOVERY_PHRASE_TOOLTIP" : "Your recovery phrase allows you to restore your wallet in case of loss of password or extended downtime on our servers.", "PASSWORD_HINT_TOOLTIP" : "Allows us to send your password hint to your verified email address in case you forget your password.", @@ -462,7 +456,7 @@ "LOGGING" : "Activity Logging", "LOG" : "Log", "LOGGING_EXPLAIN" : "Record wallet activity and display it in your activity feed.", - "ACTIVITY_1": "Accounts", + "ACTIVITY_1": "Addresses", "ACTIVITY_1_DESC": "Created My Bitcoin Wallet", "ACTIVITY_2_DESC": "Set password", "ACTIVITY_3_DESC": "Created wallet!", @@ -475,7 +469,7 @@ "EDIT_NOTE": "Edit", "DELETE_NOTE": "Delete", "ALPHA_WARNING": "Please note, this is an Alpha of a new product. Please do not test this wallet with more funds than you are willing to lose.", - "XPUB_WARNING": "You should only give this Public Key to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", + "XPUB_WARNING": "You should only give this Extended Public Key (xPub) to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", "LOST_WALLET_ID": "I've lost my Wallet ID", "LOST_WALLET_PWD": "I've lost my Wallet Password", "LOST_2FA": "I've lost my 2FA Device", @@ -502,7 +496,7 @@ "REDIRECTING": "We're redirecting you to your wallet now!", "TAKES_A_WHILE": "This may take a while. If your browser asks if you want to cancel the script, please press continue.", "NOTIFICATIONS" : "Email Notifications", - "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses in your accounts and imported addresses are eligible to trigger notifications.", + "NOTIFICATIONS_EXPLAIN" : "Enable notifications to receive an email whenever you receive bitcoins. Only labeled addresses and imported addresses are eligible to trigger notifications.", "VERIFY_EMAIL_FIRST" : "Please verify your email address first.", "UNSUBSCRIBE_SUCCESS" : "We have removed your email address (and phone number) from our systems.", "AUTHORIZE_APPROVE_SUCCESS" : "Login approved! Please return to your previous browser / tab to see your wallet.", @@ -516,5 +510,8 @@ "IP_ADDRESS" : "IP Address", "COUNTRY_OF_ORIGIN" : "Country Of Origin", "AUTHORIZE_REJECT_SUCCESS" : "Login attempt rejected! Please contact support if this was not you.", - "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt" + "CHECKING_LOGIN_ATTEMPT" : "Checking login attempt", + "SEARCH" : "Search", + "BALANCES" : "Balances", + "MUST_SELECT_ORIGIN" :"Must select where to send from" } diff --git a/tests/controllers/account_form_ctrl_spec.coffee b/tests/controllers/account_form_ctrl_spec.coffee index f3685fa29c..e7ec18f3ad 100644 --- a/tests/controllers/account_form_ctrl_spec.coffee +++ b/tests/controllers/account_form_ctrl_spec.coffee @@ -76,13 +76,6 @@ describe "AccountFormCtrl", -> expect(Wallet.accounts()[Wallet.accounts().length - 1].label).toBe("New Account") ) - it "should show a confirmation modal", inject(($uibModal)-> - spyOn($uibModal, "open").and.callThrough() - scope.createAccount() - expect($uibModal.open).toHaveBeenCalled() - expect($uibModal.open.calls.argsFor(0)[0].windowClass).toEqual("notification-modal") - ) - describe "rename", -> it "original name should be shown", -> diff --git a/tests/controllers/send_ctrl_spec.coffee b/tests/controllers/send_ctrl_spec.coffee index cc43c98485..da08354e0f 100644 --- a/tests/controllers/send_ctrl_spec.coffee +++ b/tests/controllers/send_ctrl_spec.coffee @@ -517,7 +517,7 @@ describe "SendCtrl", -> it "should set the label to an account", -> scope.transaction.destinations[0] = scope.accounts()[0] scope.updateToLabel() - expect(scope.toLabel).toEqual('Checking Account') + expect(scope.toLabel).toEqual('Checking') it "should set the label when advanced", -> scope.advanced = true From 9d5df8467a8b5354f9785a73dfcbae0686f654a1 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 29 Dec 2015 12:10:55 +0100 Subject: [PATCH 28/72] fix(LostGuid): use .rootURL --- assets/js/controllers/lostGuid.controller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/js/controllers/lostGuid.controller.js b/assets/js/controllers/lostGuid.controller.js index b52fbbd5f1..1c665139c4 100644 --- a/assets/js/controllers/lostGuid.controller.js +++ b/assets/js/controllers/lostGuid.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller('LostGuidCtrl', LostGuidCtrl); -function LostGuidCtrl($scope, $http, $translate, Wallet, Alerts) { +function LostGuidCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { $scope.currentStep = 1; $scope.fields = { email: '', @@ -11,7 +11,7 @@ function LostGuidCtrl($scope, $http, $translate, Wallet, Alerts) { $scope.refreshCaptcha = () => { let time = new Date().getTime(); - $scope.captchaSrc = `https://blockchain.info/kaptcha.jpg?timestamp=${time}`; + $scope.captchaSrc = $rootScope.rootURL + `kaptcha.jpg?timestamp=${time}`; $scope.fields.captcha = ''; }; @@ -36,7 +36,7 @@ function LostGuidCtrl($scope, $http, $translate, Wallet, Alerts) { } }; let httpOptions = { - url : 'https://blockchain.info/wallet/recover-wallet', + url : $rootScope.rootURL + 'wallet/recover-wallet', method : 'GET', params : { param1 : $scope.fields.email, @@ -50,7 +50,7 @@ function LostGuidCtrl($scope, $http, $translate, Wallet, Alerts) { // Set SID cookie by requesting headers $http({ - url: 'https://blockchain.info/wallet/login', + url: $rootScope.rootURL + 'wallet/login', method: 'HEAD', withCredentials: true }).then($scope.refreshCaptcha); From f94175e045ac90f3b5f46e4b553e8f9b6055609d Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 29 Dec 2015 12:46:11 +0100 Subject: [PATCH 29/72] fix(Sponsors): use .rootURL --- assets/js/services/adverts.service.js | 6 ++-- tests/services/adverts_service_spec.coffee | 37 +++++++++++----------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/assets/js/services/adverts.service.js b/assets/js/services/adverts.service.js index 1486d94483..27b048f2b1 100644 --- a/assets/js/services/adverts.service.js +++ b/assets/js/services/adverts.service.js @@ -2,9 +2,9 @@ angular .module('adverts', []) .factory('Adverts', Adverts); -Adverts.$inject = ['$http']; +Adverts.$inject = ['$http', '$rootScope']; -function Adverts($http) { +function Adverts($http, $rootScope) { const service = { ads : [], didFetch : false, @@ -21,7 +21,7 @@ function Adverts($http) { } function fetch() { - let advertsFeed = 'https://blockchain.info/adverts_feed?wallet_version=3'; + let advertsFeed = $rootScope.rootURL + 'adverts_feed?wallet_version=3'; $http.get(advertsFeed) .success(data => { let adverts = data.partners.home_buttons.splice(0); diff --git a/tests/services/adverts_service_spec.coffee b/tests/services/adverts_service_spec.coffee index 70e2f6d820..a9c5e001f8 100644 --- a/tests/services/adverts_service_spec.coffee +++ b/tests/services/adverts_service_spec.coffee @@ -3,44 +3,45 @@ describe "AdvertsServices", () -> rootScope = undefined sampleAd = '{"partners":{"home_buttons":[{"image":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAYAAACgu+4kAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN\/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz\/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH\/w\/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA\/g88wAAKCRFRHgg\/P9eM4Ors7ONo62Dl8t6r8G\/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt\/qIl7gRoXgugdfeLZrIPQLUAoOnaV\/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl\/AV\/1s+X48\/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H\/LcL\/\/wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93\/+8\/\/UegJQCAZkmScQAAXkQkLlTKsz\/HCAAARKCBKrBBG\/TBGCzABhzBBdzBC\/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD\/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q\/pH5Z\/YkGWcNMw09DpFGgsV\/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY\/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4\/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L\/1U\/W36p\/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N\/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26\/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE\/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV\/MN8C3yLfLT8Nvnl+F30N\/I\/9k\/3r\/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt\/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi\/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a\/zYnKOZarnivN7cyzytuQN5zvn\/\/tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO\/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3\/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA\/0HIw6217nU1R3SPVRSj9Yr60cOxx++\/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3\/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX\/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8\/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb\/1tWeOT3dvfN6b\/fF9\/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR\/cGhYPP\/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF\/6i\/suuFxYvfvjV69fO0ZjRoZfyl5O\/bXyl\/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o\/2j5sfVT0Kf7kxmTk\/8EA5jz\/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5\/wAAgOkAAHUwAADqYAAAOpgAABdvkl\/FRgAAAPdJREFUeNqc0j8rBlAcxfE7KDGiDBZKksFMSSwmi4VFTMoqUUpiUCzegWzKn5JMDBZ5ARJFUmxSBjF\/LOeppycSp+7wPefeM\/zur6DUnDr0YR5b2MA0Or+5W2qNXhz5Xq\/YRMNPBRN487su0F5bMORvOkdjpaAJt\/6uhUrBpP\/pCS0FB\/6v0YKbwA76cRZ+zmzmqx4sYxiP4aWCh8BIBjobPg434CNec7zt8FrBXWAs4Vz4JNyC93gd8XbCKwX3gfGEi+HTcCs+43XF2w2vFrwEphKuVC1MQVvVDHriHYbXS3b9Et0JB3GFmXA99rBfWZ58\/TUGvgYAVx\/rST0ljjAAAAAASUVORK5CYII=","country_code":"EU","link":"https:\/\/blockchain.info\/r?url=https%3A%2F%2Fwww.kraken.com%2F&aid=3587","button_class":"btn-primary","id":3587,"type":4,"title":"Kraken Deposit","status":0}]}}' $httpBackend = undefined - + beforeEach angular.mock.module("walletApp") - + beforeEach -> angular.mock.inject ($injector, _$rootScope_) -> - + $httpBackend = $injector.get("$httpBackend") - + Adverts = $injector.get("Adverts") - rootScope = _$rootScope_ - + rootScope = _$rootScope_ + return - return - - describe "fetch()", -> + return + + describe "fetch()", -> beforeEach -> + rootScope.rootURL = "https://blockchain.info/" $httpBackend.expectGET("https://blockchain.info/adverts_feed?wallet_version=3").respond sampleAd - + Adverts.fetch() - + $httpBackend.flush() - + it "should download the most recent adverts feed", -> expect(Adverts.ads.length).toBe 2 - + it "should get the ad title", -> expect(Adverts.ads[0].title).toBe "Kraken Deposit" - + it "should get the ad URL", -> expect(Adverts.ads[0].link).toContain "http" - + it "should get the image blob", -> expect(Adverts.ads[0].image).toContain "data:image" - + it "should get a class to add to the button", -> expect(Adverts.ads[0].button_class).toBeDefined() - + describe "fetchOnce()", -> it "should call fetch() only once", -> spyOn(Adverts, "fetch") @@ -49,6 +50,6 @@ describe "AdvertsServices", () -> Adverts.fetchOnce() expect(Adverts.fetch.calls.count()).toBe(1) - afterEach -> + afterEach -> $httpBackend.verifyNoOutstandingExpectation() $httpBackend.verifyNoOutstandingRequest() From 433de77b4a49096dc74843009cabf3bba5886932 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 29 Dec 2015 14:31:26 +0100 Subject: [PATCH 30/72] ui(Header): point header links to right places --- app/partials/navigation.jade | 4 ++-- app/partials/public.jade | 13 +++++++------ app/partials/settings/accounts.jade | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/partials/navigation.jade b/app/partials/navigation.jade index eb807724fc..469bbd6573 100644 --- a/app/partials/navigation.jade +++ b/app/partials/navigation.jade @@ -25,12 +25,12 @@ a(href="https://www.blockchain.info/wallet/bitcoin-faq", target="_blank", translate="WHATS_BITCOIN") li a(href="https://www.blockchain.info/", target="_blank", translate="BLOCKCHAIN_INFO") - li - a(href="https://www.blockchain.info/api", target="_blank", translate="API") li a(href="https://www.blockchain.info/charts", target="_blank",translate="CHARTS") li a(href="https://markets.blockchain.info/", target="_blank", translate="MARKETS") + li + a(href="https://www.blockchain.info/api", target="_blank", translate="API") li.item.visible-xs a(href="https://www.blockchain.info/wallet/bitcoin-faq", target="_blank",translate="WHATS_BITCOIN") li.item.visible-xs diff --git a/app/partials/public.jade b/app/partials/public.jade index 41cdc44b43..d952c6a692 100644 --- a/app/partials/public.jade +++ b/app/partials/public.jade @@ -2,7 +2,7 @@ .navbar.navbar-default.navbar-inverse.bc-header(role='navigation') .container-fluid .navbar-header.flex-between - a.navbar-brand(ng-click="visitTransactions()") + a.navbar-brand(ui-sref="wallet.common.home") img#logo.pll(src="img/logo-updated.png",alt="Blockchain") button.navbar-toggle(type="button", ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed") span.sr-only Toggle navigation @@ -10,17 +10,18 @@ .navbar-collapse.collapse(ng-class="{'in bg-blue' : !navCollapsed}", ng-click="navCollapsed=true") ul.nav.navbar-nav.navbar-right li.item - a.pam(href="https://www.blockchain.com", translate="HOME") + a.pam(href="https://www.blockchain.info", translate="EXPLORER") li.item - a.pam(href="https://blockchain.com/about", translate="ABOUT") + a.pam(href="https://www.blockchain.info/charts", translate="CHARTS") + li.item + a.pam(href="https://www.blockchain.info/api", translate="API") li.item.active a.pam(href="#", translate="WALLET") li.item - a.pam(href="https://www.blockchain.info", translate="EXPLORER") - li.item - a.pam(href="http://blockchain.com/our-products/blockchain-merchant/", translate="MERCHANT") + a.pam(href="https://blockchain.com/about", translate="ABOUT") li.item a.pam(href="https://blockchain.zendesk.com/", translate="SUPPORT") + .flex-center.flex-justify.flex-column .flex.flex-justify(ui-view="alerts") .login-box.mhs(ui-view="contents") diff --git a/app/partials/settings/accounts.jade b/app/partials/settings/accounts.jade index ae5719cab8..276a96ca3f 100644 --- a/app/partials/settings/accounts.jade +++ b/app/partials/settings/accounts.jade @@ -22,7 +22,7 @@ h5.well.type-h5.em-400.hidden-xs(translate="ACCOUNT_MANAGEMENT_EXPLAIN") ng-repeat="account in accounts() | filter:{label: searchText}" ) td.expand - span.account-label(ng-bind-html="account.label | highlight:searchText") + span.account-label {{ account.label | highlight:searchText }} span.man(ng-show="!account.archived && account.balance != null").prm | ({{ account.balance | toBitCurrency:settings.btcCurrency }}) span.man(ng-show="account.archived", translate="ACCOUNT_IS_ARCHIVED").prm From df856736a3c5cc54dcc4103e948f652692487be1 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 29 Dec 2015 14:43:15 +0100 Subject: [PATCH 31/72] ui(Upgrade): more subtle API upgrade warning --- app/partials/upgrade.jade | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/partials/upgrade.jade b/app/partials/upgrade.jade index af92edba3d..6f460c394f 100644 --- a/app/partials/upgrade.jade +++ b/app/partials/upgrade.jade @@ -1,8 +1,6 @@ .modal-header.flex-center h3(translate="UPGRADE_WALLET") .modal-body.upgrade-modal - uib-alert(type="danger", ng-show="settings.apiAccess") - span(translate="UPGRADE_DISABLES_API_ACCESS") uib-alert(type="danger", close="insist = false", ng-show="insist") span(translate="NEED_SECOND_PASSWORD_FOR_UPGRADE") p(translate="UPGRADE_WELCOME") @@ -13,8 +11,10 @@ li(translate="UPGRADE_PRIVACY") li(translate="UPGRADE_BACKUP") li(translate="UPGRADE_FUND_MANAGEMENT") - p(translate="UPGRADE_LIMITATIONS") - + p + span(translate="UPGRADE_LIMITATIONS") + span + b(translate="UPGRADE_DISABLES_API_ACCESS", ng-show="settings.apiAccess") .modal-footer.pal.flex-end button.button-muted.mrm(ng-click="cancel()", translate="CANCEL", ng-disabled="busy") button.button-success(ui-ladda="busy", ng-click="upgrade()" ng-disabled="waiting", ladda-translate="UPGRADE_NOW", data-style="expand-left") From ba77aa9ff930e268b1fba50fc25b27f0d21c3599 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 30 Dec 2015 16:54:40 +0100 Subject: [PATCH 32/72] fix(Adverts): set .rootURL to '/' by default --- assets/js/services/wallet.service.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 68e0fc9fd0..e59da37270 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -64,6 +64,9 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } else { wallet.api.setRootURL("/"); } + if($rootScope.rootURL === undefined) { + $rootScope.rootURL = "/"; + } wallet.payment = MyWalletPayment; wallet.tokenEndpoints = MyWalletTokenEndpoints; From d3e36b8218a2e38deb05b609820fb02f66a27f5e Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 5 Jan 2016 11:32:47 +0100 Subject: [PATCH 33/72] refactor(RecoverGuid): move functionality out of controller To wallet service and to my-wallet-v3. --- assets/js/controllers/lostGuid.controller.js | 26 ++++---------------- assets/js/services/wallet.service.js | 24 ++++++++++++++++++ 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/assets/js/controllers/lostGuid.controller.js b/assets/js/controllers/lostGuid.controller.js index 1c665139c4..05f3e9edeb 100644 --- a/assets/js/controllers/lostGuid.controller.js +++ b/assets/js/controllers/lostGuid.controller.js @@ -24,28 +24,12 @@ function LostGuidCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { let error = (res) => { $scope.working = false; $scope.refreshCaptcha(); - switch (res.data.initial_error) { - case 'Captcha Code Incorrect': - Alerts.displayError($translate.instant('CAPTCHA_INCORRECT')); - break; - case 'Quota Exceeded': - Alerts.displayError($translate.instant('QUOTA_EXCEEDED')); - break; - default: - Alerts.displayError($translate.instant('UNKNOWN_ERROR')); - } }; - let httpOptions = { - url : $rootScope.rootURL + 'wallet/recover-wallet', - method : 'GET', - params : { - param1 : $scope.fields.email, - kaptcha : $scope.fields.captcha, - format : 'json' - }, - withCredentials: true - }; - $http(httpOptions).then(success).catch(error); + + $scope.remindForm.$setPristine(); + $scope.remindForm.$setUntouched(); + + Wallet.recoverGuid($scope.fields.email, $scope.fields.captcha, success, error) }; // Set SID cookie by requesting headers diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index e59da37270..bf0b614df6 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -283,6 +283,30 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my.resendTwoFactorSms(uid, success, error); }; + wallet.recoverGuid = (email, captcha, successCallback, errorCallback) => { + let success = (message) => { + Alerts.displaySuccess(message); + successCallback(); + wallet.applyIfNeeded(); + }; + let error = (error) => { + + switch (error) { + case 'Captcha Code Incorrect': + Alerts.displayError($translate.instant('CAPTCHA_INCORRECT')); + break; + case 'Quota Exceeded': + Alerts.displayError($translate.instant('QUOTA_EXCEEDED')); + break; + default: + Alerts.displayError($translate.instant('UNKNOWN_ERROR')); + } + + errorCallback(); + wallet.applyIfNeeded(); + }; + wallet.my.recoverGuid(email, captcha, success, error); + }; wallet.create = (password, email, currency, language, success_callback) => { let success = (uid) => { Alerts.displaySuccess('Wallet created with identifier: ' + uid, true); From 18ba4615c78516c837c7cfede8820f41751e9531 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 5 Jan 2016 11:33:15 +0100 Subject: [PATCH 34/72] feat(Reset2FA): form to reset two step verification --- app/index.jade | 1 + app/partials/help.jade | 2 +- app/partials/reset-two-factor.jade | 80 +++++++++++++++++++ assets/js/controllers/app.controller.js | 5 +- assets/js/controllers/login.controller.js | 17 ++-- .../controllers/resetTwoFactor.controller.js | 54 +++++++++++++ assets/js/routes.js | 10 +++ assets/js/services/wallet.service.js | 26 ++++++ locales/en-human.json | 19 ++++- 9 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 app/partials/reset-two-factor.jade create mode 100644 assets/js/controllers/resetTwoFactor.controller.js diff --git a/app/index.jade b/app/index.jade index ab73b4504c..43d27930ba 100644 --- a/app/index.jade +++ b/app/index.jade @@ -85,6 +85,7 @@ head script(src='build/js/controllers/modalNotification.controller.js') script(src='build/js/controllers/recoverFunds.controller.js') script(src='build/js/controllers/lostGuid.controller.js') + script(src='build/js/controllers/ResetTwoFactor.controller.js') script(src='build/js/controllers/feedback.controller.js') script(src='build/js/controllers/walletNavigation.controller.js') script(src='build/js/controllers/verifyEmail.controller.js') diff --git a/app/partials/help.jade b/app/partials/help.jade index 9dd792b273..295c2d9bf3 100644 --- a/app/partials/help.jade +++ b/app/partials/help.jade @@ -23,7 +23,7 @@ div h5.blue.em-300(translate="LOST_2FA") p.em-400(translate="LOST_2FA_DESC") .flex-1.flex-end - a.button-default.button-nowrap(translate="CONTACT_SUPPORT" href="https://blockchain.zendesk.com/anonymous_requests/new" target="_blank") + a.button-default.button-nowrap(translate="RESET_2FA" ui-sref="public.reset-two-factor") .flex-end a.button-muted.mtm(ui-sref="public.login-no-uid") span(translate="GO_BACK") diff --git a/app/partials/reset-two-factor.jade b/app/partials/reset-two-factor.jade new file mode 100644 index 0000000000..7585f9a3af --- /dev/null +++ b/app/partials/reset-two-factor.jade @@ -0,0 +1,80 @@ +.pos-rel(ng-switch="currentStep") + header.flex-center.flex-between + .flex-column + h2.em-300.mtn(translate="RESET_2FA") + p(translate="RESET_2FA_HELP_1", ng-switch-when="1") + p(translate="RESET_2FA_HELP_2", ng-switch-when="1") + form.ptl.form-horizontal(name="form" autocomplete="off" novalidate) + div(ng-switch-when="1") + .form-group(ng-class="{'has-error': form.uid.$invalid && form.uid.$touched}") + label(translate="RESET_2FA_WALLET_IDENTIFIER") + input.form-control( + name="uid" + ng-model="fields.uid" + required) + p.help-block + span(translate="RESET_2FA_WALLET_IDENTIFIER_EXPLAIN") + span + a(ui-sref="public.reminder", translate="LOOK_IT_UP_HERE") + span . + .form-group(ng-class="{'has-error': form.email.$invalid && form.email.$touched}") + label(translate="RESET_2FA_REGISTERED_EMAIL") + input.form-control( + type="email" + name="email" + ng-model="fields.email" + placeholder="{{ 'OPTIONAL' | translate }}") + p.help-block(translate="RESET_2FA_REGISTERED_EMAIL_EXPLAIN") + .form-group(ng-class="{'has-error': form.newEmail.$invalid && form.newEmail.$touched}") + label(translate="RESET_2FA_NEW_EMAIL") + input.form-control( + type="email" + name="newEmail" + ng-model="fields.newEmail" + placeholder="{{ 'OPTIONAL' | translate }}") + p.help-block(translate="RESET_2FA_NEW_EMAIL_EXPLAIN") + .form-group(ng-class="{'has-error': form.secret.$invalid && form.secret.$touched}") + label(translate="RESET_2FA_SECRET_PHRASE") + input.form-control( + name="secret" + ng-model="fields.secret" + placeholder="{{ 'OPTIONAL' | translate }}") + p.help-block(translate="RESET_2FA_SECRET_PHRASE_EXPLAIN") + .form-group(ng-class="{'has-error': form.message.$invalid && form.message.$touched}") + label(translate="RESET_2FA_MESSAGE") + textarea.form-control( + name="message" + ng-model="fields.message" + placeholder="{{ 'OPTIONAL' | translate }}") + p.help-block(translate="RESET_2FA_MESSAGE_EXPLAIN") + .form-group(ng-class="{'has-error': form.captcha.$invalid && form.captcha.$touched}") + label(translate="CAPTCHA") + p(translate="CAPTCHA_EXPAIN") + img.mbl(ng-src="{{captchaSrc}}") + input.form-control( + type="text" + name="captcha" + ng-model="fields.captcha" + required) + .flex-center.flex-end.mvl(ng-switch-when="1") + img(ng-show="working" src="img/spinner.gif") + button.button-muted.mrm( + type="button" + ng-disabled="working" + ui-sref="public.help" + translate="GO_BACK") + button.button-success( + type="submit" + ng-click="resetTwoFactor()" + ng-disabled="!form.$valid || working" + translate="CONTINUE") + .flex-center.flex-justify.flex-column(ng-switch-when="2") + .level-complete.flex-center.flex-justify + i.ti-check.bright-green + h4.em-300.mtl(translate="SUCCESS") + p.em-300(translate="RESET_2FA_SUCCESS") + .flex-end + button.button-muted.mrm( + type="button" + ui-sref="public.login-no-uid" + translate="CONTINUE_TO_LOGIN") diff --git a/assets/js/controllers/app.controller.js b/assets/js/controllers/app.controller.js index 9b4f918068..c477010a38 100644 --- a/assets/js/controllers/app.controller.js +++ b/assets/js/controllers/app.controller.js @@ -2,10 +2,11 @@ angular .module('walletApp') .controller("AppCtrl", AppCtrl); -function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout, $uibModal, $window, $translate) { +function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $cookies, $location, $timeout, $uibModal, $window, $translate) { $scope.status = Wallet.status; $scope.settings = Wallet.settings; $rootScope.isMock = Wallet.isMock; + $rootScope.loginFormUID = $cookies.get("uid"); // Last entered in login form $scope.goal = Wallet.goal; $scope.menu = { isCollapsed: false @@ -59,7 +60,7 @@ function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout }; $scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => { - let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve']; + let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.reset-two-factor', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve']; if (loggedOutStates.every(s => toState.name !== s) && $scope.status.isLoggedIn === false) { $state.go("public.login-no-uid"); } diff --git a/assets/js/controllers/login.controller.js b/assets/js/controllers/login.controller.js index be08d2c25c..2fcefa26be 100644 --- a/assets/js/controllers/login.controller.js +++ b/assets/js/controllers/login.controller.js @@ -6,14 +6,16 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u $scope.status = Wallet.status; $scope.settings = Wallet.settings; $scope.disableLogin = null; - $scope.status.enterkey = false; - $scope.key = $cookies.get("key"); $scope.errors = { uid: null, password: null, twoFactor: null }; - $scope.uidAvailable = $cookies.get('uid') != null || $stateParams.uid; + + $scope.uid = $stateParams.uid || Wallet.guid || $rootScope.loginFormUID; + + $scope.uidAvailable = !!$scope.uid + $scope.user = Wallet.user; // Browser compatibility warnings: @@ -85,14 +87,6 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u Alerts.displayWarning(translation, true); }); } - if (Wallet.guid != null) { - $scope.uid = Wallet.guid; - } else { - $scope.uid = $stateParams.uid || $cookies.get("uid"); - } - if ($scope.key != null) { - $scope.status.enterkey = true; - } $scope.twoFactorCode = ""; $scope.busy = false; $scope.isValid = false; @@ -163,6 +157,7 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u }); $scope.$watch("uid + password + twoFactor", () => { + $rootScope.loginFormUID = $scope.uid; let isValid = null; $scope.errors.uid = null; $scope.errors.password = null; diff --git a/assets/js/controllers/resetTwoFactor.controller.js b/assets/js/controllers/resetTwoFactor.controller.js new file mode 100644 index 0000000000..fc82e38f7d --- /dev/null +++ b/assets/js/controllers/resetTwoFactor.controller.js @@ -0,0 +1,54 @@ +angular + .module('walletApp') + .controller('ResetTwoFactorCtrl', ResetTwoFactorCtrl); + +function ResetTwoFactorCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { + + $scope.currentStep = 1; + $scope.fields = { + uid: $rootScope.loginFormUID, + email: '', + newEmail: '', + secret: '', + message: '', + captcha: '' + }; + + $scope.refreshCaptcha = () => { + let time = new Date().getTime(); + $scope.captchaSrc = $rootScope.rootURL + `kaptcha.jpg?timestamp=${time}`; + $scope.fields.captcha = ''; + }; + + $scope.resetTwoFactor = () => { + $scope.working = true; + let success = (res) => { + $scope.working = false; + $scope.currentStep = 2; + }; + let error = (e) => { + $scope.working = false; + $scope.refreshCaptcha(); + }; + + $scope.form.$setPristine(); + $scope.form.$setUntouched(); + + Wallet.requestTwoFactorReset( + $scope.fields.uid, + $scope.fields.email, + $scope.fields.newEmail, + $scope.fields.secret, + $scope.fields.message, + $scope.fields.captcha, + success, + error) + }; + + // Set SID cookie by requesting headers + $http({ + url: $rootScope.rootURL + 'wallet/login', + method: 'HEAD', + withCredentials: true + }).then($scope.refreshCaptcha); +} diff --git a/assets/js/routes.js b/assets/js/routes.js index 3f2b3837b1..41e341ece6 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -129,6 +129,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('public.reset-two-factor', { + url: '/reset-2fa', + views: { + alerts: commonViews.alerts, + contents: { + templateUrl: 'partials/reset-two-factor.jade', + controller: 'ResetTwoFactorCtrl' + } + } + }) .state('public.authorize-approve', { url: '/authorize-approve/{token:.*}', views: { diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index bf0b614df6..a23346ece8 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -307,6 +307,32 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB }; wallet.my.recoverGuid(email, captcha, success, error); }; + + wallet.requestTwoFactorReset = (guid, email, new_email, secret, message, captcha, successCallback, errorCallback) => { + Alerts.clear() + let success = (message) => { + Alerts.displaySuccess(message); + successCallback(); + wallet.applyIfNeeded(); + }; + let error = (error) => { + switch (error) { + case 'Captcha Code Incorrect': + Alerts.displayError($translate.instant('CAPTCHA_INCORRECT'), true); + break; + case 'Quota Exceeded': + Alerts.displayError($translate.instant('QUOTA_EXCEEDED'), true); + break; + default: + Alerts.displayError(error, true); + } + + errorCallback(); + wallet.applyIfNeeded(); + }; + wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha, success, error); + }; + wallet.create = (password, email, currency, language, success_callback) => { let success = (uid) => { Alerts.displaySuccess('Wallet created with identifier: ' + uid, true); diff --git a/locales/en-human.json b/locales/en-human.json index 5ae4956d99..a4a6141bbf 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -475,13 +475,30 @@ "LOST_2FA": "I've lost my 2FA Device", "LOST_ID_DESC": "Email me a reminder with my Wallet ID to my verified email address", "LOST_PWD_DESC": "Recover your funds with your 12 word recovery passphrase", - "LOST_2FA_DESC": "Contact support to help regain access to your wallet", + "RESET_2FA" : "Reset 2FA", "REMIND_ME": "Remind Me", "REMIND_ME_HELP": "Lost your Wallet Identifier? We'll send it to you via your verified email.", "REMIND_SUCCESS": "You will receive an email containing your Wallet Identifier soon.", "CAPTCHA": "Captcha", "CAPTCHA_EXPAIN": "So that we know you're not a robot", "CAPTCHA_INCORRECT": "The captcha you provided was incorrect, please try again", + "LOST_2FA_DESC": "Reset two step verification to regain access to your wallet", + "RESET_2FA_HELP_1" : "Are you unable to gain access to your wallet because you lost your two factor authentication (2FA) device or are unable to access your email account?", + "RESET_2FA_HELP_2" : "Please submit the form below and check your email for further instructions. 2FA reset requests are automatically approved after a certain time. The process will be quicker with more precise details provided to us. Your IP address and browser information will be recorded on submission.", + "RESET_2FA_WALLET_IDENTIFIER" : "Wallet Identifier", + "RESET_2FA_WALLET_IDENTIFIER_EXPLAIN" : "If you forgot your wallet identifier (36 characters long containing 4 dashes), please", + "RESET_2FA_REGISTERED_EMAIL" : "Registered Email", + "RESET_2FA_REGISTERED_EMAIL_EXPLAIN" : "Enter the email associated with your wallet. If you lost access to this email, please enter it regardless.", + "RESET_2FA_NEW_EMAIL" : "New Email", + "RESET_2FA_NEW_EMAIL_EXPLAIN" : "If you lost access to the email associated with your wallet, enter a new email. If the 2FA reset request is approved, this email will automatically be set as your new wallet email.", + "RESET_2FA_SECRET_PHRASE" : "Secret Phrase", + "RESET_2FA_SECRET_PHRASE_EXPLAIN" : "Enter your wallet Secret Phrase here if you have one set. If the Secret Phrase is correct, your request will be approved much quicker. If you don't know what this is, leave it blank.", + "RESET_2FA_MESSAGE" : "Message", + "RESET_2FA_MESSAGE_EXPLAIN" : "Enter a message for Blockchain.info admins to review.", + "OPTIONAL" : "Optional", + "LOOK_IT_UP_HERE" : "look it up here", + "RESET_2FA_SUCCESS" : "You will receive an email with further instructions soon.", + "NEW_EMAIL" : "Registered Email", "QUOTA_EXCEEDED": "You have reached the maximum number of allowed attempts, please try again later", "UNKNOWN_ERROR": "An unknown error occurred, please try again later", "CONTINUE_TO_LOGIN": "Continue to Login", From 76fcf8e230150d99868934804c7a10789eac381c Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 5 Jan 2016 11:53:12 +0100 Subject: [PATCH 35/72] fix(VerifyEmail): guid in token was ignored --- assets/js/controllers/verifyEmail.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/js/controllers/verifyEmail.controller.js b/assets/js/controllers/verifyEmail.controller.js index 912c82e62a..79b05660a6 100644 --- a/assets/js/controllers/verifyEmail.controller.js +++ b/assets/js/controllers/verifyEmail.controller.js @@ -4,8 +4,6 @@ angular function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate, $rootScope) { const success = (uid) => { - uid = null; - if(uid) { $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS']).then(translations => { $state.go("public.login-uid", {uid: uid}).then(() =>{ From 118c884bfdff0ef5de7dc9882f6810c7c2709455 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 5 Jan 2016 12:49:20 +0100 Subject: [PATCH 36/72] feat(2FA): process reset 2FA email link --- app/index.jade | 1 + app/partials/reset-two-factor-token.jade | 7 +++++ assets/js/controllers/app.controller.js | 2 +- .../resetTwoFactorToken.controller.js | 31 +++++++++++++++++++ assets/js/routes.js | 10 ++++++ assets/js/services/wallet.service.js | 15 +++++++++ locales/en-human.json | 1 + 7 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 app/partials/reset-two-factor-token.jade create mode 100644 assets/js/controllers/resetTwoFactorToken.controller.js diff --git a/app/index.jade b/app/index.jade index 43d27930ba..21b1121042 100644 --- a/app/index.jade +++ b/app/index.jade @@ -91,6 +91,7 @@ head script(src='build/js/controllers/verifyEmail.controller.js') script(src='build/js/controllers/unsubscribe.controller.js') script(src='build/js/controllers/authorizeApprove.controller.js') + script(src='build/js/controllers/resetTwoFactorToken.controller.js') script(src='build/js/controllers/settings/settings.controller.js') script(src='build/js/controllers/settings/info.controller.js') script(src='build/js/controllers/settings/preferences.controller.js') diff --git a/app/partials/reset-two-factor-token.jade b/app/partials/reset-two-factor-token.jade new file mode 100644 index 0000000000..f7f306b955 --- /dev/null +++ b/app/partials/reset-two-factor-token.jade @@ -0,0 +1,7 @@ +div(ng-show="checkingToken") + header + hgroup + .flex-between.flex-center.flex-wrap + h2.em-300.mtn(translate="RESET_2FA_CHECKING") + hgroup + img(src="img/spinner.gif") diff --git a/assets/js/controllers/app.controller.js b/assets/js/controllers/app.controller.js index c477010a38..b662744b3d 100644 --- a/assets/js/controllers/app.controller.js +++ b/assets/js/controllers/app.controller.js @@ -60,7 +60,7 @@ function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $cookies, $location }; $scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => { - let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.reset-two-factor', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve']; + let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.reset-two-factor', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve', 'public.reset-two-factor-token']; if (loggedOutStates.every(s => toState.name !== s) && $scope.status.isLoggedIn === false) { $state.go("public.login-no-uid"); } diff --git a/assets/js/controllers/resetTwoFactorToken.controller.js b/assets/js/controllers/resetTwoFactorToken.controller.js new file mode 100644 index 0000000000..e4ce99d9d1 --- /dev/null +++ b/assets/js/controllers/resetTwoFactorToken.controller.js @@ -0,0 +1,31 @@ +angular + .module('walletApp') + .controller("ResetTwoFactorTokenCtrl", ResetTwoFactorTokenCtrl); + +function ResetTwoFactorTokenCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate, $rootScope) { + const success = (obj) => { + + $scope.checkingToken = false + + $translate(['SUCCESS']).then(translations => { + $state.go("public.login-uid", {uid: obj.guid}).then(() =>{ + $rootScope.$emit('showNotification', { + type: 'verified-email', + icon: 'ti-email', + heading: translations.SUCCESS, + msg: obj.message + }); + }); + }); + } + + const error = (message) => { + $scope.checkingToken = false + $state.go("public.login-no-uid"); + Alerts.displayError(message, true); + } + + $scope.checkingToken = true + + Wallet.resetTwoFactorToken($stateParams.token, success, error) +} diff --git a/assets/js/routes.js b/assets/js/routes.js index 41e341ece6..fd86cf55f1 100644 --- a/assets/js/routes.js +++ b/assets/js/routes.js @@ -149,6 +149,16 @@ function AppRouter($stateProvider, $urlRouterProvider) { } } }) + .state('public.reset-two-factor-token', { + url: '/reset-two-factor/{token:.*}', + views: { + alerts: commonViews.alerts, + contents: { + templateUrl: 'partials/reset-two-factor-token.jade', + controller: 'ResetTwoFactorTokenCtrl' + } + } + }) .state('signup.finish', { url: '/signup/finish', views: commonViews diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index a23346ece8..08fa6d5bc2 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -333,6 +333,21 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha, success, error); }; + wallet.resetTwoFactorToken = (token, successCallback, errorCallback) => { + const success = (obj) => { + successCallback(obj); + wallet.applyIfNeeded(); + } + + const error = (message) => { + console.log(message); + errorCallback(message); + wallet.applyIfNeeded(); + } + + wallet.tokenEndpoints.resetTwoFactor(token, success, error); + } + wallet.create = (password, email, currency, language, success_callback) => { let success = (uid) => { Alerts.displaySuccess('Wallet created with identifier: ' + uid, true); diff --git a/locales/en-human.json b/locales/en-human.json index a4a6141bbf..00a4c73b2c 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -498,6 +498,7 @@ "OPTIONAL" : "Optional", "LOOK_IT_UP_HERE" : "look it up here", "RESET_2FA_SUCCESS" : "You will receive an email with further instructions soon.", + "RESET_2FA_CHECKING" : "Processing your request", "NEW_EMAIL" : "Registered Email", "QUOTA_EXCEEDED": "You have reached the maximum number of allowed attempts, please try again later", "UNKNOWN_ERROR": "An unknown error occurred, please try again later", From e1f3580146bf5baf7396d5fda2461b664a9ee0aa Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 5 Jan 2016 16:47:47 +0100 Subject: [PATCH 37/72] deploy(Grunt): custom web socket URL, easier dist task --- Gruntfile.coffee | 24 ++++++++++++++++++++---- app/index.jade | 2 +- assets/js/services/wallet.service.js | 20 ++++++++++++-------- server.js | 3 ++- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 6fe404df38..a33684bd4b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -421,9 +421,17 @@ module.exports = (grunt) -> src: ['build/js/services/wallet.service.js'], overwrite: true, replacements: [{ - from: 'setRootURL("/")' + from: 'customRootURL = $rootScope.rootURL' to: () => - 'setRootURL("' + @rootUrl + '")' + 'customRootURL = $rootScope.rootURL = "https://' + @rootUrl + '/"' + }] + web_socket_url: + src: ['build/js/services/wallet.service.js'], + overwrite: true, + replacements: [{ + from: 'customWebSocketURL = $rootScope.webSocketURL' + to: () => + 'customWebSocketURL = "wss://' + @rootUrl + '/inv"' }] grunt.loadNpmTasks "grunt-contrib-uglify" @@ -467,7 +475,7 @@ module.exports = (grunt) -> ] # Default task(s). - grunt.registerTask "dist", (rootUrl) => + grunt.registerTask "dist", (rootUrl, port) => grunt.task.run [ "shell:clean_bower_and_npm_cache" "clean" @@ -478,10 +486,14 @@ module.exports = (grunt) -> if rootUrl @rootUrl = rootUrl + if port + @rootUrl += ":" + port + console.log("Custom root URL: " + @rootUrl) grunt.task.run [ "replace:root_url" + "replace:web_socket_url" ] grunt.task.run [ @@ -501,7 +513,7 @@ module.exports = (grunt) -> "git_changelog" ] - grunt.registerTask "dist_unsafe", (rootUrl) => + grunt.registerTask "dist_unsafe", (rootUrl, port) => grunt.task.run [ "shell:clean_bower_and_npm_cache" "clean" @@ -512,10 +524,14 @@ module.exports = (grunt) -> if rootUrl @rootUrl = rootUrl + if port + @rootUrl += ":" + port + console.log("Custom root URL: " + @rootUrl) grunt.task.run [ "replace:root_url" + "replace:web_socket_url" ] grunt.task.run [ diff --git a/app/index.jade b/app/index.jade index 21b1121042..0f767ac0eb 100644 --- a/app/index.jade +++ b/app/index.jade @@ -1,5 +1,5 @@ doctype - + head meta(charset='utf-8') diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 08fa6d5bc2..f929a32829 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -58,14 +58,18 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.store = MyWalletStore; wallet.api = MyBlockchainApi; - const customRootURL = $rootScope.rootURL; - if(customRootURL) { - wallet.api.setRootURL(customRootURL); - } else { - wallet.api.setRootURL("/"); - } - if($rootScope.rootURL === undefined) { - $rootScope.rootURL = "/"; + + // If customRootURL is set by index.jade: + // Grunt can replace this: + const customRootURL = $rootScope.rootURL || "/"; + wallet.api.setRootURL(customRootURL); + // If customRootURL is set by Grunt: + $rootScope.rootURL = customRootURL; + + // Grunt can replace this: + const customWebSocketURL = $rootScope.webSocketURL; + if(customWebSocketURL) { + wallet.my.ws.setURL(customWebSocketURL); } wallet.payment = MyWalletPayment; diff --git a/server.js b/server.js index e8321476b3..793069a91a 100644 --- a/server.js +++ b/server.js @@ -11,6 +11,7 @@ var port = process.env.PORT || 8080 , origins = (process.env.BLOCKCHAIN || '').split(' ') , whitelist = (process.env.IP_WHITELIST || '').split(' ') , rootURL = process.env.ROOT_URL || 'https://blockchain.info/' + , webSocketURL = process.env.WEBSOCKET_URL || false // App configuration var app = express(); @@ -22,7 +23,7 @@ app.use(function (req, res, next) { "style-src 'self' 'sha256-vv5i1tRAGZ/gOQeRpI3CEWtvnCpu5FCixlD2ZPu7h84=' 'sha256-47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU='", "child-src 'none'", "script-src 'self' 'sha256-mBeSvdVuQxRa2pGoL8lzKX14b2vKgssqQoW36iRlU9g=' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='", - "connect-src 'self' " + rootURL + " wss://*.blockchain.info", + "connect-src 'self' " + rootURL + " " + (webSocketURL || "wss://*.blockchain.info"), "object-src 'none'", "media-src 'self' data: mediastream: blob:", "font-src 'self'", '' From cd0d7fa6fec131d12416326ed41a4e958a0001d8 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 5 Jan 2016 17:00:14 +0100 Subject: [PATCH 38/72] copy(Legal): from alpha to beta agreement --- app/partials/signup.jade | 4 +- app/partials/terms-of-service.jade | 112 ------------------ ...pha-agreement.jade => user-agreement.jade} | 48 ++++---- assets/js/controllers/signup.controller.js | 2 +- .../walletNavigation.controller.js | 7 +- locales/en-human.json | 5 +- tests/controllers/accounts_ctrl_spec.coffee | 6 - 7 files changed, 34 insertions(+), 150 deletions(-) delete mode 100644 app/partials/terms-of-service.jade rename app/partials/{alpha-agreement.jade => user-agreement.jade} (56%) diff --git a/app/partials/signup.jade b/app/partials/signup.jade index e1c73e7e89..4130c04421 100644 --- a/app/partials/signup.jade +++ b/app/partials/signup.jade @@ -9,7 +9,7 @@ div novalidate) .security-red.mbl.mtl.em-400.flex-center i.ti-hand-stop.mrm.h3.mvn.hidden-xs - span(translate="ALPHA_WARNING") + span(translate="USER_AGREEMENT_WARNING") .form-group(ng-class="{'has-error': signupForm.email.$invalid && signupForm.email.$touched}") label.col-sm-4.control-label(translate="EMAIL") .col-sm-8 @@ -56,7 +56,7 @@ div required) label.em-300.col-sm-8 | I have read and agree to the - a.em-500(ng-click="showAgreement()") Alpha Program Participation Agreement + a.em-500(ng-click="showAgreement()", translate="USER_AGREEMENT") .flex-center.flex-end.mbl button.button-primary( type="submit" diff --git a/app/partials/terms-of-service.jade b/app/partials/terms-of-service.jade deleted file mode 100644 index 7940b25215..0000000000 --- a/app/partials/terms-of-service.jade +++ /dev/null @@ -1,112 +0,0 @@ -.modal-header.bc-modal-header - h3 - | ALPHA PROGRAM PARTICIPATION AGREEMENT -.modal-body - .flex-column.pal - p - | This Alpha Program Participation Agreement (this “Agreement”) is made and entered into by and between Blockchain Lux S.a.r.l. or its affiliates (“Blockchain”), and you, the “User” who uses the Alpha Services as hereinafter defined. The terms of any other agreement between Blockchain and User are hereby incorporated into the terms of this Agreement, however, in the case of any conflict, the terms of this Agreement supersede and control, except where otherwise specifically stated herein. This Agreement describes the terms and conditions under which User may access and use certain features, technologies and services that are not yet generally commercially available (each, a “Alpha Service”). Blockchain and User are sometimes referred to collectively as the “Parties” and each individually as a “Party.” - p - b THE SOFTWARE SUPPLIED TO USER IS IN ALPHA FORM AND SUPPLIED FOR TESTING PURPOSES ONLY. BUGS, CRASHES AND OTHER MALFUNCTIONS ARE ALMOST CERTAIN TO OCCUR. UNDER NO CIRCUMSTANCES MAY USER USE THE SOFTWARE SUPPLIED IN CONNECTION WITH THE ALPHA SERVICE TO SECURE MORE THAN $10 WORTH OF BITCOIN. - p - b ANY DISPUTES ARISING OUT OF THIS AGREEMENT WILL BE RESOLVED BY BINDING, INDIVIDUAL ARBITRATION AND THE PARTIES WAIVE THEIR RIGHTS TO GO TO COURT OR PARTICIPATE IN A CLASS ACTION LAWSUIT OR CLASS- WIDE ARBITRATION. - .bg-light-blue.flex-column.mtl.pal - h2 1. Definitions - p - ol - li - | “Alpha Materials” means any hardware, software, specifications or other technical documentation related to a specific Alpha Service that may be provided to User by Blockchain. - li - | “Alpha Use” means the testing and evaluation of a specific Alpha Service by User and certain other Blockchain customers or business partners. - li - | “Alpha Use Information” means all information relating to User’s use, testing or evaluation of a Alpha Service or any related Alpha Materials, including all observations or information regarding the nature, quality, performance, features or functionality of a Alpha Service or any related Alpha Materials. - li - | “Feedback” means all feedback, suggestions, and ideas that User provides to Blockchain concerning improvements or enhancements to a Alpha Service or any related Alpha Materials. - li - | “Confidential Information” means all nonpublic information disclosed by Blockchain or its agents to User, its affiliates, or the agents of any of the foregoing, that is designated as confidential or that, given the nature of the information or the circumstances surrounding its disclosure, reasonably should be considered as confidential. Confidential Information includes, without limitation (a) nonpublic information relating to Blockchain’ or its affiliates’ technology, customers, business plans, promotional and marketing activities, finances and other business affairs, (b) third-party information that Blockchain or its affiliates is obligated to keep confidential, (c) Alpha Materials, Alpha Use Information, Feedback, or any other information about or involving (including the existence of) any of the Alpha Uses or Alpha Services, and (d) the nature, content and existence of this Agreement and any discussions or negotiations between the Parties. Confidential Information does not include any information that (i) is or becomes publicly available without breach of this Agreement, (ii) can be shown by documentation to have been known to the receiving party at the time of its receipt from the disclosing party, (iii) is received from a third party who did not acquire or disclose such information by a wrongful or tortious act, or (iv) can be shown by documentation to have been independently developed by the receiving party without reference to any Confidential Information. - li - | “Policies” means all policies and guidelines related to any Alpha Service, Alpha Materials or other web services offered by Blockchain or its affiliates and made available to User, including privacy policies, terms of use, acceptable use policies, and any additional terms and conditions for a specific Alpha Use. - .flex-column.mtl.pal - h2 2. Participation in Alpha Program - p - ul - li - | 2.1. Generally. Blockchain grants User a limited, nonexclusive, non- transferable, royalty- free, revocable license to do the following during the term of the applicable Alpha Use: (a) access and use the Alpha Service solely for internal evaluation purposes; and (b) install, copy, and use any related Alpha Materials solely as necessary to access and use the Alpha Service in the manner permitted by this Agreement. After the conclusion of a Alpha Use, User will not have any further right to use the applicable Alpha Service, and if Blockchain releases a generally-available version of the Alpha Service, User’s use of the generally commercially available version will be subject to separate terms and conditions. However, Blockchain does not guarantee that any Alpha Service will ever be made generally commercially available, or that any generally commercially available version will contain the same or similar functionality as the version made available by Blockchain during the Alpha Use. - li - | 2.2. Blockchain reserves the right to amend this agreement at its discretion and for any reason by giving 30 days’ notice to User, after which time the amendment will become effective to bind the parties. - li - | 2.3. Restrictions and Limitations. User will not: - ul - li - | i. allow access to any Alpha Service or Alpha Materials by any third party other than User’s employees and contractors who (i) have a need to use or access the Alpha Service or Alpha Materials in connection with User’s internal evaluation activities and (ii) have executed written nondisclosure agreements obligating them to protect the confidentiality of the Alpha Service and Alpha Materials; - li - | ii. violate any usage limits for a Alpha Service that Blockchain may communicate to User; - li - | iii. export or allow access to any Alpha Service or Alpha Materials in any manner contrary to the export regulations of the United States; or - li - | iv. otherwise access or use any Alpha Service, or install, copy or use any Alpha Materials, in any manner or for any purpose not expressly permitted by this Agreement. - - | Blockchain may modify the permitted use of or suspend User’s access to any Alpha Service at any time and for any reason. Alpha Services also may be unavailable or their performance may be negatively affected by scheduled maintenance. No service levels or other uptime guarantees apply to the Alpha Services. Blockchain may or may not, at its discretion, notify User in advance of scheduled maintenance, but Blockchain is unable to provide advance notice of unscheduled or emergency maintenance. - li - | 2.4. Alpha Use Information and Feedback. Alpha Use Information and Feedback. In consideration of the rights granted in this Agreement, User will provide Alpha Use Information, when and in the form reasonably requested by Blockchain. Blockchain will have a perpetual and irrevocable right to use, evaluate and otherwise exploit all Alpha Use Information for its own purposes. User will not use any Alpha Use Information except for its internal evaluation purposes. Blockchain has a perpetual and irrevocable right to use and exploit all Feedback and may use the Feedback without accounting or compensation to User. User will not provide any Alpha Use Information or Feedback unless it has all rights necessary to do so. - .bg-light-blue.flex-column.mtl.pal - h2 3. Term and Termination - p - ul - li - | 3.1. Term. Blockchain may, at its discretion, specify the term of each individual Alpha Use, but the term will automatically terminate upon the release of a generally commercially available version of the applicable Alpha Service. The term of this Agreement will commence on the date you begin to use the Alpha Service and will continue until terminated pursuant to Section 3.2. - li - | 3.2. Termination. Either Party may terminate User’s participation in an individual Alpha Use, or this Agreement entirely, at any time for any reason upon notice to the other Party. Upon termination of this Agreement: (a) all rights and licenses granted to User in this Agreement will immediately terminate; (b) User will immediately return or, if instructed by Blockchain, destroy all Alpha Materials or any other confidential or proprietary information of Blockchain or its affiliates related to any Alpha Service or this Agreement; and (c) Sections 2.4 and 4 through 8 will survive. - .flex-column.mtl.pal - h2 4. Confidentiality - p - ul - li - | 4.1. Use and Disclosure. User may not disclose any Confidential Information during the term of this Agreement or at any time during the three (3) year period following the end of the Term. If the Parties have executed a separate non-disclosure agreement (the “NDA”) and there is a conflict between the terms of the NDA and the terms of this Section 4.1, the terms of the NDA will control. - li - | 4.2. Publicity. Neither Party will issue any press release or public statement regarding this Agreement or any Alpha Use, Alpha Service or Alpha Materials unless Blockchain has approved in writing the time, form and content of the information to be disseminated to third parties or the public. - .bg-light-blue.flex-column.mtl.pal - h2 5. DISCLAIMER OF WARRANTIES - p - ul - li - | 5.1. THE ALPHA SERVICES AND ALPHA MATERIALS ARE NOT READY FOR GENERAL COMMERCIAL RELEASE AND MAY CONTAIN BUGS, ERRORS, DEFECTS OR HARMFUL COMPONENTS. ACCORDINGLY, BLOCKCHAIN IS PROVIDING THE ALPHA SERVICES AND ALPHA MATERIALS TO USER - b “AS IS.” - | BLOCKCHAIN MAKES NO WARRANTIES OF ANY KIND WITH RESPECT TO THE ALPHA SERVICES OR ALPHA MATERIALS, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. NOTWITHSTANDING ANY PUBLISHED MATERIALS THAT STATE OTHERWISE, BLOCKCHAIN DOES NOT WARRANT THAT THE ALPHA SERVICES OR ALPHA MATERIALS WILL BE ERROR-FREE - | OR THAT THEY WILL MEET ANY SPECIFIED SERVICE LEVEL, OR WILL OPERATE WITHOUT INTERRUPTIONS OR DOWNTIME. - .flex-column.mtl.pal - h2 6. LIMITATION OF LIABILITY - p - ul - li - | 6.1. NEITHER BLOCKCHAIN NOR ANY OF ITS AFFILIATES OR LICENSORS WILL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES ARISING FROM OR RELATED TO THIS AGREEMENT, WHETHER IN AN ACTION IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, EVEN IF THE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE AGGREGATE LIABILITY OF BLOCKCHAIN AND ITS AFFILIATES AND LICENSORS ARISING FROM OR RELATED TO THIS AGREEMENT WILL NOT EXCEED THE AMOUNTS PAID (IF ANY) BY USER TO BLOCKCHAIN UNDER THIS AGREEMENT. - .bg-light-blue.flex-column.mtl.pal - h2 7. Disputes Resolution by Binding Arbitration; Jury Trial Waiver; Class Action Waiver. - p - ul - li - | 7.1. For any and all controversies, disputes, demands, claims, or causes of action between the parties (including the interpretation and scope of this Section and the arbitrability of the controversy, dispute, demand, claim, or cause of action) relating to the Alpha Service, Alpha Use or this Agreement (as well as any related or prior agreement), the Parties agree to resolve any such controversy, dispute, demand, claim, or cause of action exclusively through binding and confidential arbitration. The arbitration will take place in the federal judicial district of the User’s residence if the User resides in the United States, or in New York City if the User does not. As used in this Section, “we” and “us” mean Blockchain. In addition, “we” and “us” include any third party providing any product, service, or benefit in connection with the Alpha Services or this Agreement (as well as any related or prior agreement that you may have had with us) if such third party is named as a co-party with us in any controversy, dispute, demand, claim, or cause of action subject to this Section. - li - | 7.2. Arbitration will be subject to the Federal Arbitration Act and not any state arbitration law. The arbitration will be conducted before one commercial arbitrator from the American Arbitration Association (“AAA”) with substantial experience in resolving commercial contract disputes. As modified by this Agreement, and unless otherwise agreed upon by the parties in writing, the arbitration will be governed by the AAA’s Commercial Arbitration Rules and, if the arbitrator deems them applicable, the Supplementary Procedures for Consumer Related Disputes (collectively, the “Rules and Procedures”). Where no claims or counterclaims exceed $10,000, the dispute will be resolved by the submission of documents without a hearing, unless a hearing is - | requested by a party or deemed necessary by the arbitrator, in which case, a party may elect to participate telephonically. - li - | 7.3. You should review this provision carefully. To the extent permitted by applicable law, - b YOU ARE GIVING UP YOUR RIGHT TO GO TO COURT - | to assert or defend your rights EXCEPT for matters that you file in small claims court in the state or municipality of your residence within the jurisdictional limits of the small claims court and as long as such matter is only pending in that court. Additionally, notwithstanding this agreement to arbitrate, claims of defamation, and infringement or misappropriation of the other party’s patent, copyright, trademark, or trade secret shall not be subject to this arbitration agreement. Such claims shall be exclusively brought in the state or federal courts located in New York County, New York. Additionally, notwithstanding this agreement to arbitrate, you or we may seek emergency equitable relief before the state or federal courts located in New York County, New York in order to maintain the status quo pending arbitration and hereby agree to submit to the exclusive personal jurisdiction of the courts located within New York County, New York for such purpose. A request for interim measures shall not be deemed a waiver of the right to arbitrate. - li - | 7.4. Your rights will be determined by a NEUTRAL ARBITRATOR and NOT a judge or jury. You are entitled to a FAIR HEARING, BUT the arbitration procedures may be SIMPLER AND MORE LIMITED THAN RULES APPLICABLE IN COURT. Arbitrators’ decisions are as enforceable as any court order and are subject to VERY LIMITED REVIEW BY A COURT. - li - | 7.5. You and we must abide by the following rules: (A) ANY CLAIMS BROUGHT BY YOU OR US MUST BE BROUGHT IN THE PARTY’S INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING; (B) THE ARBITRATOR MAY NOT CONSOLIDATE MORE THAN ONE PERSON’S CLAIMS, MAY NOT OTHERWISE PRESIDE OVER ANY FORM OF A REPRESENTATIVE OR CLASS PROCEEDING, AND MAY NOT AWARD CLASS-WIDE RELIEF; (c) in the event that you are able to demonstrate that the costs of arbitration will be prohibitive as compared to the costs of litigation, we will pay as much of your filing and hearing fees in connection with the arbitration as the arbitrator deems necessary to prevent the arbitration from being cost- prohibitive as compared to the cost of litigation, (d) we also reserve the right, in our sole and exclusive discretion, to assume responsibility for any or all of the costs of the arbitration; (e) the arbitrator will honor claims of privilege and privacy recognized at law; (f) the arbitration will be confidential, and neither you nor we may disclose the existence, content, or results of any arbitration, except as may be required by applicable law or for purposes of enforcement of the arbitration award; (g) subject to the limitation of liability provisions of these Terms, the arbitrator may award any individual relief or - | individual remedies that are expressly permitted by applicable law; and (h) you and we will pay our respective attorneys’ fees and expenses, unless there is a statutory provision that requires the prevailing party to be paid its fees and litigation expenses and the arbitrator awards such attorneys’ fees and expenses to the prevailing party, and, in such instance, the fees and costs awarded will be determined by the applicable law. - li - | 7.7. This Section will survive termination of your account and this Agreement as well as any voluntary payment of any debt in full by you or any bankruptcy by you or us. With the exception of subparts (a) and (b) above of this Section (prohibiting arbitration on a class or collective basis), if any part of this arbitration provision is deemed to be invalid, unenforceable, or illegal, or otherwise conflicts with the Rules and Procedures, then the balance of this arbitration provision will remain in effect and will be construed in accordance with its terms as if the invalid, unenforceable, illegal or conflicting part was not contained herein. If, however, either subpart (a) or (b) above of this Section is found to be invalid, unenforceable, or illegal, then the entirety of this arbitration provision will be null and void, and neither you nor we will be entitled to arbitration. If for any reason a claim proceeds in court rather than in arbitration, the dispute shall be exclusively brought in state or federal court located in New York County, New York. - li - | 7.8. YOU AGREE THAT, TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, ANY CLAIM OR CAUSE OF ACTION ARISING OUT OF OR RELATING TO THE SERVICE OR THESE TERMS MUST BE FILED WITHIN ONE (1) YEAR AFTER SUCH CLAIM OR CAUSE OF ACTION AROSE OR IT WILL BE FOREVER BARRED. - .flex-column.mtl.pal - h2 8. Miscellaneous - p - ul - li - | 8.1. Except for those limited rights expressly granted in Section 2.1, Blockchain and its licensors retain all right, title and interest in and to the Alpha Services and the Alpha Materials, including all related intellectual property rights. The parties are independent contractors with respect to each other, and nothing in this Agreement shall be construed as creating an employer-employee relationship, a partnership, agency relationship or a joint venture between the parties. This Agreement further controls the actions of all party representatives, officers, agents, employees and associated individuals. The terms of this Agreement shall be binding on the parties, and all successors to the foregoing who take their rights hereunder. Neither party will assign, transfer or delegate its rights or obligations under this Agreement (in whole or in part) without the other party’s prior - | written consent except that Blockchain may assign and delegate this Agreement pursuant to a transfer of all or substantially all of Blockchain’s business and assets, whether by merger, sale of assets, sale of stock, or otherwise. Any attempted assignment, transfer or delegation in violation of the foregoing shall be null and void. All modifications to and waivers of any terms of this Agreement must be in a writing that is signed by the parties hereto and expressly references this Agreement. This Agreement shall be governed by the laws of the State of New York, without regard to New York conflict of laws rules. The exclusive venue and jurisdiction for any and all disputes, claims and controversies arising from or relating to this Agreement shall be the state or federal courts located in New York City. In the event that any provision of this Agreement conflicts with governing law or if any provision is held to be null, void or otherwise ineffective or invalid by a court of competent jurisdiction, (a) such provision shall be deemed to be restated to reflect as nearly as possible the original intentions of the parties in accordance with applicable law, and (b) the remaining terms, provisions, covenants and restrictions of this Agreement shall remain in full force and effect. No waiver of any breach of any provision of this Agreement shall constitute a waiver of any prior, concurrent or subsequent breach of the same or any other provisions hereof, and no waiver shall be effective unless made in writing and signed by an authorized representative of the waiving party. This Agreement and all expressly referenced documents constitute the entire agreement between the parties with respect to the subject matter hereof and supersedes all prior and contemporaneous agreements or communications, including any reseller or similar agreements previously executed by the parties. All notices, consents and approvals under this Agreement must be delivered in writing by email to the email address supplied by one party to the other during the account signup process, or posted to the Blockchain website. -.modal-footer.flex-end.pal - button.button-success.button-lg(ng-click="$dismiss()", translate="OK") diff --git a/app/partials/alpha-agreement.jade b/app/partials/user-agreement.jade similarity index 56% rename from app/partials/alpha-agreement.jade rename to app/partials/user-agreement.jade index 9844fdd9d5..f46659c2c2 100644 --- a/app/partials/alpha-agreement.jade +++ b/app/partials/user-agreement.jade @@ -1,12 +1,12 @@ .modal-header.bc-modal-header h3 - | ALPHA PROGRAM PARTICIPATION AGREEMENT + | BETA PROGRAM PARTICIPATION AGREEMENT .modal-body .flex-column.pal p - | This Alpha Program Participation Agreement (this “Agreement”) is made and entered into by and between Blockchain Lux S.a.r.l. or its affiliates (“Blockchain”), and you, the “User” who uses the Alpha Services as hereinafter defined. The terms of any other agreement between Blockchain and User are hereby incorporated into the terms of this Agreement, however, in the case of any conflict, the terms of this Agreement supersede and control, except where otherwise specifically stated herein. This Agreement describes the terms and conditions under which User may access and use certain features, technologies and services that are not yet generally commercially available (each, a “Alpha Service”). Blockchain and User are sometimes referred to collectively as the “Parties” and each individually as a “Party.” + | This Beta Program Participation Agreement (this “Agreement”) is made and entered into by and between Blockchain Lux S.a.r.l. or its affiliates (“Blockchain”), and you, the “User” who uses the Beta Services as hereinafter defined. The terms of any other agreement between Blockchain and User are hereby incorporated into the terms of this Agreement, however, in the case of any conflict, the terms of this Agreement supersede and control, except where otherwise specifically stated herein. This Agreement describes the terms and conditions under which User may access and use certain features, technologies and services that are not yet generally commercially available (each, a “Beta Service”). Blockchain and User are sometimes referred to collectively as the “Parties” and each individually as a “Party.” p - b THE SOFTWARE SUPPLIED TO USER IS IN ALPHA FORM AND SUPPLIED FOR TESTING PURPOSES ONLY. BUGS, CRASHES AND OTHER MALFUNCTIONS ARE ALMOST CERTAIN TO OCCUR. UNDER NO CIRCUMSTANCES MAY USER USE THE SOFTWARE SUPPLIED IN CONNECTION WITH THE ALPHA SERVICE TO SECURE MORE THAN $10 WORTH OF BITCOIN. + b THE SOFTWARE SUPPLIED TO USER IS IN BETA FORM AND SUPPLIED FOR TESTING PURPOSES ONLY. BUGS, CRASHES AND OTHER MALFUNCTIONS ARE ALMOST CERTAIN TO OCCUR. UNDER NO CIRCUMSTANCES MAY USER USE THE SOFTWARE SUPPLIED IN CONNECTION WITH THE BETA SERVICE TO SECURE MORE THAN $10 WORTH OF BITCOIN. p b ANY DISPUTES ARISING OUT OF THIS AGREEMENT WILL BE RESOLVED BY BINDING, INDIVIDUAL ARBITRATION AND THE PARTIES WAIVE THEIR RIGHTS TO GO TO COURT OR PARTICIPATE IN A CLASS ACTION LAWSUIT OR CLASS- WIDE ARBITRATION. .bg-light-blue.flex-column.mtl.pal @@ -14,48 +14,48 @@ p ol li - | “Alpha Materials” means any hardware, software, specifications or other technical documentation related to a specific Alpha Service that may be provided to User by Blockchain. + | “Beta Materials” means any hardware, software, specifications or other technical documentation related to a specific Beta Service that may be provided to User by Blockchain. li - | “Alpha Use” means the testing and evaluation of a specific Alpha Service by User and certain other Blockchain customers or business partners. + | “Beta Use” means the testing and evaluation of a specific Beta Service by User and certain other Blockchain customers or business partners. li - | “Alpha Use Information” means all information relating to User’s use, testing or evaluation of a Alpha Service or any related Alpha Materials, including all observations or information regarding the nature, quality, performance, features or functionality of a Alpha Service or any related Alpha Materials. + | “Beta Use Information” means all information relating to User’s use, testing or evaluation of a Beta Service or any related Beta Materials, including all observations or information regarding the nature, quality, performance, features or functionality of a Beta Service or any related Beta Materials. li - | “Feedback” means all feedback, suggestions, and ideas that User provides to Blockchain concerning improvements or enhancements to a Alpha Service or any related Alpha Materials. + | “Feedback” means all feedback, suggestions, and ideas that User provides to Blockchain concerning improvements or enhancements to a Beta Service or any related Beta Materials. li - | “Confidential Information” means all nonpublic information disclosed by Blockchain or its agents to User, its affiliates, or the agents of any of the foregoing, that is designated as confidential or that, given the nature of the information or the circumstances surrounding its disclosure, reasonably should be considered as confidential. Confidential Information includes, without limitation (a) nonpublic information relating to Blockchain’ or its affiliates’ technology, customers, business plans, promotional and marketing activities, finances and other business affairs, (b) third-party information that Blockchain or its affiliates is obligated to keep confidential, (c) Alpha Materials, Alpha Use Information, Feedback, or any other information about or involving (including the existence of) any of the Alpha Uses or Alpha Services, and (d) the nature, content and existence of this Agreement and any discussions or negotiations between the Parties. Confidential Information does not include any information that (i) is or becomes publicly available without breach of this Agreement, (ii) can be shown by documentation to have been known to the receiving party at the time of its receipt from the disclosing party, (iii) is received from a third party who did not acquire or disclose such information by a wrongful or tortious act, or (iv) can be shown by documentation to have been independently developed by the receiving party without reference to any Confidential Information. + | “Confidential Information” means all nonpublic information disclosed by Blockchain or its agents to User, its affiliates, or the agents of any of the foregoing, that is designated as confidential or that, given the nature of the information or the circumstances surrounding its disclosure, reasonably should be considered as confidential. Confidential Information includes, without limitation (a) nonpublic information relating to Blockchain’ or its affiliates’ technology, customers, business plans, promotional and marketing activities, finances and other business affairs, (b) third-party information that Blockchain or its affiliates is obligated to keep confidential, (c) Beta Materials, Beta Use Information, Feedback, or any other information about or involving (including the existence of) any of the Beta Uses or Beta Services, and (d) the nature, content and existence of this Agreement and any discussions or negotiations between the Parties. Confidential Information does not include any information that (i) is or becomes publicly available without breach of this Agreement, (ii) can be shown by documentation to have been known to the receiving party at the time of its receipt from the disclosing party, (iii) is received from a third party who did not acquire or disclose such information by a wrongful or tortious act, or (iv) can be shown by documentation to have been independently developed by the receiving party without reference to any Confidential Information. li - | “Policies” means all policies and guidelines related to any Alpha Service, Alpha Materials or other web services offered by Blockchain or its affiliates and made available to User, including privacy policies, terms of use, acceptable use policies, and any additional terms and conditions for a specific Alpha Use. + | “Policies” means all policies and guidelines related to any Beta Service, Beta Materials or other web services offered by Blockchain or its affiliates and made available to User, including privacy policies, terms of use, acceptable use policies, and any additional terms and conditions for a specific Beta Use. .flex-column.mtl.pal - h2 2. Participation in Alpha Program + h2 2. Participation in Beta Program p ul li - | 2.1. Generally. Blockchain grants User a limited, nonexclusive, non- transferable, royalty- free, revocable license to do the following during the term of the applicable Alpha Use: (a) access and use the Alpha Service solely for internal evaluation purposes; and (b) install, copy, and use any related Alpha Materials solely as necessary to access and use the Alpha Service in the manner permitted by this Agreement. After the conclusion of a Alpha Use, User will not have any further right to use the applicable Alpha Service, and if Blockchain releases a generally-available version of the Alpha Service, User’s use of the generally commercially available version will be subject to separate terms and conditions. However, Blockchain does not guarantee that any Alpha Service will ever be made generally commercially available, or that any generally commercially available version will contain the same or similar functionality as the version made available by Blockchain during the Alpha Use. + | 2.1. Generally. Blockchain grants User a limited, nonexclusive, non- transferable, royalty- free, revocable license to do the following during the term of the applicable Beta Use: (a) access and use the Beta Service solely for internal evaluation purposes; and (b) install, copy, and use any related Beta Materials solely as necessary to access and use the Beta Service in the manner permitted by this Agreement. After the conclusion of a Beta Use, User will not have any further right to use the applicable Beta Service, and if Blockchain releases a generally-available version of the Beta Service, User’s use of the generally commercially available version will be subject to separate terms and conditions. However, Blockchain does not guarantee that any Beta Service will ever be made generally commercially available, or that any generally commercially available version will contain the same or similar functionality as the version made available by Blockchain during the Beta Use. li | 2.2. Blockchain reserves the right to amend this agreement at its discretion and for any reason by giving 30 days’ notice to User, after which time the amendment will become effective to bind the parties. li | 2.3. Restrictions and Limitations. User will not: ul li - | i. allow access to any Alpha Service or Alpha Materials by any third party other than User’s employees and contractors who (i) have a need to use or access the Alpha Service or Alpha Materials in connection with User’s internal evaluation activities and (ii) have executed written nondisclosure agreements obligating them to protect the confidentiality of the Alpha Service and Alpha Materials; + | i. allow access to any Beta Service or Beta Materials by any third party other than User’s employees and contractors who (i) have a need to use or access the Beta Service or Beta Materials in connection with User’s internal evaluation activities and (ii) have executed written nondisclosure agreements obligating them to protect the confidentiality of the Beta Service and Beta Materials; li - | ii. violate any usage limits for a Alpha Service that Blockchain may communicate to User; + | ii. violate any usage limits for a Beta Service that Blockchain may communicate to User; li - | iii. export or allow access to any Alpha Service or Alpha Materials in any manner contrary to the export regulations of the United States; or + | iii. export or allow access to any Beta Service or Beta Materials in any manner contrary to the export regulations of the United States; or li - | iv. otherwise access or use any Alpha Service, or install, copy or use any Alpha Materials, in any manner or for any purpose not expressly permitted by this Agreement. + | iv. otherwise access or use any Beta Service, or install, copy or use any Beta Materials, in any manner or for any purpose not expressly permitted by this Agreement. - | Blockchain may modify the permitted use of or suspend User’s access to any Alpha Service at any time and for any reason. Alpha Services also may be unavailable or their performance may be negatively affected by scheduled maintenance. No service levels or other uptime guarantees apply to the Alpha Services. Blockchain may or may not, at its discretion, notify User in advance of scheduled maintenance, but Blockchain is unable to provide advance notice of unscheduled or emergency maintenance. + | Blockchain may modify the permitted use of or suspend User’s access to any Beta Service at any time and for any reason. Beta Services also may be unavailable or their performance may be negatively affected by scheduled maintenance. No service levels or other uptime guarantees apply to the Beta Services. Blockchain may or may not, at its discretion, notify User in advance of scheduled maintenance, but Blockchain is unable to provide advance notice of unscheduled or emergency maintenance. li - | 2.4. Alpha Use Information and Feedback. Alpha Use Information and Feedback. In consideration of the rights granted in this Agreement, User will provide Alpha Use Information, when and in the form reasonably requested by Blockchain. Blockchain will have a perpetual and irrevocable right to use, evaluate and otherwise exploit all Alpha Use Information for its own purposes. User will not use any Alpha Use Information except for its internal evaluation purposes. Blockchain has a perpetual and irrevocable right to use and exploit all Feedback and may use the Feedback without accounting or compensation to User. User will not provide any Alpha Use Information or Feedback unless it has all rights necessary to do so. + | 2.4. Beta Use Information and Feedback. Beta Use Information and Feedback. In consideration of the rights granted in this Agreement, User will provide Beta Use Information, when and in the form reasonably requested by Blockchain. Blockchain will have a perpetual and irrevocable right to use, evaluate and otherwise exploit all Beta Use Information for its own purposes. User will not use any Beta Use Information except for its internal evaluation purposes. Blockchain has a perpetual and irrevocable right to use and exploit all Feedback and may use the Feedback without accounting or compensation to User. User will not provide any Beta Use Information or Feedback unless it has all rights necessary to do so. .bg-light-blue.flex-column.mtl.pal h2 3. Term and Termination p ul li - | 3.1. Term. Blockchain may, at its discretion, specify the term of each individual Alpha Use, but the term will automatically terminate upon the release of a generally commercially available version of the applicable Alpha Service. The term of this Agreement will commence on the date you begin to use the Alpha Service and will continue until terminated pursuant to Section 3.2. + | 3.1. Term. Blockchain may, at its discretion, specify the term of each individual Beta Use, but the term will automatically terminate upon the release of a generally commercially available version of the applicable Beta Service. The term of this Agreement will commence on the date you begin to use the Beta Service and will continue until terminated pursuant to Section 3.2. li - | 3.2. Termination. Either Party may terminate User’s participation in an individual Alpha Use, or this Agreement entirely, at any time for any reason upon notice to the other Party. Upon termination of this Agreement: (a) all rights and licenses granted to User in this Agreement will immediately terminate; (b) User will immediately return or, if instructed by Blockchain, destroy all Alpha Materials or any other confidential or proprietary information of Blockchain or its affiliates related to any Alpha Service or this Agreement; and (c) Sections 2.4 and 4 through 8 will survive. + | 3.2. Termination. Either Party may terminate User’s participation in an individual Beta Use, or this Agreement entirely, at any time for any reason upon notice to the other Party. Upon termination of this Agreement: (a) all rights and licenses granted to User in this Agreement will immediately terminate; (b) User will immediately return or, if instructed by Blockchain, destroy all Beta Materials or any other confidential or proprietary information of Blockchain or its affiliates related to any Beta Service or this Agreement; and (c) Sections 2.4 and 4 through 8 will survive. .flex-column.mtl.pal h2 4. Confidentiality p @@ -63,15 +63,15 @@ li | 4.1. Use and Disclosure. User may not disclose any Confidential Information during the term of this Agreement or at any time during the three (3) year period following the end of the Term. If the Parties have executed a separate non-disclosure agreement (the “NDA”) and there is a conflict between the terms of the NDA and the terms of this Section 4.1, the terms of the NDA will control. li - | 4.2. Publicity. Neither Party will issue any press release or public statement regarding this Agreement or any Alpha Use, Alpha Service or Alpha Materials unless Blockchain has approved in writing the time, form and content of the information to be disseminated to third parties or the public. + | 4.2. Publicity. Neither Party will issue any press release or public statement regarding this Agreement or any Beta Use, Beta Service or Beta Materials unless Blockchain has approved in writing the time, form and content of the information to be disseminated to third parties or the public. .bg-light-blue.flex-column.mtl.pal h2 5. DISCLAIMER OF WARRANTIES p ul li - | 5.1. THE ALPHA SERVICES AND ALPHA MATERIALS ARE NOT READY FOR GENERAL COMMERCIAL RELEASE AND MAY CONTAIN BUGS, ERRORS, DEFECTS OR HARMFUL COMPONENTS. ACCORDINGLY, BLOCKCHAIN IS PROVIDING THE ALPHA SERVICES AND ALPHA MATERIALS TO USER + | 5.1. THE BETA SERVICES AND BETA MATERIALS ARE NOT READY FOR GENERAL COMMERCIAL RELEASE AND MAY CONTAIN BUGS, ERRORS, DEFECTS OR HARMFUL COMPONENTS. ACCORDINGLY, BLOCKCHAIN IS PROVIDING THE BETA SERVICES AND BETA MATERIALS TO USER b “AS IS.” - | BLOCKCHAIN MAKES NO WARRANTIES OF ANY KIND WITH RESPECT TO THE ALPHA SERVICES OR ALPHA MATERIALS, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. NOTWITHSTANDING ANY PUBLISHED MATERIALS THAT STATE OTHERWISE, BLOCKCHAIN DOES NOT WARRANT THAT THE ALPHA SERVICES OR ALPHA MATERIALS WILL BE ERROR-FREE + | BLOCKCHAIN MAKES NO WARRANTIES OF ANY KIND WITH RESPECT TO THE BETA SERVICES OR BETA MATERIALS, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. NOTWITHSTANDING ANY PUBLISHED MATERIALS THAT STATE OTHERWISE, BLOCKCHAIN DOES NOT WARRANT THAT THE BETA SERVICES OR BETA MATERIALS WILL BE ERROR-FREE | OR THAT THEY WILL MEET ANY SPECIFIED SERVICE LEVEL, OR WILL OPERATE WITHOUT INTERRUPTIONS OR DOWNTIME. .flex-column.mtl.pal h2 6. LIMITATION OF LIABILITY @@ -84,7 +84,7 @@ p ul li - | 7.1. For any and all controversies, disputes, demands, claims, or causes of action between the parties (including the interpretation and scope of this Section and the arbitrability of the controversy, dispute, demand, claim, or cause of action) relating to the Alpha Service, Alpha Use or this Agreement (as well as any related or prior agreement), the Parties agree to resolve any such controversy, dispute, demand, claim, or cause of action exclusively through binding and confidential arbitration. The arbitration will take place in the federal judicial district of the User’s residence if the User resides in the United States, or in New York City if the User does not. As used in this Section, “we” and “us” mean Blockchain. In addition, “we” and “us” include any third party providing any product, service, or benefit in connection with the Alpha Services or this Agreement (as well as any related or prior agreement that you may have had with us) if such third party is named as a co-party with us in any controversy, dispute, demand, claim, or cause of action subject to this Section. + | 7.1. For any and all controversies, disputes, demands, claims, or causes of action between the parties (including the interpretation and scope of this Section and the arbitrability of the controversy, dispute, demand, claim, or cause of action) relating to the Beta Service, Beta Use or this Agreement (as well as any related or prior agreement), the Parties agree to resolve any such controversy, dispute, demand, claim, or cause of action exclusively through binding and confidential arbitration. The arbitration will take place in the federal judicial district of the User’s residence if the User resides in the United States, or in New York City if the User does not. As used in this Section, “we” and “us” mean Blockchain. In addition, “we” and “us” include any third party providing any product, service, or benefit in connection with the Beta Services or this Agreement (as well as any related or prior agreement that you may have had with us) if such third party is named as a co-party with us in any controversy, dispute, demand, claim, or cause of action subject to this Section. li | 7.2. Arbitration will be subject to the Federal Arbitration Act and not any state arbitration law. The arbitration will be conducted before one commercial arbitrator from the American Arbitration Association (“AAA”) with substantial experience in resolving commercial contract disputes. As modified by this Agreement, and unless otherwise agreed upon by the parties in writing, the arbitration will be governed by the AAA’s Commercial Arbitration Rules and, if the arbitrator deems them applicable, the Supplementary Procedures for Consumer Related Disputes (collectively, the “Rules and Procedures”). Where no claims or counterclaims exceed $10,000, the dispute will be resolved by the submission of documents without a hearing, unless a hearing is | requested by a party or deemed necessary by the arbitrator, in which case, a party may elect to participate telephonically. @@ -106,7 +106,7 @@ p ul li - | 8.1. Except for those limited rights expressly granted in Section 2.1, Blockchain and its licensors retain all right, title and interest in and to the Alpha Services and the Alpha Materials, including all related intellectual property rights. The parties are independent contractors with respect to each other, and nothing in this Agreement shall be construed as creating an employer-employee relationship, a partnership, agency relationship or a joint venture between the parties. This Agreement further controls the actions of all party representatives, officers, agents, employees and associated individuals. The terms of this Agreement shall be binding on the parties, and all successors to the foregoing who take their rights hereunder. Neither party will assign, transfer or delegate its rights or obligations under this Agreement (in whole or in part) without the other party’s prior + | 8.1. Except for those limited rights expressly granted in Section 2.1, Blockchain and its licensors retain all right, title and interest in and to the Beta Services and the Beta Materials, including all related intellectual property rights. The parties are independent contractors with respect to each other, and nothing in this Agreement shall be construed as creating an employer-employee relationship, a partnership, agency relationship or a joint venture between the parties. This Agreement further controls the actions of all party representatives, officers, agents, employees and associated individuals. The terms of this Agreement shall be binding on the parties, and all successors to the foregoing who take their rights hereunder. Neither party will assign, transfer or delegate its rights or obligations under this Agreement (in whole or in part) without the other party’s prior | written consent except that Blockchain may assign and delegate this Agreement pursuant to a transfer of all or substantially all of Blockchain’s business and assets, whether by merger, sale of assets, sale of stock, or otherwise. Any attempted assignment, transfer or delegation in violation of the foregoing shall be null and void. All modifications to and waivers of any terms of this Agreement must be in a writing that is signed by the parties hereto and expressly references this Agreement. This Agreement shall be governed by the laws of the State of New York, without regard to New York conflict of laws rules. The exclusive venue and jurisdiction for any and all disputes, claims and controversies arising from or relating to this Agreement shall be the state or federal courts located in New York City. In the event that any provision of this Agreement conflicts with governing law or if any provision is held to be null, void or otherwise ineffective or invalid by a court of competent jurisdiction, (a) such provision shall be deemed to be restated to reflect as nearly as possible the original intentions of the parties in accordance with applicable law, and (b) the remaining terms, provisions, covenants and restrictions of this Agreement shall remain in full force and effect. No waiver of any breach of any provision of this Agreement shall constitute a waiver of any prior, concurrent or subsequent breach of the same or any other provisions hereof, and no waiver shall be effective unless made in writing and signed by an authorized representative of the waiving party. This Agreement and all expressly referenced documents constitute the entire agreement between the parties with respect to the subject matter hereof and supersedes all prior and contemporaneous agreements or communications, including any reseller or similar agreements previously executed by the parties. All notices, consents and approvals under this Agreement must be delivered in writing by email to the email address supplied by one party to the other during the account signup process, or posted to the Blockchain website. .modal-footer.flex-end.pal button.button-success.button-lg(ng-click="$close()" translate="OK") diff --git a/assets/js/controllers/signup.controller.js b/assets/js/controllers/signup.controller.js index 3f60ace587..16778370b4 100644 --- a/assets/js/controllers/signup.controller.js +++ b/assets/js/controllers/signup.controller.js @@ -31,7 +31,7 @@ function SignupCtrl($scope, $state, $cookies, $filter, $translate, $uibModal, Wa $scope.showAgreement = () => { const modalInstance = $uibModal.open({ - templateUrl: "partials/alpha-agreement.jade", + templateUrl: "partials/user-agreement.jade", controller: function () {}, windowClass: "bc-modal terms-modal" }); diff --git a/assets/js/controllers/walletNavigation.controller.js b/assets/js/controllers/walletNavigation.controller.js index 8dab6cda75..bab2bddfe1 100644 --- a/assets/js/controllers/walletNavigation.controller.js +++ b/assets/js/controllers/walletNavigation.controller.js @@ -64,10 +64,11 @@ function WalletNavigationCtrl($scope, Wallet, Alerts, SecurityCenter, $state, $s $scope.termsOfService = () => { let modalInstance = $uibModal.open({ - templateUrl: 'partials/terms-of-service.jade', - windowClass: 'bc-modal terms-modal' + templateUrl: "partials/user-agreement.jade", + controller: function () {}, + windowClass: "bc-modal terms-modal" }); - }; + } $scope.privacyPolicy = () => { let modalInstance = $uibModal.open({ diff --git a/locales/en-human.json b/locales/en-human.json index 00a4c73b2c..477a8c3052 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -447,7 +447,7 @@ "DYK_RECOVERY_TITLE": "Passwords are not stored or shared with us", "DYK_TX_FEES": "Transaction fees are used for sending bitcoins, which are collected by the Bitcoin network of miners. To assure your transaction is confirmed by the network, we automatically include the appropriate fee based on the network standards.", "DYK_TX_FEES_TITLE": "What are transaction fees for?", - "DYK_FEEDBACK": "Our Alpha Wallet depends on you! Send us what you liked, loved, or hated to our team. All of the feedback is collected and reviewed directly by our development team.", + "DYK_FEEDBACK": "Our Beta Wallet depends on you! Send us what you liked, loved, or hated to our team. All of the feedback is collected and reviewed directly by our development team.", "DYK_FEEDBACK_TITLE": "We're interested in your feedback", "DYK_BTC_VALUE": "Our wallet balances are reflected in bitcoin, but we do show a dollar estimation based on the current market price. The amount of bitcoins will stay the same regardless of market fluctuations in price.", "DYK_BTC_VALUE_TITLE": "Bitcoin's value is constantly changing", @@ -468,7 +468,8 @@ "SIGNOUT": "Sign out", "EDIT_NOTE": "Edit", "DELETE_NOTE": "Delete", - "ALPHA_WARNING": "Please note, this is an Alpha of a new product. Please do not test this wallet with more funds than you are willing to lose.", + "USER_AGREEMENT" : "Beta Program Participation Agreement", + "USER_AGREEMENT_WARNING": "Please note, this is an Beta of a new product. Please do not test this wallet with more funds than you are willing to lose.", "XPUB_WARNING": "You should only give this Extended Public Key (xPub) to those you trust. With this information, they may be able to keep track of your payments, and may be able to disrupt your access to your wallet.", "LOST_WALLET_ID": "I've lost my Wallet ID", "LOST_WALLET_PWD": "I've lost my Wallet Password", diff --git a/tests/controllers/accounts_ctrl_spec.coffee b/tests/controllers/accounts_ctrl_spec.coffee index e19defff9a..993e5bd4a4 100644 --- a/tests/controllers/accounts_ctrl_spec.coffee +++ b/tests/controllers/accounts_ctrl_spec.coffee @@ -63,12 +63,6 @@ describe "WalletNavigationCtrl", -> expect(scope.showOrHide()).toBe(false) ) - it "should open modal to see Terms of Service", inject(() -> - spyOn(modal, "open") - scope.termsOfService() - expect(modal.open).toHaveBeenCalled() - ) - it "should open modal to see Privacy Policy", inject(() -> spyOn(modal, "open") scope.privacyPolicy() From 1d35f29da815dbfcfd73aea6a14db6793a4c2b33 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 6 Jan 2016 13:01:44 +0100 Subject: [PATCH 39/72] feat(Recovery): use download attribute for nicer file name and to prevent in-browser viewing --- app/partials/confirm-recovery-phrase-modal.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/partials/confirm-recovery-phrase-modal.jade b/app/partials/confirm-recovery-phrase-modal.jade index 11f54f1dae..c0ca492df5 100644 --- a/app/partials/confirm-recovery-phrase-modal.jade +++ b/app/partials/confirm-recovery-phrase-modal.jade @@ -17,7 +17,7 @@ .row .col-xs-12 p.center - a.btn.btn-inverse(href="img/recovery.pdf", target="_blank") + a.btn.btn-inverse(href="img/recovery.pdf", target="_blank", download="recovery.pdf") span i.ti-printer.prm span(translate="PRINT_RECOVERY_SHEET") From f22ca557376f8f0b6bca94b42831c45abab243b4 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 6 Jan 2016 13:16:11 +0100 Subject: [PATCH 40/72] fix(Recover): fix redirect after recovery --- assets/js/controllers/recoverFunds.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/controllers/recoverFunds.controller.js b/assets/js/controllers/recoverFunds.controller.js index fb08dba205..60b3d63640 100644 --- a/assets/js/controllers/recoverFunds.controller.js +++ b/assets/js/controllers/recoverFunds.controller.js @@ -28,7 +28,7 @@ function RecoverFundsCtrl($scope, $rootScope, $state, $timeout, $translate, Wall console.error(err); }; $timeout(() => { - $state.go('public.login'); + $state.go('public.login-uid', {uid: wallet.guid}); Wallet.login( wallet.guid, wallet.password, null, null, loginSuccess, loginError ); From 1443b338119628f36e0b8640127883a6239e9fea Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 6 Jan 2016 13:39:08 +0100 Subject: [PATCH 41/72] fix(bcAsyncInput): spinner remained after save, blockchain/My-Wallet-V3-Frontend@867850dbdbb276 --- assets/js/directives/bc-async-input.directive.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/js/directives/bc-async-input.directive.js b/assets/js/directives/bc-async-input.directive.js index 3befda15d1..96764e08b4 100644 --- a/assets/js/directives/bc-async-input.directive.js +++ b/assets/js/directives/bc-async-input.directive.js @@ -74,6 +74,9 @@ function bcAsyncInput($timeout, Wallet) { scope.ngModel = scope.form.newValue; if (!attrs.custom) scope.bcAsyncForm.$setPristine(); + scope.$root.$safeApply(scope) + Wallet.saveActivity(2) + // Fixes issue: hit enter after changing PBKDF2 iterations // when 2nd password is enabled scope.$evalAsync(() => { From c785d759ac1f72f8314bd2acb8cfe93e3cc9c3dd Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 6 Jan 2016 14:45:54 +0100 Subject: [PATCH 42/72] fix(Deploy): fix filename for .woff2 fonts --- Gruntfile.coffee | 28 +++++++++++++--------------- server.js | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index a33684bd4b..ee351217f2 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -1,3 +1,6 @@ +fs = require("fs") +path = require("path") + module.exports = (grunt) -> grunt.initConfig @@ -263,33 +266,29 @@ module.exports = (grunt) -> format: "{{basename}}-{{hash}}.{{ext}}" callback: (befores, afters) -> + publicdir = fs.realpathSync("dist") + # Start with the longest file names, so e.g. some-font.woff2 is renamed before some-font.woff. tuples = new Array i = 0 while i < befores.length - tuples.push [befores[i],afters[i]] + tuples.push [path.relative(publicdir, befores[i]),path.relative(publicdir, afters[i])] i++ - tuples.sort((a,b) -> - if a[0].length != b[0].length - return a[0].length < b[0].length - return a < b - ) + tuples.sort((a,b) -> b[0].length - a[0].length) - befores = tuples.map((t)->t[0]) - afters = tuples.map((t)->t[1]) + ordered_befores = tuples.map((t)->t[0]) + ordered_afters = tuples.map((t)->t[1]) - publicdir = require("fs").realpathSync("dist") - path = require("path") for referring_file_path in ["dist/js/application.min.js", "dist/css/application.css", "dist/index.html"] contents = grunt.file.read(referring_file_path) before = undefined after = undefined i = 0 - while i < befores.length - before = path.relative(publicdir, befores[i]) - after = path.relative(publicdir, afters[i]) + while i < ordered_befores.length + before = ordered_befores[i] + after = ordered_afters[i] contents = contents.split("build/" + before).join(after) contents = contents.split(before).join(after) @@ -315,8 +314,7 @@ module.exports = (grunt) -> format: "{{basename}}-{{hash}}.{{ext}}" callback: (befores, afters) -> - publicdir = require("fs").realpathSync("dist") - path = require("path") + publicdir = fs.realpathSync("dist") for referring_file_path in ["dist/index.html"] contents = grunt.file.read(referring_file_path) diff --git a/server.js b/server.js index 793069a91a..d73dfadb91 100644 --- a/server.js +++ b/server.js @@ -64,7 +64,7 @@ if (dist) { } app.use(function (req, res) { - res.send('

404 Not Found

'); + res.send(404,'

404 Not Found

'); }); app.listen(port, function () { From e6477cba34c4dd1f9369e2a02d2736b80e99af3e Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 8 Jan 2016 10:32:04 +0100 Subject: [PATCH 43/72] copy(Recovery): lower case URL in PDF --- img/recovery.pdf | Bin 27786 -> 27918 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/recovery.pdf b/img/recovery.pdf index 3355babd04dfe60331d5d8e334ae87d8dfc5aa1e..dc448e16a4078e00d82c2673f40811235f99f4d9 100644 GIT binary patch delta 12616 zcmb_@WmFx(vTlF?fe?bbyTcaP8-fIP4X(k12X}Um;O_1o+}+(>f(3VXzd7fiSdN^;Pg9T=;LeNLCVV0F+&x0>aHMN&y6dxj5P7C;;rr6hJN@ z7br?H%n%CTqTu+)MPdB`^`9LMAP@@qcZ`z*$o(G}0L<|p7?2b6Zx|q|6I%rUfB^nu z0La1p@4|rq&i?^}asvKs(!aKFfMc@Y^yR=nE+Gz4At6x!5G*JN5rlF;Ay6T(ASakx zm=hw*&&v(q7UTenhzN>;MZh4)-!~`+HzxofD8eBuEF#9qufwijXYBNkCE*0eD8Rc* zbQYl%{y+)TAb=7@_PS&xdUwWqN6?BFQk4FRUVmFnEZhyJ%C`0qDa zsng(uhvcuU6Ufkw{^Je8n}Sbhxz*SRb?``v5()~M(lO(RN(cY|cZ>ouNsIsz81{GR z{y*NFAP5B~7np(*0R1~F=U-=mIR85FpBRwqpFI%QUlad@aT5Oh_#X;@IRDv#|9bcD z&j1PtC}t9g@_#E`eTTFO2jhYO`T6Y~?2PrTKA=hj^{6db6Axl~?y6l;qxj{t>G|}+ zw|$3<{hliu3DbY;nzcBv_Gz8S%;*N&Bwa2bVjB_AXphA#k%+&I*C0Ax)2-d{`X!=9Ah8w=64e(XZj z(J1vzv=MtHf$5&c!(?KK*lqBBS=Ij5J!Lz=KXmkLS(qPQ=~3}|%8)EvZdjFn;e37k z#zj85+IiNCLN?otaCjhjsGB!LP*8mh5eSmU-tJ3i54o*gv;brt{Cn^4 zui*%C6Ezs=XXCM)sVbl!lT_|-FWNzZWmB0=@41O1hMv=KGfcLXFSvUnS(j4Qpz@ij zO?Jb@xgNhB^4JR((UYZn!Z>5O1jb5%(y%vfCDUB{~dLywp3dSyE>d z|CP|F2(SLIx2misgtcOQdZiBU_SdGYP5-lbD_+K77Fv`34>^@K#D=>Tr1Z1}Pky#{ zBX%0(=vRM;yHRWbBhgFwy&as zeKtCDt(_$_(y)IHt*&%#KotM_=UVVPN7Rwo6(?lwwi&c{^?20+J zA$7q{^M%W^>;9>FBoWs;Hv>t&8aH4{-Yv5-^Ppn959&;Is37tF+DSpx^G7QN@ct(k z9h&&BEr!{H&nBJ5zOO00ZnfISC+Hh(FCOZohr+aUmoS0eIL`}JfL5>&07OI`irAe* z@VhB$6UOIY=Tp7Z;n+SLbVrr!SJCZH>ckfSf5!I!(e--VCefeJv)bl9xEG4B^Qr_bZ}SSMFkp1}xiV+r4X z?aoxjBr^&xuv4eXUQIe1#1H78iA8xo%KPkS#d>|g7uTuBd87nYlTJpv&nO8t=5syIdaiCMk$@mMi z4hN17hnrb-on4}X$V8DHW{MVZaTQXA3cr}QZbpfMiqa^SIMthR25CI|Jj7U*Mk%cs zxx8GwHu%ZUG1cmHC~I)TgA77(mN;Md7hv!Rd01Z}5y$At6LJ`L-dK6!&6h%dt~EUV z@uuS_WvFgqNk$FzDO!7|s*9t|6XjsjmygF;AspZ=N0IxyM)~U9Vff^0fnE;DVRS7a zjv940B#v^-rK2mQ$TJjBr&;5D?4%OM;(@1%*u$XPCuI-O*DM!@)ChNT93gkC)WE7r zeQtAwn<(!-L&pa>Qhu{E_MJ-bn(}>R>MSdXv2E9A{V?r(x z{1%>Cje|r(bbCR9U`{w&y{>8ycVYn6fTtdu{3}=RJ1**+t-iFuM)jEf^b+Ms-zrFNuvmB2EfO!#3on*8?2Ig9VIJqM8V5cvk{GYx5dY8b)j3J zRq5CkQ#-b3u&B05SBx28D97mF5lCY;hV?}Rb0B_Ghhy=b!v12M7?s-ZRHuEIbi)PW znfQdl&)|dYkDGM`!XX=>GQ1^>@1eBX<)s85cOy)kECPN^yu0zKkw{p=9|7dGD!*y; zr2%NZWNcbqT6otxrS3Q3_dNvJj1}%Q8)d2CcM}w1IA7`QxVJb1Ky_5I4`fbC6{Agv zIyA=iotSVwk+ccI6(n|4UwD;a#c_eVCHA5Y66hHhkyziQhiAVUzD!g*lh{LpsO5w| zDz=JaNT;RkbbUh=S6&-N#M5DG;TuE@`TIEDhVOa>;b&!peoqejo{Szy;W@+p7Q2wS zxJBN=IyRIvY~XBi_e?HD=oCwQw=kh$Im>Qiz?;psK_iW3>u>bA^Jd!9GT z=)Rj~L6WcxdOR$PP$D1;Qn17l?_ivDw`lYuAG*+%D~$90NI;vvu}Ts|fiXYC%gN{< zu$by5A{8&PKp#In_aPP<9?eyJOVRECrpqXiW^vaV=LN_H z$}``g@2$^LptBLp$nT&abwSjjl3y86(Pd}UYyfa&!_UJe$dZvRiMlhlcO-0*;EQ(|7INF2wI^M&6NV0o?0T$( znF3{6Up#E#?6^XNE~A@;a>?I7P}o{`x`!BBo+9iU^o&U_mq!sW{D(@smzfrsohKL? zCro{&eky|*Wt0@sqrh#}_Vss9X7mBCJu9>{5&({V9OxY}( zHe>{7x9sY5Gh^QgBeSA@q`yEtlApU64EVY^JhEYuR5(bh+gq9x(xW$R8{!DYZ*Mh- zPV(|DxSmHciKWvtLe2NR_s&|a6!hQ`EL{io!GBal;DCL+9 zod}DjyBJs|R>ou)7;L1X>bigP2h*~#N3_!vWzk+6hi4ie=ANO@MRo``wi6XP%$L$F!!Aclva8$7%g84>BV1&*Gc+_B3Dk2M6fzld&^SZX* zyq2#iN$BSA#h_}~WdcsV1UdB?n zZ=J`|xb5-TALI~A3gNQ83n{L#_$_I~Pid$a&+0Syzrnn&j%S(cTCfXhrmX(8tNhb8 zH^vHmB2WF{33Z#SpDoMp z-NZ|xsbsp0WIB}_ z);qy6{nQM7Xt9|%S>=r|B!MpMJ3xBR_?h&~hHJ|Yy>-zRKRrWyCj*FXA6W}Lf};y= zc^28@#E*H~H#Qp-=#tnW3gM_DwBC6O;pQGGSe0)t2fS++gowy;(qqH9@7NX@;*@&u zrU2x_JyJ&<#CLEe>xu`IVv#y5pK;@{_3695LJL;L#2WYR!lFB zhrCOVvf^7&q@|-BY@_uNt#i*xp(kZaeE$X-nIsnEL4*sPd-p{eElCVTup=F^s~M?P z!KOnrzmnuo8B!4pc}BQcM5XYj`A^N9xgwo|OgCxId^T|hNo=#*f>OEWRm0RdLFt(( zaC3at^>kIOwpCz!IqRJ2H}-GrX3f{{T0_!vm#)#Cbv^xcezV?;-MF|ryC=6sc-FMe z=tjK+ScuycXPP0&R9hvOFZ5Z~2l0*E_#e&n-H@6r4OrU30*;wXn!U~U-&X%TC?+}J zk1OvhRN~ZPRpK-+)hu`VC468to2@w=$;DDiXGL87b58WI*i4hBdUOhDO4EMRugS?3FF6j>^aO9Ji@mLg!IDMM#=>Ww`j>vnw^WEr$fcar^K)5ZBvPQA2$#l zs-c3WJ(9M;{CEZXES!^n<=^$X+6sCI?G`3^Y|!2B^Xg>E?|pfY5mO8ajR#cu9x;?& zslgtIb#mLMcdrmV8Jne3WTJ{3f?fm@isH&9RFdf2C1ek#*hn41+M@Z4n?@p&t~hlH zh#g|A6@T~`4Qa1t2}z1d6}>Z7jI#4*QBU~OJeDTXKoMXNWshY zjTu{WVZ0H?V}?KbV-A7J17YU&o7WY-uE*#*wcLNM;Sw;{Npc8d75BUrtY)^BLR(-S z5wi2O@SESWmY}IN(|lwJ`i^c~i^ za{DaZok47UJKC8qPE@}mp%RJ6>MzNJ3D^I`L0`!N3Cp()2Rel_+TnAErZM(G!(!nR zCOge?FD5265*X*qSE+7ac-v@AIYnUl$27NIxeC&?{U*?b#=1I6JZFb2hEh3d8!KOb zvy&#}t)(04IL!+_HvYI2m-_jTiLj+K0b}o_&w5&<2L7&SW<6K7kl$l&57+b8P@Ng% z@!)hz#*yJ2se?gxpF1*>$2#Naguw-UOOmYjIu5A1k&;x#jlI3iMv<NJVKW&}dYru=Nrc>s8j?Y_D3UtU)pjtX4`__o(ObO>Wte63rl*z-Zk0|3>xH(R@}gU%hT?3_ zvgoM<_2A071_y_S9)4`5Tg%lZ)1f;?MB$&?LC$Nd_$2&Jqk%bw)n2;1RHh<(6%xw! zY!`P&J?HI1Ti^R~xR>`%jw_uFCib0F1L0Q2q;Px-G4^n@5?s{2;%LDVs7_F2Xe7d! zKvS3n{oCKc=eBNI-=l0LB^Ncg5ma3DHJb^VRNkT|b_@lRbcH{aHIbj~9Z<*)cVH)|D|5HX?+5>7d)1csc7+oJExoOx*x zVK|$aOW0sdpQ7~41Q@YrKhnnitW!^CRwV+%h#}rb2@Zgfu=R(5~Urcfuzzt6E30ilv z?}VsHHDg)WVJV6_24y#y31=rR#dZs9w>(i>gVpH9Dg2tS`!=!r`@4EfwR@zFu?tm!6B=}@gr7$N)_}$j^GuL~R-> zf2#hKK(_>9{kcEI_vX%(#Jq?7Fv~5iEj;_E`o5V&iJvg~#7V3X%l(9ujJZ;Z46~So zeR~~3IaM#Q`WgY=+vP_a2lB=GFRB@3-9zYPV3T9Lis$1VbaF-utXY%JqPC!E1y zUCRp2DmCXfQVA$2C}WC^zga}t8)z-R{4v6WamILyiaBI8gO_m9#i)^RuMC|@B$jih zJ8h%-LggZE*wbT19>A40k2f!^o-ZSEt>U|7OEVK+Jns=&p77T3O|fHz5bW zHl8f0>>+Gl_QOLe)YJqWFd?e7#Q32q+bBx!{VgWUQKIP+d%`(>HLYuvr<}Q8w%#)> zX)^NsUQ1oIIOa2kQS;1cz+9zyX`E|LqH42xiy0hZJZMRp^6MZ4;1>1?topu6Wu6GzMwPbJ$dPcw zA1hw@R?+7fo??#nT&ZfmGF*sK`V)vX-kq`kj#ek1ZtC-XmO)FW+VG^Z4Vs;t0gQI5 zK2kq2D^f6$X+U}4>%iNA(hH>G1Xr+{Ymoxq--DNJ@Y*}Yx?kAa$=tDiQT2ZVsr;ZF zjAeJMsUO$SWb?Mq!`o0dO+GnMy49zdZ<^M%8)Pq#-R^mzy@E^|zniJ!h56o+EX4c> zZ8maa!QDhjqvB(*5?-eXt>m83CftEl2_0qqgaoG#QlVyYPFW`9M4mav4VhSrhmN#L z74hrcCs>liK(y?gy<1;o>^dp0JhGNnd;8k{ULN^elQPMi!Yf6+| zP|sRdR)y&C&ntUjhZZ?rEIag|r@DQn~TRo^6QC3lXUSS z3it#cbH3Z?JDUTUfk?Rh4F$fu(5X+0rRtldZ)CrNn!?LK3G2!Bn9Qjm5k+${S$f}= z*{SAIX4H1!T?28GcEMTx=2?1vC2Gxzf+_=kC4EKED&F z-T4G9un4^*UgxtfZ;jsv%X=M=aLi8=)Z(^5HbYhZxCK?SX(Epxl!=xZlG^m>9l0v< z3i^hg?kR3sC7YLI6JF?UZ6LZSjAbr`w&#qGs_SKbM`VU`!1JgpGvvIy9;U@Eeg32cVs^}5gwYk_Je|N{}A7b zk2Nkvv`nrRs!rgHimrYsobPtDc}v%^N3?=}JNfx?*P(s~XjmIMG{bvkzoluNBcAo^ z8g`l`o|R$7fBuZ#5oKXWFFXC%FeU~EI^k<64?UT3*`8v6t&lhW7`6ZqpVjr4Ca-;g zySW>&*>^k4Z~z&2C@!I1)+WD#b|;^U2f2-{L|#L0D8kzjR=QpPBre*Nx!wvLZ0q{D zhjxN$W@Pf2M@-G4`bIPF6_4)F>>0u)-PK)QRS$3>@>DKnWgy*-!Heng5gtu_PkH%7 z>!xlMN-<7 zJJzrc3QziZOR^G5_G@|{(7B;Pw=}WS(Tzd6I-cNd-fh{F}SDS%`FMb*p(T0 zlxsq55xZ9J53bk_Bct2IukG*`nV=yJP<2QzFhc52^p~7RhE)3i!*+pft z%+T@+^dhWvqm`<)+$tAt*dVK@Vvj77@rMvC*nSy?YM!xacDseerfVE_Bgvjbp!&-~VLN3U^j$~tEl8cy_S zei!ni89~1=}WOlgi32IjWx4CAi`eB5_Ml8~v5zMfZ= znW=_a z^n=5^M!@G8oFT}9Gm&egL-Gu4)HHnEN&nXc=G>QYquI};=IV@F(vriu=`&Qzj<~fW zALtsYOAw?QYxM><(~h$|(Wh*#ly-H?d~=uAp?Vi7MG1xjM7vosW}?`PJvQ+gGmT;z zfwd!?P0OMq$L@Qd@-I@%xtXByZI6%}Q~vDqIfZLyZnsVX76d=!S!u(8?^npUPq%EzwfqG-P0s1kOu z+Y&h{jJ!yV}bfm>ua&$o*nJ*;-=%rhGS`s}45CN?@~-}06)x+hzR0G#maAA3bJGK;g}3&h%^5j$SY#@;qZ3_AtpEn^FF zBSxJ8KTu!`U)aa1TsdO^_oUuK_DfO`OTal$KfEc>20RIq(GMpjoFtAY9{;0B?zkv{ z^PH_M9(WwK@U^_?rz71Q`Xn$R#qhP{`sne;+Mvj*6W>iH9dbSL|sKAlJ+Yic#=Z5gYS}mr}0Z;`Fm_)C7dC z^N&m$w^j-aH1yae=U{a-jPD|xDcg~-7km>(qgaf+hcO^+pp*B5af0!?PzTl-UE}ZV zFeld>oz-ur(sEJGh}Q32zATi>Wq$dS{exB^x@#=|Gptx9cBo0$+sk`PnQLn?lI}eg zl=3Tza{izKd@KAam{2McqO*g`AerEb$cCy8zv`gDRrdL>421;BVcb2OJ^VA1z$y4a z!lMmb2Fya7y}#!K1H-XFZ+YIzh-ah1X{`HSfRSnK++& z-+a#f$7axgItZ&&$*`%diSWbPtPlED!Jnh+~H=_{3tOM z1JOUtsO5fs*$F%IccpJJ9P(MW9z6IAhaf3|In{+B6!lqAsNx3=DhnMAC1r|# z7-CS^NR0x88IH(XW%wWjABGq~VO9(&+uz>6&->kGromrg=g_}sT9_df*0-N-trFDb zpNT)S-;UfuBizZ>E=kCZpX}4Ce_lcN(Wm65-p>f~z>Yb*uObJ!c|N}q_@!-7YYOZJ zUSR(&u8iKzZ4Vr6s*##JwthyKLIwy)_g9?M8Wv9e@%Z}krdc^&hg%KB~#!t^h=jQaR>%kt+l)=B_VOb`Y zR~hFHgGJesbcxXl%%k^w?7?fbcYBNjw5=qT>UDT|ej?g)x2RG4mahlX8}3rBE1xdQ zQMc&()G>P5(N3KkmMfZ@BSCMDa#sYvXf|R!)}4A8qdl~~aE}ynmFFzrwvd@Lt+wwA zi4co*sz(-%zQj0=4KGed(ZyfWmCoAqv zh%1?!ioZm`x=cPNGs`jm6s(4pVk=`r@l2?mWtkIoF4`BA##!jnDEs$-apa5Pns6Ht zM0%9aWQ0AEd(_iq<_;Y%d6c1QEHm2FL$7ppyF6^l}b zgk`^n;HBX;-`WVrx5pXIU1)bNKTVL^&)$oA3D&p(bHj{=JZ`87EaRQG2@o&VUdD|V zzIYAY($+5|T7>Pk#Aa~9+oV`jEL#83E`Zu#_?B3Z5dYEKc3EnRaBGZsVYjtv?`A$c76lFK~8uEDAD6|L~sb9P`2 z`axK*dnr5#2j1DMMk~V02*jX&10gggda2=5dk0a2S>srE4u_no zR<^|;FLMkHY8r|-c6vOy$5C$h%NEXTMBvSQu3UNbz2dG_m+_=~8G|mrvXWn4gYP%} z(~o@9Pl>n5Ls4!XU2;EJH@Yh~tqy>2azxi;h#ajC5XS^Uhb?a}WoBBVVaSH;j+q8@ zgAQ%kteranXWz44xi)PUs#!1moq(Nu4rCp z_&1dtKW2>`ofFtti%A@bIhgW`Y8LGwP2rbJw}2d>_@%Zw%9hJZzkV+noP==|AJK4` zG}|*h=6xt?z=&CDNt{cIN$-a@M9n6vt(&6v>Z;wtuZihT3>ZRuu_#T4ac5Q{=dFYs z2mR&Mh2@=)^?vNd>(z@Lb)rzq5L|QIo{_b}8GIfxH!isgbl^XA$Z>f|YANrV6qEZy zdC0i+;Mih)oS*(G%7>O!+7gs?)oQjpK8#h}WV|!z4UBeAO#m5~l{+Jdrp3Rmz}(#) zKl$4CCU$(1V}EA4*_Y~vMbFn?#ivMC@7?yh9p7H89e=YNbG+O>-eYaNdtXTC=ud=j zXF@ob5F}kojtSpFEHGB*c9FE@eA#K&cDT~zVmDCSjS1)ZQD(3g)rm1V|2gS2z8%v` zn3&OYb;<8u^V8g6L856$<*>Em8kfiAx4aR7yqZhfk?nGQAFnsAum=O)(cAv7jWJp& z6U8SkF;O;Np*+tC>N~H9Hz`O`qc|-_j+tHQ0Zi_!#e}a+F7>I_ZK16#DML-}gv54F zgbdRgY1AIfaU95PMn;<|WA$ma-*R_MQ(8*B{4M+ZE!`2SwnJYk87>3@`w27Z_dn*y zypk2Wjx%h&G81N8z+BdtX9T+7ttdAind)TeK3rN?kZrPl;O>h6q`GU5pSyZMcm5fu>?LDwoZ5aLlc<~f2> z1L(6&9_II*i*_dJA53OyXGC*%>HP+6D$$(X-1*39CM$h!VFy(M3lW8tF;$B}e14<; z5$-RX>6GmvJG;q0-ALD%9SbM*Iy_wG_e5JZN&3)FSq($~BmwV{c%*c$O^HwS`uKN~ z;knC?N+FTTHQa)CrBuXGiY$S=?mp4ZN6UQrYo&({z5{ji%L*2!(cF^b z_Uzawve_ReVeiK^D0wbE-|Tj>y5o?#Q}CRkBuSaLR+^r@MtK^1A{$70XCOQkvE<3f zqQNr0@gZa9*Jawo$voG${_FC?=Id_05Pd@Fuho6CuoL7%DP})1W3K-~xxmYF`M*R}w7lg8eT}B%& z>j65i&SjKjj^USisCaE?n^Jp_(%WbD%qxF_z4vHIx$Ra(>!M0j+DzzZ=BXi_P~i*2 zUvZ*{o9I0x7|_JGi6lR~z`G}8v!V9;34Cvp*g@klzwPPrMqFEW(>y^(*YvS~&E^l> z8b(6>08HN5HO2WQ*(|E~TOpL>LqK|{a(OW$J8Ovl1y$74P_%;TPGX7Aezht7m`n<` z23C-m*f=9&X(`(Lva6vQt8yC^4!WpCS1;{wHz#h^fRb~NgN*Q7mB0&!-ghzngM5Q^ z<|RTOh7^N=vnl;`?=0dN^jY605eRGl*iRf)3WOy|bd!Xu3k70{RjYQL*FI5?Bw3fP z+{5(x2GTt#(`^weQ$G#0acnF2Y@2H%E>Woy**`d#%X^Li!=HUlzj7Q?I8W?=YQZ&+ z=`Jo^XYPOGE#^&RMj8Y(psgvOs_B5$7=~004&$Q=!L13Cd#f=Fx4oo`*dnXlr#4(G zN?1Gl?|8=LYM)|6R#c4H_e1Y0AbJWS3Ne}WZ!7ETMlsn`8b|~~w5>(83>4@v98?s{ zBRBC-%&{C)BdHna2Lz~mc(9CW-_{-D%uq4_yh#aBX$Q;Ra{G+Y4YoTE3i+U;7CZma zmcaN`YlZ(aWPJ1aHvpwUPc2FvTo>@&6a!+E}?21)GbN1Hj4wQ3e2cIDtH1Ze|W(45u#W zf5hGYFL3|Bo6-7}Woj1cVwGK!B_U z#wJ{>U=sj<)d0d}#0vRKNeAhJpaw?VCe_r2GVthJ8~_do0LTgaUuJn43;sJaVr(ez uw@%^T`Tig<=U>A5e=Ae~{^hR!NBsa72ZW>A+N29n@c+3mAjZ~`@V@}J%Hc`? delta 12523 zcmb_@WmFu&wr&U{1Si1>EudeR(?dS!B@ZSir01{3v2&)1GCzMr;0t5ns!L0HWKvq=> z5CjMY#z{rNIXO5f*#5Cm*kYjmv%m%dfw=x1@UJJ1 z|Auiu{^>*ZUmOF0P{_YG1_9as2aFR4{WpVu`^*VS$U!ia2eOF^aft&(#kjcGfnZ@S zF)=nyPId?cA_NlQ0<-h;a&fYWL4`&B3V%fr4h~^4aUfVkNaU{`hX|LrIKLjNqN9oH zKZXbdN>F_5B`JhOq=L-^tn0#Lxxm92JT6)U>Y@2Hf z#McMTZYG(ns;09OeMi;+->0j+&GgL%;qX;ouczxM!q!a8{A_aCXE4|OlW_RSV%LPT zG*+PoV!;Jm29Su7bjkmYz}ch@6XprN-93GBW|vPQmKV@_bacMFH_kUo3O2Lj9~Z`0 zZH2pdYm;WR^YAp25Jk{c$jg7qtBOw|laCptvl|tc#(Dul$)Bw5Z@P@RQ@xSO^7yJt zMewJmYo=SJlYsJOmIEifDRGWHHDepjAP=TOY7;mUm={N@tdwNGzz$vM*_(w$Li>w` z2oSSqWBGTJy9+9G?OE)ya+)+bq)|$zAb9a^Yo_n_{%WTM38TQ{xdm0>37w*=<7@cZ z&)?f;`zIS0cVS$?jsh7UhF_f<45E%X_Z?o;3`hLDW99okVyLdI9;_)ArrDe-Eh z=jO`Gtig2VGC?3Tr!ZbJ#EE2Pps`l-KD`3oLKK%aMz9FryA?9VdoXk3)Uh+WSaz(z zUlR(B+&o22cX8=>u0Og+h9}D!gOt^P$aO68-?L#)L!PrPrO$5@uBckO>!Y&##F^EqOsM2L@O>PcU}Lfe7b@mGY0U%(&bY}^>0q6AoYyztTer=?HMxi8z~d!34elg7cf}q zrhj(ws~;#qlBxy?^NHW)x#po*7~l!qRPxZs{q|s(MO*+Z2)M28U+3&yPoSjNCH3S} z2goNy%rEUJCUM+;KL?`zeTaif<4aSxJ|K9%CDIy6!dC@3Ce;sqla_)5yn}=hrAA?C zG}g!fy1aL4)k=|fpV810r6ZM??I9j~iG=rCd@ zt4Ps*LdIO#j1`2%{dVE&q#HDCn}{DPw57v7D`=N?^tZe4SB`lV}`v#t2O&D+cm-xh8qS)5-W*llc(WA$3{oypXr5`C7&Y_Y*!Mn{)l5kMo49updsfgbzcDqRvVL=ItadOv6A%9|t(KgqGe=}=h<*g`Pt`VfJlKtM3Pu_~P}VFMqjHjUX< zcTYU1lVRQ4=JUu5wMgQ-zRQx&InP>OiOyf32U%Zih}LUGIH{s7K3JGp6j5{L78|}A z@P?r$F{s{12DLbHZ-^k0$9?%WxF0+BXLMfh>m}c1{^vs%xJWaFiBq={f~Zh(24+|F za7pS$jxt6rx7jzQaY`=lAra{{0Kn`^)SbxKY}J?{O*9tkz~j-L-3BGrWAI2actcmK-NFdym}W6s|(9 zfoTFZUtWJz#zeHET?O*&iMLr1E@r>I)fu1pSuBlfJay5=+uk#M)lwX;qa1LtY?`Qh z&knRlEa9mLIn=#DSjZj4V{=b1dso6k_*2I%zZGE_48O%}|K!qC3S;s_*zJ&B@}n*j zWcjq*%34TB&KLsI=M>GyjgnhB{9Z8YGE)Z;)(lWmSS-r-v~U@k_1LxSp}iS7Za&J} z3Z}yjkb_}y6>!@DicXD~vHAkFi4G8IdR3RwsMnE7>p%)}sLKkoMP!-rwAj~a+l@d( zhUmO}c+>&!AS-FqPp`v`;n7({1=(f85@aQdg2)NrVPRK$O$b@zF`{g^qe5xLYdQMF zxb@gCt+{y-nbalH6nxsqhQ|RtR0@%Q(zZU5uT8b06LJ4+RWnpae4T~?Vmye6JHv*vipyQuQmC}Qt_wya^1M+~C7NEmp6?10C$-ch5AnwWD-jSpjK=JqU8|0rW)@W9x>Q+?%&-C1(CFN zXn=|GUs+Qjz!I9fw|gR69uh67B4T%Tuy%IGN(`40bb13>**8O==NWa>P+dLIf*(l%u?M2Q*&k`;w&JYp6yL-q zBsjh4?zwxl_TKqPqcC|*6k$}RbDjRo2AT%-I}B0R#ad|78uEk?iHI+J`p) zHyi!4BSwMn;dS~zTMG@vA8Ge@#P3z)PK9R772URzu<^Txkwi;D^SwbOhq_N%AqH-<^qxgLk2IqIl`}V7|-KgO&ZAU&as0zrM0;LP< z*w??J&y-aXnLP-0m@EqHK`z2RN^gwBb!m?crpGG}?C0a0-8fOQQn<fu9 zg1tLu!|hoU=0{ZwrJ`h84@T}$iu@4SDVgD~M2Nxa&4pv67+ysfa+c~>!}dPG9x^IK zG}>pJ5yHSlzI}_p(iv1NNL#o;r5h<@&rrA?(ukh2iK@CTts7)1J22DucCI_-Re2Bk zNH6n9Kgl$l;+ey4K;cy75xt|k!a<;Z&5$=>bwi0acy(Qh*Z+q*>e!lvrg@i>JK5st zYwa_tg)@+TR~cjQc4`O(K4|?DZ=g8y4$1dks_Sf`^xvHrycrQyuLNKO1or$krZ_V8 zOh5WI-BB8HFz1}!>vh^%b}D~|_3J(o9y(xQ6p~!Q3%Cp0Rg|uxxT7H~T_+`xQ_yr3 zt|Wwn_GoYR zv`d3cWfrWHNVgKx#(FKaMLP0B26L%#RYYcJX3AF-;mV|?Az^7#?ytPKdOzM$`lh>G zAaiTLTdrt)a@tkLCWP8#D<&EaG47?F!k_b=Rmok@8S}d@(B=rpSer)%*&HKVV^M1C|6O8Ys5Ie2k}v){=y#> z^IckVDf5nqKG*;(1O^Y)ssdEC7|r#w#K=Wr)SXmwijp_j)e+2t{~Z1)J7RojHJ5;2 ze!diRhCADNuZoisv?%=12i_%5$W27WuQJvvf6uY9nG@4v2;mo~g-(*9NN`h=i*Zw( zh+T`WM7QGoe&0k_4XmbDCPxtmQNLSU&m7fSKeqRB-;+S=Nn7_dl+bkXooVX0s2e>Z z1wZ_fA>hAtY9ERA;({2sMmp8!*kVPC{Z8MPvI8N1Yz})Dvy|a0H)4de9z>} zJey124jI<*h-qn?%MWd`N0+E4xf&*Q$LlwHW0wS;5v#jcw<4bDHK&MW zBQ2t3r4tsV>P0ev=rXvT!aMi!zo)LMce7$G2CJ-M^M+%o ztN0}BM3Z|{(c1~r4ludCdHjTH+II2$k}f-Mz_vlHRkm)cE6=8l+8w#p+Tw^D3c|ym zxnc2c@j-Q+Y5mOUb#xe#2KrU3Cw*Sf|NX@q)Ux zAv>E2`!s*QF~@75bd#l_emtbl3Mz5;eL7#bZk5qZ>s_F*)R>XBTuv+#4K>%koxrjk z?W332T?rltG3m#&YLffp7EtdDHD(_e#HP4jGub*-`jix4jtXm@8>+a}BGNAt4EJ40 zem`v;So&nM?I=B4SC|X;XzswcTUsWx*CKn*`BkT8T!Vnzq;QVW&X&XeTSvhwymAPx zy_;AkUCTM9*3lxp?*?Xqg4TWmTmMYJB?E$t)_&fTmmV@=fsq_|G2iA|h?oVy;L2W~ z95Vd%Gc=^YXM=;bqU1Zn;scb3c{g>k zsyh7sXNP3mUcn#~E|isvF9rx-9WJ`zmUqZNp(~?}+DP5epG!fIG1DSCNsgvX*nHb&Tk`T$o^t^>y(H7c~)rVNj~sc$<6A7|6u ze2Qm(gTJ^$q_k;)!J5>oj_QBFKAmUHk0{^LNkoEUO*7ymA~62Vc_J;q7y0zMWW%D@ zld-y~Z>wu+HpHaE85C{JI5a!^d_86 zT5f*H2C_*}Ac%Ad3YKmsU=7J8mNeQ8LNfMsAh>8WC07dZYpRIE~$E{MP@s)oLhPZ#9v^Sl6a~gwstv=NM zSgtyZ!?5bpxYpW5BSHP3=;xREC*x^orTM8>X0(Kl&i6d^y+*=GNbHG~y{~07n4`Yg z=8fE$&~R!FDeqd04kB@Vg4qy(uT=YDs_jgWUEIweTy1njo0R*=+A7)v1%|SgZ^rv} z4H!#&*sg~4R=W?@;C$R9kAC88`F*TMWQ!R}{rLLf$uPm-n}wsj*+<_^$5=lh;YyQ> zJ`+0Zl<0Scd2sG2D4uWBPbRvs%s960^z98v5}wnO%5}%Leea>8>2wGocYxGCeFC^C zNA~wNaLlG(t#`oF1}S3|H?nNsT(6IN zbrEdgXUoy-(=*53&lR6477OW(Y$m2kaiW%;;l)+ymGU)qQd7${+@!j;F?JtgemP(Q z(3!8}uz6!~ry|u)1$P@w(febeB^o3tF3mNo&=XyIVOW4UE4SYd4zIFO{iUZ{9M<8M z47&`y4gCrE`3+*Fy~pX26fXte$5JPYm!Am_a*-K_a7qD`d1P_+{rWIw;zA{*zaK(j za4ayCMRrscvx@5a9O?ZV2g|J=7#rM0^<5=+a{!$tSd$9VmvtxZt;w^^(R~-EU7OI} zoP_rSb+^aqmo^TcVDXlaFQ4k@iSz@3Ya_j2{1ZCK?1vUW^zW`)R%2gTp}$l=GV zz&2Nc+6T4+AHH_#^eYNt-?wjxLT^87i~EVlqLEd?s)^nOFKE=f>va_tXG_$IdO`N= z1AMBuxzf@?yr_0`Qcb8gl8Rd{mY7}ZN-KnZCoNEu&vQ~ThC9e#PEFozt((Ybz-`>` z9%*+sD>|HAEg72m8=9O8=(BXE-A1ZJLJpQ1j-X6rx6hw|B$T%1RmW9>J@|3yRRFb%EcsS(az83PE52}-AKu)f5j)n4;eHPa|GybrX z5+QYP0Nm`i{3vANk7$6pTTrP{c!+%UAdVGXq{>r;Y_HB6mrvCn?PvUtZACtA?xltb z!OG;y7|_YqoP}p{dOr`AW8QtqaV3$oMGcuq5RsC?X}y|OjpSEbU^G#m6*>MFg#=T)9J)Z}wHOSel|&6GJ9aR0uU1@~m14pBgw zeHaF^Vncsi;5=(R;t{5fE1neIVKqF0R1ntP4TR)-jScRB@{iC1PIf^)si~_X)nYXB z#>Ov>Tja&M?GnpBBpCF5WmAGA?{1|vY3QcZt%@Hwb_lxlgO=a6yn*0Cupl2H=&o4S z@g!n8CYS*AzyFc=WDeD^;EZ+MiR*{LzngjLrufX;Mze`Lk$k;S>v1+q*mX_{t{fSU z6k{gj?!}j~WDk$tJ_bKD<5pq5wnbh+kj-gft0*haCf{ezu%0XiSGL$nXg>w?|4d)2 ziKVG*s|#tQ+{B^DYM?R6`#XWr81Cj)L@bAPxA08j z#8fi>@U22(=*E9;LhhXY%`H)qw2+*c|)07 zWiST2l8PGxROyH-Y z<_-x!=IeIEhqTP-CGo@Y?&)97HTqwy%Ei7r0G{QOxZ}tm9(Fx{+)A=a-W*_Cfd=?O z$x4xqzwrHt&NfIt#J1C65YX=6+SKM1Hi>Lizn(+&I68%^iNa$JtSo3M42}Y@MzIEN z0fSE?Ks~%TnP{aBQK7owwg8e2f0y=Gysy16ZYi;wicYezaQqizV_dGy)+7+r*d&bo zTNY1|+Dcb)XGRD{^w!s3f3btHRu}~}b9`~-EyyG{=1PL)O?=e=ppi@S=1~E4yZOQaJlOOIB zZc=@}78dh`KDo3SfgAPL_=%1XcMxuK98Df|5izaZ#>y>H$(>S+gUce zJW&8D$jfNg7vp2aZ_n;=%n|1N=SCcy#vX?E-o7|K_Cwu+)eo){3$hBfKiT0Yi8~FP zj5mzqfoFozoyG9DGR_~>*t3}z}B$_bIBZoVgF zRNo%#Lp}KH_8MZP;xP3*iS-rlH54- z4BBw)R6(A{u+u3#CqzxA8$?T(W$FPLmUZs_Gih<&+zTUmD~6W>BA$+pMe~&m;|Gpf z;fw+pT;jCgtGJVXp6?9obd3V0b>M3x=kQ|fjB>WWSgg#4HR2XiOmzI9^y6%X@}vbw z?y<}3!HWr@wCwV@Fqw+FS33Q`vHrN<*9muAbG_7o;w|Vbv(9)OSD>>h8k9>g`CMdf zFF5@eS;Bu1Oni~Mf5Q6iLq94`c0a0A?GxW@uTX+c9Mtw1q@#kFOE@EQi8(i01{ck1 z(^?D1xM9*cPNdui9cv)0b=xG+13^e1u}MkMQrwbKVd;Cu>c!3NgOIhtal7FG@;i* z29oUNW$17T5f4fvuv`gY<>oSpkTOJ+F=4_k%Q{rJRHYSrY4Z?;C0 z)4(yg!P6SyLI2g8D0YCU9$1DQ{&@ zt*kE5E6By|Dq9THCe+uHY|K2*GvZDIi22dYe*PZ&Doi)a?N!}JV1FL555~1axzqv! z@9>f%-o=^qPI1{U@-uO&U9AY|O)qu}IK}D>!^I4`^_mtBquFG*;Ww9zRpeVfeTjLl zP&bT(!}dVx`?A!{ML&!KUHA>|#fm9k|C7#7=pms*z$ue~QI0Pfv^nDSq0uA;ZfJCS zTJgm%)9>lWnX^=T)7RNTI%93c!m2Un%minT1g9hX&Hd{m(1WL>?+)eEKD@{@_Ql`GtR^~^ zCD!w91giRmr_QZ1$kX%ye|9f9z0VV`M1DcaMwyATGPtP=x2}^fN3C(7?t^cQPwkl0 zeU$IPk^)q6;Ur@hXC>_(8ZvmxJ5=Xd8=Ko&!<;*MwE(h)l{;vD8(wOUX*c?GqFScZ z=Mr!*Ml$9t+Og)F0;ibJF#Z@J8WUQSB2jxrbg=yE-bcA&UflYU^@CS5P1Vg_lf-0@ z*BsHOs-t%wyquPCKy-&O^uxlF!`0-~tV>zMzw>Cqyrr5Y_^S-!jmIS7=f%tuG27F_ zYV?4zxi}0C!Si-Yea1Do3WV@8>&X|HaznCS4V zF(SQOeAu5_j6aijF`<7>9blNF-4Qw58-|-OTZm7mN)fesML-?sSuh8FP;B!Gx6sbX z&rUNa^co@BE@Rs>w_|<5YRa_# zaXiD{pq@}-Y*a9k zTXeP$b@piO_TFk8DAPS^9zcSnmz+iU!U3n9QW=>f561C+l0m4bDG4T3R(Qtiw%AnO^p4a-H!O+8|47eWG7eMRXYa5Dqu@&{8rYY>a~nPbYmZSQDsr`jRhL^| zx-ggD0k@!<)_XZA3mvnh!tBH91qHr5@=TMD%eNW4!l0}` zw39TfZsZ-cgwwDlY?jnMg&|+%vxI7L(lOZ}OY5%x-(zOmlGez}t zyU&)K*0RkrPg4V$f&>v$L-^^&(i$+yUxH5fV#6Em@jkygH_z;?VPQokfhQqrsC_$I zZ8;p?G!aS;!;CLNe1lFyDJJK0k7>rniQeJI(+qH#O4|vWB!zpH>6-**3X!k@Jt0coMaSh zXt~0YB=465VDz-iMdyg;UHISe)s{jgm`sRej@sz!=&Iz&aRuxxnq(r93??}}3of;` zIfW~lFB7Lq{seKi=rBH)d&^tPUjDgnyVYk_y}V{J@@`?%=?HzgcV(ZvA+DV;&oO{&4EL(mS4`PvOImgDbpAl=?4Zh*%MZ%W zlcKXkxeWup`%#_jDqsQMt#)C(t}gpLR(f>yX_s&+lt>Rv5>zI zy{^_{N`gZq3FlU4Tr`Jy$2q{vq8RmURnBZ*;TKn(P#Q#~7wn*ecgEtNzkE{7ywM_l z;p1wXDRqI{D`pSQ=RR40pT*O5x#9JNM#%(lW^!=`qVJvD@QI+05u>5Y4-J%4tNlPL zkMea}-@TZ-gGZ_k3Rf>sew6W`_q7orEZJq7@Xh(^^O(uJito@3ZNq%3o!nY$Viv~- zyL60sYfi`VsSM80u34984ErCTRvQ7qHeSJ(lt%TE(d$c z0vG-WueYn0cK5uQY#l?MR@M=h`sL|jzXgWe%W}THyu{ya7V>u@^R03&2q68wesKTJ zW837V+PpFd#x-ncPh&q=n%w1rbCy>wkAanLq@PJ6+ot`)(|9>=Y8UBgpTaM&sDDAt z_+l#3;HwhloE<*l_k5wIFDWG_A9YVymoDrS*SFtp8|BiS(VY>clzP2$F;(b9asms( zICFU}EGALKIAhz*zpZ~O>1i^KI?07?uISIKBs<#GDw)t#0T;ii{ARV0>?lqIa86XWuD@(hWbaNK8gG{ty57ucjVhX9MKTR4>6hw)N>V^$g?a5cUjXt ziVH*OgyO5|{ZFk_z%m`vOcHwPrU4Hqb@H_*r{eVQr`3lO*%NOw`*Pynt(%vV6|k&w zXE?KmkBnVY8he5-ymii(^<_D%e@B`kB79|>LVSu49= zoc?fPB&Pm!|9v3X)b>j4T3Z$ToOEFeUG{$C<-wWcasz^F-A*Z$_VGyEsWX~ldjB5m zw#u;M*&L_;H3sp}dBW9-#L&8aYy6YwX;X0zPN=}fZ#QL@tNMaj>M=&Y{ZdAlbq;r5 z)yank1}(t)Z)U91Wr=r|Kl6RkBzBA>H$=K=%-p=VwRMkot@+hBa(m}>u!Uawi_)8} z_H#ln$`j#3cc7>&emcnZ1B;_Iss))LxgpMBnS~uh`x3%VcmhLuvP*hzzjM*SNbSdH zp>fJh?9KX_VU-b)y$4#3{0)Cu90GUIGXEnnJumh9PcK=wY0m)P3+h^fkPI~?s8rQQQ2chRnPECSD z zyy;EE@Mn4g-E&+&&~VY-%2&d{&oZU5;c)8bgupPAMVxZFXc=$tSxr8JR9QT~?0x0S zZTqCgy|lqTlTSg#qaB)eYx+%(I(q+yROQW-&KB-k(s4Du(3B-tilFhnL&9Ed0=ro9 z-E;gqA{IO9z&;R~U1}$d_u{sX`#T9e{Y^^^U465MA{M(f%2mabhC#T3n@76a^H+*oH z;?^XGp-0kYhWw`Q)E$8v!x=4*hTkc56V8T1+mqpe(5a)kmY0(kV6~Y+sF|)5^KNuC zt1J{ZPdSRoOb2P$NIpyasANp5tIFV%6uppa3vNVWI%-X@#QT3N9K>r>4OG^K*DFGl3 zDER-R4%A?pMZZbFHp5NGuqM?0|3J&<=f`kzHgt4$cQi4@00JQxw6tOh;u!x6L*=bF From 800816335f0160cf6691fdf099f9527b5986d345 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 8 Jan 2016 12:25:44 +0100 Subject: [PATCH 44/72] dev(Server): remove trailing slash in CSP header --- README.md | 2 +- app/index.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6c836627a1..c7390a00d5 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ npm install Create a file called `.env` in the root of the project. Put the following in it: ``` -ROOT_URL=https://blockchain.info/ +ROOT_URL=https://blockchain.info ``` ## Build diff --git a/app/index.jade b/app/index.jade index 0f767ac0eb..1c12104e90 100644 --- a/app/index.jade +++ b/app/index.jade @@ -1,5 +1,5 @@ doctype - + head meta(charset='utf-8') From f149f45c962d0c285f775d9917d5bf40eacca783 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 8 Jan 2016 14:08:11 +0100 Subject: [PATCH 45/72] fix(Accounts): revert accidental change in 433de77b4a --- app/partials/settings/accounts.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/partials/settings/accounts.jade b/app/partials/settings/accounts.jade index 276a96ca3f..ae5719cab8 100644 --- a/app/partials/settings/accounts.jade +++ b/app/partials/settings/accounts.jade @@ -22,7 +22,7 @@ h5.well.type-h5.em-400.hidden-xs(translate="ACCOUNT_MANAGEMENT_EXPLAIN") ng-repeat="account in accounts() | filter:{label: searchText}" ) td.expand - span.account-label {{ account.label | highlight:searchText }} + span.account-label(ng-bind-html="account.label | highlight:searchText") span.man(ng-show="!account.archived && account.balance != null").prm | ({{ account.balance | toBitCurrency:settings.btcCurrency }}) span.man(ng-show="account.archived", translate="ACCOUNT_IS_ARCHIVED").prm From c313e2eaeb4cccccb43fbde56f71593d9f9f193f Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 8 Jan 2016 15:57:45 +0100 Subject: [PATCH 46/72] refactor(WalletTokenEndpoints): slightly modified result --- assets/js/services/wallet.service.js | 35 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index f929a32829..a6e963065d 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -343,9 +343,8 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.applyIfNeeded(); } - const error = (message) => { - console.log(message); - errorCallback(message); + const error = (e) => { + errorCallback(e.error); wallet.applyIfNeeded(); } @@ -1279,15 +1278,15 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB }; wallet.verifyEmail = (token, successCallback, errorCallback) => { - const success = (guid) => { + const success = (res) => { wallet.user.isEmailVerified = true; - successCallback(guid); + successCallback(res.guid); wallet.applyIfNeeded(); } - const error = (message) => { - console.log(message); - errorCallback(message); + const error = (res) => { + console.log(res.error); + errorCallback(res.error); wallet.applyIfNeeded(); } @@ -1295,14 +1294,14 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } wallet.unsubscribe = (token, successCallback, errorCallback) => { - const success = (guid) => { - successCallback(guid); + const success = (res) => { + successCallback(res.guid); wallet.applyIfNeeded(); } - const error = (message) => { - console.log(message); - errorCallback(message); + const error = (res) => { + console.log(res.error); + errorCallback(res.error); wallet.applyIfNeeded(); } @@ -1310,14 +1309,14 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } wallet.authorizeApprove = (token, successCallback, differentBrowserCallback, differentBrowserApproved, errorCallback) => { - const success = (guid) => { - successCallback(guid); + const success = (res) => { + successCallback(res.guid); wallet.applyIfNeeded(); } - const error = (message) => { - console.log(message); - errorCallback(message); + const error = (res) => { + console.log(res.error); + errorCallback(res.error); wallet.applyIfNeeded(); } From a2a6d940752378b0ab16695e5d004771980f1e34 Mon Sep 17 00:00:00 2001 From: plondon Date: Sun, 10 Jan 2016 11:56:03 -0500 Subject: [PATCH 47/72] fix(Recovery): fix syntax error in recovery phase modal template --- app/partials/confirm-recovery-phrase-modal.jade | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/partials/confirm-recovery-phrase-modal.jade b/app/partials/confirm-recovery-phrase-modal.jade index 11f54f1dae..5944c1798a 100644 --- a/app/partials/confirm-recovery-phrase-modal.jade +++ b/app/partials/confirm-recovery-phrase-modal.jade @@ -32,8 +32,7 @@ .row .col-xs-3(ng-repeat="word in recoveryPhrase.slice(offset, offset + 4)") p.center - b - {{ offset + $index + 1 }}. {{ word }} + b {{ offset + $index + 1 }}. {{ word }} .row .col-xs-12 p.center From 1a60c04fc7197da7a0b96625c3d96090a15c7d72 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 11 Jan 2016 20:01:31 +0100 Subject: [PATCH 48/72] refactor(API): use more promises Als switched back to official my-wallet-v3 bower version. --- Changelog.md | 65 +++++++++++---- .../authorizeApprove.controller.js | 12 ++- assets/js/controllers/lostGuid.controller.js | 2 +- .../controllers/resetTwoFactor.controller.js | 3 +- .../resetTwoFactorToken.controller.js | 2 +- .../js/controllers/unsubscribe.controller.js | 2 +- .../js/controllers/verifyEmail.controller.js | 2 +- assets/js/services/wallet.service.js | 82 +++++++++++++------ bower.json | 2 +- .../my_wallet/my_blockchain_api_mock.coffee | 1 - 10 files changed, 121 insertions(+), 52 deletions(-) diff --git a/Changelog.md b/Changelog.md index 473d33243f..5d9bdf734b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ __Blockchain HD Frontend__ _Recent changes_ -# (2015-12-21) +# (2016-01-11) @@ -10,49 +10,79 @@ _Recent changes_ ## Bug Fixes +- **Accounts:** revert accidental change in 433de77b4a + ([f149f45c](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/f149f45c962d0c285f775d9917d5bf40eacca783)) +- **Adverts:** set .rootURL to '/' by default + ([ba77aa9f](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/ba77aa9ff930e268b1fba50fc25b27f0d21c3599)) +- **Currency:** correct decimal places for bits and mBTC + ([e5d3c6c4](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/e5d3c6c4f2cb2b377b54b259be6561d129f6da18)) - **Deploy:** + - fix filename for .woff2 fonts + ([c785d759](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/c785d759ac1f72f8314bd2acb8cfe93e3cc9c3dd)) - ROOT_URL now required for dev, defaults to / in production - ([93dd3c2d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/93dd3c2df8dcf7127c0e98e927d9fda734a54e09)) + ([465c694a](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/465c694af5082dc61667659141cffd23184a3314)) - dmore aggresive cleaning in dist task - ([32ef6349](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/32ef6349804aacadc57c1c1ea61b5ec34e450396)) + ([fa1d4906](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/fa1d4906ba7d9198eff28527b691116848851a29)) - clean bower and npm cache in dist task - ([38be7640](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/38be7640dea9e3dbda21a2526c6814f953ed627f)) + ([f6349813](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/f6349813fa6f433a903362d3fbdf37e5f3245dfb)) - move CSS and JS to /js so that relative font path works - ([bef9a15e](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/bef9a15ea18120da85e9c694abafb421e3134f40)) + ([a0e377e0](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/a0e377e0db41614e086eea4ad4d3c294bbfa0496)) - **Feedback:** - missing controller file - ([41b74a85](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/41b74a858117489b1ffbf93ed916af824cb8a9d3)) + ([950f3824](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/950f3824946f3f2ce231098345058d51c6e63612)) - use new endpoint - ([7857e2b2](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/7857e2b2542cabf01baa12511c1372d7e62127fc)) + ([46348250](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/463482500c01c104e59256505568b61f95c1574f)) +- **Login:** disable browser validation and autocomplete + ([c0ecd11d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/c0ecd11da6262a42662cc79e18f0f83648424184)) +- **LostGuid:** use .rootURL + ([9d5df846](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/9d5df8467a8b5354f9785a73dfcbae0686f654a1)) +- **Recover:** fix redirect after recovery + ([f22ca557](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/f22ca557376f8f0b6bca94b42831c45abab243b4)) - **Signup:** improved validation - ([fb9966fa](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/fb9966face09ae4037c5ae0f8d2645e069fba7bc)) + ([4baac6ea](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/4baac6ea6b2699861ba3dad8b08ee09ec31878c2)) +- **Sponsors:** use .rootURL + ([f94175e0](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/f94175e045ac90f3b5f46e4b553e8f9b6055609d)) +- **VerifyEmail:** guid in token was ignored + ([76fcf8e2](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/76fcf8e230150d99868934804c7a10789eac381c)) +- **bcAsyncInput:** spinner remained after save, blockchain/My-Wallet-V3-Frontend@867850dbdbb276 + ([1443b338](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/1443b338119628f36e0b8640127883a6239e9fea)) ## Features - remove beta invite system - ([788b8aa5](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/788b8aa503aca65b20819a730531b2b7b9062b07)) + ([a2b4d07c](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/a2b4d07c7059e4667d96674458e8e336cc3f06ed)) +- **2FA:** process reset 2FA email link + ([118c884b](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/118c884bfdff0ef5de7dc9882f6810c7c2709455)) +- **Deploy:** pass backend URL to grunt dist + ([eae05c42](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/eae05c42b0db8e838619285c63220e840ced0720)) - **Login:** - new browser approval - different browser - ([c3195ad6](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/c3195ad6e3b99830cfb71b4c5eef39a40be2b0fc)) + ([d53490d1](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/d53490d140265861218fc1f208869c1929cb39af)) - new browser approval - assuming user is in the same browser - ([4ad6e26c](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/4ad6e26cd9e91056589e53e97a856d17dbbb8dc5)) + ([58fc7d26](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/58fc7d26166367abb846cb33057c8df5cedc3fb5)) +- **Recovery:** use download attribute for nicer file name and to prevent in-browser viewing + ([1d35f29d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/1d35f29da815dbfcfd73aea6a14db6793a4c2b33)) - **Release:** use beta version of my-wallet-v3 bower - ([0d828172](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/0d82817246118dd6d7488388154e341f556fae40)) + ([85fbbd2d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/85fbbd2d908a4fa1a7e29e5e3f20032e02e8b026)) +- **Reset2FA:** form to reset two step verification + ([18ba4615](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/18ba4615c78516c837c7cfede8820f41751e9531)) - **Routes:** - show modal after verifying email - ([d1a8991d](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/d1a8991df43236d29dbab783716eecbb3ecf447d)) + ([4d1bdfaa](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/4d1bdfaaf19be2e8486ae578ba76dd849be76aa9)) - login and verify email routes and endpoint support - ([ff315a76](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/ff315a76844d6f2ee5c079227bdb5b1230f76c07)) + ([206d60b5](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/206d60b51c58e07bbc43a41db46816112cfcfebb)) - **Unsubscribe:** unsubscribe route - ([9ea51ccb](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/9ea51ccb2c74d57bc658f338c62a24ef93eb45a3)) + ([632db4c6](https://github.com/blockchain/My-Wallet-HD-Frontend/commit/632db4c634ac6b2792cc0f27310740b2c73ed8e4)) ## Refactor - **Cookies:** replace references to deprecated with - **Dependencies:** removed unused npm dependencies. Removed unmaintained E2E tests. +- **RecoverGuid:** move functionality out of controller - **Signup:** cleaned up controller and improved tests +- **WalletTokenEndpoints:** slightly modified result ## Test @@ -61,6 +91,11 @@ _Recent changes_ - **Mocks:** added MyWalletTokenEndpoints mock +## Chore + +- **Changelog:** update for beta branch + + --- *Generated with [git-changelog](https://github.com/rafinskipg/git-changelog). If you have any problem or suggestion, create an issue.* :) **Thanks** \ No newline at end of file diff --git a/assets/js/controllers/authorizeApprove.controller.js b/assets/js/controllers/authorizeApprove.controller.js index fb3a11541a..fd911875be 100644 --- a/assets/js/controllers/authorizeApprove.controller.js +++ b/assets/js/controllers/authorizeApprove.controller.js @@ -44,11 +44,15 @@ function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Ale $scope.checkingToken = true; - Wallet.authorizeApprove($stateParams.token, success, differentBrowser, null, error); + Wallet.authorizeApprove($stateParams.token, differentBrowser, null) + .then(success) + .catch(error); $scope.approve = () => { $scope.busyApproving = true; - Wallet.authorizeApprove($stateParams.token, success, () => {}, true, error); + Wallet.authorizeApprove($stateParams.token, () => {}, true) + .then(success) + .catch(error); } $scope.reject = () => { @@ -64,6 +68,8 @@ function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Ale }); }; - Wallet.authorizeApprove($stateParams.token, rejected, () => {}, false, error); + Wallet.authorizeApprove($stateParams.token, () => {}, false) + .then(rejected) + .catch(error); } } diff --git a/assets/js/controllers/lostGuid.controller.js b/assets/js/controllers/lostGuid.controller.js index 05f3e9edeb..57856a21cb 100644 --- a/assets/js/controllers/lostGuid.controller.js +++ b/assets/js/controllers/lostGuid.controller.js @@ -29,7 +29,7 @@ function LostGuidCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { $scope.remindForm.$setPristine(); $scope.remindForm.$setUntouched(); - Wallet.recoverGuid($scope.fields.email, $scope.fields.captcha, success, error) + Wallet.recoverGuid($scope.fields.email, $scope.fields.captcha).then(success).catch(error); }; // Set SID cookie by requesting headers diff --git a/assets/js/controllers/resetTwoFactor.controller.js b/assets/js/controllers/resetTwoFactor.controller.js index fc82e38f7d..896d4675f0 100644 --- a/assets/js/controllers/resetTwoFactor.controller.js +++ b/assets/js/controllers/resetTwoFactor.controller.js @@ -41,8 +41,7 @@ function ResetTwoFactorCtrl($scope, $rootScope, $http, $translate, Wallet, Alert $scope.fields.secret, $scope.fields.message, $scope.fields.captcha, - success, - error) + ).then(success).catch(error); }; // Set SID cookie by requesting headers diff --git a/assets/js/controllers/resetTwoFactorToken.controller.js b/assets/js/controllers/resetTwoFactorToken.controller.js index e4ce99d9d1..218de4e04c 100644 --- a/assets/js/controllers/resetTwoFactorToken.controller.js +++ b/assets/js/controllers/resetTwoFactorToken.controller.js @@ -27,5 +27,5 @@ function ResetTwoFactorTokenCtrl($scope, Wallet, $stateParams, $state, Alerts, $ $scope.checkingToken = true - Wallet.resetTwoFactorToken($stateParams.token, success, error) + Wallet.resetTwoFactorToken($stateParams.token).then(success).catch(error); } diff --git a/assets/js/controllers/unsubscribe.controller.js b/assets/js/controllers/unsubscribe.controller.js index a85e300c2b..9d1dac2e68 100644 --- a/assets/js/controllers/unsubscribe.controller.js +++ b/assets/js/controllers/unsubscribe.controller.js @@ -27,5 +27,5 @@ function UnsubscribeCtrl($scope, Wallet, $stateParams, $state, Alerts, $translat Alerts.displayError(message, true); } - Wallet.unsubscribe($stateParams.token, success, error) + Wallet.unsubscribe($stateParams.token).then(success).catch(error); } diff --git a/assets/js/controllers/verifyEmail.controller.js b/assets/js/controllers/verifyEmail.controller.js index 79b05660a6..fdaf575c10 100644 --- a/assets/js/controllers/verifyEmail.controller.js +++ b/assets/js/controllers/verifyEmail.controller.js @@ -36,5 +36,5 @@ function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts, $translat Alerts.displayError(message, true); } - Wallet.verifyEmail($stateParams.token, success, error) + Wallet.verifyEmail($stateParams.token).then(success).catch(error); } diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index a6e963065d..3d18e7ebb6 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -62,14 +62,14 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB // If customRootURL is set by index.jade: // Grunt can replace this: const customRootURL = $rootScope.rootURL || "/"; - wallet.api.setRootURL(customRootURL); + wallet.api.ROOT_URL=customRootURL; // If customRootURL is set by Grunt: $rootScope.rootURL = customRootURL; // Grunt can replace this: const customWebSocketURL = $rootScope.webSocketURL; if(customWebSocketURL) { - wallet.my.ws.setURL(customWebSocketURL); + wallet.my.ws.wsUrl=customWebSocketURL; } wallet.payment = MyWalletPayment; @@ -287,10 +287,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my.resendTwoFactorSms(uid, success, error); }; - wallet.recoverGuid = (email, captcha, successCallback, errorCallback) => { + wallet.recoverGuid = (email, captcha) => { + let defer = $q.defer() let success = (message) => { Alerts.displaySuccess(message); - successCallback(); + defer.resolve(); wallet.applyIfNeeded(); }; let error = (error) => { @@ -306,17 +307,20 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB Alerts.displayError($translate.instant('UNKNOWN_ERROR')); } - errorCallback(); + defer.reject(); wallet.applyIfNeeded(); }; - wallet.my.recoverGuid(email, captcha, success, error); + wallet.my.recoverGuid(email, captcha).then(success).catch(error); + return defer.promise; }; - wallet.requestTwoFactorReset = (guid, email, new_email, secret, message, captcha, successCallback, errorCallback) => { + wallet.requestTwoFactorReset = (guid, email, new_email, secret, message, captcha) => { + let defer = $q.defer() + Alerts.clear() let success = (message) => { Alerts.displaySuccess(message); - successCallback(); + defer.resolve(); wallet.applyIfNeeded(); }; let error = (error) => { @@ -331,24 +335,34 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB Alerts.displayError(error, true); } - errorCallback(); + defer.reject(); wallet.applyIfNeeded(); }; - wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha, success, error); + wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha) + .then(success) + .catch(error); + + return defer.promise; }; - wallet.resetTwoFactorToken = (token, successCallback, errorCallback) => { + wallet.resetTwoFactorToken = (token) => { + let defer = $q.defer() + const success = (obj) => { - successCallback(obj); + defer.resolve(obj); wallet.applyIfNeeded(); } const error = (e) => { - errorCallback(e.error); + defer.reject(e.error); wallet.applyIfNeeded(); } - wallet.tokenEndpoints.resetTwoFactor(token, success, error); + wallet.tokenEndpoints.resetTwoFactor(token) + .then(success) + .catch(error); + + return defer.promise; } wallet.create = (password, email, currency, language, success_callback) => { @@ -1277,46 +1291,58 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my.wallet.encrypt(password, success, error, encrypting, syncing); }; - wallet.verifyEmail = (token, successCallback, errorCallback) => { + wallet.verifyEmail = (token) => { + let defer = $q.defer(); + const success = (res) => { wallet.user.isEmailVerified = true; - successCallback(res.guid); + defer.resolve(res.guid); wallet.applyIfNeeded(); } const error = (res) => { console.log(res.error); - errorCallback(res.error); + defer.reject(res.error); wallet.applyIfNeeded(); } - wallet.tokenEndpoints.verifyEmail(token, success, error); + wallet.tokenEndpoints.verifyEmail(token) + .then(success) + .catch(error); + + return defer.promise; } - wallet.unsubscribe = (token, successCallback, errorCallback) => { + wallet.unsubscribe = (token) => { + let defer = $q.defer(); + const success = (res) => { - successCallback(res.guid); + defer.resolve(res.guid); wallet.applyIfNeeded(); } const error = (res) => { console.log(res.error); - errorCallback(res.error); + defer.reject(res.error); wallet.applyIfNeeded(); } - wallet.tokenEndpoints.unsubscribe(token, success, error); + wallet.tokenEndpoints.unsubscribe(token).then(success).catch(error); + + return defer.promise; } - wallet.authorizeApprove = (token, successCallback, differentBrowserCallback, differentBrowserApproved, errorCallback) => { + wallet.authorizeApprove = (token, differentBrowserCallback, differentBrowserApproved) => { + let defer = $q.defer() + const success = (res) => { - successCallback(res.guid); + defer.resolve(res.guid); wallet.applyIfNeeded(); } const error = (res) => { console.log(res.error); - errorCallback(res.error); + defer.reject(res.error); wallet.applyIfNeeded(); } @@ -1325,7 +1351,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.applyIfNeeded(); } - wallet.tokenEndpoints.authorizeApprove(token, success, differentBrowser, differentBrowserApproved, error); + wallet.tokenEndpoints.authorizeApprove(token, differentBrowser, differentBrowserApproved) + .then(success) + .catch(error); + + return defer.promise; } // Testing: only works on mock MyWallet diff --git a/bower.json b/bower.json index c5f6a6eee0..a9e233679e 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,7 @@ "browserdetection": "0.3.*", "bc-qr-reader": "0.2.*", "bootstrap-sass": "3.3.*", - "blockchain-wallet": "blockchain/my-wallet-v3-bower#beta", + "blockchain-wallet": "3.5.*", "bc-phone-number": "5.0.*" }, "devDependencies": { diff --git a/tests/mocks/my_wallet/my_blockchain_api_mock.coffee b/tests/mocks/my_wallet/my_blockchain_api_mock.coffee index 709cb2caa3..07cfed5eb2 100644 --- a/tests/mocks/my_wallet/my_blockchain_api_mock.coffee +++ b/tests/mocks/my_wallet/my_blockchain_api_mock.coffee @@ -8,5 +8,4 @@ angular $q.resolve(result) getFiatAtTime: (time, amount, currency) -> $q.resolve(amount.toFixed(2)) - setRootURL: () -> }) From 70c1745478765daac47b0f649d4450b49dc5bc74 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 12 Jan 2016 14:21:24 +0100 Subject: [PATCH 49/72] deploy(Gruntfile): remove deprecated deploy tasks --- Gruntfile.coffee | 64 ------------------------------------------------ 1 file changed, 64 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ee351217f2..5d068ad1df 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -341,34 +341,6 @@ module.exports = (grunt) -> command: () -> 'bower cache clean && npm cache clean' - deploy_static_to_dev: - command: () -> - 'rsync -rz --delete dist hd-dev@server:' - - deploy_server_to_dev: - command: () -> - 'rsync -rz --delete server.js hd-dev@server:' - - deploy_static_to_staging: - command: () -> - 'rsync -rz --delete dist hd-staging@server:' - - deploy_server_to_staging: - command: () -> - 'rsync -rz --delete server.js hd-staging@server:' - - deploy_start_dev: - command: () -> - 'ssh hd-dev@server "./start.sh"' - - deploy_start_staging: - command: () -> - 'ssh hd-staging@server "./start.sh"' - - deploy_start_alpha: - command: () -> - 'ssh hd-alpha@server "./start.sh"' - check_dependencies: command: () -> 'mkdir -p build && ruby ./check-dependencies.rb' @@ -545,42 +517,6 @@ module.exports = (grunt) -> "rename:html" ] - grunt.registerTask "deploy_static_to_dev", [ - "dist" - "shell:deploy_static_to_dev" - "shell:deploy_start_dev" - ] - - grunt.registerTask "deploy_server_to_dev", [ - "shell:deploy_server_to_dev" - "shell:deploy_start_dev" - ] - - grunt.registerTask "deploy_to_dev", [ - "dist" - "shell:deploy_static_to_dev" - "shell:deploy_server_to_dev" - "shell:deploy_start_dev" - ] - - grunt.registerTask "deploy_static_to_staging", [ - "dist" - "shell:deploy_static_to_staging" - "shell:deploy_start_staging" - ] - - grunt.registerTask "deploy_server_to_staging", [ - "shell:deploy_server_to_staging" - "shell:deploy_start_staging" - ] - - grunt.registerTask "deploy_to_staging", [ - "dist" - "shell:deploy_static_to_staging" - "shell:deploy_server_to_staging" - "shell:deploy_start_staging" - ] - grunt.registerTask "check_translations", [ "shell:check_translations" ] From 97338fdfc4a1727b0a4b939d28ac0f37fd457465 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 12 Jan 2016 14:35:46 +0100 Subject: [PATCH 50/72] copy(Strings): remove unused strings --- check_translations.rb | 4 ++-- locales/en-human.json | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/check_translations.rb b/check_translations.rb index 5efeb07259..0bd6abd873 100755 --- a/check_translations.rb +++ b/check_translations.rb @@ -3,8 +3,8 @@ puts "Don't forget to run 'grunt dist' or 'grunt dist_unsafe' first..." -js_file = Dir.entries("dist").keep_if{|entry| entry.include?("application-") && entry.include?(".js") } -js = File.read("dist/" + js_file[0]) +js_file = Dir.entries("dist/js").keep_if{|entry| entry.include?("application-") } +js = File.read("dist/js/" + js_file[0]) orphaned = false for key, string in JSON.parse(File.read('locales/en-human.json')) diff --git a/locales/en-human.json b/locales/en-human.json index 477a8c3052..2ff71e58fc 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -143,7 +143,6 @@ "TO" : "To", "FEE" : "Fee", "TOTAL" : "Total", - "INTERNAL" : "Internal", "GOOGLE_AUTH_CODE" : "Google Authenticator", "SMS_CODE" : "SMS Code", "EMAIL_CODE" : "Email Code", @@ -217,7 +216,6 @@ "SHOW_PAIRING_CODE" : "Show Pairing Code", "PAIRING_CODE_EXPLAIN" : "Scan the code below with your iPhone or Android Blockchain Wallet App to quickly and easily connect to your wallet. Note that this only works with the V3 beta iPhone or Android app, not with the current store version.", "MOBILE" : "Mobile", - "INSUFFICIENT" : "Insufficient", "WATCH_ONLY" : "Watch Only", "WARNING" : "Warning", "IMPORT_BITCOIN_ADDRESS" : "Import Existing Bitcoin Address", @@ -346,7 +344,6 @@ "RESET_FORM" : "Reset Form", "REGULAR_SEND" : "Regular Send", "MINERS_FEE" : "Miner's Fee", - "MINERS_FEE_EXPLAIN" : "Select from a predetermined list or enter a custom amount", "TOTAL_AVAILABLE": "Total available", "GO_BACK" : "Go Back", "CLICK_HERE_TO_CREATE_WALLET" : "create a wallet", From f9b5a7882444b0f2b9d07bdf42130f74394e7eb8 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 12 Jan 2016 15:44:44 +0100 Subject: [PATCH 51/72] refactor(WalletService): use $safeApply instead of applyIfNeeded --- assets/js/services/wallet.service.js | 168 +++++++++++++-------------- 1 file changed, 81 insertions(+), 87 deletions(-) diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 3d18e7ebb6..6650b61880 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -138,12 +138,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB }; wallet.my.wallet.getHistory().then(didFetchTransactions); } - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); if (successCallback != null) { successCallback(); } - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let needsTwoFactorCode = (method) => { @@ -157,12 +157,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB needsTwoFactorCallback(); wallet.settings.twoFactorMethod = method; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let wrongTwoFactorCode = (message) => { errorCallback('twoFactor', message); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let loginError = (error) => { @@ -175,7 +175,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB Alerts.displayError(error, true); errorCallback(); } - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; if (two_factor_code != null && two_factor_code !== '') { wallet.settings.needs2FA = true; @@ -185,13 +185,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let authorizationProvided = () => { wallet.goal.auth = true; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let authorizationRequired = (callback) => { callback(authorizationProvided()); Alerts.displayWarning('Please check your email to approve this login attempt.', true); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; $window.root = 'https://blockchain.info/'; @@ -221,7 +221,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.updateTransactions(); }); successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { @@ -265,7 +265,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = () => { wallet.hdAddresses(account.index)(true); successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; $translate('DEFAULT_NEW_ADDRESS_LABEL').then((translation) => { account.setLabelForReceivingAddress(account.receiveIndex, translation) @@ -277,12 +277,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = () => { $translate('RESENT_2FA_SMS').then(Alerts.displaySuccess); successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = (e) => { $translate('RESENT_2FA_SMS_FAILED').then(Alerts.displayError); errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.my.resendTwoFactorSms(uid, success, error); }; @@ -292,7 +292,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = (message) => { Alerts.displaySuccess(message); defer.resolve(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = (error) => { @@ -308,7 +308,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } defer.reject(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.my.recoverGuid(email, captcha).then(success).catch(error); return defer.promise; @@ -321,7 +321,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = (message) => { Alerts.displaySuccess(message); defer.resolve(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = (error) => { switch (error) { @@ -336,7 +336,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } defer.reject(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha) .then(success) @@ -350,12 +350,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const success = (obj) => { defer.resolve(obj); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } const error = (e) => { defer.reject(e.error); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } wallet.tokenEndpoints.resetTwoFactor(token) @@ -457,17 +457,17 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = (res) => { wallet.appendTransactions(res); successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let allTransactionsLoaded = () => { allTransactionsLoadedCallback && allTransactionsLoadedCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; if (where === '') { @@ -488,12 +488,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = () => { wallet.hdAddresses(accountIdx)(true); successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = (msg) => { errorCallback(msg); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let account = wallet.accounts()[parseInt(accountIdx)]; @@ -510,12 +510,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.makePairingCode = (successCallback, errorCallback) => { let success = (code) => { successCallback(code); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.my.makePairingCode(success, error); @@ -547,12 +547,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = () => { wallet.settings.ipWhitelist = ips; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.settings_api.update_IP_lock(ips, success, error); @@ -561,12 +561,12 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.resendEmailConfirmation = (successCallback, errorCallback) => { let success = () => { successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.settings_api.resendEmailConfirmation(wallet.user.email, success, error); @@ -584,10 +584,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.updateLoggingLevel(level, () => { wallet.settings.loggingLevel = level; wallet.saveActivity(4); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { Alerts.displayError('Failed to update logging level'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -614,7 +614,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.addAddressOrPrivateKey = (addressOrPrivateKey, needsBipPassphraseCallback, successCallback, errorCallback, cancel) => { let success = (address) => { successCallback(address); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let proceed = (secondPassword='') => { @@ -624,7 +624,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } else { errorCallback(message); } - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let proceedWithBip38 = (bipPassphrase) => { @@ -836,7 +836,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } } wallet.status.didLoadTransactions = true; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.appendTransactions = (transactions, override) => { @@ -897,7 +897,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB Alerts.displayError(translation); }); } else if (event === 'ticker_updated' || event === 'did_set_latest_block') { - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } else if (event === 'logging_out') { if (wallet.didLogoutByChoice) { $translate('LOGGED_OUT').then((translation) => { @@ -906,7 +906,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } else { $translate('LOGGED_OUT_AUTOMATICALLY').then((translation) => { $cookies.put('alert-warning', translation); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); } wallet.status.isLoggedIn = false; @@ -922,13 +922,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } else if (event.type !== void 0) { if (event.type === 'error') { Alerts.displayError(event.msg); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } else if (event.type === 'success') { Alerts.displaySuccess(event.msg); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } else if (event.type === 'notice') { Alerts.displayWarning(event.msg); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } else { } } else { @@ -991,11 +991,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.user.email = email; wallet.user.isEmailVerified = false; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }), () => { $translate('CHANGE_EMAIL_FAILED').then((translation) => { Alerts.displayError(translation); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); errorCallback(); }); @@ -1003,11 +1003,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.enableNotifications = () => { let success = () => { wallet.settings.notifications = true; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { Alerts.displayError('Failed to enable notifications'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.my.wallet.enableNotifications(success, error); }; @@ -1015,11 +1015,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.disableNotifications = () => { let success = () => { wallet.settings.notifications = false; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { Alerts.displayError('Failed to disable notifications'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.my.wallet.disableNotifications(success, error); }; @@ -1048,13 +1048,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.user.mobile = mobile; wallet.user.isMobileVerified = false; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }), () => { $translate('CHANGE_MOBILE_FAILED').then((translation) => { Alerts.displayError(translation); }); errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1062,29 +1062,23 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.verifyMobile(code, (() => { wallet.user.isMobileVerified = true; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }), () => { $translate('VERIFY_MOBILE_FAILED').then((translation) => { errorCallback(translation); }); - wallet.applyIfNeeded(); - }); - }; - - wallet.applyIfNeeded = () => { - if (MyWallet.mockShouldReceiveNewTransaction === void 0) { $rootScope.$safeApply(); - } + }); }; wallet.changePasswordHint = (hint, successCallback, errorCallback) => { wallet.settings_api.update_password_hint1(hint, (() => { wallet.user.passwordHint = hint; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }), (err) => { errorCallback(err); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1094,10 +1088,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.unsetTwoFactor(() => { wallet.settings.needs2FA = false; wallet.settings.twoFactorMethod = null; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1105,10 +1099,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.setTwoFactorSMS(() => { wallet.settings.needs2FA = true; wallet.settings.twoFactorMethod = 5; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1116,10 +1110,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.setTwoFactorEmail(() => { wallet.settings.needs2FA = true; wallet.settings.twoFactorMethod = 2; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1128,21 +1122,21 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings.needs2FA = true; wallet.settings.twoFactorMethod = 1; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, (error) => { console.log(error); errorCallback(error); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; wallet.setTwoFactorGoogleAuthenticator = () => { wallet.settings_api.setTwoFactorGoogleAuthenticator((secret) => { wallet.settings.googleAuthenticatorSecret = secret; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1152,10 +1146,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings.twoFactorMethod = 4; wallet.settings.googleAuthenticatorSecret = null; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1163,11 +1157,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = () => { wallet.settings.rememberTwoFactor = true; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.settings_api.toggleSave2FA(false, success, error); }; @@ -1176,11 +1170,11 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB let success = () => { wallet.settings.rememberTwoFactor = false; successCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; let error = () => { errorCallback(); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }; wallet.settings_api.toggleSave2FA(true, success, error); }; @@ -1193,20 +1187,20 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.enableBlockTOR = () => { wallet.settings_api.update_tor_ip_block(1, () => { wallet.settings.blockTOR = true; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; wallet.disableBlockTOR = () => { wallet.settings_api.update_tor_ip_block(0, () => { wallet.settings.blockTOR = false; - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1214,10 +1208,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.update_IP_lock_on(true, () => { wallet.settings.restrictToWhitelist = true; wallet.saveActivity(2); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1225,10 +1219,10 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.settings_api.update_IP_lock_on(false, () => { wallet.settings.restrictToWhitelist = false; wallet.saveActivity(2); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }, () => { console.log('Failed'); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); }); }; @@ -1297,13 +1291,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const success = (res) => { wallet.user.isEmailVerified = true; defer.resolve(res.guid); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } const error = (res) => { console.log(res.error); defer.reject(res.error); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } wallet.tokenEndpoints.verifyEmail(token) @@ -1318,13 +1312,13 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const success = (res) => { defer.resolve(res.guid); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } const error = (res) => { console.log(res.error); defer.reject(res.error); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } wallet.tokenEndpoints.unsubscribe(token).then(success).catch(error); @@ -1337,18 +1331,18 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB const success = (res) => { defer.resolve(res.guid); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } const error = (res) => { console.log(res.error); defer.reject(res.error); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } const differentBrowser = (details) => { differentBrowserCallback(details); - wallet.applyIfNeeded(); + $rootScope.$safeApply(); } wallet.tokenEndpoints.authorizeApprove(token, differentBrowser, differentBrowserApproved) From d80a744d99cb089c7fc30454c6e4021e98edd213 Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 12:20:33 -0500 Subject: [PATCH 52/72] fix(Security) remove hard coded height for security levels --- assets/css/modules/_security-center.scss | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/assets/css/modules/_security-center.scss b/assets/css/modules/_security-center.scss index 9d7db54520..655eb17c1d 100644 --- a/assets/css/modules/_security-center.scss +++ b/assets/css/modules/_security-center.scss @@ -55,6 +55,17 @@ } section.level { margin-bottom: 30px; + @media (min-width: 768px) { + .level-badge { + height: auto; + padding-bottom: 20px; + } + &.level_one { + .level-badge { + width: 33.333%; + } + } + } } header { margin-bottom: 20px; @@ -62,8 +73,9 @@ ul { background-color: #fdfdfd; border: 1px solid #eaeaea; + border-bottom: 0px; + overflow: hidden; list-style: none; - height: 160px; } .level-badge { width: 22%; @@ -77,8 +89,9 @@ display: block; text-align: center; width: 100%; - height: 160px; padding: 20px 15px 30px 15px; + padding-bottom: 200px; + margin-bottom: -200px; cursor: pointer; img { width: 75px; @@ -111,6 +124,7 @@ .level-header { border-right: 1px solid #ebebeb; padding: 30px 35px; + height: auto; img { width: 85px; } From 9d91f97add9eed002e5917486f06eadccfd05404 Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 12:29:54 -0500 Subject: [PATCH 53/72] fix(Security) move active class up one parent --- app/partials/security-center.jade | 24 ++++++++++++------------ assets/css/modules/_security-center.scss | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/partials/security-center.jade b/app/partials/security-center.jade index 18ff03fbcc..2275a700ca 100644 --- a/app/partials/security-center.jade +++ b/app/partials/security-center.jade @@ -6,8 +6,8 @@ ul.flex.flex-start.pln.mbn li.level-header.bg-light-blue.flex-center.flex-justify img(src="img/security-icon-1.png") - li.level-badge(ng-class="{'badge-active': user.isEmailVerified}") - a(ng-click="user.isEmailVerified || toggle('email')", ng-class="{'active' : display.action == 'email' }") + li.level-badge(ng-click="user.isEmailVerified || toggle('email')", ng-class="{'active' : display.action == 'email', 'badge-active': user.isEmailVerified}") + a .level-incomplete-container(ng-hide="user.isEmailVerified") .flex-center.flex-column img(src="img/badge-verifyemail.png") @@ -18,8 +18,8 @@ i.ti-check.bright-green p.bright-green.em-300(translate="NICELY_DONE") completed-level(ng-show="user.isEmailVerified" placement="bottom" content="EMAIL_ADDRESS_TOOLTIP" img="img/badge-verifyemail.png" message="EMAIL_VERIFIED") - li.level-badge(ng-class="{'badge-active': status.didConfirmRecoveryPhrase}") - a(ng-click="status.didConfirmRecoveryPhrase || toggle('securityphrase')", ng-class="{'active' : display.action == 'securityphrase' }") + li.level-badge(ng-click="status.didConfirmRecoveryPhrase || toggle('securityphrase')", ng-class="{'active' : display.action == 'securityphrase', 'badge-active': status.didConfirmRecoveryPhrase }") + a .level-incomplete-container(ng-hide="status.didConfirmRecoveryPhrase") .flex-center.flex-column img(src="img/badge-securityphrase.png") @@ -30,8 +30,8 @@ i.ti-check.bright-green p.bright-green.em-300(translate="NICELY_DONE") completed-level(ng-show="status.didConfirmRecoveryPhrase" placement="bottom" content="RECOVERY_PHRASE_TOOLTIP" img="img/badge-securityphrase.png" message="PHRASE_BACKED") - li.level-badge(ng-class="{'badge-active': user.passwordHint}") - a(ng-click="user.passwordHint || toggle('passwordhint')", ng-class="{'active' : display.action == 'passwordhint' }") + li.level-badge(ng-click="user.passwordHint || toggle('passwordhint')", ng-class="{'active' : display.action == 'passwordhint', 'badge-active': user.passwordHint }") + a .level-incomplete-container(ng-hide="user.passwordHint") .flex-center.flex-column img(src="img/badge-passwordhint.png") @@ -82,8 +82,8 @@ ul.flex.flex-start.pln.mbn li.level-header.bg-light-blue.flex-center.flex-justify img(src="img/security-icon-2.png") - li.level-badge(ng-class="{'badge-active': user.isMobileVerified}") - a(ng-click="user.isMobileVerified || toggle('mobilenumber')", ng-class="{'active' : display.action == 'mobilenumber' }") + li.level-badge(ng-click="user.isMobileVerified || toggle('mobilenumber')", ng-class="{'active' : display.action == 'mobilenumber', 'badge-active': user.isMobileVerified }") + a .level-incomplete-container(ng-hide="user.isMobileVerified") .flex-center.flex-column img(src="img/badge-addmobile.png") @@ -94,8 +94,8 @@ i.ti-check.bright-green p.bright-green.em-300(translate="NICELY_DONE") completed-level(ng-show="user.isMobileVerified" content="MOBILE_NUMBER_TOOLTIP" img="img/badge-addmobile.png" message="MOBILE_LINKED") - li.level-badge(ng-class="{'badge-active': settings.needs2FA}") - a(ng-click="settings.needs2FA || toggle('twofactor')", ng-class="{'active' : display.action == 'twofactor' }") + li.level-badge(ng-click="settings.needs2FA || toggle('twofactor')", ng-class="{'active' : display.action == 'twofactor', 'badge-active': settings.needs2FA }") + a .level-incomplete-container(ng-hide="settings.needs2FA") .flex-column.flex-center img(src="img/badge-add2FA.png") @@ -125,8 +125,8 @@ ul.flex.flex-start.pln.mbn li.level-header.bg-light-blue.flex-center.flex-justify img(src="img/security-icon-3.png") - li.level-badge(ng-class="{'badge-active': settings.blockTOR}") - a(ng-click="settings.blockTOR || toggle('blocktor')", ng-class="{'active' : display.action == 'blocktor' }") + li.level-badge(ng-click="settings.blockTOR || toggle('blocktor')", ng-class="{'active' : display.action == 'blocktor', 'badge-active': settings.blockTOR }") + a .level-incomplete-container(ng-hide="settings.blockTOR") .flex-column.flex-center img(src="img/badge-blockTOR.png") diff --git a/assets/css/modules/_security-center.scss b/assets/css/modules/_security-center.scss index 655eb17c1d..eb18e8cd55 100644 --- a/assets/css/modules/_security-center.scss +++ b/assets/css/modules/_security-center.scss @@ -73,7 +73,6 @@ ul { background-color: #fdfdfd; border: 1px solid #eaeaea; - border-bottom: 0px; overflow: hidden; list-style: none; } From 426a024b3ec5101a33e3c3edd0520cfec754903c Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 12:42:12 -0500 Subject: [PATCH 54/72] fix(Security) apply class active styling to parent --- assets/css/modules/_security-center.scss | 28 +++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/assets/css/modules/_security-center.scss b/assets/css/modules/_security-center.scss index eb18e8cd55..b5a55049ad 100644 --- a/assets/css/modules/_security-center.scss +++ b/assets/css/modules/_security-center.scss @@ -73,12 +73,29 @@ ul { background-color: #fdfdfd; border: 1px solid #eaeaea; - overflow: hidden; list-style: none; } .level-badge { width: 22%; + position: relative; transition: all .3s ease-in-out; + &.active { + background-color: white; + border-bottom: none; + font-weight: bold; + border-top: none; + border-right: 1px solid rgb(234, 234, 234); + border-left: 1px solid rgb(234, 234, 234); + box-shadow: 0px 4px 8px -1px rgba(243, 243, 243, 1) inset; + &:after { + content: ''; + width: 100%; + height: 1px; + bottom: -1px; + position: absolute; + background-color: #fff; + } + } img { opacity: .3; -webkit-filter: grayscale(100%); @@ -100,15 +117,6 @@ color: rgba(0, 0, 0, 0.5); } } - a.active { - background-color: white; - border-bottom: none; - font-weight: bold; - box-shadow: 0px 4px 8px -1px rgba(243, 243, 243, 1) inset; - border-top: none; - border-right: 1px solid rgb(234, 234, 234); - border-left: 1px solid rgb(234, 234, 234); - } } .badge-active { img { From 45dae884586be0fdd714e2e1ce060d1aa0350056 Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 13:11:46 -0500 Subject: [PATCH 55/72] fix(Security) use different box shadow styling on mobile and desktop and edit active borders --- assets/css/modules/_security-center.scss | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/assets/css/modules/_security-center.scss b/assets/css/modules/_security-center.scss index b5a55049ad..f4b323874d 100644 --- a/assets/css/modules/_security-center.scss +++ b/assets/css/modules/_security-center.scss @@ -55,14 +55,18 @@ } section.level { margin-bottom: 30px; - @media (min-width: 768px) { + @media (min-width: 769px) { .level-badge { height: auto; - padding-bottom: 20px; } &.level_one { .level-badge { width: 33.333%; + &:last-child { + &.active { + border-right: 0px; + } + } } } } @@ -78,15 +82,22 @@ .level-badge { width: 22%; position: relative; + padding-bottom: 20px; transition: all .3s ease-in-out; &.active { background-color: white; border-bottom: none; font-weight: bold; border-top: none; - border-right: 1px solid rgb(234, 234, 234); - border-left: 1px solid rgb(234, 234, 234); - box-shadow: 0px 4px 8px -1px rgba(243, 243, 243, 1) inset; + box-shadow: 0px 0px 8px 4px rgba(243, 243, 243, 1) inset; + @media (min-width: 769px) { + border-right: 1px solid rgb(234, 234, 234); + border-left: 1px solid rgb(234, 234, 234); + box-shadow: 0px 4px 8px -1px rgba(243, 243, 243, 1) inset; + &:nth-child(2) { + border-left: 0px; + } + } &:after { content: ''; width: 100%; From f5f48af89c696ad66989c78ac7101842e57128a1 Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 16:45:48 -0500 Subject: [PATCH 56/72] fix(AdvancedSend) helper text visible on confirmation step and resolve flickering between steps --- app/partials/send.jade | 4 ++-- assets/css/components/_modals.scss | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/partials/send.jade b/app/partials/send.jade index edc23b9216..a9099c238c 100644 --- a/app/partials/send.jade +++ b/app/partials/send.jade @@ -11,10 +11,10 @@ span(translate="STEP") span {{ confirmationStep ? 2 : 1 }} span(translate="OF_2") -.modal-body#send(ng-class="{ 'advanced-send-scroll' : advanced }") +.modal-body#send(ng-class="{ 'advanced-send-scroll' : advanced, 'confirmation': confirmationStep }") uib-alert(ng-repeat="alert in alerts", type="{{alert.type}}", close="closeAlert(alert)") | {{::alert.msg}} - div(ng-hide="confirmationStep") + div.advanced-send(ng-hide="confirmationStep") form.ph-form(role="form",name="sendForm",novalidate) //- From .form-group.row(ng-show="numberOfActiveAccountsAndLegacyAddresses() > 1") diff --git a/assets/css/components/_modals.scss b/assets/css/components/_modals.scss index da68cceb99..a4b3474bc2 100644 --- a/assets/css/components/_modals.scss +++ b/assets/css/components/_modals.scss @@ -316,7 +316,17 @@ width: 228px; height: 430px; } - .advanced-send-scroll { max-height: 400px; overflow-y: scroll; } + .advanced-send-scroll { + max-height: 400px; + min-height: 400px; + overflow-y: scroll; + &.confirmation { + overflow: visible; + .advanced-send { + display: none; + } + } + } [name='sendForm'], [name='requestForm'] { label { text-align: right; } From 02e738b1289b841211b7391ab43a7fc720ec4933 Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 18:10:42 -0500 Subject: [PATCH 57/72] fix(AdvancedSend) resolve flickering issue while maintaining ng-show and ng-hide functionality --- app/partials/send.jade | 2 +- assets/css/components/_modals.scss | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/partials/send.jade b/app/partials/send.jade index a9099c238c..4c8b862518 100644 --- a/app/partials/send.jade +++ b/app/partials/send.jade @@ -14,7 +14,7 @@ .modal-body#send(ng-class="{ 'advanced-send-scroll' : advanced, 'confirmation': confirmationStep }") uib-alert(ng-repeat="alert in alerts", type="{{alert.type}}", close="closeAlert(alert)") | {{::alert.msg}} - div.advanced-send(ng-hide="confirmationStep") + div(ng-hide="confirmationStep") form.ph-form(role="form",name="sendForm",novalidate) //- From .form-group.row(ng-show="numberOfActiveAccountsAndLegacyAddresses() > 1") diff --git a/assets/css/components/_modals.scss b/assets/css/components/_modals.scss index a4b3474bc2..20db3cccca 100644 --- a/assets/css/components/_modals.scss +++ b/assets/css/components/_modals.scss @@ -317,13 +317,17 @@ height: 430px; } .advanced-send-scroll { - max-height: 400px; - min-height: 400px; - overflow-y: scroll; - &.confirmation { + padding: 0px; + > div { + max-height: 400px; + min-height: 400px; overflow: visible; - .advanced-send { - display: none; + padding: 15px 0; + &:first-child { + overflow-y: scroll; + } + &:last-child { + padding: 15px 2em; } } } From 853f3f00a69f8373d25713cd565b8250f09eba7d Mon Sep 17 00:00:00 2001 From: plondon Date: Wed, 13 Jan 2016 18:24:53 -0500 Subject: [PATCH 58/72] fix(SingleClick): call digest after executing copy command --- assets/js/directives/single-click-select.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/directives/single-click-select.js.coffee b/assets/js/directives/single-click-select.js.coffee index 5b4ef05f6f..57bd286533 100644 --- a/assets/js/directives/single-click-select.js.coffee +++ b/assets/js/directives/single-click-select.js.coffee @@ -24,6 +24,7 @@ angular.module('walletApp').directive('singleClickSelect', ($window) -> elem.bind('click', -> scope.select() + scope.$digest() ) action = (newVal, oldVal) -> From 42a45ba5827f97a5e544e43bc13953f8ca3c5fb6 Mon Sep 17 00:00:00 2001 From: plondon Date: Wed, 13 Jan 2016 18:28:39 -0500 Subject: [PATCH 59/72] fix(Translations): move success copy to translations --- app/partials/request.jade | 2 +- assets/js/directives/single-click-select.js.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/partials/request.jade b/app/partials/request.jade index b9ded761a6..91af53402b 100644 --- a/app/partials/request.jade +++ b/app/partials/request.jade @@ -65,7 +65,7 @@ span(translate="COPY_ADDRESS") p.success.mtm.flex-center(ng-show="browserCanExecCommand && highlighted") i.ti-check.mrs - span Successfully copied to your clipboard! + span(translate="COPY_SUCCESS") .form-group.mvl.row(ng-class="{'has-error': !requestForm.label.$valid}", ng-show="advanced && fields.to.index != undefined") label.pts.col-sm-3.col-xs-12(translate="LABEL_ADDRESS:") .col-sm-9.col-xs-12 diff --git a/assets/js/directives/single-click-select.js.coffee b/assets/js/directives/single-click-select.js.coffee index 57bd286533..5e9060a97c 100644 --- a/assets/js/directives/single-click-select.js.coffee +++ b/assets/js/directives/single-click-select.js.coffee @@ -21,10 +21,10 @@ angular.module('walletApp').directive('singleClickSelect', ($window) -> if scope.browserCanExecCommand $window.document.execCommand('copy') + scope.$digest() elem.bind('click', -> scope.select() - scope.$digest() ) action = (newVal, oldVal) -> From 24e0d45615786bbe008fc01ad2dc99a73303aa06 Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 14:13:14 -0500 Subject: [PATCH 60/72] fix(SingleClick): use safeApply instead of digest --- assets/js/directives/single-click-select.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/directives/single-click-select.js.coffee b/assets/js/directives/single-click-select.js.coffee index 5e9060a97c..c7b8dc15ad 100644 --- a/assets/js/directives/single-click-select.js.coffee +++ b/assets/js/directives/single-click-select.js.coffee @@ -21,7 +21,7 @@ angular.module('walletApp').directive('singleClickSelect', ($window) -> if scope.browserCanExecCommand $window.document.execCommand('copy') - scope.$digest() + scope.$safeApply() elem.bind('click', -> scope.select() From 9856ac2697d68cddffe785ae5fcc9add307f29dd Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 14 Jan 2016 14:16:28 -0500 Subject: [PATCH 61/72] fix(Translations): add copy success to locales instead of build --- locales/en-human.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/locales/en-human.json b/locales/en-human.json index 477a8c3052..119ec02b90 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -329,6 +329,8 @@ "BITCOIN_CURRENCY_EXPLAIN": "Adjust the precision you would prefer bitcoin values to be displayed in.", "JUST_RECEIVED_BITCOIN" : "You've just received Bitcoin!", "SUCCESS" : "Success!", + "COPY_SUCCESS": "Successfully copied to your clipboard!", + "ACCOUNT_CREATED" : "You've successfully created an account", "BITCOIN_SENT" : "You've successfully sent bitcoin", "NO_TRANSACTIONS_YET" : "Your Transactions", "SORRY_ZERO_TXS" : "Sorry, we couldn't find any transactions!", From 07196db9d848ac0e0b8f44007bbedffaf4fc669a Mon Sep 17 00:00:00 2001 From: plondon Date: Fri, 15 Jan 2016 09:55:18 -0500 Subject: [PATCH 62/72] fix(Translations): remove account created --- locales/en-human.json | 1 - 1 file changed, 1 deletion(-) diff --git a/locales/en-human.json b/locales/en-human.json index 7ed713a17b..695b5a1bde 100644 --- a/locales/en-human.json +++ b/locales/en-human.json @@ -328,7 +328,6 @@ "JUST_RECEIVED_BITCOIN" : "You've just received Bitcoin!", "SUCCESS" : "Success!", "COPY_SUCCESS": "Successfully copied to your clipboard!", - "ACCOUNT_CREATED" : "You've successfully created an account", "BITCOIN_SENT" : "You've successfully sent bitcoin", "NO_TRANSACTIONS_YET" : "Your Transactions", "SORRY_ZERO_TXS" : "Sorry, we couldn't find any transactions!", From 4624aed5c7ccc9172a5f01e2112b47f94dbefbd7 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 15 Jan 2016 17:05:51 +0100 Subject: [PATCH 63/72] feat(Deploy): ROOT_PATH variable You can specify the path where the frontend is, `/` by default. This is used for registering the URL handler. --- Gruntfile.coffee | 31 ++++++++++++++++++++++++++-- app/index.jade | 2 +- assets/js/services/wallet.service.js | 11 ++++++++-- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 5d068ad1df..8e67e0cbd0 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -395,6 +395,14 @@ module.exports = (grunt) -> to: () => 'customRootURL = $rootScope.rootURL = "https://' + @rootUrl + '/"' }] + root_path: + src: ['build/js/services/wallet.service.js'], + overwrite: true, + replacements: [{ + from: 'customRootPath = $rootScope.rootPath' + to: () => + 'customRootPath = $rootScope.rootPath = "' + @rootPath + '/"' + }] web_socket_url: src: ['build/js/services/wallet.service.js'], overwrite: true, @@ -445,7 +453,7 @@ module.exports = (grunt) -> ] # Default task(s). - grunt.registerTask "dist", (rootUrl, port) => + grunt.registerTask "dist", (rootUrl, port, rootPath) => grunt.task.run [ "shell:clean_bower_and_npm_cache" "clean" @@ -459,6 +467,7 @@ module.exports = (grunt) -> if port @rootUrl += ":" + port + console.log("Custom root URL: " + @rootUrl) grunt.task.run [ @@ -466,6 +475,15 @@ module.exports = (grunt) -> "replace:web_socket_url" ] + if rootPath + @rootPath = rootPath + + console.log("Custom root path: " + @rootPath) + + grunt.task.run [ + "replace:root_path" + ] + grunt.task.run [ "shell:check_dependencies" "shell:npm_install_dependencies" @@ -483,7 +501,7 @@ module.exports = (grunt) -> "git_changelog" ] - grunt.registerTask "dist_unsafe", (rootUrl, port) => + grunt.registerTask "dist_unsafe", (rootUrl, port, rootPath) => grunt.task.run [ "shell:clean_bower_and_npm_cache" "clean" @@ -504,6 +522,15 @@ module.exports = (grunt) -> "replace:web_socket_url" ] + if rootPath + @rootPath = rootPath + + console.log("Custom root path: " + @rootPath) + + grunt.task.run [ + "replace:root_path" + ] + grunt.task.run [ "shell:skip_check_dependencies" "concat:application_dependencies" diff --git a/app/index.jade b/app/index.jade index 1c12104e90..9684848643 100644 --- a/app/index.jade +++ b/app/index.jade @@ -1,5 +1,5 @@ doctype - + head meta(charset='utf-8') diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 6650b61880..318badd095 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -59,13 +59,19 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.api = MyBlockchainApi; - // If customRootURL is set by index.jade: + // If a custom rootURL is set by index.jade: // Grunt can replace this: const customRootURL = $rootScope.rootURL || "/"; wallet.api.ROOT_URL=customRootURL; // If customRootURL is set by Grunt: $rootScope.rootURL = customRootURL; + // If a custom root path is set by index.jade: + // Grunt can replace this: + const customRootPath = $rootScope.rootPath || ""; + // If customRootPath is set by Grunt: + $rootScope.rootPath = customRootPath; + // Grunt can replace this: const customWebSocketURL = $rootScope.webSocketURL; if(customWebSocketURL) { @@ -1181,7 +1187,8 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.handleBitcoinLinks = () => { wallet.saveActivity(2); - $window.navigator.registerProtocolHandler('bitcoin', $window.location.origin + '/#/open/%s', 'Blockchain'); + const uri = $window.location.origin + "/" + $rootScope.rootPath + '#/open/%s'; + $window.navigator.registerProtocolHandler('bitcoin', uri, 'Blockchain'); }; wallet.enableBlockTOR = () => { From 77ed67d4c21a9dc8a4519eb07aed0f8e15306c74 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 15 Jan 2016 17:10:03 +0100 Subject: [PATCH 64/72] chore(Deploy): make dist_unsafe faster --- Gruntfile.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 8e67e0cbd0..638cab505d 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -502,10 +502,9 @@ module.exports = (grunt) -> ] grunt.registerTask "dist_unsafe", (rootUrl, port, rootPath) => + console.warn "Do not deploy this to production." + console.warn "Make sure your bower_components and node_modules are up to date" grunt.task.run [ - "shell:clean_bower_and_npm_cache" - "clean" - "shell:npm_install_dependencies" "build" ] From e450fe1d8e16c489937b8b76af4ac59e4b65d0eb Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 12 Jan 2016 14:10:06 +0100 Subject: [PATCH 65/72] refactor(WalletService): use MyWalletNetwork Depends on blockchain/My-Wallet-V3#87 and a new bower release. Also uses the new promise based resendTwoFactorSms() method. --- app/index.jade | 1 + assets/js/core/walletNetwork.service.js | 7 +++++++ assets/js/services/wallet.service.js | 11 ++++++----- bower.json | 2 +- tests/mocks/my_wallet/my_wallet_network.coffee | 2 ++ 5 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 assets/js/core/walletNetwork.service.js create mode 100644 tests/mocks/my_wallet/my_wallet_network.coffee diff --git a/app/index.jade b/app/index.jade index 9684848643..451badb76a 100644 --- a/app/index.jade +++ b/app/index.jade @@ -56,6 +56,7 @@ head script(src='build/js/core/myWallet.service.js') script(src='build/js/core/payment.service.js') script(src='build/js/core/walletTokenEndpoints.service.js') + script(src='build/js/core/walletNetwork.service.js') script(src='build/js/browser-polyfill.js') script(src='build/js/app.js') diff --git a/assets/js/core/walletNetwork.service.js b/assets/js/core/walletNetwork.service.js new file mode 100644 index 0000000000..7e3bb726b4 --- /dev/null +++ b/assets/js/core/walletNetwork.service.js @@ -0,0 +1,7 @@ +angular + .module('walletApp.core') + .factory('MyWalletNetwork', MyWalletNetwork); + +function MyWalletNetwork() { + return Blockchain.WalletNetwork +} diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index 318badd095..b47e4d0b82 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -9,9 +9,9 @@ angular .module('walletServices', []) .factory('Wallet', Wallet); -Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', 'MyWalletTokenEndpoints', '$rootScope', 'ngAudio', '$cookies', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; +Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', 'MyWalletTokenEndpoints', 'MyWalletNetwork','$rootScope', 'ngAudio', '$cookies', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; -function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, MyWalletTokenEndpoints, $rootScope, ngAudio, $cookies, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { +function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, MyWalletTokenEndpoints, MyWalletNetwork, $rootScope, ngAudio, $cookies, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { const wallet = { goal: { auth: false @@ -80,6 +80,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.payment = MyWalletPayment; wallet.tokenEndpoints = MyWalletTokenEndpoints; + wallet.network = MyWalletNetwork; wallet.transactions = []; wallet.api_code = '1770d5d9-bcea-4d28-ad21-6cbd5be018a8'; @@ -290,7 +291,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB errorCallback(); $rootScope.$safeApply(); }; - wallet.my.resendTwoFactorSms(uid, success, error); + wallet.network.resendTwoFactorSms(uid).then(success).catch(error); }; wallet.recoverGuid = (email, captcha) => { @@ -316,7 +317,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB defer.reject(); $rootScope.$safeApply(); }; - wallet.my.recoverGuid(email, captcha).then(success).catch(error); + wallet.network.recoverGuid(email, captcha).then(success).catch(error); return defer.promise; }; @@ -344,7 +345,7 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB defer.reject(); $rootScope.$safeApply(); }; - wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha) + wallet.network.requestTwoFactorReset(guid, email, new_email, secret, message, captcha) .then(success) .catch(error); diff --git a/bower.json b/bower.json index a9e233679e..430d2cba49 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,7 @@ "browserdetection": "0.3.*", "bc-qr-reader": "0.2.*", "bootstrap-sass": "3.3.*", - "blockchain-wallet": "3.5.*", + "blockchain-wallet": "3.6.*", "bc-phone-number": "5.0.*" }, "devDependencies": { diff --git a/tests/mocks/my_wallet/my_wallet_network.coffee b/tests/mocks/my_wallet/my_wallet_network.coffee new file mode 100644 index 0000000000..f3901d722c --- /dev/null +++ b/tests/mocks/my_wallet/my_wallet_network.coffee @@ -0,0 +1,2 @@ +angular.module('walletApp.core').factory 'MyWalletNetwork', () -> + this From 1ba4eab18ffb80d6bf0383c6bb30cfde7c0ca369 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 12 Jan 2016 16:15:53 +0100 Subject: [PATCH 66/72] refactor(WalletNetwork): move 7 functions out of Wallet service --- app/index.jade | 1 + .../authorizeApprove.controller.js | 8 +- assets/js/controllers/login.controller.js | 4 +- assets/js/controllers/lostGuid.controller.js | 4 +- .../controllers/resetTwoFactor.controller.js | 4 +- .../resetTwoFactorToken.controller.js | 4 +- .../js/controllers/unsubscribe.controller.js | 4 +- .../js/controllers/verifyEmail.controller.js | 4 +- assets/js/services/wallet.service.js | 165 +--------------- assets/js/services/walletNetwork.service.js | 182 ++++++++++++++++++ 10 files changed, 201 insertions(+), 179 deletions(-) create mode 100644 assets/js/services/walletNetwork.service.js diff --git a/app/index.jade b/app/index.jade index 451badb76a..2ee67e2898 100644 --- a/app/index.jade +++ b/app/index.jade @@ -154,6 +154,7 @@ head script(src='build/js/directives/destinationInput.directive.js') script(src='build/js/services/wallet.service.js') + script(src='build/js/services/walletNetwork.service.js') script(src='build/js/services/securityCenter.service.js') script(src='build/js/services/adverts.service.js') script(src='build/js/services/bcTranslationLoader.service.js') diff --git a/assets/js/controllers/authorizeApprove.controller.js b/assets/js/controllers/authorizeApprove.controller.js index fd911875be..0ef9fc0daf 100644 --- a/assets/js/controllers/authorizeApprove.controller.js +++ b/assets/js/controllers/authorizeApprove.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("AuthorizeApproveCtrl", AuthorizeApproveCtrl); -function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Alerts, $translate) { +function AuthorizeApproveCtrl($window, $scope, WalletNetwork, $stateParams, $state, Alerts, $translate) { const success = (uid) => { $scope.checkingToken = false; $scope.busyApproving = false; @@ -44,13 +44,13 @@ function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Ale $scope.checkingToken = true; - Wallet.authorizeApprove($stateParams.token, differentBrowser, null) + WalletNetwork.authorizeApprove($stateParams.token, differentBrowser, null) .then(success) .catch(error); $scope.approve = () => { $scope.busyApproving = true; - Wallet.authorizeApprove($stateParams.token, () => {}, true) + WalletNetwork.authorizeApprove($stateParams.token, () => {}, true) .then(success) .catch(error); } @@ -68,7 +68,7 @@ function AuthorizeApproveCtrl($window, $scope, Wallet, $stateParams, $state, Ale }); }; - Wallet.authorizeApprove($stateParams.token, () => {}, false) + WalletNetwork.authorizeApprove($stateParams.token, () => {}, false) .then(rejected) .catch(error); } diff --git a/assets/js/controllers/login.controller.js b/assets/js/controllers/login.controller.js index 2fcefa26be..439a9f6cb9 100644 --- a/assets/js/controllers/login.controller.js +++ b/assets/js/controllers/login.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("LoginCtrl", LoginCtrl); -function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $uibModal, $state, $stateParams, $timeout, $translate, filterFilter) { +function LoginCtrl($scope, $rootScope, $log, $http, Wallet, WalletNetwork, Alerts, $cookies, $uibModal, $state, $stateParams, $timeout, $translate, filterFilter) { $scope.status = Wallet.status; $scope.settings = Wallet.settings; $scope.disableLogin = null; @@ -136,7 +136,7 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u const error = () => { $scope.resending = false; }; - Wallet.resendTwoFactorSms($scope.uid, success, error); + WalletNetwork.resendTwoFactorSms($scope.uid).then(success).catch(error); } }; diff --git a/assets/js/controllers/lostGuid.controller.js b/assets/js/controllers/lostGuid.controller.js index 57856a21cb..70ec579ded 100644 --- a/assets/js/controllers/lostGuid.controller.js +++ b/assets/js/controllers/lostGuid.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller('LostGuidCtrl', LostGuidCtrl); -function LostGuidCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { +function LostGuidCtrl($scope, $rootScope, $http, $translate, WalletNetwork, Alerts) { $scope.currentStep = 1; $scope.fields = { email: '', @@ -29,7 +29,7 @@ function LostGuidCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { $scope.remindForm.$setPristine(); $scope.remindForm.$setUntouched(); - Wallet.recoverGuid($scope.fields.email, $scope.fields.captcha).then(success).catch(error); + WalletNetwork.recoverGuid($scope.fields.email, $scope.fields.captcha).then(success).catch(error); }; // Set SID cookie by requesting headers diff --git a/assets/js/controllers/resetTwoFactor.controller.js b/assets/js/controllers/resetTwoFactor.controller.js index 896d4675f0..3eec3548fb 100644 --- a/assets/js/controllers/resetTwoFactor.controller.js +++ b/assets/js/controllers/resetTwoFactor.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller('ResetTwoFactorCtrl', ResetTwoFactorCtrl); -function ResetTwoFactorCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) { +function ResetTwoFactorCtrl($scope, $rootScope, $http, $translate, WalletNetwork, Alerts) { $scope.currentStep = 1; $scope.fields = { @@ -34,7 +34,7 @@ function ResetTwoFactorCtrl($scope, $rootScope, $http, $translate, Wallet, Alert $scope.form.$setPristine(); $scope.form.$setUntouched(); - Wallet.requestTwoFactorReset( + WalletNetwork.requestTwoFactorReset( $scope.fields.uid, $scope.fields.email, $scope.fields.newEmail, diff --git a/assets/js/controllers/resetTwoFactorToken.controller.js b/assets/js/controllers/resetTwoFactorToken.controller.js index 218de4e04c..b28f859ca1 100644 --- a/assets/js/controllers/resetTwoFactorToken.controller.js +++ b/assets/js/controllers/resetTwoFactorToken.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("ResetTwoFactorTokenCtrl", ResetTwoFactorTokenCtrl); -function ResetTwoFactorTokenCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate, $rootScope) { +function ResetTwoFactorTokenCtrl($scope, WalletNetwork, $stateParams, $state, Alerts, $translate, $rootScope) { const success = (obj) => { $scope.checkingToken = false @@ -27,5 +27,5 @@ function ResetTwoFactorTokenCtrl($scope, Wallet, $stateParams, $state, Alerts, $ $scope.checkingToken = true - Wallet.resetTwoFactorToken($stateParams.token).then(success).catch(error); + WalletNetwork.resetTwoFactorToken($stateParams.token).then(success).catch(error); } diff --git a/assets/js/controllers/unsubscribe.controller.js b/assets/js/controllers/unsubscribe.controller.js index 9d1dac2e68..24f84c3b17 100644 --- a/assets/js/controllers/unsubscribe.controller.js +++ b/assets/js/controllers/unsubscribe.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("UnsubscribeCtrl", UnsubscribeCtrl); -function UnsubscribeCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate) { +function UnsubscribeCtrl($scope, WalletNetwork, $stateParams, $state, Alerts, $translate) { const success = (uid) => { if(uid) { @@ -27,5 +27,5 @@ function UnsubscribeCtrl($scope, Wallet, $stateParams, $state, Alerts, $translat Alerts.displayError(message, true); } - Wallet.unsubscribe($stateParams.token).then(success).catch(error); + WalletNetwork.unsubscribe($stateParams.token).then(success).catch(error); } diff --git a/assets/js/controllers/verifyEmail.controller.js b/assets/js/controllers/verifyEmail.controller.js index fdaf575c10..570d757ebe 100644 --- a/assets/js/controllers/verifyEmail.controller.js +++ b/assets/js/controllers/verifyEmail.controller.js @@ -2,7 +2,7 @@ angular .module('walletApp') .controller("VerifyEmailCtrl", VerifyEmailCtrl); -function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts, $translate, $rootScope) { +function VerifyEmailCtrl($scope, WalletNetwork, $stateParams, $state, Alerts, $translate, $rootScope) { const success = (uid) => { if(uid) { $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS']).then(translations => { @@ -36,5 +36,5 @@ function VerifyEmailCtrl($scope, Wallet, $stateParams, $state, Alerts, $translat Alerts.displayError(message, true); } - Wallet.verifyEmail($stateParams.token).then(success).catch(error); + WalletNetwork.verifyEmail($stateParams.token).then(success).catch(error); } diff --git a/assets/js/services/wallet.service.js b/assets/js/services/wallet.service.js index b47e4d0b82..21f7685cd4 100644 --- a/assets/js/services/wallet.service.js +++ b/assets/js/services/wallet.service.js @@ -9,9 +9,9 @@ angular .module('walletServices', []) .factory('Wallet', Wallet); -Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', 'MyWalletTokenEndpoints', 'MyWalletNetwork','$rootScope', 'ngAudio', '$cookies', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; +Wallet.$inject = ['$http', '$window', '$timeout', 'Alerts', 'MyWallet', 'MyBlockchainApi', 'MyBlockchainSettings', 'MyWalletStore', 'MyWalletPayment', '$rootScope', 'ngAudio', '$cookies', '$translate', '$filter', '$state', '$q', 'bcPhoneNumber', 'languages', 'currency']; -function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, MyWalletTokenEndpoints, MyWalletNetwork, $rootScope, ngAudio, $cookies, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { +function Wallet( $http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyBlockchainSettings, MyWalletStore, MyWalletPayment, $rootScope, ngAudio, $cookies, $translate, $filter, $state, $q, bcPhoneNumber, languages, currency) { const wallet = { goal: { auth: false @@ -79,8 +79,6 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB } wallet.payment = MyWalletPayment; - wallet.tokenEndpoints = MyWalletTokenEndpoints; - wallet.network = MyWalletNetwork; wallet.transactions = []; wallet.api_code = '1770d5d9-bcea-4d28-ad21-6cbd5be018a8'; @@ -280,98 +278,6 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB }); }; - wallet.resendTwoFactorSms = (uid, successCallback, errorCallback) => { - let success = () => { - $translate('RESENT_2FA_SMS').then(Alerts.displaySuccess); - successCallback(); - $rootScope.$safeApply(); - }; - let error = (e) => { - $translate('RESENT_2FA_SMS_FAILED').then(Alerts.displayError); - errorCallback(); - $rootScope.$safeApply(); - }; - wallet.network.resendTwoFactorSms(uid).then(success).catch(error); - }; - - wallet.recoverGuid = (email, captcha) => { - let defer = $q.defer() - let success = (message) => { - Alerts.displaySuccess(message); - defer.resolve(); - $rootScope.$safeApply(); - }; - let error = (error) => { - - switch (error) { - case 'Captcha Code Incorrect': - Alerts.displayError($translate.instant('CAPTCHA_INCORRECT')); - break; - case 'Quota Exceeded': - Alerts.displayError($translate.instant('QUOTA_EXCEEDED')); - break; - default: - Alerts.displayError($translate.instant('UNKNOWN_ERROR')); - } - - defer.reject(); - $rootScope.$safeApply(); - }; - wallet.network.recoverGuid(email, captcha).then(success).catch(error); - return defer.promise; - }; - - wallet.requestTwoFactorReset = (guid, email, new_email, secret, message, captcha) => { - let defer = $q.defer() - - Alerts.clear() - let success = (message) => { - Alerts.displaySuccess(message); - defer.resolve(); - $rootScope.$safeApply(); - }; - let error = (error) => { - switch (error) { - case 'Captcha Code Incorrect': - Alerts.displayError($translate.instant('CAPTCHA_INCORRECT'), true); - break; - case 'Quota Exceeded': - Alerts.displayError($translate.instant('QUOTA_EXCEEDED'), true); - break; - default: - Alerts.displayError(error, true); - } - - defer.reject(); - $rootScope.$safeApply(); - }; - wallet.network.requestTwoFactorReset(guid, email, new_email, secret, message, captcha) - .then(success) - .catch(error); - - return defer.promise; - }; - - wallet.resetTwoFactorToken = (token) => { - let defer = $q.defer() - - const success = (obj) => { - defer.resolve(obj); - $rootScope.$safeApply(); - } - - const error = (e) => { - defer.reject(e.error); - $rootScope.$safeApply(); - } - - wallet.tokenEndpoints.resetTwoFactor(token) - .then(success) - .catch(error); - - return defer.promise; - } - wallet.create = (password, email, currency, language, success_callback) => { let success = (uid) => { Alerts.displaySuccess('Wallet created with identifier: ' + uid, true); @@ -1293,73 +1199,6 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB wallet.my.wallet.encrypt(password, success, error, encrypting, syncing); }; - wallet.verifyEmail = (token) => { - let defer = $q.defer(); - - const success = (res) => { - wallet.user.isEmailVerified = true; - defer.resolve(res.guid); - $rootScope.$safeApply(); - } - - const error = (res) => { - console.log(res.error); - defer.reject(res.error); - $rootScope.$safeApply(); - } - - wallet.tokenEndpoints.verifyEmail(token) - .then(success) - .catch(error); - - return defer.promise; - } - - wallet.unsubscribe = (token) => { - let defer = $q.defer(); - - const success = (res) => { - defer.resolve(res.guid); - $rootScope.$safeApply(); - } - - const error = (res) => { - console.log(res.error); - defer.reject(res.error); - $rootScope.$safeApply(); - } - - wallet.tokenEndpoints.unsubscribe(token).then(success).catch(error); - - return defer.promise; - } - - wallet.authorizeApprove = (token, differentBrowserCallback, differentBrowserApproved) => { - let defer = $q.defer() - - const success = (res) => { - defer.resolve(res.guid); - $rootScope.$safeApply(); - } - - const error = (res) => { - console.log(res.error); - defer.reject(res.error); - $rootScope.$safeApply(); - } - - const differentBrowser = (details) => { - differentBrowserCallback(details); - $rootScope.$safeApply(); - } - - wallet.tokenEndpoints.authorizeApprove(token, differentBrowser, differentBrowserApproved) - .then(success) - .catch(error); - - return defer.promise; - } - // Testing: only works on mock MyWallet wallet.refresh = () => { diff --git a/assets/js/services/walletNetwork.service.js b/assets/js/services/walletNetwork.service.js new file mode 100644 index 0000000000..bd68a7d8d6 --- /dev/null +++ b/assets/js/services/walletNetwork.service.js @@ -0,0 +1,182 @@ +angular + .module('walletApp') + .factory('WalletNetwork', WalletNetwork); + +WalletNetwork.$inject = ['MyWalletTokenEndpoints', 'MyWalletNetwork', '$q', '$rootScope', 'Alerts', '$translate']; + +function WalletNetwork(MyWalletTokenEndpoints, MyWalletNetwork, $q, $rootScope, Alerts, $translate) { + const service = { + resetTwoFactorToken: resetTwoFactorToken, + verifyEmail: verifyEmail, + unsubscribe: unsubscribe, + authorizeApprove: authorizeApprove, + requestTwoFactorReset : requestTwoFactorReset, + resendTwoFactorSms: resendTwoFactorSms, + recoverGuid : recoverGuid + } + + function resetTwoFactorToken(token) { + let defer = $q.defer() + + const success = (obj) => { + defer.resolve(obj); + $rootScope.$safeApply(); + } + + const error = (e) => { + defer.reject(e.error); + $rootScope.$safeApply(); + } + + MyWalletTokenEndpoints.resetTwoFactor(token) + .then(success) + .catch(error); + + return defer.promise; + } + + function verifyEmail(token) { + let defer = $q.defer(); + + const success = (res) => { + defer.resolve(res.guid); + $rootScope.$safeApply(); + } + + const error = (res) => { + console.log(res.error); + defer.reject(res.error); + $rootScope.$safeApply(); + } + + MyWalletTokenEndpoints.verifyEmail(token) + .then(success) + .catch(error); + + return defer.promise; + } + + function unsubscribe(token) { + let defer = $q.defer(); + + const success = (res) => { + defer.resolve(res.guid); + $rootScope.$safeApply(); + } + + const error = (res) => { + console.log(res.error); + defer.reject(res.error); + $rootScope.$safeApply(); + } + + MyWalletTokenEndpoints.unsubscribe(token).then(success).catch(error); + + return defer.promise; + } + + function authorizeApprove(token, differentBrowserCallback, differentBrowserApproved) { + let defer = $q.defer() + + const success = (res) => { + defer.resolve(res.guid); + $rootScope.$safeApply(); + } + + const error = (res) => { + console.log(res.error); + defer.reject(res.error); + $rootScope.$safeApply(); + } + + const differentBrowser = (details) => { + differentBrowserCallback(details); + $rootScope.$safeApply(); + } + + MyWalletTokenEndpoints.authorizeApprove(token, differentBrowser, differentBrowserApproved) + .then(success) + .catch(error); + + return defer.promise; + } + + function requestTwoFactorReset(guid, email, new_email, secret, message, captcha) { + let defer = $q.defer() + + Alerts.clear() + let success = (message) => { + Alerts.displaySuccess(message); + defer.resolve(); + $rootScope.$safeApply(); + }; + let error = (error) => { + switch (error) { + case 'Captcha Code Incorrect': + Alerts.displayError($translate.instant('CAPTCHA_INCORRECT'), true); + break; + case 'Quota Exceeded': + Alerts.displayError($translate.instant('QUOTA_EXCEEDED'), true); + break; + default: + Alerts.displayError(error, true); + } + + defer.reject(); + $rootScope.$safeApply(); + }; + MyWalletNetwork.requestTwoFactorReset(guid, email, new_email, secret, message, captcha) + .then(success) + .catch(error); + + return defer.promise; + }; + + function resendTwoFactorSms(uid) { + let defer = $q.defer() + + let success = () => { + $translate('RESENT_2FA_SMS').then(Alerts.displaySuccess); + defer.resolve(); + $rootScope.$safeApply(); + }; + let error = (e) => { + $translate('RESENT_2FA_SMS_FAILED').then(Alerts.displayError); + defer.reject(); + $rootScope.$safeApply(); + }; + + MyWalletNetwork.resendTwoFactorSms(uid).then(success).catch(error); + + return defer.promise; + }; + + function recoverGuid(email, captcha) { + let defer = $q.defer() + let success = (message) => { + Alerts.displaySuccess(message); + defer.resolve(); + $rootScope.$safeApply(); + }; + let error = (error) => { + + switch (error) { + case 'Captcha Code Incorrect': + Alerts.displayError($translate.instant('CAPTCHA_INCORRECT')); + break; + case 'Quota Exceeded': + Alerts.displayError($translate.instant('QUOTA_EXCEEDED')); + break; + default: + Alerts.displayError($translate.instant('UNKNOWN_ERROR')); + } + + defer.reject(); + $rootScope.$safeApply(); + }; + MyWalletNetwork.recoverGuid(email, captcha).then(success).catch(error); + return defer.promise; + }; + + return service; +} From 881dac0ac7091278fdb89d81147a578ca6bdb4cf Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 12 Jan 2016 18:39:29 +0100 Subject: [PATCH 67/72] test(VerifyEmail): add tests Also fixed and expanded broken test for LoginCtrl. --- .../js/controllers/verifyEmail.controller.js | 28 +------ assets/js/services/alerts.service.js | 16 +++- tests/controllers/login_ctrl_spec.coffee | 45 +++++++---- .../controllers/verify_email_ctrl_spec.coffee | 78 +++++++++++++++++++ tests/mocks/ui_router_mock.coffee | 23 +++--- 5 files changed, 140 insertions(+), 50 deletions(-) create mode 100644 tests/controllers/verify_email_ctrl_spec.coffee diff --git a/assets/js/controllers/verifyEmail.controller.js b/assets/js/controllers/verifyEmail.controller.js index 570d757ebe..e4ad8b627b 100644 --- a/assets/js/controllers/verifyEmail.controller.js +++ b/assets/js/controllers/verifyEmail.controller.js @@ -4,31 +4,11 @@ angular function VerifyEmailCtrl($scope, WalletNetwork, $stateParams, $state, Alerts, $translate, $rootScope) { const success = (uid) => { - if(uid) { - $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS']).then(translations => { - $state.go("public.login-uid", {uid: uid}).then(() =>{ - $rootScope.$emit('showNotification', { - type: 'verified-email', - icon: 'ti-email', - heading: translations.SUCCESS, - msg: translations.EMAIL_VERIFIED_SUCCESS - }); - }); + $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS']).then(translations => { + $state.go("public.login-uid", {uid: uid}).then(() =>{ + Alerts.displayVerifiedEmail() }); - } else { - $translate(['SUCCESS', 'EMAIL_VERIFIED_SUCCESS_NO_UID']).then(translations => { - $state.go("public.login-no-uid").then(() => { - $rootScope.$emit('showNotification', { - type: 'verified-email', - icon: 'ti-email', - heading: translations.SUCCESS, - msg: translations.EMAIL_VERIFIED_SUCCESS_NO_UID - }); - }); - }); - } - - + }); } const error = (message) => { diff --git a/assets/js/services/alerts.service.js b/assets/js/services/alerts.service.js index 47f22d3b17..b7253ba5eb 100644 --- a/assets/js/services/alerts.service.js +++ b/assets/js/services/alerts.service.js @@ -2,9 +2,9 @@ angular .module('walletApp') .factory('Alerts', Alerts); -Alerts.$inject = ['$timeout']; +Alerts.$inject = ['$timeout', '$rootScope']; -function Alerts($timeout) { +function Alerts($timeout, $rootScope) { const service = { alerts : [], close : close, @@ -13,7 +13,8 @@ function Alerts($timeout) { displaySuccess : display.bind(null, 'success'), displayWarning : display.bind(null, ''), displayError : display.bind(null, 'danger'), - displayReceivedBitcoin : display.bind(null, 'received-bitcoin') + displayReceivedBitcoin : display.bind(null, 'received-bitcoin'), + displayVerifiedEmail : displayVerifiedEmail }; function close(alert, context=service.alerts) { @@ -34,5 +35,14 @@ function Alerts($timeout) { context.push(alert); } + function displayVerifiedEmail() { + $rootScope.$emit('showNotification', { + type: 'verified-email', + icon: 'ti-email', + heading: translations.SUCCESS, + msg: translations.EMAIL_VERIFIED_SUCCESS + }); + } + return service; } diff --git a/tests/controllers/login_ctrl_spec.coffee b/tests/controllers/login_ctrl_spec.coffee index 71bcd7eb16..f54a782d73 100644 --- a/tests/controllers/login_ctrl_spec.coffee +++ b/tests/controllers/login_ctrl_spec.coffee @@ -2,7 +2,7 @@ describe "LoginCtrl", -> scope = undefined modal = - open: (args) -> + open: (args) -> result: then: -> @@ -11,37 +11,54 @@ describe "LoginCtrl", -> beforeEach -> angular.mock.inject ($injector, $rootScope, $controller) -> Wallet = $injector.get("Wallet") - MyWallet = $injector.get("MyWallet") - + WalletNetwork = $injector.get("WalletNetwork") + + spyOn(WalletNetwork, "resendTwoFactorSms").and.callFake(()-> + { + then: (callback) -> + callback() + { + catch: (callback) -> + if false + callback() + { + } + } + } + ) + + MyWallet = $injector.get("MyWallet") + scope = $rootScope.$new() - + $controller "LoginCtrl", $scope: scope, $stateParams: {} $uibModal: modal - + return return - + it "should login", inject((Wallet) -> scope.uid = "user" scope.password = "pass" - + spyOn(Wallet, "login") - + scope.login() - + return ) - it "should resend two factor sms", inject((Wallet) -> + it "should resend two factor sms", inject((Wallet, WalletNetwork) -> Wallet.settings.twoFactorMethod = 5 - - spyOn(Wallet, "resendTwoFactorSms") + scope.uid = "user" scope.resend() - expect(Wallet.resendTwoFactorSms).toHaveBeenCalled() + expect(WalletNetwork.resendTwoFactorSms).toHaveBeenCalled() + expect(WalletNetwork.resendTwoFactorSms).toHaveBeenCalledWith("user") + return - ) \ No newline at end of file + ) diff --git a/tests/controllers/verify_email_ctrl_spec.coffee b/tests/controllers/verify_email_ctrl_spec.coffee new file mode 100644 index 0000000000..440c5a1e3a --- /dev/null +++ b/tests/controllers/verify_email_ctrl_spec.coffee @@ -0,0 +1,78 @@ +describe "VerifyEmailController", -> + scope = undefined + + beforeEach angular.mock.module("walletApp") + + beforeEach -> + angular.mock.inject ($injector, $rootScope, $controller) -> + WalletNetwork = $injector.get("WalletNetwork") + $state = $injector.get("$state") # This is a mock + Alerts = $injector.get("Alerts") + + spyOn(WalletNetwork, "verifyEmail").and.callFake((token)-> + { + then: (callback) -> + if token == "token" + callback("1234") + { + catch: (callback) -> + if token == "wrong-token" + callback() + { + } + } + } + ) + + spyOn($state, "go").and.callThrough() + + spyOn(Alerts, "displayError").and.callFake(() ->) + spyOn(Alerts, "displayVerifiedEmail").and.callFake(() ->) + + return + + return + + describe "with token", -> + beforeEach -> + angular.mock.inject ($controller, $rootScope) -> + + scope = $rootScope.$new() + + $controller "VerifyEmailCtrl", + $scope: scope, + $stateParams: {token: "token"} + + it "should show call WalletNetwork.verifyEmail()", inject((WalletNetwork) -> + expect(WalletNetwork.verifyEmail).toHaveBeenCalled() + ) + + it "should pass the token parameter along", inject((WalletNetwork) -> + expect(WalletNetwork.verifyEmail).toHaveBeenCalledWith("token") + ) + + it "should redirect to the login page", inject(($state)-> + expect($state.go).toHaveBeenCalledWith("public.login-uid", { uid : '1234' }) + ) + + it "should request a modal success message", inject((Alerts) -> + expect(Alerts.displayVerifiedEmail).toHaveBeenCalled() + ) + + describe "with wrong token", -> + beforeEach -> + angular.mock.inject ($controller, $rootScope) -> + + scope = $rootScope.$new() + + $controller "VerifyEmailCtrl", + $scope: scope, + $stateParams: {token: "wrong-token"} + + it "should display an error message", inject((Alerts)-> + expect(Alerts.displayError).toHaveBeenCalled() + ) + + it "should redirect to the login page", inject(($state)-> + expect($state.go).toHaveBeenCalledWith("public.login-no-uid") + ) diff --git a/tests/mocks/ui_router_mock.coffee b/tests/mocks/ui_router_mock.coffee index 3faa40b826..3bf93bd24d 100644 --- a/tests/mocks/ui_router_mock.coffee +++ b/tests/mocks/ui_router_mock.coffee @@ -5,18 +5,23 @@ angular.module("ui.router", ["ng"]).run [ ] angular.module("ui.router").provider "$state", -> - + $get: () -> $state = {current: "somewhere"} - - $state.go = (destination) -> - return - + + $state.go = (destination) -> + { + then: (callback) -> + callback() + { + } + } + return $state - + angular.module("ui.router").provider "$stateParams", -> - + $get: () -> $stateParams = {} - - return $stateParams \ No newline at end of file + + return $stateParams From 1ab336dfb7dee3a70a43afacc92b5c54d569a645 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 15 Jan 2016 13:09:02 +0100 Subject: [PATCH 68/72] fix(AuthorizeApprove): promise might be resolved even if result is null --- .../authorizeApprove.controller.js | 21 +++++++------------ assets/js/services/walletNetwork.service.js | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/assets/js/controllers/authorizeApprove.controller.js b/assets/js/controllers/authorizeApprove.controller.js index 0ef9fc0daf..5e0d320b62 100644 --- a/assets/js/controllers/authorizeApprove.controller.js +++ b/assets/js/controllers/authorizeApprove.controller.js @@ -3,27 +3,22 @@ angular .controller("AuthorizeApproveCtrl", AuthorizeApproveCtrl); function AuthorizeApproveCtrl($window, $scope, WalletNetwork, $stateParams, $state, Alerts, $translate) { - const success = (uid) => { + const success = (res) => { $scope.checkingToken = false; $scope.busyApproving = false; $scope.busyRejecting = false; + // If differentBrowser is called, success will be null: + if (res.success == null) return; $window.close(); // This is sometimes ignored, hence the code below: - if(uid) { - $translate('AUTHORIZE_APPROVE_SUCCESS').then(translation => { - $state.go("public.login-uid", {uid: uid}).then(() => { - Alerts.displaySuccess(translation) - }); + $translate('AUTHORIZE_APPROVE_SUCCESS').then(translation => { + $state.go("public.login-uid", {uid: res.guid}).then(() => { + Alerts.displaySuccess(translation) }); - } else { - $translate('AUTHORIZE_APPROVE_SUCCESS').then(translation => { - $state.go("public.login-no-uid").then(() => { - Alerts.displaySuccess(translation) - }); - }); - } + }); + } const error = (message) => { diff --git a/assets/js/services/walletNetwork.service.js b/assets/js/services/walletNetwork.service.js index bd68a7d8d6..f37ff10c79 100644 --- a/assets/js/services/walletNetwork.service.js +++ b/assets/js/services/walletNetwork.service.js @@ -79,7 +79,7 @@ function WalletNetwork(MyWalletTokenEndpoints, MyWalletNetwork, $q, $rootScope, let defer = $q.defer() const success = (res) => { - defer.resolve(res.guid); + defer.resolve(res); $rootScope.$safeApply(); } From c6f8018cc16681ea941f4ff4095440975ace5c61 Mon Sep 17 00:00:00 2001 From: plondon Date: Fri, 15 Jan 2016 13:58:55 -0500 Subject: [PATCH 69/72] fix(SecurityCenter): clicking mobile cancel number will toggle the active state of the form --- app/partials/security-center.jade | 2 +- assets/css/modules/_security-center.scss | 2 ++ assets/js/controllers/securityCenter.controller.js | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/partials/security-center.jade b/app/partials/security-center.jade index 2275a700ca..b63878d800 100644 --- a/app/partials/security-center.jade +++ b/app/partials/security-center.jade @@ -109,7 +109,7 @@ .badge-panel(ng-show="display.action == 'mobilenumber' || display.action == 'twofactor'") div(ng-show="display.action == 'mobilenumber'") - configure-mobile-number(on-success="mobileNumber.step = 2" on-cancel="mobileNumber.step = 0" ng-show="mobileNumber.step == 1" button-lg full-width) + configure-mobile-number(on-success="mobileNumber.step = 2" on-cancel="mobileNumber.step = 1; cancelNumber();" ng-show="mobileNumber.step == 1" button-lg full-width) div(ng-show="mobileNumber.step == 2") h2.status.complete.hidden-xs.long-input verify-mobile-number(on-success="mobileNumber.step = 0" button-lg full-width) diff --git a/assets/css/modules/_security-center.scss b/assets/css/modules/_security-center.scss index f4b323874d..80f2319b1a 100644 --- a/assets/css/modules/_security-center.scss +++ b/assets/css/modules/_security-center.scss @@ -149,8 +149,10 @@ } .badge-panel { border: 1px solid rgb(234, 234, 234); + position: relative; border-top: none; padding: 30px; + z-index: 10; input { border: none; border-bottom: 1px solid #DBDBDB; diff --git a/assets/js/controllers/securityCenter.controller.js b/assets/js/controllers/securityCenter.controller.js index 74208daf81..b63f7255d5 100644 --- a/assets/js/controllers/securityCenter.controller.js +++ b/assets/js/controllers/securityCenter.controller.js @@ -68,6 +68,10 @@ function SettingsSecurityCenterCtrl($scope, Wallet, SecurityCenter, filterFilter Wallet.changePasswordHint(hint, success, error); }; + $scope.cancelNumber = () => { + $scope.toggle('mobilenumber'); + } + $scope.$watchCollection("user", (newValue, oldValue) => { if (!($scope.display.action === "mobilenumber" && !$scope.user.isMobileVerified)) { $scope.nextAction(); From 05090fe172fb6748e79c1f9cd69dc0515f151a19 Mon Sep 17 00:00:00 2001 From: plondon Date: Fri, 15 Jan 2016 15:16:50 -0500 Subject: [PATCH 70/72] fix(LoginModal): rocket gif alignment and aspect ratio --- app/partials/first-login-modal.jade | 2 +- assets/css/components/_modals.scss | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/partials/first-login-modal.jade b/app/partials/first-login-modal.jade index ff9243ca66..7ca3a1a865 100644 --- a/app/partials/first-login-modal.jade +++ b/app/partials/first-login-modal.jade @@ -3,4 +3,4 @@ h3.mtn.h2.em-300.center-align(translate="FIRST_LOGIN_TITLE") p.center-align.mtm.width-100.h4.em-300(translate="FIRST_LOGIN_TEXT") button.button-success.button-lg.mtl(ng-click="ok()", translate="FIRST_LOGIN_ACTION") - img.rocket-gif.flex-1(src="img/rocket.gif") + img.rocket-gif(src="img/rocket.gif") diff --git a/assets/css/components/_modals.scss b/assets/css/components/_modals.scss index 20db3cccca..688b3b6f75 100644 --- a/assets/css/components/_modals.scss +++ b/assets/css/components/_modals.scss @@ -313,7 +313,6 @@ //Specific to the first login modal .rocket-gif { border-radius: 5px; - width: 228px; height: 430px; } .advanced-send-scroll { From 36b3930fc3b8710a23f7bbc005981f175d113af5 Mon Sep 17 00:00:00 2001 From: plondon Date: Fri, 15 Jan 2016 15:51:23 -0500 Subject: [PATCH 71/72] refactor(SecurityCenter): call toggle from template instead of controller --- app/partials/security-center.jade | 2 +- assets/js/controllers/securityCenter.controller.js | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/partials/security-center.jade b/app/partials/security-center.jade index b63878d800..bdeb824ae4 100644 --- a/app/partials/security-center.jade +++ b/app/partials/security-center.jade @@ -109,7 +109,7 @@ .badge-panel(ng-show="display.action == 'mobilenumber' || display.action == 'twofactor'") div(ng-show="display.action == 'mobilenumber'") - configure-mobile-number(on-success="mobileNumber.step = 2" on-cancel="mobileNumber.step = 1; cancelNumber();" ng-show="mobileNumber.step == 1" button-lg full-width) + configure-mobile-number(on-success="mobileNumber.step = 2" on-cancel="mobileNumber.step = 1; toggle('mobilenumber');" ng-show="mobileNumber.step == 1" button-lg full-width) div(ng-show="mobileNumber.step == 2") h2.status.complete.hidden-xs.long-input verify-mobile-number(on-success="mobileNumber.step = 0" button-lg full-width) diff --git a/assets/js/controllers/securityCenter.controller.js b/assets/js/controllers/securityCenter.controller.js index b63f7255d5..40ed995507 100644 --- a/assets/js/controllers/securityCenter.controller.js +++ b/assets/js/controllers/securityCenter.controller.js @@ -67,11 +67,7 @@ function SettingsSecurityCenterCtrl($scope, Wallet, SecurityCenter, filterFilter $scope.changePasswordHint = (hint, success, error) => { Wallet.changePasswordHint(hint, success, error); }; - - $scope.cancelNumber = () => { - $scope.toggle('mobilenumber'); - } - + $scope.$watchCollection("user", (newValue, oldValue) => { if (!($scope.display.action === "mobilenumber" && !$scope.user.isMobileVerified)) { $scope.nextAction(); From ac800b8860739368c10393ec16e2cf23aa01c128 Mon Sep 17 00:00:00 2001 From: plondon Date: Mon, 18 Jan 2016 17:26:39 -0500 Subject: [PATCH 72/72] fix(SendModal): ie support for FROM addresses and remove unsightly gray bar in dropdown --- app/partials/send.jade | 2 +- app/templates/destination-input.jade | 2 +- assets/css/modules/_send-receive.scss | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/partials/send.jade b/app/partials/send.jade index 4c8b862518..054ee50d0f 100644 --- a/app/partials/send.jade +++ b/app/partials/send.jade @@ -28,7 +28,7 @@ ng-model="transaction.from" name="from" ng-change="validateAmounts(); checkForSameDestination(); setPaymentFrom()" - required) + required).send-from-dropdown ui-select-match label-origin(origin="$select.selected") ui-select-choices(repeat="origin in origins | filter: getFilter($select.search)" group-by="'type'" ui-disable-choice="hasZeroBalance(origin)") diff --git a/app/templates/destination-input.jade b/app/templates/destination-input.jade index 8328fc2e9e..d3b813c695 100644 --- a/app/templates/destination-input.jade +++ b/app/templates/destination-input.jade @@ -30,5 +30,5 @@ span.sr-only Toggle Dropdown ul.uib-dropdown-menu.dropdown-menu-right.drop-menu li(ng-repeat="account in accounts") - a(ng-click="setModel(account)" ng-disabled="true") + a(ng-click="setModel(account)" ng-disabled="account.index === model.index") | {{ account.label }} diff --git a/assets/css/modules/_send-receive.scss b/assets/css/modules/_send-receive.scss index c40feb3717..b1f6e232e8 100644 --- a/assets/css/modules/_send-receive.scss +++ b/assets/css/modules/_send-receive.scss @@ -4,7 +4,12 @@ .modal-body#send { input { - height: 33px; + height: 34px; + } + .send-from-dropdown { + .dropdown-header { + display: none; + } } textarea { border-radius: 3px;