This commit is contained in:
Matias Alejo Garcia 2016-08-25 11:18:10 -03:00
parent 246f1927dc
commit d5d3f9ee28
No known key found for this signature in database
GPG Key ID: 02470DB551277AB3
13 changed files with 310 additions and 119 deletions

View File

@ -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;

View File

@ -63,7 +63,7 @@
ng-show="addressbookForm.address.$invalid && addressbookEntry.address"></i>
</div>
<div class="qr-scan-icon">
<qr-scanner on-scan="onQrCodeScanned(data, addressbookForm)"></qr-scanner>
<qr-scanner on-scan="onQrCodeScanned(data)"></qr-scanner>
<input type="text"
id="address"
name="address"

View File

@ -1,11 +1,11 @@
<ion-modal-view>
<ion-modal-view ng-controller="scannerController" ng-init="init()">
<ion-header-bar align-title="center" class="bar-stable">
<button ng-click="cancel()" class="button button-clear button-positive" translate>
Close
</button>
<h1 class="title ellipsis" translate>QR-Scanner</h1>
</ion-header-bar>
<ion-content ng-controller="tabScanController" ng-init="init()">
<ion-content >
<canvas id="qr-canvas" width="200" height="150"></canvas>
<video id="qrcode-scanner-video" width="300" height="225"></video>
</ion-content>

View File

@ -3,7 +3,7 @@ Create tabs with an icon and label, using the tabs-positive style.
Each tab's child <ion-nav-view> directive will have its own
navigation history that also transitions its views in and out.
-->
<ion-tabs class="tabs-icon-top tabs-color-active-positive">
<ion-tabs class="tabs-icon-top tabs-color-active-positive" ng-controller="tabsController">
<ion-tab title="Home" icon-off="ion-ios-pulse" icon-on="ion-ios-pulse-strong" href="#/tabs/home">
<ion-nav-view name="tab-home"></ion-nav-view>
@ -14,11 +14,13 @@ navigation history that also transitions its views in and out.
</ion-tab>
<ion-tab title="Scan" icon-off="ion-ios-camera-outline" icon-on="ion-ios-camera" href="#/tabs/scan">
<ion-nav-view name="tab-scan"></ion-nav-view>
</ion-tab>
<!-- this actually NEVER gets rendered -->
<qr-scanner class="qr-icon size-24" style="display:none" set-fn="setScanFn(theScanFn)" on-scan="onScan(data)"></qr-scanner>
<ion-tab title="Scan" icon-off="ion-ios-camera" ng-click="scan()" >
</ion-tab>
<ion-tab title="Send" icon-off="ion-ios-chatboxes-outline" icon-on="ion-ios-chatboxes" href="#/tabs/send">
<ion-nav-view name="tab-send"></ion-nav-view>
</ion-tab>

View File

@ -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',

View File

@ -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();
};
});

View File

@ -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) {

View File

@ -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();
};
};
});

View File

@ -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: '<a on-tap="openScanner()"><i class="icon ion-qr-scanner"></i></a>'
template: '<a ng-click="openScanner()"><i class="icon ion-qr-scanner"></i></a>'
}
});

View File

@ -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'

View File

@ -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;
});

View File

@ -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);
}
};

View File

@ -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;
});