mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #3289 from cmgustavo/feat/touch-id-ios
Set touch id to spend funds (only iOS)
This commit is contained in:
commit
0cb4b6b2a3
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue