From a7d484c944f1a0cd9b59d5f5da3b01a2af399b9a Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 30 Jun 2014 18:41:17 -0300 Subject: [PATCH 1/7] basic URI handling --- index.html | 1 + js/routes.js | 1 - js/services/controllerUtils.js | 3 ++- js/services/uriHandler.js | 11 +++++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 js/services/uriHandler.js diff --git a/index.html b/index.html index 339e7e7ce..b86275c94 100644 --- a/index.html +++ b/index.html @@ -926,6 +926,7 @@ on supported browsers please check http://www.w + diff --git a/js/routes.js b/js/routes.js index a4218112c..599f48677 100644 --- a/js/routes.js +++ b/js/routes.js @@ -64,7 +64,6 @@ angular }) .run(function($rootScope, $location) { $rootScope.$on('$routeChangeStart', function(event, next, current) { - if (!util.supports.data) { $location.path('unsupported'); } else { diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index 342b33cdf..bf2f387a7 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -2,7 +2,7 @@ var bitcore = require('bitcore'); angular.module('copayApp.services') - .factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, Socket, video) { + .factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, Socket, video, uriHandler) { var root = {}; root.getVideoMutedStatus = function(copayer) { @@ -64,6 +64,7 @@ angular.module('copayApp.services') }; root.setupRootVariables = function() { + uriHandler.register(); $rootScope.unitName = config.unitName; $rootScope.txAlertCount = 0; $rootScope.insightError = 0; diff --git a/js/services/uriHandler.js b/js/services/uriHandler.js new file mode 100644 index 000000000..564bfb5a8 --- /dev/null +++ b/js/services/uriHandler.js @@ -0,0 +1,11 @@ +'use strict'; + +var UriHandler = function() {}; + +UriHandler.prototype.register = function() { + navigator.registerProtocolHandler('bitcoin', + 'uri=%s', + 'Copay'); +}; + +angular.module('copayApp.services').value('uriHandler', new UriHandler()); From 16293b035d21216c4f3b264cd5ef1070771ddfd0 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 30 Jun 2014 18:48:48 -0300 Subject: [PATCH 2/7] add tests --- test/unit/services/servicesSpec.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unit/services/servicesSpec.js b/test/unit/services/servicesSpec.js index badab5154..0358d1b74 100644 --- a/test/unit/services/servicesSpec.js +++ b/test/unit/services/servicesSpec.js @@ -181,3 +181,20 @@ describe("Unit: isMobile Service", function() { isMobile.any().should.equal(true); })); }); +describe("Unit: video service", function() { + beforeEach(angular.mock.module('copayApp.services')); + it('should contain a video service', inject(function(video) { + should.exist(video); + })); +}); +describe("Unit: uriHandler service", function() { + beforeEach(angular.mock.module('copayApp.services')); + it('should contain a uriHandler service', inject(function(uriHandler) { + should.exist(uriHandler); + })); + it('should register', inject(function(uriHandler) { + (function() { + uriHandler.register(); + }).should.not.throw(); + })); +}); From 433f1570de2938af1d82065938e253e7eebb1c42 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Tue, 1 Jul 2014 19:35:15 -0300 Subject: [PATCH 3/7] add first uri handler --- index.html | 16 ++++++++++++++++ js/controllers/uriPayment.js | 20 ++++++++++++++++++++ js/routes.js | 7 +++---- js/services/uriHandler.js | 6 ++++-- 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 js/controllers/uriPayment.js diff --git a/index.html b/index.html index b86275c94..9ca64a66d 100644 --- a/index.html +++ b/index.html @@ -884,6 +884,21 @@ on supported browsers please check http://www.w + + + + diff --git a/js/controllers/uriPayment.js b/js/controllers/uriPayment.js new file mode 100644 index 000000000..f1004eca6 --- /dev/null +++ b/js/controllers/uriPayment.js @@ -0,0 +1,20 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('UriPaymentController', function($rootScope, $scope, $routeParams) { + var data = $routeParams.data; + var splitDots = data.split(':'); + $scope.protocol = splitDots[0]; + data = splitDots[1]; + var splitQuestion = data.split('?'); + $scope.address = splitQuestion[0]; + data = decodeURIComponent(splitQuestion[1]); + var search = splitQuestion[1]; + data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}', + function(key, value) { + return key === "" ? value : decodeURIComponent(value); + }); + $scope.amount = data.amount; + $scope.message = data.message; + + +}); diff --git a/js/routes.js b/js/routes.js index 599f48677..e4fe14cff 100644 --- a/js/routes.js +++ b/js/routes.js @@ -26,10 +26,6 @@ angular templateUrl: 'addresses.html', validate: true }) - .when('/join/:id', { - templateUrl: 'join.html', - validate: true - }) .when('/transactions', { templateUrl: 'transactions.html', validate: true @@ -49,6 +45,9 @@ angular .when('/unsupported', { templateUrl: 'unsupported.html' }) + .when('/uri_payment/:data', { + templateUrl: 'uri_payment.html' + }) .otherwise({ templateUrl: '404.html' }); diff --git a/js/services/uriHandler.js b/js/services/uriHandler.js index 564bfb5a8..413672f3b 100644 --- a/js/services/uriHandler.js +++ b/js/services/uriHandler.js @@ -3,9 +3,11 @@ var UriHandler = function() {}; UriHandler.prototype.register = function() { + var base = window.location.origin + '/'; + var url = base + '#/uri_payment/%s'; + console.log(url); navigator.registerProtocolHandler('bitcoin', - 'uri=%s', - 'Copay'); + url, 'Copay'); }; angular.module('copayApp.services').value('uriHandler', new UriHandler()); From 8b25b5932f9e86a57087dc09664713ed0fd5f67d Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Tue, 1 Jul 2014 19:49:05 -0300 Subject: [PATCH 4/7] add tests and fix some minor issues --- js/controllers/uriPayment.js | 6 ++--- js/services/uriHandler.js | 1 - test/unit/controllers/controllersSpec.js | 28 +++++++++++++++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/js/controllers/uriPayment.js b/js/controllers/uriPayment.js index f1004eca6..458a9e596 100644 --- a/js/controllers/uriPayment.js +++ b/js/controllers/uriPayment.js @@ -1,20 +1,18 @@ 'use strict'; angular.module('copayApp.controllers').controller('UriPaymentController', function($rootScope, $scope, $routeParams) { - var data = $routeParams.data; + var data = decodeURIComponent($routeParams.data); var splitDots = data.split(':'); $scope.protocol = splitDots[0]; data = splitDots[1]; var splitQuestion = data.split('?'); $scope.address = splitQuestion[0]; - data = decodeURIComponent(splitQuestion[1]); var search = splitQuestion[1]; data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function(key, value) { return key === "" ? value : decodeURIComponent(value); }); - $scope.amount = data.amount; + $scope.amount = parseInt(data.amount); $scope.message = data.message; - }); diff --git a/js/services/uriHandler.js b/js/services/uriHandler.js index 413672f3b..ef50e1b5a 100644 --- a/js/services/uriHandler.js +++ b/js/services/uriHandler.js @@ -5,7 +5,6 @@ var UriHandler = function() {}; UriHandler.prototype.register = function() { var base = window.location.origin + '/'; var url = base + '#/uri_payment/%s'; - console.log(url); navigator.registerProtocolHandler('bitcoin', url, 'Copay'); }; diff --git a/test/unit/controllers/controllersSpec.js b/test/unit/controllers/controllersSpec.js index 8525dc782..96a30f4be 100644 --- a/test/unit/controllers/controllersSpec.js +++ b/test/unit/controllers/controllersSpec.js @@ -300,7 +300,7 @@ describe("Unit: Controllers", function() { '
' + '' + '
' - ); + ); scope.model = { amount: null }; @@ -368,4 +368,30 @@ describe("Unit: Controllers", function() { }); }); + describe('UriPayment Controller', function() { + var what; + beforeEach(inject(function($controller, $rootScope) { + scope = $rootScope.$new(); + var routeParams = { + data: 'bitcoin:19mP9FKrXqL46Si58pHdhGKow88SUPy1V8%3Famount=1&message=a%20bitcoin%20donation' + }; + what = $controller('UriPaymentController', { + $scope: scope, + $routeParams: routeParams + }); + })); + + it('should exist', function() { + should.exist(what); + }); + + it('should parse url correctly', function() { + should.exist(what); + scope.protocol.should.equal('bitcoin'); + scope.address.should.equal('19mP9FKrXqL46Si58pHdhGKow88SUPy1V8'); + scope.amount.should.equal(1); + scope.message.should.equal('a bitcoin donation'); + }); + }); + }); From 8acaca75fb899ec798719482676d27d97b0a5841 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 2 Jul 2014 17:35:37 -0300 Subject: [PATCH 5/7] autocomplete send form after uri handled --- js/controllers/header.js | 4 +++- js/controllers/send.js | 10 +++++++++- js/controllers/signin.js | 4 ++++ js/controllers/uriPayment.js | 16 ++++++++++++++-- js/services/controllerUtils.js | 6 +++++- test/unit/controllers/controllersSpec.js | 4 ++-- 6 files changed, 37 insertions(+), 7 deletions(-) diff --git a/js/controllers/header.js b/js/controllers/header.js index a638f7a2c..841e9ac9b 100644 --- a/js/controllers/header.js +++ b/js/controllers/header.js @@ -71,7 +71,9 @@ angular.module('copayApp.controllers').controller('HeaderController', $rootScope.$watch('txAlertCount', function(txAlertCount) { if (txAlertCount && txAlertCount > 0) { - notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? 'You have a pending transaction proposal' : 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount); + notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? + 'You have a pending transaction proposal' : + 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount); } }); diff --git a/js/controllers/send.js b/js/controllers/send.js index 5d94e90fd..0ab3d3858 100644 --- a/js/controllers/send.js +++ b/js/controllers/send.js @@ -23,6 +23,13 @@ angular.module('copayApp.controllers').controller('SendController', return flag; }; + if ($rootScope.pendingPayment) { + var pp = $rootScope.pendingPayment; + $scope.address = pp.address; + var amount = pp.amount / config.unitToSatoshi * 100000000; + $scope.amount = amount; + } + // Detect protocol $scope.isHttp = ($window.location.protocol.indexOf('http') === 0); @@ -61,6 +68,7 @@ angular.module('copayApp.controllers').controller('SendController', $scope.loading = false; }); } + $rootScope.pendingPayment = null; }); // reset fields @@ -265,7 +273,7 @@ angular.module('copayApp.controllers').controller('SendController', }; $scope.topAmount = function(form) { - $scope.amount = $scope.getAvailableAmount(); + $scope.amount = $scope.getAvailableAmount(); form.amount.$pristine = false; }; }); diff --git a/js/controllers/signin.js b/js/controllers/signin.js index b2e6ef0c8..6503d46ab 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -13,6 +13,10 @@ angular.module('copayApp.controllers').controller('SigninController', $scope.selectedWalletId = $scope.wallets.length ? $scope.wallets[0].id : null; $scope.openPassword = ''; + if ($rootScope.pendingPayment) { + notification.info('Login Required', 'Please open wallet to complete payment'); + } + $scope.open = function(form) { if (form && form.$invalid) { notification.error('Error', 'Please enter the required fields'); diff --git a/js/controllers/uriPayment.js b/js/controllers/uriPayment.js index 458a9e596..ccc701205 100644 --- a/js/controllers/uriPayment.js +++ b/js/controllers/uriPayment.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('UriPaymentController', function($rootScope, $scope, $routeParams) { +angular.module('copayApp.controllers').controller('UriPaymentController', function($rootScope, $scope, $routeParams, $timeout, $location) { var data = decodeURIComponent($routeParams.data); var splitDots = data.split(':'); $scope.protocol = splitDots[0]; @@ -12,7 +12,19 @@ angular.module('copayApp.controllers').controller('UriPaymentController', functi function(key, value) { return key === "" ? value : decodeURIComponent(value); }); - $scope.amount = parseInt(data.amount); + $scope.amount = parseFloat(data.amount); $scope.message = data.message; + $rootScope.pendingPayment = { + protocol: $scope.protocol, + address: $scope.address, + amount: $scope.amount, + message: $scope.message + }; + + $timeout(function() { + $location.path('signin'); + }, 1000); + + }); diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index bf2f387a7..aa386b618 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -103,7 +103,11 @@ angular.module('copayApp.services') }); w.on('ready', function(myPeerID) { $rootScope.wallet = w; - $location.path('addresses'); + if ($rootScope.pendingPayment) { + $location.path('send'); + } else { + $location.path('addresses'); + } if (!config.disableVideo) video.setOwnPeer(myPeerID, w, handlePeerVideo); }); diff --git a/test/unit/controllers/controllersSpec.js b/test/unit/controllers/controllersSpec.js index 96a30f4be..046ea0704 100644 --- a/test/unit/controllers/controllersSpec.js +++ b/test/unit/controllers/controllersSpec.js @@ -373,7 +373,7 @@ describe("Unit: Controllers", function() { beforeEach(inject(function($controller, $rootScope) { scope = $rootScope.$new(); var routeParams = { - data: 'bitcoin:19mP9FKrXqL46Si58pHdhGKow88SUPy1V8%3Famount=1&message=a%20bitcoin%20donation' + data: 'bitcoin:19mP9FKrXqL46Si58pHdhGKow88SUPy1V8%3Famount=0.1&message=a%20bitcoin%20donation' }; what = $controller('UriPaymentController', { $scope: scope, @@ -389,7 +389,7 @@ describe("Unit: Controllers", function() { should.exist(what); scope.protocol.should.equal('bitcoin'); scope.address.should.equal('19mP9FKrXqL46Si58pHdhGKow88SUPy1V8'); - scope.amount.should.equal(1); + scope.amount.should.equal(0.1); scope.message.should.equal('a bitcoin donation'); }); }); From 1b780d268b0ce806fa853d663cc69f5bd7744d17 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 3 Jul 2014 12:52:28 -0300 Subject: [PATCH 6/7] get URI parsing into copay core --- js/controllers/uriPayment.js | 23 +++++------------------ js/models/core/Structure.js | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/js/controllers/uriPayment.js b/js/controllers/uriPayment.js index ccc701205..9cbc383c6 100644 --- a/js/controllers/uriPayment.js +++ b/js/controllers/uriPayment.js @@ -2,25 +2,12 @@ angular.module('copayApp.controllers').controller('UriPaymentController', function($rootScope, $scope, $routeParams, $timeout, $location) { var data = decodeURIComponent($routeParams.data); - var splitDots = data.split(':'); - $scope.protocol = splitDots[0]; - data = splitDots[1]; - var splitQuestion = data.split('?'); - $scope.address = splitQuestion[0]; - var search = splitQuestion[1]; - data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}', - function(key, value) { - return key === "" ? value : decodeURIComponent(value); - }); - $scope.amount = parseFloat(data.amount); - $scope.message = data.message; + $rootScope.pendingPayment = copay.Structure.parseBitcoinURI($routeParams.data); - $rootScope.pendingPayment = { - protocol: $scope.protocol, - address: $scope.address, - amount: $scope.amount, - message: $scope.message - }; + $scope.protocol = $rootScope.pendingPayment.protocol; + $scope.address = $rootScope.pendingPayment.address; + $scope.amount = $rootScope.pendingPayment.amount; + $scope.message = $rootScope.pendingPayment.message; $timeout(function() { $location.path('signin'); diff --git a/js/models/core/Structure.js b/js/models/core/Structure.js index 88543f966..aafee4c0f 100644 --- a/js/models/core/Structure.js +++ b/js/models/core/Structure.js @@ -50,4 +50,23 @@ Structure.MAX_NON_HARDENED = MAX_NON_HARDENED; Structure.SHARED_INDEX = SHARED_INDEX; Structure.ID_INDEX = ID_INDEX; +Structure.parseBitcoinURI = function(uri) { + var ret = {}; + var data = decodeURIComponent(uri); + var splitDots = data.split(':'); + ret.protocol = splitDots[0]; + data = splitDots[1]; + var splitQuestion = data.split('?'); + ret.address = splitQuestion[0]; + var search = splitQuestion[1]; + data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}', + function(key, value) { + return key === "" ? value : decodeURIComponent(value); + }); + ret.amount = parseFloat(data.amount); + ret.message = data.message; + + return ret; +}; + module.exports = require('soop')(Structure); From 4fdc5c4b5163be4731d6852941cf2bef70046017 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 3 Jul 2014 13:13:45 -0300 Subject: [PATCH 7/7] add Structure test --- test/test.Structure.js | 43 +++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/test/test.Structure.js b/test/test.Structure.js index 04b224668..3541c0f9c 100644 --- a/test/test.Structure.js +++ b/test/test.Structure.js @@ -39,13 +39,34 @@ describe('Structure model', function() { }); [ - ['m/45\'/0/0/0', {index: 0, isChange: false}], - ['m/45\'/0/0/1', {index: 1, isChange: false}], - ['m/45\'/0/0/2', {index: 2, isChange: false}], - ['m/45\'/0/1/0', {index: 0, isChange: true}], - ['m/45\'/0/1/1', {index: 1, isChange: true}], - ['m/45\'/0/1/2', {index: 2, isChange: true}], - ['m/45\'/0/0/900', {index: 900, isChange: false}], + ['m/45\'/0/0/0', { + index: 0, + isChange: false + }], + ['m/45\'/0/0/1', { + index: 1, + isChange: false + }], + ['m/45\'/0/0/2', { + index: 2, + isChange: false + }], + ['m/45\'/0/1/0', { + index: 0, + isChange: true + }], + ['m/45\'/0/1/1', { + index: 1, + isChange: true + }], + ['m/45\'/0/1/2', { + index: 2, + isChange: true + }], + ['m/45\'/0/0/900', { + index: 900, + isChange: false + }], ].forEach(function(datum) { var path = datum[0]; var result = datum[1]; @@ -55,5 +76,13 @@ describe('Structure model', function() { i.isChange.should.equal(result.isChange); }); }); + it('should get the correct result for bitcoin uri', function() { + var uri = 'bitcoin:19mP9FKrXqL46Si58pHdhGKow88SUPy1V8%3Famount=0.1&message=a%20bitcoin%20donation'; + var result = Structure.parseBitcoinURI(uri); + result.address.should.equal('19mP9FKrXqL46Si58pHdhGKow88SUPy1V8'); + result.amount.should.equal(0.1); + result.message.should.equal('a bitcoin donation'); + result.protocol.should.equal('bitcoin'); + }); });