From d5d3f9ee280a2513bb394175ef374a59c84f9a2e Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 25 Aug 2016 11:18:10 -0300 Subject: [PATCH] . --- {src/js/controllers => old}/tab-scan.js | 5 +- public/views/modals/addressbook.html | 2 +- public/views/modals/scanner.html | 4 +- public/views/tabs.html | 10 ++- src/js/controllers/join.js | 16 +++- src/js/controllers/modals/scanner.js | 102 ++++++++++++++++++++++++ src/js/controllers/tab-send.js | 14 +--- src/js/controllers/tabsController.js | 21 +++++ src/js/directives/qrScanner.js | 67 +++++++++++++++- src/js/routes.js | 10 +-- src/js/services/incomingData.js | 87 ++++++++++++++++++++ src/js/services/openURL.js | 48 ++--------- src/js/services/walletService.js | 43 ---------- 13 files changed, 310 insertions(+), 119 deletions(-) rename {src/js/controllers => old}/tab-scan.js (98%) create mode 100644 src/js/controllers/modals/scanner.js create mode 100644 src/js/controllers/tabsController.js create mode 100644 src/js/services/incomingData.js diff --git a/src/js/controllers/tab-scan.js b/old/tab-scan.js similarity index 98% rename from src/js/controllers/tab-scan.js rename to old/tab-scan.js index 00f6045e7..423fc30d9 100644 --- a/src/js/controllers/tab-scan.js +++ b/old/tab-scan.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabScanController', function($scope, $timeout, $ionicModal, $log, $ionicPopup, configService, gettextCatalog, platformInfo, bitcore, lodash, $state, walletService) { +angular.module('copayApp.controllers').controller('tabScanController', function($scope, $timeout, $ionicModal, $log, $ionicPopup, configService, gettextCatalog, platformInfo, bitcore, lodash, $state, incomingData) { var isCordova = platformInfo.isCordova; var isWP = platformInfo.isWP; @@ -22,7 +22,7 @@ angular.module('copayApp.controllers').controller('tabScanController', function( var _dataScanned = function(data) { $log.debug('Scanned:' + data); - if (!walletService.redirFromUri(data)) { + if (!incomingData.redir(data)) { $log.warn('Fail to process scanned data'); _showAlert('Bad bitcoin address', 'Could not recognize the bitcoin address', function(res) { $scope.init(); @@ -84,7 +84,6 @@ angular.module('copayApp.controllers').controller('tabScanController', function( } }; - // QR code Scanner var video; var canvas; diff --git a/public/views/modals/addressbook.html b/public/views/modals/addressbook.html index 0b2662b80..fd800ca42 100644 --- a/public/views/modals/addressbook.html +++ b/public/views/modals/addressbook.html @@ -63,7 +63,7 @@ ng-show="addressbookForm.address.$invalid && addressbookEntry.address">
- + +

QR-Scanner

- + diff --git a/public/views/tabs.html b/public/views/tabs.html index bde3d6e96..d6a5c5ff9 100644 --- a/public/views/tabs.html +++ b/public/views/tabs.html @@ -3,7 +3,7 @@ Create tabs with an icon and label, using the tabs-positive style. Each tab's child directive will have its own navigation history that also transitions its views in and out. --> - + @@ -14,11 +14,13 @@ navigation history that also transitions its views in and out. - - - + + + + + diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js index 3c1650d83..4afdbd963 100644 --- a/src/js/controllers/join.js +++ b/src/js/controllers/join.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('joinController', - function($scope, $rootScope, $timeout, $state, profileService, configService, storageService, applicationService, gettext, lodash, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, $log) { + function($scope, $rootScope, $timeout, $state, profileService, configService, storageService, applicationService, gettext, lodash, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, $log, $stateParams) { var isChromeApp = platformInfo.isChromeApp; var isDevel = platformInfo.isDevel; @@ -12,12 +12,22 @@ angular.module('copayApp.controllers').controller('joinController', $scope.derivationPath = derivationPathHelper.default; $scope.account = 1; + this.onQrCodeScanned = function(data) { +console.log('[join.js.16:data:]',data); //TODO $scope.secret = data; - $scope.joinForm.secret.$setViewValue(data); - $scope.joinForm.secret.$render(); + if ($scope.joinForm) { + $scope.joinForm.secret.$setViewValue(data); + $scope.joinForm.secret.$render(); + } }; + if ($stateParams.url) { + var data = $stateParams.url; + data = data.replace('copay:', ''); + this.onQrCodeScanned(data); + } + var updateSeedSourceSelect = function() { self.seedOptions = [{ id: 'new', diff --git a/src/js/controllers/modals/scanner.js b/src/js/controllers/modals/scanner.js new file mode 100644 index 000000000..c1bccb708 --- /dev/null +++ b/src/js/controllers/modals/scanner.js @@ -0,0 +1,102 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('scannerController', function($scope, $timeout) { + + // QR code Scanner + var video; + var canvas; + var $video; + var context; + var localMediaStream; + var prevResult; + var scanTimer; + + var _scan = function(evt) { + if (localMediaStream) { + context.drawImage(video, 0, 0, 300, 225); + try { + qrcode.decode(); + } catch (e) { + //qrcodeError(e); + } + } + scanTimer = $timeout(_scan, 800); + }; + + var _scanStop = function() { + $timeout.cancel(scanTimer); + if (localMediaStream && localMediaStream.active) { + var localMediaStreamTrack = localMediaStream.getTracks(); + for (var i = 0; i < localMediaStreamTrack.length; i++) { + localMediaStreamTrack[i].stop(); + } + } else { + try { + localMediaStream.stop(); + } catch (e) { + // Older Chromium not support the STOP function + }; + } + localMediaStream = null; + video.src = ''; + }; + + qrcode.callback = function(data) { + if (prevResult != data) { + prevResult = data; + return; + } + _scanStop(); + $scope.cancel(); + $scope.onScan({ + data: data + }); + }; + + var _successCallback = function(stream) { + video.src = (window.URL && window.URL.createObjectURL(stream)) || stream; + localMediaStream = stream; + video.play(); + $timeout(_scan, 1000); + }; + + var _videoError = function(err) { + $scope.cancel(); + }; + + var setScanner = function() { + navigator.getUserMedia = navigator.getUserMedia || + navigator.webkitGetUserMedia || navigator.mozGetUserMedia || + navigator.msGetUserMedia; + window.URL = window.URL || window.webkitURL || + window.mozURL || window.msURL; + }; + + $scope.init = function() { + setScanner(); + $timeout(function() { + if ($scope.beforeScan) { + $scope.beforeScan(); + } + canvas = document.getElementById('qr-canvas'); + context = canvas.getContext('2d'); + + video = document.getElementById('qrcode-scanner-video'); + $video = angular.element(video); + canvas.width = 300; + canvas.height = 225; + context.clearRect(0, 0, 300, 225); + + navigator.getUserMedia({ + video: true + }, _successCallback, _videoError); + }, 500); + }; + + $scope.cancel = function() { + _scanStop(); + $scope.scannerModal.hide(); + $scope.scannerModal.remove(); + }; + +}); diff --git a/src/js/controllers/tab-send.js b/src/js/controllers/tab-send.js index 904b27c46..d73272e5a 100644 --- a/src/js/controllers/tab-send.js +++ b/src/js/controllers/tab-send.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabSendController', function($scope, $ionicModal, $log, $timeout, addressbookService, profileService, lodash, $state, walletService, bitcore ) { +angular.module('copayApp.controllers').controller('tabSendController', function($scope, $ionicModal, $log, $timeout, addressbookService, profileService, lodash, $state, walletService, incomingData ) { var originalList; @@ -55,16 +55,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function( $scope.findContact = function(search, opts) { opts = opts || {}; - if (search.indexOf('bitcoin:') === 0) { - if (!walletService.redirFromUri(search)) { - $log.error(err); - } - } else if (/^https?:\/\//.test(search)) { - return $state.go('send.confirm', {paypro: search}) - } else if (bitcore.Address.isValid(search, 'livenet')) { - return $state.go('send.amount', {toAddress: search}) - } else if (bitcore.Address.isValid(search, 'testnet')) { - return $state.go('send.amount', {toAddress: search}) + if (incomingData.redir(search)) { + return; } if (!search || search.length < 2) { diff --git a/src/js/controllers/tabsController.js b/src/js/controllers/tabsController.js new file mode 100644 index 000000000..b9114db36 --- /dev/null +++ b/src/js/controllers/tabsController.js @@ -0,0 +1,21 @@ + +'use strict'; + +angular.module('copayApp.controllers').controller('tabsController', function($log, $scope, $ionicModal, incomingData) { + + $scope.onScan = function(data) { +console.log('[tabsController.js.6:data:]',data); //TODO + if (!incomingData.redir(data)) { + $ionicPopup.alert({ + title: 'Invalid data', + }); + } + } + + $scope.setScanFn = function(scanFn) { + $scope.scan = function() { + $log.debug('Scanning...'); + scanFn(); + }; + }; + }); diff --git a/src/js/directives/qrScanner.js b/src/js/directives/qrScanner.js index b29290209..61fcde64c 100644 --- a/src/js/directives/qrScanner.js +++ b/src/js/directives/qrScanner.js @@ -1,16 +1,77 @@ 'use strict'; angular.module('copayApp.directives') - .directive('qrScanner', function() { + .directive('qrScanner', function($rootScope, $timeout, $ionicModal, gettextCatalog, platformInfo) { + + var isCordova = platformInfo.isCordova; + var isWP = platformInfo.isWP; + var isIOS = platformInfo.isIOS; + + var controller = function($scope) { + + var onSuccess = function(result) { + $timeout(function() { + window.plugins.spinnerDialog.hide(); + }, 100); + if (isWP && result.cancelled) return; + + $timeout(function() { + var data = isIOS ? result : result.text; + $scope.onScan({ + data: data + }); + }, 1000); + }; + + var onError = function(error) { + $timeout(function() { + window.plugins.spinnerDialog.hide(); + }, 100); + }; + + $scope.cordovaOpenScanner = function() { + window.plugins.spinnerDialog.show(null, gettextCatalog.getString('Preparing camera...'), true); + $timeout(function() { + if (isIOS) { + cloudSky.zBar.scan({}, onSuccess, onError); + } else { + cordova.plugins.barcodeScanner.scan(onSuccess, onError); + } + if ($scope.beforeScan) { + $scope.beforeScan(); + } + }, 100); + }; + + $scope.modalOpenScanner = function() { + $ionicModal.fromTemplateUrl('views/modals/scanner.html', { + scope: $scope, + animation: 'slide-in-up' + }).then(function(modal) { + $scope.scannerModal = modal; + $scope.scannerModal.show(); + }); + }; + + $scope.openScanner = function() { + if (isCordova) { + $scope.cordovaOpenScanner(); + } else { + $scope.modalOpenScanner(); + } + }; + $scope.setFn({theScanFn: $scope.openScanner}); + }; return { restrict: 'E', scope: { onScan: "&", + setFn: "&", beforeScan: "&" }, - controller: 'tabScanController', + controller: controller, replace: true, - template: '' + template: '' } }); diff --git a/src/js/routes.js b/src/js/routes.js index 2daf19559..04b4d043a 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -313,14 +313,6 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } } }) - .state('tabs.scan', { - url: '/scan', - views: { - 'tab-scan': { - templateUrl: 'views/tab-scan.html', - } - } - }) .state('tabs.send', { url: '/send', views: { @@ -393,7 +385,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('add.join', { - url: '/join', + url: '/join/:url', views: { 'add': { templateUrl: 'views/join.html' diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js new file mode 100644 index 000000000..6daa50306 --- /dev/null +++ b/src/js/services/incomingData.js @@ -0,0 +1,87 @@ +'use strict'; + +angular.module('copayApp.services').factory('incomingData', function($log, $ionicModal, $state, bitcore) { + + var root = {}; + + root.redir = function(data) { + $log.debug('Processing incoming data:' +data); + + function sanitizeUri(data) { + // Fixes when a region uses comma to separate decimals + var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i; + var match = regex.exec(data); + if (!match || match.length === 0) { + return data; + } + var value = match[0].replace(',', '.'); + var newUri = data.replace(regex, value); + + // mobile devices, uris like copay://glidera + newUri.replace('://', ':'); + + return newUri; + }; + + // data extensions for Payment Protocol with non-backwards-compatible request + if ((/^bitcoin:\?r=[\w+]/).exec(data)) { + data = decodeURIComponent(data.replace('bitcoin:?r=', '')); + $state.go('send.confirm', {paypro: data}) + } + + + data = sanitizeUri(data); + + // BIP21 + if (bitcore.URI.isValid(data)) { + var parsed = new bitcore.URI(data); + + var addr = parsed.address ? parsed.address.toString() : ''; + var message = parsed.message; + + var amount = parsed.amount ? parsed.amount : ''; + + if (parsed.r) { + $state.go('send.confirm', {paypro: parsed.r}); + } else { + if (amount) { + $state.go('send.confirm', {toAmount: amount, toAddress: addr, description:message}) + } else { + $state.go('send.amount', {toAddress: addr}) + } + } + return true; + + // Plain URL + } else if (/^https?:\/\//.test(data)) { + return $state.go('send.confirm', {paypro: data}) + + // Plain Address + } else if (bitcore.Address.isValid(data, 'livenet')) { + return $state.go('send.amount', {toAddress: data}) + } else if (bitcore.Address.isValid(data, 'testnet')) { + return $state.go('send.amount', {toAddress: data}) + + + // copay: protocol + } else if (data.indexOf('copay:glidera')==0) { + return $state.go('send.uriglidera', {url: data}) + } else if (data.indexOf('copay:coinbase')==0) { + return $state.go('send.uricoinbase', {url: data}) + + // Join + } else if (data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) { + return $state.go('add.join', {url: data}) + + // Old join + } else if (data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) { + return $state.go('add.join', {url: data}) + } + + + return false; + + }; + + return root; +}); diff --git a/src/js/services/openURL.js b/src/js/services/openURL.js index 084126a57..a384214f3 100644 --- a/src/js/services/openURL.js +++ b/src/js/services/openURL.js @@ -1,37 +1,17 @@ 'use strict'; -angular.module('copayApp.services').factory('openURLService', function($rootScope, $ionicHistory, $document, $log, $state, go, platformInfo, lodash, profileService) { +angular.module('copayApp.services').factory('openURLService', function($rootScope, $ionicHistory, $document, $log, $state, go, platformInfo, lodash, profileService, incomingData) { var root = {}; - root.registeredUriHandlers = [{ - name: 'Bitcoin BIP21 URL', - startsWith: 'bitcoin:', - transitionTo: 'uripayment', - }, { - name: 'Glidera Authentication Callback', - startsWith: 'copay:glidera', - transitionTo: 'uriglidera', - }, { - name: 'Coinbase Authentication Callback', - startsWith: 'copay:coinbase', - transitionTo: 'uricoinbase', - }]; - - var handleOpenURL = function(args) { $log.info('Handling Open URL: ' + JSON.stringify(args)); - if (!profileService.isBound) { - $log.warn('Profile not bound yet. Waiting'); - - return $rootScope.$on('Local/ProfileBound', function() { - // Wait ux to settle - setTimeout(function() { - $log.warn('Profile ready, retrying...'); - handleOpenURL(args); - }, 2000); - }); - }; + profileService.whenAvailable(function() { + // Wait ux to settle + setTimeout(function() { + handleOpenURL(args); + }, 2000); + }); // Stop it from caching the first view as one to return when the app opens $ionicHistory.nextViewOptions({ @@ -55,19 +35,7 @@ angular.module('copayApp.services').factory('openURLService', function($rootScop document.addEventListener('handleopenurl', handleOpenURL, false); - var x = lodash.find(root.registeredUriHandlers, function(x) { - return url.indexOf(x.startsWith) == 0 || - url.indexOf('web+' + x.startsWith) == 0 || // web protocols - url.indexOf(x.startsWith.replace(':', '://')) == 0 // from mobile devices - ; - }); - - if (x) { - $log.debug('openURL GOT ' + x.name + ' URL'); - return $state.transitionTo(x.transitionTo, { - url: url - }); - } else { + if (!incomingData.redir(url)) { $log.warn('Unknown URL! : ' + url); } }; diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js index 8cc238a31..c3c1c52cc 100644 --- a/src/js/services/walletService.js +++ b/src/js/services/walletService.js @@ -1123,50 +1123,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim return finale; }; - // Here? - root.redirFromUri = function(uri) { - function sanitizeUri(uri) { - // Fixes when a region uses comma to separate decimals - var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i; - var match = regex.exec(uri); - if (!match || match.length === 0) { - return uri; - } - var value = match[0].replace(',', '.'); - var newUri = uri.replace(regex, value); - return newUri; - }; - - // URI extensions for Payment Protocol with non-backwards-compatible request - if ((/^bitcoin:\?r=[\w+]/).exec(uri)) { - uri = decodeURIComponent(uri.replace('bitcoin:?r=', '')); - $state.go('send.confirm', {paypro: uri}) - } else { - uri = sanitizeUri(uri); - - if (!bitcore.URI.isValid(uri)) { - return false; - } - var parsed = new bitcore.URI(uri); - - var addr = parsed.address ? parsed.address.toString() : ''; - var message = parsed.message; - - var amount = parsed.amount ? parsed.amount : ''; - - if (parsed.r) { - $state.go('send.confirm', {paypro: parsed.r}); - } else { - if (amount) { - $state.go('send.confirm', {toAmount: amount, toAddress: addr, description:message}) - } else { - $state.go('send.amount', {toAddress: addr}) - } - } - return true; - } - }; return root; });