Complete flow of confirmation popup before send a tx

This commit is contained in:
Gustavo Maximiliano Cortez 2016-01-27 16:41:12 -03:00
commit df834c50c3
12 changed files with 203 additions and 98 deletions

View file

@ -27,6 +27,7 @@
<div notifications="right top"></div>
<div ng-include="'views/includes/password.html'" ng-if="index.askPassword"></div>
<div ng-include="'views/includes/alert.html'" ng-if="index.showAlert"></div>
<div ng-include="'views/includes/confirm-tx.html'" ng-if="index.confirmTx"></div>
<div id="sectionContainer">
<div id="mainSection">
<section ui-view="main"

View file

@ -1,4 +1,4 @@
<div class="passModalMask">
<div class="modalMask">
</div>
<div class="alertModal">

View file

@ -1,42 +1,40 @@
<div class="modalMask"></div>
<div class="passModalMask">
</div>
<div class="confirmTxModal" ng-controller="confirmTxController as confirm" ng-init="tx = index.confirmTx.txp">
<div class="alertModal">
<div class="row">
<div class="columns">
<h4 class="text-center m10v size-18" translate>Confirm transaction</h4>
<ul class="no-bullet size-14 m0">
<li class="line-b p10">
<span class="text-gray" translate>Amount</span>
<span class="right">{{home.confirmTxPopup.amountStr}}
<span ng-show="txp.alternativeAmountStr" class="label gray radius">{{home.confirmTxPopup.alternativeAmountStr}}</span>
</span>
</li>
<li class="line-b p10" ng-show="txp.action != 'received'">
<span class="text-gray" translate>Fee</span>
<span class="right">{{home.confirmTxPopup.feeStr}}</span>
</li>
</ul>
</div>
<div class="confirmHead" ng-style="{'background-color':index.backgroundColor}">
<h1 class="m0 text-center text-white size-18" translate>Confirm transaction</h1>
</div>
<div class="row m20t">
<div class="small-6 columns">
<button
ng-click="home.closeConfirmTx()"
class="round small-6 columns outline dark-gray expand" translate>
Cancel
</button>
<div class="p10">
<div class="size-36">{{tx.amountStr}}</div>
<div class="size-14 text-light" ng-show="tx.alternativeAmountStr">{{tx.alternativeAmountStr}}</div>
<i class="db fi-arrow-down size-24 m10v"></i>
<div class="payment-proposal-to" ng-click="copyAddress(tx.toAddress)">
<i class="fi-bitcoin left m10l"></i>
<contact ng-if="!tx.hasMultiplesOutputs" class="dib enable_text_select ellipsis m5t m5b m15l size-14" address="{{tx.toAddress}}"></contact>
<span ng-if="tx.hasMultiplesOutputs" translate>
Multiple recipients
</span>
</div>
<div class="small-6 columns">
<button
ng-click="home.acceptConfirmTx()"
class="round expand"
ng-style="{'background-color':index.backgroundColor}" translate>
Accept
</button>
<div class="text-bold m10t size-12">
<span translate>Fee</span>: {{tx.feeStr}}
</div>
<div class="row m20t">
<div class="large-6 medium-6 small-6 columns">
<button
ng-click="confirm.close(index.confirmTx.callback)"
class="small m10b round outline dark-gray expand" translate>
Cancel
</button>
</div>
<div class="large-6 medium-6 small-6 columns">
<button
ng-click="confirm.accept(index.confirmTx.callback)"
class="small m10b round expand"
ng-style="{'background-color':index.backgroundColor}" translate>
Accept
</button>
</div>
</div>
</div>
</div>

View file

@ -1,5 +1,5 @@
<div class="passModalMask">
<div class="modalMask">
</div>
<div ng-controller="passwordController as pass" class="passModal"

View file

@ -31,17 +31,19 @@
</div>
<div class="oh">
<div class="box-notification m20t" ng-show="error">
<div class="box-notification" ng-show="error">
<span class="text-warning size-14">
{{error|translate}}
</span>
</div>
<div class="row column m20t text-center text-warning size-12" ng-if="tx.removed" translate>
The payment was removed by creator
<div class="row" ng-if="tx.removed">
<div class="column m20t text-center text-warning size-12" translate>
The payment was removed by creator
</div>
</div>
<div class="oh p20t white" ng-if="tx.pendingForUs">
<div class="row p20t white" ng-if="tx.pendingForUs">
<div class="large-6 medium-6 small-6 columns" ng-show="isShared">
<button class="button outline round dark-gray expand" ng-click="reject(tx);"
ng-disabled="loading">

View file

@ -511,8 +511,6 @@
<div class="extra-margin-bottom"></div>
</div> <!-- END Send -->
<div ng-include="'views/includes/confirm-tx.html'" ng-if="home.confirmTxPopup"></div>
<div id="{{view.id}}" class="{{view.class}} tab-view" ng-repeat="view in index.addonViews" ng-include="view.template">
</div>

View file

@ -1373,9 +1373,30 @@ input.ng-invalid-match, input.ng-invalid-match:focus {
}
}
/* Confirmation popup */
.confirmTxModal {
background: white;
border-radius: 5px;
position: absolute;
width: 90%;
left: 0;
right: 0;
margin: 15% auto;
z-index: 1100;
text-align: center;
}
.confirmHead {
padding: 10px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
/*******************/
.alertModal {
background: #FFFFFF;
box-shadow: 0px 0px 6px 0px rgba(0,0,0,0.50);
border-radius: 5px;
position: absolute;
width: 90%;
@ -1386,7 +1407,6 @@ input.ng-invalid-match, input.ng-invalid-match:focus {
.passModal {
background: #FFFFFF;
box-shadow: 0px 0px 6px 0px rgba(0,0,0,0.50);
border-radius: 5px;
position: absolute;
width: 90%;
@ -1395,12 +1415,12 @@ input.ng-invalid-match, input.ng-invalid-match:focus {
z-index: 1100;
}
.passModalMask {
.modalMask {
position: absolute;
width: 100%;
height: 100%;
z-index: 1099;
opacity:0.3;
opacity:0.8;
background: black;
}

View file

@ -0,0 +1,13 @@
'use strict';
angular.module('copayApp.controllers').controller('confirmTxController', function() {
this.close = function(cb) {
return cb();
};
this.accept = function(cb) {
return cb(true);
};
});

View file

@ -391,7 +391,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
self.setFeeAndSendMax = function(cb) {
self.feeToSendMaxStr = null;
self.availableMaxBalance = null;
self.currentFeePerKb = null;
@ -1453,6 +1452,19 @@ angular.module('copayApp.controllers').controller('indexController', function($r
self.setTab(tab, reset);
});
$rootScope.$on('Local/NeedConfirmation', function(event, txp, cb) {
self.confirmTx = {
txp : txFormatService.processTx(txp),
callback: function(accept) {
self.confirmTx = null;
return cb(accept);
}
};
$timeout(function() {
$rootScope.$apply();
});
});
$rootScope.$on('Local/NeedsPassword', function(event, isSetup, cb) {
self.askPassword = {
isSetup: isSetup,

View file

@ -771,6 +771,8 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
var currentSpendUnconfirmed = configWallet.spendUnconfirmed;
var currentFeeLevel = walletSettings.feeLevel || 'normal';
var outputs = [];
this.resetError();
if (isCordova && this.isWindowsPhoneApp) {
@ -793,14 +795,6 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
return self.setSendError(gettext(msg));
}
var getFee = function(cb) {
if (self.lockedCurrentFeePerKb) {
cb(null, self.lockedCurrentFeePerKb);
} else {
feeService.getCurrentFeeValue(currentFeeLevel, cb);
}
};
$timeout(function() {
var paypro = self._paypro;
var address, amount;
@ -808,57 +802,88 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
address = form.address.$modelValue;
amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
outputs.push({
'toAddress' : address,
'amount': amount,
'message': comment
});
txSignService.prepare(function(err) {
if (err) {
return self.setSendError(err);
}
var opts = {
toAddress: address,
amount: amount,
outputs: outputs,
message: comment,
payProUrl: paypro ? paypro.url : null,
lockedCurrentFeePerKb: self.lockedCurrentFeePerKb
};
self.setOngoingProcess(gettextCatalog.getString('Creating transaction'));
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: currentSpendUnconfirmed ? false : true
}, function(err, txp) {
if (err) {
self.setOngoingProcess();
return self.setSendError(err);
}
txSignService.createTx(opts, function(err, txp) {
self.setOngoingProcess();
if (err) {
return self.setSendError(err);
}
if (!fc.canSign() && !fc.isPrivKeyExternal()) {
self.setOngoingProcess();
$log.info('No signing proposal: No private key')
self.resetForm();
txStatus.notify(txp, function() {
return $scope.$emit('Local/TxProposalAction');
});
return;
}
txSignService.signAndBroadcast(txp, {
reporterFn: self.setOngoingProcess.bind(self)
}, function(err, txp) {
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);
} else {
go.walletHome();
txStatus.notify(txp, function() {
$scope.$emit('Local/TxProposalAction', txp.status == 'broadcasted');
});
};
if (!fc.canSign() && !fc.isPrivKeyExternal()) {
self.setOngoingProcess();
$log.info('No signing proposal: No private key');
self.resetForm();
txStatus.notify(txp, function() {
return $scope.$emit('Local/TxProposalAction');
});
});
return;
} else {
$rootScope.$emit('Local/NeedConfirmation', txp, function(accept) {
if (accept) self.acceptTx(txp);
else self.resetForm();
});
}
});
});
}, 100);
};
this.acceptTx = function(txp) {
var self = this;
this.confirmTxPopup = null;
this.setOngoingProcess(gettextCatalog.getString('Sending transaction'));
txSignService.publishTx(txp.id, function(err) {
self.setOngoingProcess();
if (err) {
$log.debug(err);
self.setSendError(err);
} else {
self.resetForm();
// self.signAndBroadcastTx(txp);
}
});
};
this.signAndBroadcastTx = function() {
var self = this;
txSignService.signAndBroadcast(txp, {
reporterFn: self.setOngoingProcess.bind(self)
}, function(err, txp) {
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);
} else {
go.walletHome();
txStatus.notify(txp, function() {
$scope.$emit('Local/TxProposalAction', txp.status == 'broadcasted');
});
};
});
};
this.setForm = function(to, amount, comment) {

View file

@ -30,6 +30,7 @@ angular.module('copayApp.services').factory('txFormatService', function(profileS
tx.hasMultiplesOutputs = true;
tx.recipientCount = outputs;
}
tx.toAddress = tx.outputs[0].toAddress;
tx.amount = lodash.reduce(tx.outputs, function(total, o) {
o.amountStr = formatAmountStr(o.amount);
o.alternativeAmountStr = formatAlternativeStr(o.amount);

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services').factory('txSignService', function($rootScope, profileService, gettextCatalog, lodash, trezor, ledger, configService, bwsError, $log) {
angular.module('copayApp.services').factory('txSignService', function($rootScope, profileService, gettextCatalog, lodash, trezor, ledger, configService, bwsError, $log, feeService) {
var root = {};
var reportSigningStatus = function(opts) {
@ -85,8 +85,43 @@ angular.module('copayApp.services').factory('txSignService', function($rootScope
};
return cb();
});
});
};
root.createTx = function(opts, cb) {
var fc = profileService.focusedClient;
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
var currentSpendUnconfirmed = configWallet.spendUnconfirmed;
var currentFeeLevel = walletSettings.feeLevel || 'normal';
var getFee = function(cb) {
if (opts.lockedCurrentFeePerKb) {
cb(null, opts.lockedCurrentFeePerKb);
} else {
feeService.getCurrentFeeValue(currentFeeLevel, cb);
}
};
getFee(function(err, feePerKb) {
if (err) $log.debug(err);
fc.createTxProposal(opts, function(err, txp) {
if (err) return cb(err);
else return cb(null, txp);
});
});
};
root.publishTx = function(txId, cb) {
var fc = profileService.focusedClient;
fc.publishTxProposal(txId, function(err) {
if (err) return cb(err);
else return cb();
});
};
var _signWithLedger = function(txp, cb) {