Skip to content
Snippets Groups Projects
Commit 06634efd authored by Benoit Lavenier's avatar Benoit Lavenier
Browse files

[enh] TX history: show pubkeys, when TX has more than one issuers or recipients

parent 3f4adea0
No related branches found
No related tags found
No related merge requests found
Pipeline #7875 failed
...@@ -1187,8 +1187,29 @@ body { ...@@ -1187,8 +1187,29 @@ body {
width: 40%; width: 40%;
} }
.width-cup{ p.comment{
width: 175px; width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
}
p.text-wrap {
white-space: normal;
a {
white-space: nowrap;
}
}
.col-pubkey {
max-width: 80%;
.pubkeys {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
max-height: 130px;
overflow: hidden;
}
} }
} }
......
...@@ -2,38 +2,36 @@ ...@@ -2,38 +2,36 @@
angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
'cesium.settings.services', 'cesium.wot.services' ]) 'cesium.settings.services', 'cesium.wot.services' ])
.factory('csTx', function($q, $timeout, $filter, $translate, FileSaver, UIUtils, BMA, Api, .factory('csTx', function($q, $timeout, $filter, $translate, FileSaver, UIUtils, BMA, Api,
csConfig, csSettings, csWot, csCurrency) { csConfig, csSettings, csWot, csCurrency) {
'ngInject'; 'ngInject';
var defaultBMA = BMA; const defaultBMA = BMA;
function CsTx(id, BMA) { function CsTx(id, BMA) {
BMA = BMA || defaultBMA; BMA = BMA || defaultBMA;
var var
api = new Api(this, "csTx-" + id), api = new Api(this, "csTx-" + id);
_reduceTxAndPush = function(pubkey, txArray, result, processedTxMap, allowPendings) { function reduceTxAndPush(pubkey, txArray, result, processedTxMap, allowPendings) {
if (!txArray || !txArray.length) return; // Skip if empty if (!txArray || !txArray.length) return; // Skip if empty
_.forEach(txArray, function(tx) { _.forEach(txArray, function(tx) {
if (tx.block_number !== null || allowPendings) { if (tx.block_number !== null || allowPendings) {
var walletIsIssuer = false; var walletIsIssuer = false;
var otherIssuer = tx.issuers.reduce(function(issuer, res) { var otherIssuers = tx.issuers.reduce(function(res, issuer) {
walletIsIssuer = walletIsIssuer || (res === pubkey); walletIsIssuer = walletIsIssuer || (issuer === pubkey);
return issuer + ((res !== pubkey) ? ', ' + res : ''); return (issuer !== pubkey) ? res.concat(issuer) : res;
}, ''); }, []);
if (otherIssuer.length > 0) { var otherRecipients = [],
otherIssuer = otherIssuer.substring(2); outputBase,
} sources = [],
var otherReceiver; lockedOutputs;
var outputBase;
var sources = [];
var lockedOutputs;
var amount = tx.outputs.reduce(function(sum, output, noffset) { var amount = tx.outputs.reduce(function(sum, output, noffset) {
// FIXME duniter v1.4.13 // FIXME duniter v1.4.13
var outputArray = (typeof output == 'string') ? output.split(':',3) : [output.amount,output.base,output.conditions]; var outputArray = (typeof output === 'string') ? output.split(':',3) : [output.amount,output.base,output.conditions];
outputBase = parseInt(outputArray[1]); outputBase = parseInt(outputArray[1]);
var outputAmount = powBase(parseInt(outputArray[0]), outputBase); var outputAmount = powBase(parseInt(outputArray[0]), outputBase);
var outputCondition = outputArray[2]; var outputCondition = outputArray[2];
...@@ -59,11 +57,15 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -59,11 +57,15 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
}); });
} }
} }
else { // output is for someone else
if (outputPubkey !== '' && outputPubkey != otherIssuer) { // The output is for someone else
otherReceiver = outputPubkey; else {
// Add into recipients list(if not a issuer)
if (outputPubkey !== '' && !_.contains(otherIssuers, outputPubkey)) {
otherRecipients.push(outputPubkey);
} }
if (walletIsIssuer) { if (walletIsIssuer) {
// TODO: should be fix, when TX has multiple issuers (need a repartition)
return sum - outputAmount; return sum - outputAmount;
} }
} }
...@@ -75,14 +77,14 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -75,14 +77,14 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
if (lockedOutput) { if (lockedOutput) {
// Add a source // Add a source
sources.push(angular.merge({ sources.push(angular.merge({
amount: parseInt(outputArray[0]), amount: parseInt(outputArray[0]),
base: outputBase, base: outputBase,
type: 'T', type: 'T',
identifier: tx.hash, identifier: tx.hash,
noffset: noffset, noffset: noffset,
conditions: outputCondition, conditions: outputCondition,
consumed: false consumed: false
}, lockedOutput)); }, lockedOutput));
lockedOutput.amount = outputAmount; lockedOutput.amount = outputAmount;
lockedOutputs = lockedOutputs || []; lockedOutputs = lockedOutputs || [];
lockedOutputs.push(lockedOutput); lockedOutputs.push(lockedOutput);
...@@ -94,7 +96,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -94,7 +96,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
return sum; return sum;
}, 0); }, 0);
var txPubkey = amount > 0 ? otherIssuer : otherReceiver; var txPubkeys = amount > 0 ? otherIssuers : otherRecipients;
var time = tx.time || tx.blockstampTime; var time = tx.time || tx.blockstampTime;
// Avoid duplicated tx, or tx to him self // Avoid duplicated tx, or tx to him self
...@@ -104,14 +106,16 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -104,14 +106,16 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
var newTx = { var newTx = {
time: time, time: time,
amount: amount, amount: amount,
pubkey: txPubkey, pubkey: txPubkeys.length === 1 ? txPubkeys[0] : undefined,
pubkeys: txPubkeys.length > 1 ? txPubkeys : undefined,
comment: tx.comment, comment: tx.comment,
isUD: false, isUD: false,
hash: tx.hash, hash: tx.hash,
locktime: tx.locktime, locktime: tx.locktime,
block_number: tx.block_number block_number: tx.block_number
}; };
// If pending: store sources and inputs for a later use - see method processTransactionsAndSources()
// If pending: store sources and inputs for a later use - see method processTransactionsAndSources()
if (walletIsIssuer && tx.block_number === null) { if (walletIsIssuer && tx.block_number === null) {
newTx.inputs = tx.inputs; newTx.inputs = tx.inputs;
newTx.sources = sources; newTx.sources = sources;
...@@ -123,9 +127,10 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -123,9 +127,10 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
} }
} }
}); });
}, }
loadTx = function(pubkey, fromTime) { function loadTx(pubkey, fromTime) {
return $q(function(resolve, reject) { return $q(function(resolve, reject) {
var nowInSec = moment().utc().unix(); var nowInSec = moment().utc().unix();
...@@ -138,10 +143,6 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -138,10 +143,6 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
}; };
var processedTxMap = {}; var processedTxMap = {};
var _reducePendingTx = function (res) {
_reduceTxAndPush(pubkey, res.history.sending, tx.pendings, processedTxMap, true /*allow pendings*/);
_reduceTxAndPush(pubkey, res.history.pending, tx.pendings, processedTxMap, true /*allow pendings*/);
};
var jobs = [ var jobs = [
// get current block // get current block
...@@ -149,14 +150,17 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -149,14 +150,17 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
// get pending tx // get pending tx
BMA.tx.history.pending({pubkey: pubkey}) BMA.tx.history.pending({pubkey: pubkey})
.then(_reducePendingTx) .then(function (res) {
reduceTxAndPush(pubkey, res.history.sending, tx.pendings, processedTxMap, true /*allow pendings*/);
reduceTxAndPush(pubkey, res.history.pending, tx.pendings, processedTxMap, true /*allow pendings*/);
})
]; ];
// get TX history since // get TX history since
if (fromTime !== 'pending') { if (fromTime !== 'pending') {
var _reduceTx = function (res) { var reduceTxFn = function (res) {
_reduceTxAndPush(pubkey, res.history.sent, tx.history, processedTxMap, false); reduceTxAndPush(pubkey, res.history.sent, tx.history, processedTxMap, false);
_reduceTxAndPush(pubkey, res.history.received, tx.history, processedTxMap, false); reduceTxAndPush(pubkey, res.history.received, tx.history, processedTxMap, false);
}; };
// get TX from a given time // get TX from a given time
...@@ -166,19 +170,19 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -166,19 +170,19 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
fromTime = fromTime - (fromTime % sliceTime); fromTime = fromTime - (fromTime % sliceTime);
for(var i = fromTime; i - sliceTime < nowInSec; i += sliceTime) { for(var i = fromTime; i - sliceTime < nowInSec; i += sliceTime) {
jobs.push(BMA.tx.history.times({pubkey: pubkey, from: i, to: i+sliceTime-1}, true /*with cache*/) jobs.push(BMA.tx.history.times({pubkey: pubkey, from: i, to: i+sliceTime-1}, true /*with cache*/)
.then(_reduceTx) .then(reduceTxFn)
); );
} }
// Last slide: no cache // Last slide: no cache
jobs.push(BMA.tx.history.times({pubkey: pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999}, false/*no cache*/) jobs.push(BMA.tx.history.times({pubkey: pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999}, false/*no cache*/)
.then(_reduceTx)); .then(reduceTxFn));
} }
// get all TX // get all TX
else { else {
jobs.push(BMA.tx.history.all({pubkey: pubkey}) jobs.push(BMA.tx.history.all({pubkey: pubkey})
.then(_reduceTx) .then(reduceTxFn)
); );
} }
...@@ -216,7 +220,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -216,7 +220,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
console.debug('Error while loading UDs history, on extension point.'); console.debug('Error while loading UDs history, on extension point.');
console.error(err); console.error(err);
}) })
); );
} }
} }
...@@ -242,27 +246,27 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -242,27 +246,27 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
}) })
.catch(reject); .catch(reject);
}); });
}, }
powBase = function(amount, base) { function powBase(amount, base) {
return base <= 0 ? amount : amount * Math.pow(10, base); return base <= 0 ? amount : amount * Math.pow(10, base);
}, }
addSource = function(src, sources, sourcesIndexByKey) { function addSource(src, sources, sourcesIndexByKey) {
var srcKey = src.type+':'+src.identifier+':'+src.noffset; var srcKey = src.type+':'+src.identifier+':'+src.noffset;
if (angular.isUndefined(sourcesIndexByKey[srcKey])) { if (angular.isUndefined(sourcesIndexByKey[srcKey])) {
sources.push(src); sources.push(src);
sourcesIndexByKey[srcKey] = sources.length - 1; sourcesIndexByKey[srcKey] = sources.length - 1;
} }
}, }
addSources = function(result, sources) { function addSources(result, sources) {
_(sources).forEach(function(src) { _(sources).forEach(function(src) {
addSource(src, result.sources, result.sourcesIndexByKey); addSource(src, result.sources, result.sourcesIndexByKey);
}); });
}, }
loadSourcesAndBalance = function(pubkey) { function loadSourcesAndBalance(pubkey) {
return BMA.tx.sources({pubkey: pubkey}) return BMA.tx.sources({pubkey: pubkey})
.then(function(res){ .then(function(res){
var data = { var data = {
...@@ -283,19 +287,19 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -283,19 +287,19 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
console.warn("[tx] Error while getting sources...", err); console.warn("[tx] Error while getting sources...", err);
throw err; throw err;
}); });
}, }
loadData = function(pubkey, fromTime) { function loadData(pubkey, fromTime) {
var now = Date.now(); var now = Date.now();
return $q.all([ return $q.all([
// Load Sources // Load Sources
loadSourcesAndBalance(pubkey), loadSourcesAndBalance(pubkey),
// Load Tx // Load Tx
loadTx(pubkey, fromTime) loadTx(pubkey, fromTime)
]) ])
.then(function(res) { .then(function(res) {
// Copy sources and balance // Copy sources and balance
...@@ -383,91 +387,91 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services', ...@@ -383,91 +387,91 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
console.warn("[tx] Error while getting sources and tx...", err); console.warn("[tx] Error while getting sources and tx...", err);
throw err; throw err;
}); });
}, }
loadSources = function(pubkey) { function loadSources(pubkey) {
console.debug("[tx] Loading sources for " + pubkey.substring(0,8)); console.debug("[tx] Loading sources for " + pubkey.substring(0,8));
return loadData(pubkey, 'pending'); return loadData(pubkey, 'pending');
}; }
// Download TX history file // Download TX history file
downloadHistoryFile = function(pubkey, options) { function downloadHistoryFile(pubkey, options) {
options = options || {}; options = options || {};
options.fromTime = options.fromTime || -1; options.fromTime = options.fromTime || -1;
console.debug("[tx] Exporting TX history for pubkey [{0}]".format(pubkey.substr(0,8)));
return $q.all([
$translate(['ACCOUNT.HEADERS.TIME',
'COMMON.UID',
'COMMON.PUBKEY',
'COMMON.UNIVERSAL_DIVIDEND',
'ACCOUNT.HEADERS.AMOUNT',
'ACCOUNT.HEADERS.COMMENT']),
csCurrency.blockchain.current(true/*withCache*/),
loadData(pubkey, options.fromTime)
])
.then(function(result){
var translations = result[0];
var currentBlock = result[1];
var currentTime = (currentBlock && currentBlock.medianTime) || moment().utc().unix();
var currency = currentBlock && currentBlock.currency;
var data = result[2];
// no TX
if (!data || !data.tx || !data.tx.history) {
return UIUtils.toast.show('INFO.EMPTY_TX_HISTORY');
}
return $translate('ACCOUNT.FILE_NAME', {currency: currency, pubkey: pubkey, currentTime : currentTime}) console.debug("[tx] Exporting TX history for pubkey [{0}]".format(pubkey.substr(0,8)));
.then(function(filename){
return $q.all([
var formatDecimal = $filter('formatDecimal'); $translate(['ACCOUNT.HEADERS.TIME',
var medianDate = $filter('medianDate'); 'COMMON.UID',
var formatSymbol = $filter('currencySymbolNoHtml'); 'COMMON.PUBKEY',
'COMMON.UNIVERSAL_DIVIDEND',
var headers = [ 'ACCOUNT.HEADERS.AMOUNT',
translations['ACCOUNT.HEADERS.TIME'], 'ACCOUNT.HEADERS.COMMENT']),
translations['COMMON.UID'], csCurrency.blockchain.current(true/*withCache*/),
translations['COMMON.PUBKEY'], loadData(pubkey, options.fromTime)
translations['ACCOUNT.HEADERS.AMOUNT'] + ' (' + formatSymbol(currency) + ')', ])
translations['ACCOUNT.HEADERS.COMMENT'] .then(function(result){
]; var translations = result[0];
var content = data.tx.history.concat(data.tx.validating).reduce(function(res, tx){ var currentBlock = result[1];
return res.concat([ var currentTime = (currentBlock && currentBlock.medianTime) || moment().utc().unix();
var currency = currentBlock && currentBlock.currency;
var data = result[2];
// no TX
if (!data || !data.tx || !data.tx.history) {
return UIUtils.toast.show('INFO.EMPTY_TX_HISTORY');
}
return $translate('ACCOUNT.FILE_NAME', {currency: currency, pubkey: pubkey, currentTime : currentTime})
.then(function(filename){
var formatDecimal = $filter('formatDecimal');
var medianDate = $filter('medianDate');
var formatSymbol = $filter('currencySymbolNoHtml');
var headers = [
translations['ACCOUNT.HEADERS.TIME'],
translations['COMMON.UID'],
translations['COMMON.PUBKEY'],
translations['ACCOUNT.HEADERS.AMOUNT'] + ' (' + formatSymbol(currency) + ')',
translations['ACCOUNT.HEADERS.COMMENT']
];
var content = data.tx.history.concat(data.tx.validating).reduce(function(res, tx){
return res.concat([
medianDate(tx.time), medianDate(tx.time),
tx.uid, tx.uid,
tx.pubkey, tx.pubkey,
formatDecimal(tx.amount/100), formatDecimal(tx.amount/100),
'"' + (tx.isUD ? translations['COMMON.UNIVERSAL_DIVIDEND'] : tx.comment) + '"' '"' + (tx.isUD ? translations['COMMON.UNIVERSAL_DIVIDEND'] : tx.comment) + '"'
].join(';') + '\n'); ].join(';') + '\n');
}, [headers.join(';') + '\n']); }, [headers.join(';') + '\n']);
var file = new Blob(content, {type: 'text/plain; charset=utf-8'}); var file = new Blob(content, {type: 'text/plain; charset=utf-8'});
FileSaver.saveAs(file, filename); FileSaver.saveAs(file, filename);
}); });
}); });
}; }
// Register extension points // Register extension points
api.registerEvent('data', 'loadUDs'); api.registerEvent('data', 'loadUDs');
return { return {
id: id, id: id,
load: loadData, load: loadData,
loadSources: loadSources, loadSources: loadSources,
downloadHistoryFile: downloadHistoryFile, downloadHistoryFile: downloadHistoryFile,
// api extension // api extension
api: api api: api
}; };
} }
var service = new CsTx('default'); var service = new CsTx('default');
service.instance = function(id, bma) { service.instance = function(id, bma) {
return new CsTx(id, bma); return new CsTx(id, bma);
}; };
return service; return service;
}); });
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<i class="avatar" ng-if="::tx.avatar" style="background-image: url({{::tx.avatar.src}})"></i> <i class="avatar" ng-if="::tx.avatar" style="background-image: url({{::tx.avatar.src}})"></i>
<div class="row no-padding"> <div class="row no-padding">
<div class="col no-padding"> <div class="col col-pubkey no-padding">
<a class="" ui-sref="app.wot_identity({pubkey:tx.pubkey, uid:tx.uid})" ng-if="::tx.uid"> <a class="" ui-sref="app.wot_identity({pubkey:tx.pubkey, uid:tx.uid})" ng-if="::tx.uid">
{{::tx.name||tx.uid}} {{::tx.name||tx.uid}}
</a> </a>
...@@ -13,8 +13,14 @@ ...@@ -13,8 +13,14 @@
{{::tx.pubkey | formatPubkey}} {{::tx.pubkey | formatPubkey}}
<span ng-if="::tx.name"> - {{::tx.name | truncText:40}}</span> <span ng-if="::tx.name"> - {{::tx.name | truncText:40}}</span>
</a> </a>
<p class="dark visible-xs width-cup text-italic" <p ng-if="::tx.pubkeys" class="pubkeys">
data-toggle="tooltip" <a class="gray" ng-repeat="pubkey in ::tx.pubkeys.slice(0, 4)"
ui-sref="app.wot_identity({pubkey:pubkey})" >
<i class="ion-key gray"></i>&nbsp;{{::pubkey | formatPubkey}}
</a>
<span ng-if="::tx.pubkeys.length &gt; 4">...</span>
</p>
<p class="dark visible-xs comment text-italic"
ng-if="::tx.comment" ng-if="::tx.comment"
title="{{::tx.comment}}"> title="{{::tx.comment}}">
<i class="ion-ios-chatbubble-outline"></i> <i class="ion-ios-chatbubble-outline"></i>
...@@ -29,8 +35,8 @@ ...@@ -29,8 +35,8 @@
</span> </span>
</h4> </h4>
</div> </div>
<div class="col col-50 no-padding" ng-if="::tx.comment"> <div class="col col-50 col-comment no-padding padding-left hidden-xs" ng-if="::tx.comment">
<p class="vertical-center gray text-italic hidden-xs" <p class="vertical-center gray text-italic "
data-toggle="tooltip" data-toggle="tooltip"
title="{{::tx.comment}}">{{::tx.comment}}</p> title="{{::tx.comment}}">{{::tx.comment}}</p>
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment