Merge pull request #1740 from cmgustavo/bug/tx-proposals

Refresh list of pending transactions proposals after any events
This commit is contained in:
Esteban Ordano 2014-11-07 15:16:36 -03:00
commit d475ea4f16
7 changed files with 113 additions and 130 deletions

View File

@ -16,24 +16,10 @@ angular.module('copayApp.controllers').controller('HistoryController',
$scope.blockchain_txs = [];
$scope.alternativeCurrency = [];
var satToUnit = 1 / w.settings.unitToSatoshi;
$scope.update = function() {
$scope.loading = true;
var from = ($scope.txpCurrentPage - 1) * $scope.txpItemsPerPage;
var opts = {
pending: false,
skip: [from, from + $scope.txpItemsPerPage]
};
controllerUtils.updateTxs(opts);
setTimeout(function() {
$rootScope.$digest();
}, 0);
$scope.getTransactions();
};
$scope.show = function() {
$scope.loading = true;
setTimeout(function() {
@ -61,9 +47,6 @@ angular.module('copayApp.controllers').controller('HistoryController',
_.each(res, function(r) {
r.ts = r.minedTs || r.sentTs;
if (r.action === 'sent' && r.peerActions) {
r.actionList = controllerUtils.getActionList(r.peerActions);
}
});
$scope.blockchain_txs = w.cached_txs = res;
$scope.loading = false;
@ -76,13 +59,11 @@ angular.module('copayApp.controllers').controller('HistoryController',
$scope.hasAction = function(actions, action) {
return actions.hasOwnProperty('create');
}
};
$scope.getShortNetworkName = function() {
var w = $rootScope.wallet;
return w.getNetworkName().substring(0, 4);
};
// Autoload transactions
$scope.getTransactions();
});

View File

@ -72,11 +72,8 @@ angular.module('copayApp.controllers').controller('SendController',
});
$scope.loadTxs = function() {
var opts = {
pending: true,
skip: null
};
controllerUtils.updateTxs(opts);
controllerUtils.updateTxs();
setTimeout(function() {
$scope.loading = false;
$rootScope.$digest();

View File

@ -72,10 +72,6 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
if (controllerUtils.isFocusedWallet(wid)) return;
var w = $rootScope.iden.getWalletById(wid);
$scope.wallets.push(w);
controllerUtils.updateTxs({
wallet: w,
pending: true
});
controllerUtils.updateBalance(w, function() {
$rootScope.$digest();
})

View File

@ -361,7 +361,7 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
} else {
ev = {
type: 'corrupt',
cId: senderId,
cId: senderId
};
}
if (ev)
@ -513,7 +513,7 @@ Wallet.prototype._onReject = function(senderId, data) {
this.emitAndKeepAlive('txProposalEvent', {
type: 'rejected',
cId: senderId,
txId: data.ntxid,
txId: data.ntxid
});
};
@ -536,7 +536,7 @@ Wallet.prototype._onSeen = function(senderId, data) {
this.emitAndKeepAlive('txProposalEvent', {
type: 'seen',
cId: senderId,
txId: data.ntxid,
txId: data.ntxid
});
};
@ -1281,6 +1281,64 @@ Wallet.prototype.getTxProposals = function() {
return ret;
};
/**
* @desc get list of actions (see {@link getPendingTxProposals})
*/
Wallet.prototype._getActionList = function(actions) {
if (!actions) return;
var peers = Object.keys(actions).map(function(i) {
return {
cId: i,
actions: actions[i]
}
});
return peers.sort(function(a, b) {
return !!b.actions.create - !!a.actions.create;
});
};
/**
* @desc Retrieve Pendings Transaction proposals (see {@link TxProposals})
* @return {Object[]} each object returned represents a transaction proposal
*/
Wallet.prototype.getPendingTxProposals = function() {
var that = this;
var ret = [];
ret.txs = [];
var pendingForUs = 0;
var txps = this.getTxProposals();
var satToUnit = 1 / this.settings.unitToSatoshi;
_.find(txps, function(txp) {
if (txp.isPending) {
pendingForUs++;
var tx = txp.builder.build();
var outs = [];
tx.outs.forEach(function(o) {
var addr = bitcore.Address.fromScriptPubKey(o.getScript(), that.getNetworkName())[0].toString();
if (!that.addressIsOwn(addr, {
excludeMain: true
})) {
outs.push({
address: addr,
value: bitcore.util.valueToBigInt(o.getValue()) * satToUnit,
});
}
});
// extra fields
txp.outs = outs;
txp.fee = txp.builder.feeSat * satToUnit;
txp.missingSignatures = tx.countInputMissingSignatures(0);
txp.actionList = that._getActionList(txp.peerActions);
ret.txs.push(txp);
}
});
ret.pendingForUs = pendingForUs;
return ret;
};
/**
* @desc Removes old transactions
* @param {boolean} deleteAll - if true, remove all the transactions
@ -2882,6 +2940,13 @@ Wallet.prototype.getTransactionHistory = function(cb) {
tx.merchant = proposal.merchant;
tx.peerActions = proposal.peerActions;
tx.finallyRejected = proposal.finallyRejected;
tx.merchant = proposal.merchant;
tx.peerActions = proposal.peerActions;
tx.finallyRejected = proposal.finallyRejected;
if (tx.peerActions) {
tx.actionList = self._getActionList(tx.peerActions);
}
}
};

View File

@ -63,13 +63,10 @@ angular.module('copayApp.services')
root.updateTxsAndBalance = _.debounce(function(w) {
root.updateTxs({
wallet: w,
pending: true,
});
root.updateTxs();
root.updateBalance(w, function() {
$rootScope.$digest();
})
});
}, 3000);
root.installWalletHandlers = function($scope, w) {
@ -139,19 +136,29 @@ angular.module('copayApp.services')
w.on('txProposalEvent', function(e) {
root.updateTxsAndBalance(w);
// TODO: add wallet name notification
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
var name = w.getName();
switch (e.type) {
case 'new':
notification.info('['+ name +'] New Transaction',
$filter('translate')('You received a transaction proposal from') + ' ' + user);
break;
case 'signed':
notification.info('Transaction Update', $filter('translate')('A transaction was signed by') + ' ' + user);
notification.info('['+ name +'] Transaction Signed',
$filter('translate')('A transaction was signed by') + ' ' + user);
break;
case 'rejected':
notification.info('Transaction Update', $filter('translate')('A transaction was rejected by') + ' ' + user);
notification.info('['+ name +'] Transaction Rejected',
$filter('translate')('A transaction was rejected by') + ' ' + user);
break;
case 'corrupt':
notification.error('Transaction Error', $filter('translate')('Received corrupt transaction from') + ' ' + user);
notification.error('['+ name +'] Transaction Error',
$filter('translate')('Received corrupt transaction from') + ' ' + user);
break;
}
$rootScope.$digest();
});
w.on('addressBookUpdated', function(dontDigest) {
if (root.isFocusedWallet(wid)) {
@ -210,6 +217,7 @@ angular.module('copayApp.services')
$rootScope.wallet = w;
w.updateFocusedTimestamp(Date.now());
root.redirIfLogged();
root.updateTxs();
root.updateBalance(w, function() {
$rootScope.$digest();
})
@ -323,75 +331,26 @@ angular.module('copayApp.services')
});
};
root.updateTxs = function(opts) {
function computeAlternativeAmount(w, tx, cb) {
rateService.whenAvailable(function() {
_.each(tx.outs, function(out) {
var valueSat = out.value * w.settings.unitToSatoshi;
out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
out.alternativeIsoCode = w.settings.alternativeIsoCode;
});
if (cb) return cb();
root.computeAlternativeAmount = function(w, tx, cb) {
rateService.whenAvailable(function() {
_.each(tx.outs, function(out) {
var valueSat = out.value * w.settings.unitToSatoshi;
out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
out.alternativeIsoCode = w.settings.alternativeIsoCode;
});
};
var w = opts.wallet || $rootScope.wallet;
if (!w) return;
opts = opts || $rootScope.txsOpts || {};
var satToUnit = 1 / w.settings.unitToSatoshi;
var myCopayerId = w.getMyCopayerId();
var pendingForUs = 0;
var inT = w.getTxProposals().sort(function(t1, t2) {
return t2.createdTs - t1.createdTs
if (cb) return cb(tx);
});
var txs = [];
};
inT.forEach(function(i, index) {
if (opts.skip && (index < opts.skip[0] || index >= opts.skip[1])) {
return txs.push(null);
}
if (i.isPending && myCopayerId != i.creator && !i.rejectedByUs && !i.signedByUs) {
pendingForUs++;
}
if (!!opts.pending == !!i.isPending) {
var tx = i.builder.build();
var outs = [];
tx.outs.forEach(function(o) {
var addr = bitcore.Address.fromScriptPubKey(o.getScript(), w.getNetworkName())[0].toString();
if (!w.addressIsOwn(addr, {
excludeMain: true
})) {
outs.push({
address: addr,
value: bitcore.util.valueToBigInt(o.getValue()) * satToUnit,
});
}
});
// extra fields
i.outs = outs;
i.fee = i.builder.feeSat * satToUnit;
i.missingSignatures = tx.countInputMissingSignatures(0);
i.actionList = getActionList(i.peerActions);
if (i.isPending) {
computeAlternativeAmount(w, i);
}
txs.push(i);
}
});
// Disabling this as discrepancies in local time on copayer machines is causing
// valid TXPs to get removed
//w.removeTxWithSpentInputs();
$rootScope.txs = txs;
$rootScope.txsOpts = opts;
if ($rootScope.pendingTxCount < pendingForUs) {
$rootScope.txAlertCount = pendingForUs;
root.updateTxs = function() {
var w = $rootScope.wallet;
if (!w) return root.onErrorDigest();
var res = w.getPendingTxProposals();
$rootScope.txps = res.txs;
if ($rootScope.pendingTxCount < res.pendingForUs) {
$rootScope.txAlertCount = res.pendingForUs;
}
$rootScope.pendingTxCount = pendingForUs;
$rootScope.pendingTxCount = res.pendingForUs;
};
root.deleteWallet = function($scope, w) {
@ -404,24 +363,5 @@ angular.module('copayApp.services')
});
};
root.getActionList = function(actions) {
return getActionList(actions);
};
function getActionList(actions) {
var peers = Object.keys(actions).map(function(i) {
return {
cId: i,
actions: actions[i]
}
});
return peers.sort(function(a, b) {
return !!b.actions.create - !!a.actions.create;
});
}
return root;
});

View File

@ -43,6 +43,7 @@ describe("Unit: Controllers", function() {
scope = $rootScope.$new();
$rootScope.iden = sinon.stub();
$rootScope.safeUnspentCount = 1;
$rootScope.pendingTxCount = 0;
var w = {};
w.isReady = sinon.stub().returns(true);
@ -72,7 +73,10 @@ describe("Unit: Controllers", function() {
w.sendTx = sinon.stub().yields(null);
w.requiresMultipleSignatures = sinon.stub().returns(true);
w.getTxProposals = sinon.stub().returns([1, 2, 3]);
w.getPendingTxProposals = sinon.stub().returns({
txs : [{ isPending : true }],
pendingForUs: 1
});
$rootScope.wallet = w;
}));

View File

@ -1,15 +1,15 @@
<div class="send" data-ng-controller="SendController" data-ng-init="loadTxs()">
<div class="send" ng-controller="SendController" ng-init="loadTxs()">
<div ng-show='$root.wallet.isReady()'>
<div class="row" ng-show="txs.length != 0">
<div class="row" ng-show="$root.txps.length != 0">
<div class="large-12 columns">
<h2 translate>Pending Transactions Proposals</h2>
<div class="last-transactions"
ng-repeat="tx in txs | paged"
ng-repeat="tx in $root.txps | paged"
ng-include="'views/includes/transaction.html'"></div>
</div>
</div>
<div ng-show="txs.length != 0" class="line-dashed-h m20b"></div>
<div ng-show="$root.txps.length != 0" class="line-dashed-h m20b"></div>
<h1 class="hide-for-large-up">{{$root.title}}</h1>
<div class="row">