From 94df421b88ec889cacf4c0dbc0a6a82e91af5983 Mon Sep 17 00:00:00 2001
From: blavenie <benoit.lavenier@e-is.pro>
Date: Tue, 18 Dec 2018 19:32:05 +0100
Subject: [PATCH] [fix] Better TX loading, and disable "track by" in templates
 - fix #780 [enh] Use a special formater for medianTime (add offset), and no
 more formatDateXxx

---
 www/i18n/locale-en-GB.json                    |   6 +-
 www/i18n/locale-en.json                       |   6 +-
 www/i18n/locale-es-ES.json                    |   6 +-
 www/i18n/locale-fr-FR.json                    |  12 +-
 www/i18n/locale-it-IT.json                    |   6 +-
 www/i18n/locale-nl-NL.json                    |   4 +-
 www/js/controllers/wallet-controllers.js      |   4 +-
 www/js/filters.js                             |  67 ++++++--
 www/js/services/currency-services.js          |   2 +-
 www/js/services/tx-services.js                | 148 +++++++++---------
 www/js/services/wallet-services.js            |   3 +-
 www/plugins/es/i18n/locale-en-GB.json         |   2 +-
 www/plugins/es/i18n/locale-en.json            |   2 +-
 www/plugins/es/i18n/locale-es-ES.json         |   2 +-
 www/plugins/es/i18n/locale-fr-FR.json         |   2 +-
 www/plugins/es/i18n/locale-it-IT.json         |   2 +-
 .../js/controllers/currency-controllers.js    |   8 +-
 www/plugins/rml9/plugin-final.js              |   2 -
 www/templates/blockchain/item_block.html      |   2 +-
 .../blockchain/item_block_empty_lg.html       |   2 +-
 www/templates/blockchain/item_block_lg.html   |   2 +-
 .../blockchain/unlock_condition_popover.html  |   2 +-
 www/templates/blockchain/view_block.html      |   6 +-
 www/templates/currency/items_network.html     |   2 +-
 www/templates/currency/items_parameters.html  |   2 +-
 www/templates/network/popover_peer_info.html  |   2 +-
 www/templates/wallet/item_tx.html             |  11 +-
 www/templates/wallet/item_ud.html             |   2 +-
 .../wallet/tx_locked_outputs_popover.html     |   2 +-
 www/templates/wallet/view_wallet.html         |   2 +-
 www/templates/wallet/view_wallet_tx.html      |  47 +++---
 www/templates/wot/item_certification.html     |   4 +-
 www/templates/wot/view_identity.html          |   2 +-
 www/templates/wot/view_identity_tx.html       |  46 +++---
 34 files changed, 237 insertions(+), 183 deletions(-)

diff --git a/www/i18n/locale-en-GB.json b/www/i18n/locale-en-GB.json
index 52cf86bb..44760282 100644
--- a/www/i18n/locale-en-GB.json
+++ b/www/i18n/locale-en-GB.json
@@ -356,7 +356,7 @@
       "PENDING": "Pending registrations:",
       "PENDING_COUNT": "{{count}} pending registrations",
       "REGISTERED": "Registered {{sigDate | formatFromNow}}",
-      "MEMBER_FROM": "Member since {{memberDate|formatFromNowShort}}",
+      "MEMBER_FROM": "Member since {{memberDate|medianFromNowShort}}",
       "BTN_NEWCOMERS": "Latest members",
       "BTN_PENDING": "Pending registrations",
       "SHOW_MORE": "Show more",
@@ -458,7 +458,7 @@
     "NO_TX": "No transaction",
     "SHOW_MORE_TX": "Show more",
     "SHOW_ALL_TX": "Show all",
-    "TX_FROM_DATE": "(current limit to {{fromTime|formatFromNowShort}})",
+    "TX_FROM_DATE": "(current limit to {{fromTime|medianFromNowShort}})",
     "PENDING_TX": "Pending transactions",
     "VALIDATING_TX": "Transactions being validated",
     "ERROR_TX": "Transaction not executed",
@@ -707,7 +707,7 @@
     "INVALID_COMMENT": "Field 'reference' has a bad format.",
     "INVALID_PUBKEY": "Public key has a bad format.",
     "INVALID_PUBKEY_CHECKSUM": "Invalid checksum.",
-    "IDENTITY_REVOKED": "This identity <b>has been revoked {{revocationTime|formatFromNow}}</b> ({{revocationTime|formatDate}}). It can no longer become a member.",
+    "IDENTITY_REVOKED": "This identity <b>has been revoked {{revocationTime|medianFromNow}}</b> ({{revocationTime|medianDate}}). It can no longer become a member.",
     "IDENTITY_PENDING_REVOCATION": "The <b>revocation of this identity</b> has been requested and is awaiting processing. Certification is therefore disabled.",
     "IDENTITY_INVALID_BLOCK_HASH": "This membership application is no longer valid (because it references a block that network peers are cancelled): the person must renew its application for membership <b>before</b> being certified.",
     "IDENTITY_EXPIRED": "This identity has expired: this person must re-apply <b>before</b> being certified.",
diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json
index 4426b5c0..3f2e6955 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -356,7 +356,7 @@
       "PENDING": "Pending registrations:",
       "PENDING_COUNT": "{{count}} pending registrations",
       "REGISTERED": "Registered {{sigDate | formatFromNow}}",
-      "MEMBER_FROM": "Member since {{memberDate|formatFromNowShort}}",
+      "MEMBER_FROM": "Member since {{memberDate|medianFromNowShort}}",
       "BTN_NEWCOMERS": "Latest members",
       "BTN_PENDING": "Pending registrations",
       "SHOW_MORE": "Show more",
@@ -458,7 +458,7 @@
     "NO_TX": "No transaction",
     "SHOW_MORE_TX": "Show more",
     "SHOW_ALL_TX": "Show all",
-    "TX_FROM_DATE": "(current limit to {{fromTime|formatFromNowShort}})",
+    "TX_FROM_DATE": "(current limit to {{fromTime|medianFromNowShort}})",
     "PENDING_TX": "Pending transactions",
     "VALIDATING_TX": "Transactions being validated",
     "ERROR_TX": "Transaction not executed",
@@ -707,7 +707,7 @@
     "INVALID_COMMENT": "Field 'reference' has a bad format.",
     "INVALID_PUBKEY": "Public key has a bad format.",
     "INVALID_PUBKEY_CHECKSUM": "Invalid checksum.",
-    "IDENTITY_REVOKED": "This identity <b>has been revoked {{revocationTime|formatFromNow}}</b> ({{revocationTime|formatDate}}). It can no longer become a member.",
+    "IDENTITY_REVOKED": "This identity <b>has been revoked {{revocationTime|medianFromNow}}</b> ({{revocationTime|medianDate}}). It can no longer become a member.",
     "IDENTITY_PENDING_REVOCATION": "The <b>revocation of this identity</b> has been requested and is awaiting processing. Certification is therefore disabled.",
     "IDENTITY_INVALID_BLOCK_HASH": "This membership application is no longer valid (because it references a block that network peers are cancelled): the person must renew its application for membership <b>before</b> being certified.",
     "IDENTITY_EXPIRED": "This identity has expired: this person must re-apply <b>before</b> being certified.",
diff --git a/www/i18n/locale-es-ES.json b/www/i18n/locale-es-ES.json
index 79153762..c5e21a35 100644
--- a/www/i18n/locale-es-ES.json
+++ b/www/i18n/locale-es-ES.json
@@ -349,7 +349,7 @@
       "PENDING": "Inscripciones en espera:",
       "PENDING_COUNT": "{{count}} inscripciones en espera",
       "REGISTERED": "Inscrito {{sigDate | formatFromNow}}",
-      "MEMBER_FROM": "Miembro desde {{memberDate|formatFromNowShort}}",
+      "MEMBER_FROM": "Miembro desde {{memberDate|medianFromNowShort}}",
       "BTN_NEWCOMERS": "Nuevos miembros",
       "BTN_PENDING": "Inscripciones en espera",
       "SHOW_MORE": "Mostrar más",
@@ -451,7 +451,7 @@
     "NO_TX": "Ninguna transacción",
     "SHOW_MORE_TX": "Mostrar más",
     "SHOW_ALL_TX": "Mostrar todo",
-    "TX_FROM_DATE": "(límite actual a {{fromTime|formatFromNowShort}})",
+    "TX_FROM_DATE": "(límite actual a {{fromTime|medianFromNowShort}})",
     "PENDING_TX": "Transacciones en proceso de se procesadas",
     "VALIDATING_TX": "Transacciones validadas",
     "ERROR_TX": "Transacciones no ejecutadas",
@@ -663,7 +663,7 @@
     "INVALID_COMMENT": "El campo 'referencia’ no debe contener carácteres acentuados.",
     "INVALID_PUBKEY": "La clave pública no tiene el formato esperado.",
     "INVALID_PUBKEY_CHECKSUM": "Suma de comprobación inválida.",
-    "IDENTITY_REVOKED": "Esta identidad <b>fue revocada {{revocationTime|formatFromNow}}</b> ({{revocationTime|formatDate}}). No puede estar miembro.",
+    "IDENTITY_REVOKED": "Esta identidad <b>fue revocada {{revocationTime|medianFromNow}}</b> ({{revocationTime|medianDate}}). No puede estar miembro.",
     "IDENTITY_PENDING_REVOCATION": "La <b>revocación de esta identidad</b> fue solicitado y esta en espera de tratamiento. Por lo que, la certificación es desactivada.",
     "IDENTITY_INVALID_BLOCK_HASH": "Esta solicitud de adhesión no es valida (porque denomina un bloque los nodos de la red han anulado): esta persona debe renovelar su solicitud de adhesión <b>antes que</b> estar certificada.",
     "IDENTITY_EXPIRED": "La publicación de esta identidad ha caducada: esta persona debe realizar una nueva solicitud de adhesión <b>antes que</b> estar certificada.",
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index 2f36f21b..3619b5e0 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -356,7 +356,7 @@
       "PENDING": "Inscriptions en attente",
       "PENDING_COUNT": "{{count}} inscriptions en attente",
       "REGISTERED": "Inscrit {{sigDate | formatFromNow}}",
-      "MEMBER_FROM": "Membre depuis {{memberDate|formatFromNowShort}}",
+      "MEMBER_FROM": "Membre depuis {{memberDate|medianFromNowShort}}",
       "BTN_NEWCOMERS": "Nouveaux membres",
       "BTN_PENDING": "Inscriptions en attente",
       "SHOW_MORE": "Afficher plus",
@@ -453,14 +453,14 @@
   "ACCOUNT": {
     "TITLE": "Mon compte",
     "BALANCE": "Solde",
-    "LAST_TX": "Dernières transactions",
+    "LAST_TX": "Dernières transactions validées",
     "BALANCE_ACCOUNT": "Solde du compte",
     "NO_TX": "Aucune transaction",
     "SHOW_MORE_TX": "Afficher plus",
     "SHOW_ALL_TX": "Afficher tout",
-    "TX_FROM_DATE": "(limite actuelle à {{fromTime|formatFromNowShort}})",
-    "PENDING_TX": "Transactions en cours de traitement",
-    "VALIDATING_TX": "Transactions en cours de validation",
+    "TX_FROM_DATE": "(limite actuelle à {{fromTime|medianFromNowShort}})",
+    "PENDING_TX": "Transactions en attente de traitement",
+    "VALIDATING_TX": "Transactions traitées, non validée",
     "ERROR_TX": "Transactions non executées",
     "ERROR_TX_SENT": "Transactions envoyées en échec",
     "PENDING_TX_RECEIVED": "Transactions en attente de réception",
@@ -707,7 +707,7 @@
     "INVALID_COMMENT": "Le champ 'référence' ne doit pas contenir de caractères accentués.",
     "INVALID_PUBKEY": "La clé publique n'a pas le format attendu.",
     "INVALID_PUBKEY_CHECKSUM": "Somme de contrôle invalide.",
-    "IDENTITY_REVOKED": "Cette identité <b>a été révoquée {{revocationTime|formatFromNow}}</b> ({{revocationTime|formatDate}}). Elle ne peut plus devenir membre.",
+    "IDENTITY_REVOKED": "Cette identité <b>a été révoquée {{revocationTime|medianFromNow}}</b> ({{revocationTime|medianDate}}). Elle ne peut plus devenir membre.",
     "IDENTITY_PENDING_REVOCATION": "La <b>révocation de cette identité</b> a été demandée et est en attente de traitement. La certification est donc désactivée.",
     "IDENTITY_INVALID_BLOCK_HASH": "Cette demande d'adhésion n'est plus valide (car elle référence un bloc que les nœuds du réseau ont annulé) : cette personne doit renouveler sa demande d'adhésion <b>avant</b> d'être certifiée.",
     "IDENTITY_EXPIRED": "La publication de cette identité a expirée : cette personne doit effectuer une nouvelle demande d'adhésion <b>avant</b> d'être certifiée.",
diff --git a/www/i18n/locale-it-IT.json b/www/i18n/locale-it-IT.json
index ae1333ac..b5ab8127 100644
--- a/www/i18n/locale-it-IT.json
+++ b/www/i18n/locale-it-IT.json
@@ -339,7 +339,7 @@
        "PENDING": "Registrazioni pendenti",
        "PENDING_COUNT": "{{count}} inscrizioni pendenti",
        "REGISTERED": "Registrato {{sigDate | formatFromNow}}",
-       "MEMBER_FROM": "Membro dal {{memberDate|formatFromNowShort}}",
+       "MEMBER_FROM": "Membro dal {{memberDate|medianFromNowShort}}",
        "BTN_NEWCOMERS": "Ultimi membri",
        "BTN_PENDING": "Registrazioni pendenti",
        "SHOW_MORE": "Vedere di più",
@@ -445,7 +445,7 @@
      "NO_TX": "Nessuna transazione",
      "SHOW_MORE_TX": "Mostrare di più",
      "SHOW_ALL_TX": "Mostrare tutte",
-     "TX_FROM_DATE": "(limite attuale del {{fromTime|formatFromNowShort}})",
+     "TX_FROM_DATE": "(limite attuale del {{fromTime|medianFromNowShort}})",
      "PENDING_TX": "Transazioni pendenti",
      "VALIDATING_TX": "Transazioni in corso di convalida",
      "ERROR_TX": "Transaction non eseguite",
@@ -652,7 +652,7 @@
      "INVALID_USER_ID": "Il campo del 'pseudonimo' non deve avere spazi vuoti o caratteri speciali.",
      "INVALID_COMMENT": "Il formato del campo 'reference' è errato.",
      "INVALID_PUBKEY": "If formato della chiave pubblica è errato.",
-     "IDENTITY_REVOKED": "Questa identità <b>è stata revocata {{revocationTime|formatFromNow}}</b> ({{revocationTime|formatDate}}). Non puo più diventare membro.",
+     "IDENTITY_REVOKED": "Questa identità <b>è stata revocata {{revocationTime|medianFromNow}}</b> ({{revocationTime|medianDate}}). Non puo più diventare membro.",
      "IDENTITY_PENDING_REVOCATION": "L'<b>annulamento di questa identità</b> è stata richiesta ed è in corso di evaluazione. Capacità ad inviare certificazioni disabilitata",
      "IDENTITY_INVALID_BLOCK_HASH": "Questa richiesta di certificazione non è più valida (perche si riferisce ad un blocco che è stato eliminato dai peers): la persona deve rinnovare la sua domanda di certificazione <b>prima</b> di essere certificata.",
      "IDENTITY_EXPIRED": "Questa identità è scaduta: la persona deve fare una nuova domanda di certificazione <b>prima di</b> essere certificata.",
diff --git a/www/i18n/locale-nl-NL.json b/www/i18n/locale-nl-NL.json
index c34abead..100aa2b4 100644
--- a/www/i18n/locale-nl-NL.json
+++ b/www/i18n/locale-nl-NL.json
@@ -289,7 +289,7 @@
       "NEWCOMERS": "Nieuwe leden:",
       "PENDING": "Aspirant leden:",
       "REGISTERED": "Geregistreerd {{sigDate | formatFromNow}}",
-      "MEMBER_FROM": "Lid sinds {{memberDate|formatFromNowShort}}",
+      "MEMBER_FROM": "Lid sinds {{memberDate|medianFromNowShort}}",
       "BTN_NEWCOMERS": "Nieuwste leden",
       "BTN_PENDING": "Registraties in afwachting",
       "SHOW_MORE": "Toon meer",
@@ -339,7 +339,7 @@
     "NO_TX": "Geen transacties",
     "SHOW_MORE_TX": "Show more",
     "SHOW_ALL_TX": "Show all",
-    "TX_FROM_DATE": "(huidige limiet op {{fromTime|formatFromNowShort}})",
+    "TX_FROM_DATE": "(huidige limiet op {{fromTime|medianFromNowShort}})",
     "PENDING_TX": "Transacties in afwachting",
     "ERROR_TX": "Niet uitgevoerde transacties",
     "ERROR_TX_SENT": "Verzonden transacties",
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
index a8c821b7..c97dc0bd 100644
--- a/www/js/controllers/wallet-controllers.js
+++ b/www/js/controllers/wallet-controllers.js
@@ -1063,11 +1063,11 @@ function WalletTxErrorController($scope, UIUtils, csSettings, csWallet) {
   };
 
   $scope.hasReceivedTx = function(){
-    return $scope.formData.tx && !!_($scope.formData.tx.errors || []).find($scope.filterReceivedTx);
+    return $scope.formData.tx && _($scope.formData.tx.errors || []).find($scope.filterReceivedTx) && true;
   };
 
   $scope.hasSentTx = function(){
-    return $scope.formData.tx && !!_($scope.formData.tx.errors || []).find($scope.filterSentTx);
+    return $scope.formData.tx && _($scope.formData.tx.errors || []).find($scope.filterSentTx) && true;
   };
 
 }
diff --git a/www/js/filters.js b/www/js/filters.js
index f37aeddb..d7e1fa9e 100644
--- a/www/js/filters.js
+++ b/www/js/filters.js
@@ -10,7 +10,7 @@ angular.module('cesium.filters', ['cesium.config', 'cesium.platform', 'pascalpre
       startPromise,
       that = this;
 
-    that.MEDIAN_TIME_OFFSET = 3600 /*G1 default value*/;
+    that.MEDIAN_TIME_OFFSET = 3600  /*G1 default value*/;
 
     // Update some translations, when locale changed
     function onLocaleChange() {
@@ -160,7 +160,6 @@ angular.module('cesium.filters', ['cesium.config', 'cesium.platform', 'pascalpre
     };
   })
 
-
   .filter('currencySymbol', function(filterTranslations, $filter, csSettings) {
     return function(input, useRelative) {
       if (!input) return '';
@@ -179,7 +178,6 @@ angular.module('cesium.filters', ['cesium.config', 'cesium.platform', 'pascalpre
     };
   })
 
-
   .filter('formatDecimal', function(csConfig, csCurrency) {
     var minValue = 1 / Math.pow(10, csConfig.decimalCount || 4);
     var format = '0,0.0' + Array(csConfig.decimalCount || 4).join('0');
@@ -209,13 +207,13 @@ angular.module('cesium.filters', ['cesium.config', 'cesium.platform', 'pascalpre
 
   .filter('formatDate', function(filterTranslations) {
     return function(input) {
-      return input ? moment.unix(parseInt(input) + filterTranslations.MEDIAN_TIME_OFFSET).local().format(filterTranslations.DATE_PATTERN || 'YYYY-MM-DD HH:mm') : '';
+      return input ? moment.unix(parseInt(input)).local().format(filterTranslations.DATE_PATTERN || 'YYYY-MM-DD HH:mm') : '';
     };
   })
 
   .filter('formatDateShort', function(filterTranslations) {
     return function(input) {
-      return input ? moment.unix(parseInt(input) + filterTranslations.MEDIAN_TIME_OFFSET).local().format(filterTranslations.DATE_SHORT_PATTERN || 'YYYY-MM-DD') : '';
+      return input ? moment.unix(parseInt(input)).local().format(filterTranslations.DATE_SHORT_PATTERN || 'YYYY-MM-DD') : '';
     };
   })
 
@@ -231,18 +229,24 @@ angular.module('cesium.filters', ['cesium.config', 'cesium.platform', 'pascalpre
     };
   })
 
-  .filter('formatTime', function(filterTranslations) {
+  .filter('formatTime', function() {
     return function(input) {
-      return input ? moment.unix(parseInt(input)+filterTranslations.MEDIAN_TIME_OFFSET).local().format('HH:mm') : '';
+      return input ? moment.unix(parseInt(input)).local().format('HH:mm') : '';
     };
   })
 
-  .filter('formatFromNow', function(filterTranslations) {
+  .filter('formatFromNow', function() {
     return function(input) {
-      return input ? moment.unix(parseInt(input)+filterTranslations.MEDIAN_TIME_OFFSET).fromNow() : '';
+      return input ? moment.unix(parseInt(input)).fromNow() : '';
     };
   })
 
+  .filter('formatFromNowAndDate', function(filterTranslations) {
+    return function(input, options) {
+      const m = input && moment.unix(parseInt(input));
+      return m && (m.fromNow() + (options && options.separator || ' | ') + m.local().format(filterTranslations.DATE_PATTERN || 'YYYY-MM-DD HH:mm')) || '';
+    };
+  })
 
   .filter('formatDurationTo', function() {
     return function(input) {
@@ -289,12 +293,55 @@ angular.module('cesium.filters', ['cesium.config', 'cesium.platform', 'pascalpre
     };
   })
 
-  .filter('formatFromNowShort', function(filterTranslations) {
+  .filter('formatFromNowShort', function() {
+    return function(input) {
+      return input ? moment.unix(parseInt(input)+offset).fromNow(true) : '';
+    };
+  })
+
+  /* -- median time (apply currency offset)-- */
+
+  .filter('medianDate', function(filterTranslations) {
+    return function(input) {
+      return input ? moment.unix(parseInt(input) + filterTranslations.MEDIAN_TIME_OFFSET).local().format(filterTranslations.DATE_PATTERN || 'YYYY-MM-DD HH:mm') : '';
+    };
+  })
+
+  .filter('medianDateShort', function(filterTranslations) {
+    return function(input) {
+      return input ? moment.unix(parseInt(input) + filterTranslations.MEDIAN_TIME_OFFSET).local().format(filterTranslations.DATE_SHORT_PATTERN || 'YYYY-MM-DD') : '';
+    };
+  })
+
+
+  .filter('medianTime', function(filterTranslations) {
+    return function(input) {
+      return input ? moment.unix(parseInt(input)+filterTranslations.MEDIAN_TIME_OFFSET).local().format('HH:mm') : '';
+    };
+  })
+
+  .filter('medianFromNow', function(filterTranslations) {
+    return function(input) {
+      return input ? moment.unix(parseInt(input) + filterTranslations.MEDIAN_TIME_OFFSET).fromNow() : '';
+    };
+  })
+
+  .filter('medianFromNowShort', function(filterTranslations) {
     return function(input) {
       return input ? moment.unix(parseInt(input)+filterTranslations.MEDIAN_TIME_OFFSET).fromNow(true) : '';
     };
   })
 
+  .filter('medianFromNowAndDate', function(filterTranslations) {
+    return function(input, options) {
+      const m = input && moment.unix(parseInt(input)+filterTranslations.MEDIAN_TIME_OFFSET);
+      return m && (m.fromNow() + (options && options.separator || ' | ')  + m.local().format(filterTranslations.DATE_PATTERN || 'YYYY-MM-DD HH:mm')) || '';
+    };
+  })
+
+
+  /* -- text filter -- */
+
   .filter('capitalize', function() {
     return function(input) {
       if (!input) return '';
diff --git a/www/js/services/currency-services.js b/www/js/services/currency-services.js
index 08447336..63a650c4 100644
--- a/www/js/services/currency-services.js
+++ b/www/js/services/currency-services.js
@@ -240,7 +240,7 @@ angular.module('cesium.currency.services', ['ngApi', 'cesium.bma.services'])
 
         .then(function(currentBlock) {
 
-          var now = moment().utc().unix();
+          const now = moment().utc().unix();
 
           if (cache) {
             if (currentBlock && (now - currentBlock.receivedAt) < 60/*1min*/) {
diff --git a/www/js/services/tx-services.js b/www/js/services/tx-services.js
index 5725d682..dcc48ef1 100644
--- a/www/js/services/tx-services.js
+++ b/www/js/services/tx-services.js
@@ -127,23 +127,25 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
 
       loadTx = function(pubkey, fromTime) {
         return $q(function(resolve, reject) {
-          var txHistory = [];
-          var udHistory = [];
-          var txPendings = [];
 
-          var nowInSec = moment().utc().unix();
+          const nowInSec = moment().utc().unix();
           fromTime = fromTime || fromTime || (nowInSec - csSettings.data.walletHistoryTimeSecond);
-          var processedTxMap = {};
-          var tx = {};
-
-          var _reduceTx = function(res){
-            _reduceTxAndPush(pubkey, res.history.sent, txHistory, processedTxMap);
-            _reduceTxAndPush(pubkey, res.history.received, txHistory, processedTxMap);
-            _reduceTxAndPush(pubkey, res.history.sending, txPendings, processedTxMap, true /*allow pendings*/);
-            _reduceTxAndPush(pubkey, res.history.pending, txPendings, processedTxMap, true /*allow pendings*/);
+          const tx = {
+            pendings: [],
+            validating: [],
+            history: [],
+            errors: []
           };
 
-          var jobs = [
+          const processedTxMap = {};
+          const _reduceTx = function (res) {
+            _reduceTxAndPush(pubkey, res.history.sent, tx.history, processedTxMap);
+            _reduceTxAndPush(pubkey, res.history.received, tx.history, processedTxMap);
+            _reduceTxAndPush(pubkey, res.history.sending, tx.pendings, processedTxMap, true /*allow pendings*/);
+            _reduceTxAndPush(pubkey, res.history.pending, tx.pendings, processedTxMap, true /*allow pendings*/);
+          };
+
+          const jobs = [
             // get current block
             csCurrency.blockchain.current(true),
 
@@ -158,7 +160,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
             // get TX from a given time
             if (fromTime > 0) {
               // Use slice, to be able to cache requests result
-              var sliceTime = csSettings.data.walletHistorySliceSecond;
+              const sliceTime = csSettings.data.walletHistorySliceSecond;
               fromTime = fromTime - (fromTime % sliceTime);
               for(var i = fromTime; i - sliceTime < nowInSec; i += sliceTime)  {
                 jobs.push(BMA.tx.history.times({pubkey: pubkey, from: i, to: i+sliceTime-1})
@@ -183,16 +185,16 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
                 BMA.ud.history({pubkey: pubkey})
                   .then(function(res){
                     udHistory = !res.history || !res.history.history ? [] :
-                      res.history.history.reduce(function(res, ud){
+                      _.forEach(res.history.history, function(ud){
                         if (ud.time < fromTime) return res; // skip to old UD
                         var amount = powBase(ud.amount, ud.base);
-                        return res.concat({
+                        udHistory.push({
                           time: ud.time,
                           amount: amount,
                           isUD: true,
                           block_number: ud.block_number
                         });
-                      }, []);
+                      });
                   }));*/
               // API extension
               jobs.push(
@@ -202,9 +204,9 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
                 })
                   .then(function(res) {
                     if (!res || !res.length) return;
-                    udHistory = res.reduce(function(res, hits) {
-                      return res.concat(hits);
-                    }, udHistory);
+                    _.forEach(res, function(hits) {
+                      tx.history.push(hits);
+                    });
                   })
 
                   .catch(function(err) {
@@ -218,31 +220,26 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
           // Execute jobs
           $q.all(jobs)
             .then(function(res){
-              var current = res[0];
+              const current = res[0];
 
               // sort by time desc
-              txHistory = txHistory.concat(udHistory).sort(function(tx1, tx2) {
+              tx.history.sort(function(tx1, tx2) {
                 return (tx2.time - tx1.time);
               });
-              tx.validating = txHistory.filter(function(tx) {
+              tx.validating = tx.history.filter(function(tx) {
                 return (tx.block_number > current.number - csSettings.data.blockValidityWindow);
               });
-              tx.history = (!tx.validating.length) ? txHistory : txHistory.slice(tx.validating.length);
+              // remove validating from history
+              if (tx.validating.length) {
+                tx.history.splice(0, tx.validating.length);
+              }
 
-              tx.pendings = txPendings;
               tx.fromTime = fromTime;
               tx.toTime = tx.history.length ? tx.history[0].time /*=max(tx.time)*/: fromTime;
 
               resolve(tx);
             })
-            .catch(function(err) {
-              tx.history = [];
-              tx.pendings = [];
-              tx.errors = [];
-              delete tx.fromTime;
-              delete tx.toTime;
-              reject(err);
-            });
+            .catch(reject);
         });
       },
 
@@ -267,7 +264,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
       loadSourcesAndBalance = function(pubkey) {
         return BMA.tx.sources({pubkey: pubkey})
           .then(function(res){
-            var result = {
+            const data = {
               sources: [],
               sourcesIndexByKey: [],
               balance: 0
@@ -275,18 +272,17 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
             if (res.sources && res.sources.length) {
               _.forEach(res.sources, function(src) {
                 src.consumed = false;
-                result.balance += powBase(src.amount, src.base);
+                data.balance += powBase(src.amount, src.base);
               });
-              addSources(result, res.sources);
+              addSources(data, res.sources);
             }
-            return result;
+            return data;
           });
       },
 
       loadData = function(pubkey, fromTime) {
-        var now = Date.now();
+        const now = Date.now();
 
-        var data = {};
         return $q.all([
 
             // Load Sources
@@ -297,25 +293,26 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
           ])
 
           .then(function(res) {
-            angular.merge(data, res[0]);
+            // Copy sources and balance
+            const data = res[0];
             data.tx = res[1];
 
-            var txPendings = [];
-            var txErrors = [];
-            var balanceFromSource = data.balance;
-            var balanceWithPending = data.balance;
+            const txPendings = [];
+            let txErrors = [];
+            const balanceFromSource = data.balance;
+            let balanceWithPending = data.balance;
 
             function _processPendingTx(tx) {
-              var consumedSources = [];
-              var valid = true;
+              const consumedSources = [];
+              let valid = true;
               if (tx.amount > 0) { // do not check sources from received TX
                 valid = false;
                 // TODO get sources from the issuer ?
               }
               else {
                 _.forEach(tx.inputs, function(input) {
-                  var inputKey = input.split(':').slice(2).join(':');
-                  var srcIndex = data.sourcesIndexByKey[inputKey];
+                  const inputKey = input.split(':').slice(2).join(':');
+                  const srcIndex = data.sourcesIndexByKey[inputKey];
                   if (angular.isDefined(srcIndex)) {
                     consumedSources.push(data.sources[srcIndex]);
                   }
@@ -342,9 +339,9 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
               }
             }
 
-            var txs = data.tx.pendings;
-            var retry = true;
-            while(txs && txs.length > 0) {
+            let txs = data.tx.pendings;
+            let retry = true;
+            while(txs && txs.length) {
               // process TX pendings
               _.forEach(txs, _processPendingTx);
 
@@ -359,17 +356,22 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
               }
             }
 
-            data.tx.pendings = txPendings;
-            data.tx.errors = txErrors;
+            data.tx = data.tx || {};
+            data.tx.pendings = txPendings.sort(function(tx1, tx2) {
+              return (tx2.time - tx1.time);
+            });
+            data.tx.errors = txErrors.sort(function(tx1, tx2) {
+              return (tx2.time - tx1.time);
+            });
             // Negative balance not allow (use only source's balance) - fix #769
             data.balance = (balanceWithPending < 0) ? balanceFromSource : balanceWithPending;
 
             // Will add uid (+ plugin will add name, avatar, etc. if enable)
-            return csWot.extendAll((data.tx.history || []).concat(data.tx.validating||[]).concat(data.tx.pendings||[]), 'pubkey');
-          })
-          .then(function() {
-            console.debug('[tx] TX and sources loaded in '+ (Date.now()-now) +'ms');
-            return data;
+            return csWot.extendAll((data.tx.history || []).concat(data.tx.validating||[]).concat(data.tx.pendings||[]), 'pubkey')
+              .then(function() {
+                console.debug('[tx] TX and sources loaded in '+ (Date.now()-now) +'ms');
+                return data;
+              })
           });
     },
 
@@ -396,39 +398,35 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
         loadData(pubkey, options.fromTime)
       ])
         .then(function(result){
+          const translations = result[0];
+          const currentBlock = result[1];
+          const currentTime = (currentBlock && currentBlock.medianTime) || moment().utc().unix();
+          const currency = currentBlock && currentBlock.currency;
 
-          var translations = result[0];
-
-          var currentBlock = result[1];
-          var currentTime = (currentBlock && currentBlock.medianTime) || moment().utc().unix();
-          var currency = currentBlock && currentBlock.currency;
-
-          result = result[2];
+          const data = result[2];
 
           // no TX
-          if (!result || !result.tx || !result.tx.history) {
+          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 formatPubkey = $filter('formatPubkey');
-              var formatDate = $filter('formatDate');
-              var formatDateForFile = $filter('formatDateForFile');
-              var formatSymbol = $filter('currencySymbolNoHtml');
+              const formatDecimal = $filter('formatDecimal');
+              const medianDate = $filter('medianDate');
+              const formatSymbol = $filter('currencySymbolNoHtml');
 
-              var headers = [
+              const headers = [
                 translations['ACCOUNT.HEADERS.TIME'],
                 translations['COMMON.UID'],
                 translations['COMMON.PUBKEY'],
                 translations['ACCOUNT.HEADERS.AMOUNT'] + ' (' + formatSymbol(currency) + ')',
                 translations['ACCOUNT.HEADERS.COMMENT']
               ];
-              var content = result.tx.history.reduce(function(res, tx){
+              const content = data.tx.history.concat(data.tx.validating).reduce(function(res, tx){
                 return res.concat([
-                    formatDate(tx.time),
+                    medianDate(tx.time),
                     tx.uid,
                     tx.pubkey,
                     formatDecimal(tx.amount/100),
@@ -436,7 +434,7 @@ angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
                   ].join(';') + '\n');
               }, [headers.join(';') + '\n']);
 
-              var file = new Blob(content, {type: 'text/plain; charset=utf-8'});
+              const file = new Blob(content, {type: 'text/plain; charset=utf-8'});
               FileSaver.saveAs(file, filename);
             });
         });
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index b5c237aa..5bb2202e 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -98,6 +98,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       data.tx = data.tx || {};
       data.tx.history = [];
       data.tx.pendings = [];
+      data.tx.validating = [];
       data.tx.errors = [];
       delete data.tx.fromTime;
       delete data.tx.toTime;
@@ -2246,7 +2247,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       options.restore =  angular.isDefined(options.restore) ? options.restore : (id === 'default');
 
       console.debug('[wallet] Starting...');
-      var now = Date.now();
+      const now = Date.now();
 
       startPromise = $q.all([
           csSettings.ready()
diff --git a/www/plugins/es/i18n/locale-en-GB.json b/www/plugins/es/i18n/locale-en-GB.json
index d6f5fc10..bbe9e86b 100644
--- a/www/plugins/es/i18n/locale-en-GB.json
+++ b/www/plugins/es/i18n/locale-en-GB.json
@@ -207,7 +207,7 @@
       "TX_SEARCH_FILTER": {
         "MEMBER_FLOWS": "<b class=\"ion-person\"></b> Members input/output",
         "EXISTING_TRANSACTION": "<b class=\"ion-card\"></b> Having transactions",
-        "PERIOD": "<b class=\"ion-clock\"></b> Between <b class=\"gray\">{{params[1]|formatDateShort}}</b> ({{params[1]|formatTime}}) and <b class=\"gray\">{{params[2]|formatDateShort}}</b> ({{params[2]|formatTime}})",
+        "PERIOD": "<b class=\"ion-clock\"></b> Between <b class=\"gray\">{{params[1]|medianDateShort}}</b> ({{params[1]|medianTime}}) and <b class=\"gray\">{{params[2]|medianDateShort}}</b> ({{params[2]|medianTime}})",
         "ISSUER": "<b class=\"ion-android-desktop\"></b> Computed by {{params[1]|formatPubkey}}",
         "TX_PUBKEY": "<b class=\"ion-card\"></b> Transactions concerning <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}"
       }
diff --git a/www/plugins/es/i18n/locale-en.json b/www/plugins/es/i18n/locale-en.json
index d6f5fc10..bbe9e86b 100644
--- a/www/plugins/es/i18n/locale-en.json
+++ b/www/plugins/es/i18n/locale-en.json
@@ -207,7 +207,7 @@
       "TX_SEARCH_FILTER": {
         "MEMBER_FLOWS": "<b class=\"ion-person\"></b> Members input/output",
         "EXISTING_TRANSACTION": "<b class=\"ion-card\"></b> Having transactions",
-        "PERIOD": "<b class=\"ion-clock\"></b> Between <b class=\"gray\">{{params[1]|formatDateShort}}</b> ({{params[1]|formatTime}}) and <b class=\"gray\">{{params[2]|formatDateShort}}</b> ({{params[2]|formatTime}})",
+        "PERIOD": "<b class=\"ion-clock\"></b> Between <b class=\"gray\">{{params[1]|medianDateShort}}</b> ({{params[1]|medianTime}}) and <b class=\"gray\">{{params[2]|medianDateShort}}</b> ({{params[2]|medianTime}})",
         "ISSUER": "<b class=\"ion-android-desktop\"></b> Computed by {{params[1]|formatPubkey}}",
         "TX_PUBKEY": "<b class=\"ion-card\"></b> Transactions concerning <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}"
       }
diff --git a/www/plugins/es/i18n/locale-es-ES.json b/www/plugins/es/i18n/locale-es-ES.json
index 8bd5909a..1702ab27 100644
--- a/www/plugins/es/i18n/locale-es-ES.json
+++ b/www/plugins/es/i18n/locale-es-ES.json
@@ -203,7 +203,7 @@
       "TX_SEARCH_FILTER": {
         "MEMBER_FLOWS": "Entradas/salidas de miembros",
         "EXISTING_TRANSACTION": "Con transacciones",
-        "PERIOD": "<b class=\"ion-clock\"></b> Entre el <b class=\"gray\">{{params[1]|formatDateShort}}</b> ({{params[1]|formatTime}}) y el <b class=\"gray\">{{params[2]|formatDateShort}}</b> ({{params[2]|formatTime}})",
+        "PERIOD": "<b class=\"ion-clock\"></b> Entre el <b class=\"gray\">{{params[1]|medianDateShort}}</b> ({{params[1]|medianTime}}) y el <b class=\"gray\">{{params[2]|medianDateShort}}</b> ({{params[2]|medianTime}})",
         "ISSUER": "<b class=\"ion-android-desktop\"></b> Calculado por <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}",
         "TX_PUBKEY": "<b class=\"ion-card\"></b> Transacciones que implican <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}"
       }
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index ad238519..14a9b96e 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -207,7 +207,7 @@
       "TX_SEARCH_FILTER": {
         "MEMBER_FLOWS": "<b class=\"ion-person\"></b> Entrées/sorties de membres",
         "EXISTING_TRANSACTION": "<b class=\"ion-card\"></b> Avec transactions",
-        "PERIOD": "<b class=\"ion-clock\"></b> Entre <b class=\"gray\">{{params[1]|formatDateShort}}</b> ({{params[1]|formatTime}}) et <b class=\"gray\">{{params[2]|formatDateShort}}</b> ({{params[2]|formatTime}})",
+        "PERIOD": "<b class=\"ion-clock\"></b> Entre <b class=\"gray\">{{params[1]|medianDateShort}}</b> ({{params[1]|medianTime}}) et <b class=\"gray\">{{params[2]|medianDateShort}}</b> ({{params[2]|medianTime}})",
         "ISSUER": "<b class=\"ion-android-desktop\"></b> Calculé par <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}",
         "TX_PUBKEY": "<b class=\"ion-card\"></b> Transactions concernant <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}"
       }
diff --git a/www/plugins/es/i18n/locale-it-IT.json b/www/plugins/es/i18n/locale-it-IT.json
index 37f4c3a6..9227ffe4 100644
--- a/www/plugins/es/i18n/locale-it-IT.json
+++ b/www/plugins/es/i18n/locale-it-IT.json
@@ -199,7 +199,7 @@
       "TX_SEARCH_FILTER": {
         "MEMBER_FLOWS": "<b class=\"ion-person\"></b> Input/output Membri",
         "EXISTING_TRANSACTION": "<b class=\"ion-card\"></b> hanno transazioni",
-        "PERIOD": "<b class=\"ion-clock\"></b> Tra <b class=\"gray\">{{params[1]|formatDateShort}}</b> ({{params[1]|formatTime}}) e <b class=\"gray\">{{params[2]|formatDateShort}}</b> ({{params[2]|formatTime}})",
+        "PERIOD": "<b class=\"ion-clock\"></b> Tra <b class=\"gray\">{{params[1]|medianDateShort}}</b> ({{params[1]|medianTime}}) e <b class=\"gray\">{{params[2]|medianDateShort}}</b> ({{params[2]|medianTime}})",
         "ISSUER": "<b class=\"ion-android-desktop\"></b> Calcolato da {{params[1]|formatPubkey}}",
         "TX_PUBKEY": "<b class=\"ion-card\"></b> Transazioni legate a  <b class=\"ion-key\"></b> {{params[1]|formatPubkey}}"
       }
diff --git a/www/plugins/graph/js/controllers/currency-controllers.js b/www/plugins/graph/js/controllers/currency-controllers.js
index 1454c571..d323ae44 100644
--- a/www/plugins/graph/js/controllers/currency-controllers.js
+++ b/www/plugins/graph/js/controllers/currency-controllers.js
@@ -151,10 +151,10 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
         var blocksPeriod = result.times[result.times.length-1] - result.times[0];
         var formatDate;
         if (blocksPeriod < 31557600/* less than 1 year */) {
-          formatDate = $filter('formatDateShort');
+          formatDate = $filter('medianDateShort');
         }
         else {
-          formatDate = $filter('formatDateMonth');
+          formatDate = $filter('formatDateMonth'); //see #683
         }
 
         var formatAmount =  $filter('formatDecimal');
@@ -338,7 +338,7 @@ function GpCurrencyDUController($scope, $q, $controller, $translate, gpColor, gp
         var blocksPeriod = result.times[result.times.length-1] - result.times[0];
         var dateFilter;
         if (blocksPeriod < 31557600/* less than 1 year */) {
-          dateFilter = $filter('formatDateShort');
+          dateFilter = $filter('medianDateShort');
         }
         else {
           dateFilter = $filter('formatDateMonth');
@@ -446,7 +446,7 @@ function GpCurrencyMembersCountController($scope, $controller, $q, $state, $tran
         var blocksPeriod = result.times[result.blocks.length-1] - result.times[0];
         var dateFilter;
         if (blocksPeriod < 31557600/* less than 1 year*/) {
-          dateFilter = $filter('formatDateShort');
+          dateFilter = $filter('medianDateShort');
         }
         else {
           dateFilter = $filter('formatDateMonth');
diff --git a/www/plugins/rml9/plugin-final.js b/www/plugins/rml9/plugin-final.js
index ee81968f..17f21d3f 100644
--- a/www/plugins/rml9/plugin-final.js
+++ b/www/plugins/rml9/plugin-final.js
@@ -111,9 +111,7 @@ angular.module('cesium.rml9.plugin', ['ngFileSaver', 'cesium.services'])
           if (!result || !result.tx || !result.tx.history) return; // no TX
 
           var formatDecimal = $filter('formatDecimal');
-          var formatPubkey = $filter('formatPubkey');
           var formatDate = $filter('formatDate');
-          var formatDateForFile = $filter('formatDateForFile');
 
           var headers = [translations['RML9.HEADERS.TIME'],
             translations['COMMON.UID'],
diff --git a/www/templates/blockchain/item_block.html b/www/templates/blockchain/item_block.html
index e5e78015..49714cdc 100644
--- a/www/templates/blockchain/item_block.html
+++ b/www/templates/blockchain/item_block.html
@@ -11,7 +11,7 @@
     <div class="col" style="min-width: 110px; max-width: 130px;">
       <h4 ng-class="{'gray': block.compacted, 'dark': !block.compacted}">
         <i class="ion-clock"></i>
-        {{:rebind:block.medianTime|formatDate}}
+        {{:rebind:block.medianTime|medianDate}}
       </h4>
       <h4 ng-if="!block.empty">
         <!-- joiners/leavers -->
diff --git a/www/templates/blockchain/item_block_empty_lg.html b/www/templates/blockchain/item_block_empty_lg.html
index 1fecc317..aa217c2a 100644
--- a/www/templates/blockchain/item_block_empty_lg.html
+++ b/www/templates/blockchain/item_block_empty_lg.html
@@ -8,7 +8,7 @@
     <div class="col">
       <h3 class="gray">
         <i class="ion-clock"></i>
-        {{:rebind:block.medianTime|formatDate}}
+        {{:rebind:block.medianTime|medianDate}}
       </h3>
     </div>
 
diff --git a/www/templates/blockchain/item_block_lg.html b/www/templates/blockchain/item_block_lg.html
index 466c6013..61227ca3 100644
--- a/www/templates/blockchain/item_block_lg.html
+++ b/www/templates/blockchain/item_block_lg.html
@@ -8,7 +8,7 @@
 
   <div class="row no-padding">
     <div class="col">
-      <h3 class="dark"><i class="ion-clock"></i> {{:rebind:block.medianTime|formatDate}}</h3>
+      <h3 class="dark"><i class="ion-clock"></i> {{:rebind:block.medianTime|medianDate}}</h3>
       <h4 class="gray">{{:rebind:'BLOCKCHAIN.HASH'|translate}} {{:rebind:block.hash|formatHash}}</h4>
     </div>
 
diff --git a/www/templates/blockchain/unlock_condition_popover.html b/www/templates/blockchain/unlock_condition_popover.html
index 14a59272..bdf4c293 100644
--- a/www/templates/blockchain/unlock_condition_popover.html
+++ b/www/templates/blockchain/unlock_condition_popover.html
@@ -29,7 +29,7 @@
       <div ng-if="condition.type=='CLTV'">
         <i class="icon ion-clock dark"></i>
         <span class="dark" ng-bind-html="::'BLOCKCHAIN.VIEW.TX_OUTPUT_FUNCTION.CLTV' | translate"></span>
-        {{::condition.value|formatDate}}
+        {{::condition.value|medianDate}}
       </div>
     </div>
   </ion-content>
diff --git a/www/templates/blockchain/view_block.html b/www/templates/blockchain/view_block.html
index f8c172d0..9b3af549 100644
--- a/www/templates/blockchain/view_block.html
+++ b/www/templates/blockchain/view_block.html
@@ -29,11 +29,7 @@
             <h3>
               <span class="dark">
                 <i class="icon ion-clock"></i>
-                {{formData.medianTime | formatDate}}
-              </span>
-              <span class="gray">
-                |
-                {{formData.medianTime | formatFromNow}}
+                {{formData.medianTime | medianFromNowAndDate}}
               </span>
             </h3>
 
diff --git a/www/templates/currency/items_network.html b/www/templates/currency/items_network.html
index ec7cb255..42924b37 100644
--- a/www/templates/currency/items_network.html
+++ b/www/templates/currency/items_network.html
@@ -3,7 +3,7 @@
             class="item-icon-left item-text-wrap">
     <i class="icon ion-clock"></i>
     <span class="col col-60" translate>CURRENCY.VIEW.MEDIAN_TIME</span>
-    <span class="badge badge-stable">{{formData.medianTime | formatDate}}</span>
+    <span class="badge badge-stable">{{formData.medianTime | medianDate}}</span>
   </ion-item>
 
 
diff --git a/www/templates/currency/items_parameters.html b/www/templates/currency/items_parameters.html
index 22d318c0..efb03f15 100644
--- a/www/templates/currency/items_parameters.html
+++ b/www/templates/currency/items_parameters.html
@@ -113,7 +113,7 @@
       <span translate>CURRENCY.VIEW.UD_REEVAL_TIME0</span>
       <span class="gray">(t0<sub>{{'CURRENCY.VIEW.REEVAL_SYMBOL'|translate}}</sub>)</span>
     </div>
-    <span class="item-note dark" ng-if="!loading">{{formData.udReevalTime0|formatDate}}
+    <span class="item-note dark" ng-if="!loading">{{formData.udReevalTime0|medianDate}}
     </span>
   </ion-item>
 
diff --git a/www/templates/network/popover_peer_info.html b/www/templates/network/popover_peer_info.html
index e3f558fd..c3ce01e1 100644
--- a/www/templates/network/popover_peer_info.html
+++ b/www/templates/network/popover_peer_info.html
@@ -48,7 +48,7 @@
           <i class="ion-clock"></i>
           {{'CURRENCY.VIEW.MEDIAN_TIME'|translate}}
         <div class="badge dark">
-          {{:rebind:formData.medianTime | formatDate}}
+          {{:rebind:formData.medianTime | medianDate}}
         </div>
       </div>
 
diff --git a/www/templates/wallet/item_tx.html b/www/templates/wallet/item_tx.html
index cd56e4d5..2e4956ed 100644
--- a/www/templates/wallet/item_tx.html
+++ b/www/templates/wallet/item_tx.html
@@ -5,14 +5,13 @@
 
 <div class="row no-padding">
   <div class="col no-padding">
-    <b class="ion-clock" ng-if="::pending"> </b>
-    <b class="ion-clock" ng-if="::validating"> </b>
-    <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}}
     </a>
-    <a class="gray" ui-sref="app.wot_identity({pubkey:tx.pubkey, uid:tx.uid})" ng-if="!tx.uid">
+    <a class="gray" ui-sref="app.wot_identity({pubkey:tx.pubkey, uid:tx.uid})" ng-if="::!tx.uid">
       <i class="ion-key gray"></i>
       {{::tx.pubkey | formatPubkey}}
+      <span ng-if="::tx.name"> - {{::tx.name | truncText:40}}</span>
     </a>
     <p class="dark visible-xs width-cup text-italic"
        data-toggle="tooltip"
@@ -23,10 +22,10 @@
     </p>
     <h4>
       <a ng-if="::!pending" class="gray underline" ui-sref="app.view_block({number: tx.block_number})">
-        {{::tx.time | formatFromNow}} | {{::tx.time | formatDate}}
+        {{::tx.time | medianFromNowAndDate: false}}
       </a>
       <span ng-if="::pending" class="gray">
-        {{::tx.time | formatFromNow}} | {{::tx.time | formatDate}}
+        {{::tx.time | medianFromNowAndDate: false}}
       </span>
     </h4>
   </div>
diff --git a/www/templates/wallet/item_ud.html b/www/templates/wallet/item_ud.html
index e4c95d22..e7f5a9f4 100644
--- a/www/templates/wallet/item_ud.html
+++ b/www/templates/wallet/item_ud.html
@@ -5,7 +5,7 @@
     <span class="energized" translate>COMMON.UNIVERSAL_DIVIDEND</span>
     <h4>
       <a class="gray underline" ui-sref="app.view_block({number: tx.block_number})">
-        {{::tx.time | formatFromNow}} | {{::tx.time | formatDate}}
+        {{::tx.time | medianFromNowAndDate}}
       </a>
     </h4>
   </div>
diff --git a/www/templates/wallet/tx_locked_outputs_popover.html b/www/templates/wallet/tx_locked_outputs_popover.html
index 55c88672..15146e04 100644
--- a/www/templates/wallet/tx_locked_outputs_popover.html
+++ b/www/templates/wallet/tx_locked_outputs_popover.html
@@ -54,7 +54,7 @@
         <div ng-if="condition.type=='CLTV'">
           <i class="icon ion-clock dark"></i>
           <span class="dark" ng-bind-html="::'BLOCKCHAIN.VIEW.TX_OUTPUT_FUNCTION.CLTV' | translate"></span>
-          {{::condition.value|formatDate}}
+          {{::condition.value|medianDate}}
         </div>
       </div>
     </div>
diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html
index 2b068e26..06ecc09a 100644
--- a/www/templates/wallet/view_wallet.html
+++ b/www/templates/wallet/view_wallet.html
@@ -126,7 +126,7 @@
             <span translate>COMMON.UID</span>
             <h5 class="dark" ng-if=":rebind:formData.sigDate">
               <span translate>WOT.REGISTERED_SINCE</span>
-              {{:rebind:formData.sigDate | formatDate}}
+              {{:rebind:formData.sigDate | medianDate}}
             </h5>
             <span class="badge badge-stable">{{:rebind:formData.uid}}</span>
           </ion-item>
diff --git a/www/templates/wallet/view_wallet_tx.html b/www/templates/wallet/view_wallet_tx.html
index 7a0c23ca..1c893d53 100644
--- a/www/templates/wallet/view_wallet_tx.html
+++ b/www/templates/wallet/view_wallet_tx.html
@@ -101,29 +101,37 @@
           </a>
 
           <!-- Pending transactions -->
-          <span class="item item-pending item-divider" ng-if="formData.tx.pendings && formData.tx.pendings.length">
-            {{:locale:'ACCOUNT.PENDING_TX'|translate}}
-          </span>
-
-          <div class="item item-pending item-tx item-icon-left"
-               ng-repeat="tx in formData.tx.pendings"
-               ng-init="pending=true;"
-               ng-include="'templates/wallet/item_tx.html'">
-          </div>
+          <ng-if ng-if="formData.tx.pendings.length">
+            <span class="item item-pending item-divider" >
+               <b class="ion-clock"> </b>
+              {{:locale:'ACCOUNT.PENDING_TX'|translate}}
+            </span>
+
+            <div class="item item-pending item-tx item-icon-left"
+                 ng-repeat="tx in formData.tx.pendings"
+                 ng-init="pending=true;"
+                 ng-include="'templates/wallet/item_tx.html'">
+            </div>
+          </ng-if>
 
           <!-- Validating transactions -->
-          <span class="item item-pending item-divider" ng-if="formData.tx.validating &&formData.tx.validating.length">
-            {{:locale:'ACCOUNT.VALIDATING_TX'|translate}}
-          </span>
-
-          <div class="item item-pending item-tx item-icon-left"
-               ng-repeat="tx in formData.tx.validating"
-               ng-init="validating=true;"
-               ng-include="'templates/wallet/item_tx.html'">
-          </div>
+          <ng-if ng-if="formData.tx.validating.length">
+            <span class="item item-pending item-divider">
+              <b class="icon ion-checkmark" style="font-size: 12px;"> </b>
+              <b class="icon-secondary ion-help" style="font-size: 12px; top: 2px; left: 11px;"> </b>
+              {{:locale:'ACCOUNT.VALIDATING_TX'|translate}}
+            </span>
+
+            <div class="item item-pending item-tx item-icon-left"
+                 ng-repeat="tx in formData.tx.validating"
+                 ng-init="validating=true;"
+                 ng-include="::'templates/wallet/item_tx.html'">
+            </div>
+          </ng-if>
 
           <!-- Last Transactions -->
           <span class="item item-divider" ng-if="!loading">
+            <b class="icon ion-checkmark"> </b>
             {{:locale:'ACCOUNT.LAST_TX'|translate}}
             <a id="helptip-wallet-tx" style="position: relative; bottom: 0; right: 0px;">&nbsp;</a>
           </span>
@@ -132,7 +140,10 @@
             <span class="gray">{{:locale:'ACCOUNT.NO_TX'|translate}}</span>
           </span>
 
+          <!-- Fix #780: do NOT use hash ash id
           <div ng-repeat="tx in formData.tx.history track by tx.hash"
+          -->
+          <div ng-repeat="tx in formData.tx.history"
              class="item item-tx item-icon-left"
              ng-include="::!tx.isUD ? 'templates/wallet/item_tx.html' : 'templates/wallet/item_ud.html'">
           </div>
diff --git a/www/templates/wot/item_certification.html b/www/templates/wot/item_certification.html
index 7af7b1de..2ee80c5b 100644
--- a/www/templates/wot/item_certification.html
+++ b/www/templates/wot/item_certification.html
@@ -16,7 +16,7 @@
               <h4 class="gray">
                 <i class="ion-key"></i>
                 {{::cert.pubkey | formatPubkey}}
-                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray"> | {{::cert.time|medianDate}}</span>
                 <span class="gray" ng-if="$root.settings.expertMode"> | {{::cert.pending ? 'WOT.SIGNED_ON_BLOCK' : 'WOT.WRITTEN_ON_BLOCK' | translate:cert}}</span>
               </h4>
             </span>
@@ -39,7 +39,7 @@
                   <i class="ion-key"></i>
                   {{::cert.pubkey | formatPubkey}}
                 </span>
-                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray"> | {{::cert.time|medianDate}}</span>
                 <span class="gray" ng-if="$root.settings.expertMode"> | {{::cert.pending ? 'WOT.SIGNED_ON_BLOCK' : 'WOT.WRITTEN_ON_BLOCK' | translate:cert}}</span>
               </h4>
             </span>
diff --git a/www/templates/wot/view_identity.html b/www/templates/wot/view_identity.html
index 3368d1b8..411bb4c8 100644
--- a/www/templates/wot/view_identity.html
+++ b/www/templates/wot/view_identity.html
@@ -103,7 +103,7 @@
           <span translate>COMMON.UID</span>
           <h5 class="dark" ng-if=":rebind:formData.sigDate ">
             <span translate>WOT.REGISTERED_SINCE</span>
-            {{:rebind:formData.sigDate| formatDate}}
+            {{:rebind:formData.sigDate|medianDate}}
           </h5>
           <span class="badge badge-stable">{{:rebind:formData.uid}}</span>
         </ion-item>
diff --git a/www/templates/wot/view_identity_tx.html b/www/templates/wot/view_identity_tx.html
index 9c9570b7..8c90520c 100644
--- a/www/templates/wot/view_identity_tx.html
+++ b/www/templates/wot/view_identity_tx.html
@@ -55,7 +55,7 @@
           </div>
 
           <!-- Errors transactions-->
-          <div class="item item-icon-left " ng-if="formData.tx.errors && formData.tx.errors.length">
+          <div class="item item-icon-left " ng-if="formData.tx.errors.length">
             <i class="icon ion-alert-circled"></i>
             {{:locale:'ACCOUNT.ERROR_TX'|translate}}
             <div class="badge badge-assertive">
@@ -64,27 +64,31 @@
           </div>
 
           <!-- Pending transactions -->
-          <span class="item item-pending item-divider" ng-if="formData.tx.pendings && formData.tx.pendings.length">
-            <i class="ion-clock"></i>
-            {{:locale:'ACCOUNT.PENDING_TX'|translate}}
-          </span>
-
-          <div class="item item-pending item-tx item-icon-left"
-               ng-repeat="tx in formData.tx.pendings"
-               ng-init="pending=true;"
-               ng-include="'templates/wallet/item_tx.html'">
-          </div>
+          <ng-if ng-if="formData.tx.pendings.length">
+            <span class="item item-pending item-divider" >
+              <i class="ion-clock"></i>
+              {{:locale:'ACCOUNT.PENDING_TX'|translate}}
+            </span>
+
+            <div class="item item-pending item-tx item-icon-left"
+                 ng-repeat="tx in formData.tx.pendings"
+                 ng-init="pending=true;"
+                 ng-include="::'templates/wallet/item_tx.html'">
+            </div>
+          </ng-if>
 
           <!-- Validating transactions -->
-          <span class="item item-pending item-divider" ng-if="formData.tx.validating &&formData.tx.validating.length">
-            {{:locale:'ACCOUNT.VALIDATING_TX'|translate}}
-          </span>
-
-          <div class="item item-pending item-tx item-icon-left"
-               ng-repeat="tx in formData.tx.validating"
-               ng-init="validating=true;"
-               ng-include="'templates/wallet/item_tx.html'">
-          </div>
+          <ng-if ng-if="formData.tx.validating.length">
+            <span class="item item-pending item-divider">
+              {{:locale:'ACCOUNT.VALIDATING_TX'|translate}}
+            </span>
+
+            <div class="item item-pending item-tx item-icon-left"
+                 ng-repeat="tx in formData.tx.validating"
+                 ng-init="validating=true;"
+                 ng-include="::'templates/wallet/item_tx.html'">
+            </div>
+          </ng-if>
 
           <span class="item item-divider" ng-if="!loading">
             {{:locale:'ACCOUNT.LAST_TX'|translate}}
@@ -92,7 +96,7 @@
           </span>
 
           <!-- iterate on each TX -->
-          <div ng-repeat="tx in formData.tx.history track by tx.hash"
+          <div ng-repeat="tx in formData.tx.history"
                class="item item-tx item-icon-left"
                ng-include="::!tx.isUD ? 'templates/wallet/item_tx.html' : 'templates/wallet/item_ud.html'">
           </div>
-- 
GitLab