Merge pull request #3289 from cmgustavo/feat/touch-id-ios

Set touch id to spend funds (only iOS)
This commit is contained in:
Matias Alejo Garcia 2015-10-08 12:09:43 -03:00
commit 0cb4b6b2a3
6 changed files with 200 additions and 98 deletions

View File

@ -129,6 +129,9 @@ if [ ! -d $PROJECT ]; then
cordova plugin add org.apache.cordova.file
checkOK
cordova plugin add https://github.com/EddyVerbruggen/cordova-plugin-touch-id && cordova prepare
checkOK
fi
if $DBGJS

View File

@ -6,7 +6,7 @@
</div>
<div class="content preferences" ng-controller="preferencesController as preferences">
<div class="content preferences" ng-controller="preferencesController as preferences" ng-init="preferences.init()">
<ul class="no-bullet m0 size-14" ng-show="!index.noFocusedWallet">
<h4 class="title m0">{{index.alias}} [{{index.walletName}}] <span translate>settings</span></h4>
@ -40,13 +40,17 @@
<span translate>Request Password for Spending Funds</span>
<switch id="network-name" name="encrypt" ng-model="encrypt" class="green right"></switch>
</li>
<li class="line-b p20" ng-show="preferences.touchidAvailable">
<span translate>Request Touch ID for Spending Funds</span>
<switch id="touchid" name="touchid" ng-model="touchid" class="green right"></switch>
</li>
<li class="line-b p20" ng-show="index.isPrivKeyExternal">
<span translate>Hardware wallet</span>
<span class="right text-gray">
{{preferences.externalSource}}
<!-- (Accont {{preferences.externalAccount}}) -->
</span>
</li>
</li>
<li class="line-b p20" ng-click="$root.go('backup')" ng-hide="index.isPrivKeyExternal">
<i class="icon-arrow-right3 size-24 right text-gray"></i>
<span class="text-warning right" ng-show="index.needsBackup">

View File

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettext, amMoment, nodeWebkit, addonManager, feeService, isChromeApp, bwsError, txFormatService, uxLanguage, $state, glideraService, isMobile) {
angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, nodeWebkit, addonManager, feeService, isChromeApp, bwsError, txFormatService, uxLanguage, $state, glideraService, isMobile) {
var self = this;
self.isCordova = isCordova;
self.isChromeApp = isChromeApp;
@ -1152,6 +1152,20 @@ angular.module('copayApp.controllers').controller('indexController', function($r
self.setTab(tab, reset);
});
$rootScope.$on('Local/RequestTouchid', function(event, cb) {
window.plugins.touchid.verifyFingerprint(
gettextCatalog.getString('Scan your fingerprint please'),
function(msg) {
// OK
return cb();
},
function(msg) {
// ERROR
return cb(gettext('Invalid Touch ID'));
}
);
});
$rootScope.$on('Local/ShowAlert', function(event, msg, cb) {
self.showErrorPopup(msg, cb);
});

View File

@ -2,24 +2,34 @@
angular.module('copayApp.controllers').controller('preferencesController',
function($scope, $rootScope, $filter, $timeout, $modal, $log, lodash, configService, profileService, uxLanguage) {
var config = configService.getSync();
this.unitName = config.wallet.settings.unitName;
this.bwsurl = config.bws.url;
this.currentLanguageName = uxLanguage.getCurrentLanguageName();
this.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
};
$scope.spendUnconfirmed = config.wallet.spendUnconfirmed;
$scope.glideraEnabled = config.glidera.enabled;
$scope.glideraTestnet = config.glidera.testnet;
var fc = profileService.focusedClient;
if (fc) {
$scope.encrypt = fc.hasPrivKeyEncrypted();
this.externalSource = fc.getPrivKeyExternalSourceName() == 'ledger' ? "Ledger" : null;
// TODO externalAccount
//this.externalIndex = fc.getExternalIndex();
}
this.init = function() {
var config = configService.getSync();
this.unitName = config.wallet.settings.unitName;
this.bwsurl = config.bws.url;
this.currentLanguageName = uxLanguage.getCurrentLanguageName();
this.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
};
$scope.spendUnconfirmed = config.wallet.spendUnconfirmed;
$scope.glideraEnabled = config.glidera.enabled;
$scope.glideraTestnet = config.glidera.testnet;
var fc = profileService.focusedClient;
if (fc) {
$scope.encrypt = fc.hasPrivKeyEncrypted();
this.externalSource = fc.getPrivKeyExternalSourceName() == 'ledger' ? "Ledger" : null;
// TODO externalAccount
//this.externalIndex = fc.getExternalIndex();
}
if (window.touchidAvailable) {
var walletId = fc.credentials.walletId;
this.touchidAvailable = true;
config.touchIdFor = config.touchIdFor || {};
$scope.touchid = config.touchIdFor[walletId];
}
};
var unwatchSpendUnconfirmed = $scope.$watch('spendUnconfirmed', function(newVal, oldVal) {
if (newVal == oldVal) return;
@ -94,10 +104,43 @@ angular.module('copayApp.controllers').controller('preferencesController',
});
});
var unwatchRequestTouchid = $scope.$watch('touchid', function(newVal, oldVal) {
if (newVal == oldVal || $scope.touchidError) {
$scope.touchidError = false;
return;
}
var walletId = profileService.focusedClient.credentials.walletId;
var opts = {
touchIdFor: {}
};
opts.touchIdFor[walletId] = newVal;
$rootScope.$emit('Local/RequestTouchid', function(err) {
if (err) {
$log.debug(err);
$timeout(function() {
$scope.touchidError = true;
$scope.touchid = oldVal;
}, 100);
}
else {
configService.set(opts, function(err) {
if (err) {
$log.debug(err);
$scope.touchidError = true;
$scope.touchid = oldVal;
}
});
}
});
});
$scope.$on('$destroy', function() {
unwatch();
unwatchSpendUnconfirmed();
unwatchGlideraEnabled();
unwatchGlideraTestnet();
unwatchRequestTouchid();
});
});

View File

@ -5,17 +5,19 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
var self = this;
$rootScope.hideMenuBar = false;
$rootScope.wpInputFocused = false;
$scope.currentSpendUnconfirmed = configService.getSync().wallet.spendUnconfirmed;
var config = configService.getSync();
var configWallet = config.wallet;
$scope.currentSpendUnconfirmed = configWallet.spendUnconfirmed;
// INIT
var config = configService.getSync().wallet.settings;
this.unitToSatoshi = config.unitToSatoshi;
var walletSettings = configWallet.settings;
this.unitToSatoshi = walletSettings.unitToSatoshi;
this.satToUnit = 1 / this.unitToSatoshi;
this.unitName = config.unitName;
this.alternativeIsoCode = config.alternativeIsoCode;
this.alternativeName = config.alternativeName;
this.unitName = walletSettings.unitName;
this.alternativeIsoCode = walletSettings.alternativeIsoCode;
this.alternativeName = walletSettings.alternativeName;
this.alternativeAmount = 0;
this.unitDecimals = config.unitDecimals;
this.unitDecimals = walletSettings.unitDecimals;
this.isCordova = isCordova;
this.addresses = [];
this.isMobile = isMobile.any();
@ -85,6 +87,16 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
$rootScope.hideMenuBar = false;
});
var requestTouchid = function(cb) {
var fc = profileService.focusedClient;
config.touchIdFor = config.touchIdFor || {};
if (window.touchidAvailable && config.touchIdFor[fc.credentials.walletId]) {
$rootScope.$emit('Local/RequestTouchid', cb);
} else {
return cb();
}
};
rateService.whenAvailable(function() {
self.isRateAvailable = true;
$rootScope.$digest();
@ -249,7 +261,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
};
$scope.sign = function(txp) {
var fc = profileService.focusedClient;
var fc = profileService.focusedClient;
if (!fc.canSign() && !fc.isPrivKeyExternal())
return;
@ -264,45 +276,56 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
});
return;
};
self._setOngoingForSigning();
$scope.loading = true;
$scope.error = null;
$scope.error = null;
$timeout(function() {
profileService.signTxProposal(txp, function(err, txpsi) {
self.setOngoingProcess();
requestTouchid(function(err) {
if (err) {
$scope.$emit('UpdateTx');
self.setOngoingProcess();
$scope.loading = false;
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not accept payment'));
profileService.lockFC();
$scope.error = err;
$scope.$digest();
} else {
//if txp has required signatures then broadcast it
var txpHasRequiredSignatures = txpsi.status == 'accepted';
if (txpHasRequiredSignatures) {
self.setOngoingProcess(gettext('Broadcasting transaction'));
$scope.loading = true;
fc.broadcastTxProposal(txpsi, function(err, txpsb, memo) {
self.setOngoingProcess();
$scope.loading = false;
if (err) {
$scope.$emit('UpdateTx');
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment'));
$scope.$digest();
} else {
$log.debug('Transaction signed and broadcasted')
if (memo)
$log.info(memo);
refreshUntilItChanges = true;
$modalInstance.close(txpsb);
}
});
} else {
$scope.loading = false;
$modalInstance.close(txpsi);
}
return;
}
profileService.signTxProposal(txp, function(err, txpsi) {
self.setOngoingProcess();
if (err) {
$scope.$emit('UpdateTx');
$scope.loading = false;
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not accept payment'));
$scope.$digest();
} else {
//if txp has required signatures then broadcast it
var txpHasRequiredSignatures = txpsi.status == 'accepted';
if (txpHasRequiredSignatures) {
self.setOngoingProcess(gettext('Broadcasting transaction'));
$scope.loading = true;
fc.broadcastTxProposal(txpsi, function(err, txpsb, memo) {
self.setOngoingProcess();
$scope.loading = false;
if (err) {
$scope.$emit('UpdateTx');
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment'));
$scope.$digest();
} else {
$log.debug('Transaction signed and broadcasted')
if (memo)
$log.info(memo);
refreshUntilItChanges = true;
$modalInstance.close(txpsb);
}
});
} else {
$scope.loading = false;
$modalInstance.close(txpsi);
}
}
});
});
}, 100);
};
@ -787,44 +810,56 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
} else {
feeService.getCurrentFeeValue(self.currentSendFeeLevel, cb);
}
};
};
getFee(function(err, feePerKb) {
if (err) $log.debug(err);
fc.sendTxProposal({
toAddress: address,
amount: amount,
message: comment,
payProUrl: paypro ? paypro.url : null,
feePerKb: feePerKb,
excludeUnconfirmedUtxos: $scope.currentSpendUnconfirmed ? false : true
}, function(err, txp) {
if (err) {
self.setOngoingProcess();
profileService.lockFC();
return self.setSendError(err);
}
requestTouchid(function(err) {
if (err) {
profileService.lockFC();
self.setOngoingProcess();
self.error = err;
$timeout(function() {
$scope.$digest();
}, 1);
return;
}
if (!fc.canSign() && !fc.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key')
self.setOngoingProcess();
self.resetForm();
txStatus.notify(txp, function() {
return $scope.$emit('Local/TxProposalAction');
});
return;
}
self.signAndBroadcast(txp, function(err) {
self.setOngoingProcess();
self.resetForm();
getFee(function(err, feePerKb) {
if (err) $log.debug(err);
fc.sendTxProposal({
toAddress: address,
amount: amount,
message: comment,
payProUrl: paypro ? paypro.url : null,
feePerKb: feePerKb,
excludeUnconfirmedUtxos: $scope.currentSpendUnconfirmed ? false : true
}, function(err, txp) {
if (err) {
self.error = err.message ? err.message : gettext('The payment was created but could not be completed. Please try again from home screen');
$scope.$emit('Local/TxProposalAction');
$timeout(function() {
$scope.$digest();
}, 1);
self.setOngoingProcess();
profileService.lockFC();
return self.setSendError(err);
}
if (!fc.canSign() && !fc.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key')
self.setOngoingProcess();
self.resetForm();
txStatus.notify(txp, function() {
return $scope.$emit('Local/TxProposalAction');
});
return;
}
self.signAndBroadcast(txp, function(err) {
self.setOngoingProcess();
self.resetForm();
if (err) {
self.error = err.message ? err.message : gettext('The payment was created but could not be completed. Please try again from home screen');
$scope.$emit('Local/TxProposalAction');
$timeout(function() {
$scope.$digest();
}, 1);
}
});
});
});
});
@ -1122,7 +1157,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
var fc = profileService.focusedClient;
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.btx = btx;
$scope.settings = config;
$scope.settings = walletSettings;
$scope.color = fc.backgroundColor;
$scope.copayerId = fc.credentials.copayerId;
$scope.isShared = fc.credentials.n > 1;

View File

@ -57,8 +57,6 @@ angular.element(document).ready(function() {
window.location = '#/preferences';
}, false);
setTimeout(function() {
navigator.splashscreen.hide();
}, 2000);
@ -67,6 +65,11 @@ angular.element(document).ready(function() {
window.plugins.webintent.onNewIntent(handleBitcoinURI);
window.handleOpenURL = handleBitcoinURI;
window.plugins.touchid.isAvailable(
function(msg) { window.touchidAvailable = true; }, // success handler: TouchID available
function(msg) { window.touchidAvailable = false; } // error handler: no TouchID available
);
startAngular();
}, false);
} else {