Merge pull request #1740 from cmgustavo/bug/tx-proposals
Refresh list of pending transactions proposals after any events
This commit is contained in:
commit
d475ea4f16
7 changed files with 113 additions and 130 deletions
|
|
@ -16,24 +16,10 @@ angular.module('copayApp.controllers').controller('HistoryController',
|
||||||
$scope.blockchain_txs = [];
|
$scope.blockchain_txs = [];
|
||||||
$scope.alternativeCurrency = [];
|
$scope.alternativeCurrency = [];
|
||||||
|
|
||||||
var satToUnit = 1 / w.settings.unitToSatoshi;
|
|
||||||
|
|
||||||
|
|
||||||
$scope.update = function() {
|
$scope.update = function() {
|
||||||
$scope.loading = true;
|
$scope.getTransactions();
|
||||||
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.show = function() {
|
$scope.show = function() {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
|
@ -61,9 +47,6 @@ angular.module('copayApp.controllers').controller('HistoryController',
|
||||||
|
|
||||||
_.each(res, function(r) {
|
_.each(res, function(r) {
|
||||||
r.ts = r.minedTs || r.sentTs;
|
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.blockchain_txs = w.cached_txs = res;
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
|
|
@ -76,13 +59,11 @@ angular.module('copayApp.controllers').controller('HistoryController',
|
||||||
|
|
||||||
$scope.hasAction = function(actions, action) {
|
$scope.hasAction = function(actions, action) {
|
||||||
return actions.hasOwnProperty('create');
|
return actions.hasOwnProperty('create');
|
||||||
}
|
};
|
||||||
|
|
||||||
$scope.getShortNetworkName = function() {
|
$scope.getShortNetworkName = function() {
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
return w.getNetworkName().substring(0, 4);
|
return w.getNetworkName().substring(0, 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Autoload transactions
|
|
||||||
$scope.getTransactions();
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,8 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.loadTxs = function() {
|
$scope.loadTxs = function() {
|
||||||
var opts = {
|
controllerUtils.updateTxs();
|
||||||
pending: true,
|
|
||||||
skip: null
|
|
||||||
};
|
|
||||||
controllerUtils.updateTxs(opts);
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,6 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
|
||||||
if (controllerUtils.isFocusedWallet(wid)) return;
|
if (controllerUtils.isFocusedWallet(wid)) return;
|
||||||
var w = $rootScope.iden.getWalletById(wid);
|
var w = $rootScope.iden.getWalletById(wid);
|
||||||
$scope.wallets.push(w);
|
$scope.wallets.push(w);
|
||||||
controllerUtils.updateTxs({
|
|
||||||
wallet: w,
|
|
||||||
pending: true
|
|
||||||
});
|
|
||||||
controllerUtils.updateBalance(w, function() {
|
controllerUtils.updateBalance(w, function() {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
|
||||||
} else {
|
} else {
|
||||||
ev = {
|
ev = {
|
||||||
type: 'corrupt',
|
type: 'corrupt',
|
||||||
cId: senderId,
|
cId: senderId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (ev)
|
if (ev)
|
||||||
|
|
@ -513,7 +513,7 @@ Wallet.prototype._onReject = function(senderId, data) {
|
||||||
this.emitAndKeepAlive('txProposalEvent', {
|
this.emitAndKeepAlive('txProposalEvent', {
|
||||||
type: 'rejected',
|
type: 'rejected',
|
||||||
cId: senderId,
|
cId: senderId,
|
||||||
txId: data.ntxid,
|
txId: data.ntxid
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -536,7 +536,7 @@ Wallet.prototype._onSeen = function(senderId, data) {
|
||||||
this.emitAndKeepAlive('txProposalEvent', {
|
this.emitAndKeepAlive('txProposalEvent', {
|
||||||
type: 'seen',
|
type: 'seen',
|
||||||
cId: senderId,
|
cId: senderId,
|
||||||
txId: data.ntxid,
|
txId: data.ntxid
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
@ -1281,6 +1281,64 @@ Wallet.prototype.getTxProposals = function() {
|
||||||
return ret;
|
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
|
* @desc Removes old transactions
|
||||||
* @param {boolean} deleteAll - if true, remove all the transactions
|
* @param {boolean} deleteAll - if true, remove all the transactions
|
||||||
|
|
@ -2882,6 +2940,13 @@ Wallet.prototype.getTransactionHistory = function(cb) {
|
||||||
tx.merchant = proposal.merchant;
|
tx.merchant = proposal.merchant;
|
||||||
tx.peerActions = proposal.peerActions;
|
tx.peerActions = proposal.peerActions;
|
||||||
tx.finallyRejected = proposal.finallyRejected;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,13 +63,10 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
|
|
||||||
root.updateTxsAndBalance = _.debounce(function(w) {
|
root.updateTxsAndBalance = _.debounce(function(w) {
|
||||||
root.updateTxs({
|
root.updateTxs();
|
||||||
wallet: w,
|
|
||||||
pending: true,
|
|
||||||
});
|
|
||||||
root.updateBalance(w, function() {
|
root.updateBalance(w, function() {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
})
|
});
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
root.installWalletHandlers = function($scope, w) {
|
root.installWalletHandlers = function($scope, w) {
|
||||||
|
|
@ -139,19 +136,29 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
w.on('txProposalEvent', function(e) {
|
w.on('txProposalEvent', function(e) {
|
||||||
|
|
||||||
|
root.updateTxsAndBalance(w);
|
||||||
// TODO: add wallet name notification
|
// TODO: add wallet name notification
|
||||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||||
|
var name = w.getName();
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
|
case 'new':
|
||||||
|
notification.info('['+ name +'] New Transaction',
|
||||||
|
$filter('translate')('You received a transaction proposal from') + ' ' + user);
|
||||||
|
break;
|
||||||
case 'signed':
|
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;
|
break;
|
||||||
case 'rejected':
|
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;
|
break;
|
||||||
case 'corrupt':
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
$rootScope.$digest();
|
||||||
});
|
});
|
||||||
w.on('addressBookUpdated', function(dontDigest) {
|
w.on('addressBookUpdated', function(dontDigest) {
|
||||||
if (root.isFocusedWallet(wid)) {
|
if (root.isFocusedWallet(wid)) {
|
||||||
|
|
@ -210,6 +217,7 @@ angular.module('copayApp.services')
|
||||||
$rootScope.wallet = w;
|
$rootScope.wallet = w;
|
||||||
w.updateFocusedTimestamp(Date.now());
|
w.updateFocusedTimestamp(Date.now());
|
||||||
root.redirIfLogged();
|
root.redirIfLogged();
|
||||||
|
root.updateTxs();
|
||||||
root.updateBalance(w, function() {
|
root.updateBalance(w, function() {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
})
|
})
|
||||||
|
|
@ -323,75 +331,26 @@ angular.module('copayApp.services')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.updateTxs = function(opts) {
|
root.computeAlternativeAmount = function(w, tx, cb) {
|
||||||
function computeAlternativeAmount(w, tx, cb) {
|
rateService.whenAvailable(function() {
|
||||||
rateService.whenAvailable(function() {
|
_.each(tx.outs, function(out) {
|
||||||
_.each(tx.outs, function(out) {
|
var valueSat = out.value * w.settings.unitToSatoshi;
|
||||||
var valueSat = out.value * w.settings.unitToSatoshi;
|
out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
|
||||||
out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
|
out.alternativeIsoCode = w.settings.alternativeIsoCode;
|
||||||
out.alternativeIsoCode = w.settings.alternativeIsoCode;
|
|
||||||
});
|
|
||||||
if (cb) return cb();
|
|
||||||
});
|
});
|
||||||
};
|
if (cb) return cb(tx);
|
||||||
|
|
||||||
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
|
|
||||||
});
|
});
|
||||||
var txs = [];
|
};
|
||||||
|
|
||||||
inT.forEach(function(i, index) {
|
root.updateTxs = function() {
|
||||||
if (opts.skip && (index < opts.skip[0] || index >= opts.skip[1])) {
|
var w = $rootScope.wallet;
|
||||||
return txs.push(null);
|
if (!w) return root.onErrorDigest();
|
||||||
}
|
var res = w.getPendingTxProposals();
|
||||||
|
$rootScope.txps = res.txs;
|
||||||
if (i.isPending && myCopayerId != i.creator && !i.rejectedByUs && !i.signedByUs) {
|
if ($rootScope.pendingTxCount < res.pendingForUs) {
|
||||||
pendingForUs++;
|
$rootScope.txAlertCount = res.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;
|
|
||||||
}
|
}
|
||||||
$rootScope.pendingTxCount = pendingForUs;
|
$rootScope.pendingTxCount = res.pendingForUs;
|
||||||
};
|
};
|
||||||
|
|
||||||
root.deleteWallet = function($scope, w) {
|
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;
|
return root;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ describe("Unit: Controllers", function() {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
$rootScope.iden = sinon.stub();
|
$rootScope.iden = sinon.stub();
|
||||||
$rootScope.safeUnspentCount = 1;
|
$rootScope.safeUnspentCount = 1;
|
||||||
|
$rootScope.pendingTxCount = 0;
|
||||||
|
|
||||||
var w = {};
|
var w = {};
|
||||||
w.isReady = sinon.stub().returns(true);
|
w.isReady = sinon.stub().returns(true);
|
||||||
|
|
@ -72,7 +73,10 @@ describe("Unit: Controllers", function() {
|
||||||
w.sendTx = sinon.stub().yields(null);
|
w.sendTx = sinon.stub().yields(null);
|
||||||
w.requiresMultipleSignatures = sinon.stub().returns(true);
|
w.requiresMultipleSignatures = sinon.stub().returns(true);
|
||||||
w.getTxProposals = sinon.stub().returns([1, 2, 3]);
|
w.getTxProposals = sinon.stub().returns([1, 2, 3]);
|
||||||
|
w.getPendingTxProposals = sinon.stub().returns({
|
||||||
|
txs : [{ isPending : true }],
|
||||||
|
pendingForUs: 1
|
||||||
|
});
|
||||||
|
|
||||||
$rootScope.wallet = w;
|
$rootScope.wallet = w;
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -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 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">
|
<div class="large-12 columns">
|
||||||
<h2 translate>Pending Transactions Proposals</h2>
|
<h2 translate>Pending Transactions Proposals</h2>
|
||||||
<div class="last-transactions"
|
<div class="last-transactions"
|
||||||
ng-repeat="tx in txs | paged"
|
ng-repeat="tx in $root.txps | paged"
|
||||||
ng-include="'views/includes/transaction.html'"></div>
|
ng-include="'views/includes/transaction.html'"></div>
|
||||||
</div>
|
</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>
|
<h1 class="hide-for-large-up">{{$root.title}}</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue