From 42c4f3c9fe7f6a6fb220d76904fc56a0dc2026b2 Mon Sep 17 00:00:00 2001
From: Benoit Lavenier <benoit.lavenier@e-is.pro>
Date: Tue, 24 Dec 2019 14:01:04 +0100
Subject: [PATCH] [enh] Wot add a cache on load requirements [enh] Make sure to
 clean all cache when BMA node changed [enh] Cesium+: Add cache on some
 requests

---
 www/js/controllers/wot-controllers.js         |  2 +-
 www/js/services/bma-services.js               | 32 ++++++++----
 www/js/services/cache-services.js             |  9 ++--
 www/js/services/http-services.js              |  8 +--
 www/js/services/storage-services.js           |  7 ++-
 www/js/services/wallet-services.js            | 16 +++---
 www/js/services/wot-services.js               | 47 +++++++++++++----
 .../es/js/services/blockchain-services.js     |  4 +-
 www/plugins/es/js/services/http-services.js   | 50 +++++++++++++------
 .../es/js/services/profile-services.js        |  8 +--
 www/plugins/es/js/services/wot-services.js    | 16 ++++--
 .../js/controllers/account-controllers.js     | 21 ++++++++
 12 files changed, 154 insertions(+), 66 deletions(-)

diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js
index 2f6115fa..3b35469a 100644
--- a/www/js/controllers/wot-controllers.js
+++ b/www/js/controllers/wot-controllers.js
@@ -334,7 +334,7 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
 
     return  csWot.newcomers(offset, size)
       .then(function(res){
-        if ($scope.search.type != 'newcomers') return false; // could have change
+        if ($scope.search.type !== 'newcomers') return false; // could have change
         $scope.doDisplayResult(res && res.hits, offset, size, res && res.total);
         return true;
       })
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index f57661ff..8c19c2e9 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -154,7 +154,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
 
         if (!that.started) {
           if (!that._startPromise) {
-            console.error('[BMA] Trying to get [{0}] before start()...'.format(path));
+            console.warn('[BMA] Trying to get [{0}] before start(). Waiting...'.format(path));
           }
           return that.ready().then(function() {
             return getRequestFn(params);
@@ -178,7 +178,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
             if (err && err.ucode == exports.errorCodes.HTTP_LIMITATION) {
               // If max number of retry not reach
               if (execCount <= exports.constants.LIMIT_REQUEST_COUNT) {
-                if (execCount == 1) {
+                if (execCount === 1) {
                   console.warn("[BMA] Too many HTTP requests: Will wait then retry...");
                   // Update the loading message (if exists)
                   UIUtils.loading.update({template: "COMMON.LOADING_WAIT"});
@@ -422,13 +422,17 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
           all: get('/wot/members', csHttp.cache.LONG),
           pending: get('/wot/pending', csHttp.cache.SHORT)
         },
-        requirements: get('/wot/requirements/:pubkey', csHttp.cache.LONG),
+        requirements: function(params, withCache) {
+          // No cache by default
+          if (withCache !== true) return exports.raw.requirementsNoCache(params);
+          return exports.raw.requirementsWithCache(params);
+        },
         add: post('/wot/add'),
         certify: post('/wot/certify'),
         revoke: post('/wot/revoke')
       },
       blockchain: {
-        parameters: get('/blockchain/parameters', csHttp.cache.LONG),
+        parameters: get('/blockchain/parameters', csHttp.cache.VERY_LONG),
         block: get('/blockchain/block/:block', csHttp.cache.SHORT),
         blocksSlice: get('/blockchain/blocks/:count/:from'),
         current: get('/blockchain/current', csHttp.cache.SHORT),
@@ -436,7 +440,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
         stats: {
           ud: get('/blockchain/with/ud', csHttp.cache.MEDIUM),
           tx: get('/blockchain/with/tx'),
-          newcomers: get('/blockchain/with/newcomers'),
+          newcomers: get('/blockchain/with/newcomers', csHttp.cache.MEDIUM),
           hardship: get('/blockchain/hardship/:pubkey'),
           difficulties: get('/blockchain/difficulties')
         }
@@ -457,7 +461,10 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
       },
       uri: {},
       version: {},
-      raw: {}
+      raw: {
+        requirementsWithCache: get('/wot/requirements/:pubkey', csHttp.cache.LONG),
+        requirementsNoCache: get('/wot/requirements/:pubkey')
+      }
     };
     exports.regex = exports.regexp; // deprecated
 
@@ -642,11 +649,14 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
 
     exports.copy = function(otherNode) {
       var wasStarted = that.started;
-      // Stop, if need
-      if (wasStarted) that.stop();
-      that.init(otherNode.host, otherNode.port, otherNode.useSsl, that.useCache/*keep original value*/);
-      // Restart (only if was already started)
-      return wasStarted ? that.start() : $q.when();
+
+      var server = csHttp.getUrl(otherNode.host, otherNode.port, ''/*path*/, otherNode.useSsl);
+      var hasChanged = (server !== that.url);
+      if (hasChanged) {
+        that.init(otherNode.host, otherNode.port, otherNode.useSsl, that.useCache/*keep original value*/);
+        // Restart (only if was already started)
+        return wasStarted ? that.restart() : $q.when();
+      }
     };
 
     exports.wot.member.uids = function() {
diff --git a/www/js/services/cache-services.js b/www/js/services/cache-services.js
index b96f15cf..3ea97aca 100644
--- a/www/js/services/cache-services.js
+++ b/www/js/services/cache-services.js
@@ -3,8 +3,10 @@ angular.module('cesium.cache.services', ['angular-cache'])
 .factory('csCache', function($http, $window, csSettings, CacheFactory) {
   'ngInject';
 
+  console.log('')
   var
     constants = {
+      VERY_LONG: 54000000, /*15 days*/
       LONG: 1 * 60  * 60 * 1000 /*1 hour*/,
       MEDIUM: 5  * 60 * 1000 /*5 min*/,
       SHORT: csSettings.defaultSettings.cacheTimeMs // around 1min
@@ -68,7 +70,7 @@ angular.module('cesium.cache.services', ['angular-cache'])
   function clearFromPrefix(cachePrefix) {
     _.forEach(_.keys(cacheNames), function(cacheName) {
       if (cacheName.startsWith(cachePrefix)) {
-        var cache = CacheFactory.get(cacheNames);
+        var cache = CacheFactory.get(cacheName);
         if (cache) {
           cache.removeAll();
         }
@@ -80,10 +82,7 @@ angular.module('cesium.cache.services', ['angular-cache'])
     get: getOrCreateCache,
     clear: clearFromPrefix,
     clearAll: clearAllCaches,
-    constants: {
-      LONG : constants.LONG,
-      SHORT: constants.SHORT
-    }
+    constants: constants
   };
 })
 ;
diff --git a/www/js/services/http-services.js b/www/js/services/http-services.js
index fecede4c..94e57bfc 100644
--- a/www/js/services/http-services.js
+++ b/www/js/services/http-services.js
@@ -7,7 +7,7 @@ angular.module('cesium.http.services', ['cesium.cache.services'])
 
   var
     sockets = [],
-    cachePrefix = 'csHttp'
+    cachePrefix = 'csHttp-'
   ;
 
   if (!timeout) {
@@ -48,14 +48,14 @@ angular.module('cesium.http.services', ['cesium.cache.services'])
 
   function prepare(url, params, config, callback) {
     var pkeys = [], queryParams = {}, newUri = url;
-    if (typeof params == 'object') {
+    if (typeof params === 'object') {
       pkeys = _.keys(params);
     }
 
     _.forEach(pkeys, function(pkey){
       var prevURI = newUri;
       newUri = newUri.replace(':' + pkey, params[pkey]);
-      if (prevURI == newUri) {
+      if (prevURI === newUri) {
         queryParams[pkey] = params[pkey];
       }
     });
@@ -89,7 +89,7 @@ angular.module('cesium.http.services', ['cesium.cache.services'])
     };
   }
 
-  function getResourceWithCache(host, port, path, useSsl, maxAge, autoRefresh, forcedTimeout) {
+  function getResourceWithCache(host, port, path, useSsl, maxAge, autoRefresh, forcedTimeout, cachePrefix) {
     var url = getUrl(host, port, path, useSsl);
     maxAge = maxAge || csCache.constants.LONG;
     //console.debug('[http] will cache ['+url+'] ' + maxAge + 'ms' + (autoRefresh ? ' with auto-refresh' : ''));
diff --git a/www/js/services/storage-services.js b/www/js/services/storage-services.js
index a134ad66..4f92253d 100644
--- a/www/js/services/storage-services.js
+++ b/www/js/services/storage-services.js
@@ -64,6 +64,11 @@ angular.module('cesium.storage.services', [ 'cesium.config'])
       return $q.when();
     };
 
+    exports.standard.remove = function(key, value) {
+      exports.standard.storage.removeItem(key);
+      return $q.when();
+    };
+
     exports.standard.get = function(key, defaultValue) {
       return $q.when(exports.standard.storage[key] || defaultValue);
     };
@@ -165,7 +170,7 @@ angular.module('cesium.storage.services', [ 'cesium.config'])
         });
       }
 
-      // Fallback to session storage (locaStorage could have been disabled on some browser)
+      // Fallback to session storage (localStorage could have been disabled on some browser)
       else {
         console.debug('[storage] Starting {session} storage...');
         // Set standard storage as default
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index b787c38b..54773068 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -688,12 +688,12 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       return data;
     },
 
-    loadRequirements = function() {
+    loadRequirements = function(withCache) {
       // Clean existing events
       cleanEventsByContext('requirements');
 
       // Get requirements
-      return csWot.loadRequirements(data);
+      return csWot.loadRequirements(data, withCache);
     },
 
     loadTxAndSources = function(fromTime) {
@@ -896,7 +896,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       return $q.all([
 
           // Get requirements
-          loadRequirements()
+          loadRequirements(true)
             .then(function(data) {
               if (data.requirements && (data.requirements.isMember || data.requirements.wasMember)) {
                 // Load sigStock
@@ -973,7 +973,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
         cleanEventsByContext('requirements');
 
         jobs.push(
-          loadRequirements()
+          loadRequirements(true)
 
             // Add wallet events
             .then(addEvents)
@@ -1532,7 +1532,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
         .then(function () {
           if (!!needToLoadRequirements) {
             // Refresh membership data (if need)
-            return loadRequirements()
+            return loadRequirements(false/*no cache*/)
 
               // Add wallet events
               .then(addEvents);
@@ -1583,7 +1583,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
           })
           .then(function() {
             return $timeout(function() {
-              return loadRequirements();
+              return loadRequirements(false /*no cache*/);
             }, 1000); // waiting for node to process membership doc
           })
 
@@ -1838,7 +1838,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
         .then(function() {
 
           return $timeout(function() {
-            return loadRequirements();
+            return loadRequirements(false/*no cache*/);
           }, 1000); // waiting for node to process membership doc
         })
 
@@ -1865,7 +1865,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
         .then(function(res) {
           if (isLogin()) {
             return $timeout(function () {
-              return loadRequirements();
+              return loadRequirements(false/*no cache*/);
             }, 1000) // waiting for node to process membership doc
 
              // Add wallet events
diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js
index 4d8385c1..36de50d0 100644
--- a/www/js/services/wot-services.js
+++ b/www/js/services/wot-services.js
@@ -2,14 +2,16 @@
 angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.crypto.services', 'cesium.utils.services',
   'cesium.settings.services'])
 
-.factory('csWot', function($q, $timeout, BMA, Api, CacheFactory, csConfig, csCurrency, csSettings, csCache) {
+.factory('csWot', function($rootScope, $q, $timeout, BMA, Api, CacheFactory, csConfig, csCurrency, csSettings, csCache) {
   'ngInject';
 
   function factory(id) {
 
     var
       api = new Api(this, "csWot-" + id),
-      identityCache = csCache.get('csWot-idty-', csCache.constants.SHORT),
+      cachePrefix = 'csWot-',
+      identityCache = csCache.get(cachePrefix + 'idty-', csCache.constants.MEDIUM),
+      requirementsCache = csCache.get(cachePrefix + 'requirements-', csCache.constants.MEDIUM),
 
       // Add id, and remove duplicated id
       _addUniqueIds = function(idties) {
@@ -174,8 +176,18 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
           });
       },
 
-      loadRequirements = function(data) {
-        if (!data || (!data.pubkey && !data.uid)) return $q.when(data);
+      loadRequirements = function(inputData, withCache) {
+        if (!inputData || (!inputData.pubkey && !inputData.uid)) return $q.when(inputData);
+
+        var cacheKey =  inputData.pubkey||inputData.uid;
+        var data = (withCache !== false) ? requirementsCache.get(cacheKey) : null;
+        if (data) {
+          console.debug("[wot] Requirements " + cacheKey + " found in cache");
+          // Update data with cache
+          angular.merge(inputData, data);
+          return $q.when(data)
+        }
+        data = {pubkey: inputData.pubkey, uid: inputData.uid};
 
         var now = Date.now();
 
@@ -184,7 +196,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
           csCurrency.get(),
 
           // Get requirements
-          BMA.wot.requirements({pubkey: data.pubkey||data.uid})
+          BMA.wot.requirements({pubkey: data.pubkey||data.uid}, false/*no cache*/)
             .then(function(res) {
               return _fillIdentitiesMeta(res && res.identities);
             })
@@ -244,18 +256,23 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
               });
             }
 
+            /// Save to cache
+            requirementsCache.put(cacheKey, data);
+
+            angular.merge(inputData, data); // Update the input data
+
             console.debug("[wot] Requirements for '{0}' loaded in {1}ms".format((data.pubkey && data.pubkey.substring(0,8))||data.uid, Date.now() - now));
 
-            return data;
+            return inputData;
           })
           .catch(function(err) {
-            _resetRequirements(data);
+            _resetRequirements(inputData);
             // If not a member: continue
             if (!!err &&
                 (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER ||
                  err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) {
-              data.requirements.loaded = true;
-              return data;
+              inputData.requirements.loaded = true;
+              return inputData;
             }
             throw err;
           });
@@ -660,7 +677,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
 
         // Check cached data
         if (pubkey) {
-          data = (!angular.isDefined(options.cache) || options.cache) ? identityCache.get(pubkey) : null;
+          data = (options.cache !== false) ? identityCache.get(pubkey) : null;
           if (data && (!uid || data.uid === uid) && (!options.blockUid || data.blockUid === options.blockUid)) {
             console.debug("[wot] Identity " + pubkey.substring(0, 8) + " found in cache");
             return $q.when(data);
@@ -707,7 +724,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
               }),
 
             // Get requirements
-            loadRequirements(data),
+            loadRequirements(data, options.cache !== false),
 
             // Get identity using lookup
             loadIdentityByLookup(pubkey, uid)
@@ -1160,6 +1177,11 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
         event.messageParams = event.messageParams || {};
         data.events = data.events || [];
         data.events.push(event);
+      },
+
+      cleanCache = function() {
+        console.debug("[wot] Cleaning cache...");
+        csCache.clear(cachePrefix);
       }
     ;
 
@@ -1167,6 +1189,9 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
     api.registerEvent('data', 'load');
     api.registerEvent('data', 'search');
 
+    // Listen if node changed
+    BMA.api.node.on.restart($rootScope, cleanCache, this);
+
     return {
       id: id,
       load: loadData,
diff --git a/www/plugins/es/js/services/blockchain-services.js b/www/plugins/es/js/services/blockchain-services.js
index 824aa3a4..9116acf0 100644
--- a/www/plugins/es/js/services/blockchain-services.js
+++ b/www/plugins/es/js/services/blockchain-services.js
@@ -31,9 +31,9 @@ angular.module('cesium.es.blockchain.services', ['cesium.services', 'cesium.es.h
         block: {},
         raw: {
           block: {
-            search: esHttp.post('/:currency/block/_search'),
+            search: esHttp.post('/:currency/block/_search', esHttp.cache.SHORT),
             searchText: esHttp.get('/:currency/block/_search?q=:text'),
-            get: esHttp.get('/:currency/block/:number/_source')
+            get: esHttp.get('/:currency/block/:number/_source', esHttp.cache.SHORT)
           }
         },
         regexp: {
diff --git a/www/plugins/es/js/services/http-services.js b/www/plugins/es/js/services/http-services.js
index d3779255..7e619a0b 100644
--- a/www/plugins/es/js/services/http-services.js
+++ b/www/plugins/es/js/services/http-services.js
@@ -4,7 +4,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
  * Elastic Search Http
  */
 .factory('esHttp', function($q, $timeout, $rootScope, $state, $sce, $translate, $window, $filter,
-                            CryptoUtils, UIUtils, csHttp, csConfig, csSettings, BMA, csWallet, csPlatform, Api) {
+                            CryptoUtils, UIUtils, csHttp, csConfig, csSettings, csCache, BMA, csWallet, csPlatform, Api) {
   'ngInject';
 
   // Allow to force SSL connection with port different from 443
@@ -14,10 +14,11 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
     console.debug('[ES] [https] Enable SSL (forced by config or detected in URL)');
   }
 
-  function EsHttp(host, port, useSsl) {
+  function EsHttp(host, port, useSsl, useCache) {
 
     var
       that = this,
+      cachePrefix = 'esHttp-',
       constants = {
         ES_USER_API_ENDPOINT: 'ES_USER_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))',
         MAX_UPLOAD_BODY_SIZE: csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.maxUploadBodySize || 2097152 /*=2M*/
@@ -42,9 +43,10 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
     that.started = false;
     that.init = init;
 
-    init(host, port, useSsl);
+    init(host, port, useSsl, useCache);
+    that.useCache = angular.isDefined(useCache) ? useCache : false; // need here because used in get() function
 
-    function init(host, port, useSsl) {
+    function init(host, port, useSsl, useCache) {
       // Use settings as default
       if (!host && csSettings.data) {
         host = host || (csSettings.data.plugins && csSettings.data.plugins.es ? csSettings.data.plugins.es.host : null);
@@ -56,6 +58,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
       that.host = host;
       that.port = port || ((useSsl || forceUseSsl) ? 443 : 80);
       that.useSsl = angular.isDefined(useSsl) ? useSsl : (that.port == 443 || forceUseSsl);
+
       that.server = csHttp.getServer(host, port);
     }
 
@@ -126,6 +129,8 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
         sock.close();
       });
       that.cache = _emptyCache();
+
+      csCache.clear(cachePrefix);
     };
 
     that.copy = function(otherNode) {
@@ -144,28 +149,36 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
       return csHttp.getUrl(that.host, that.port, path, that.useSsl);
     };
 
-    that.get = function (path) {
+    that.get = function (path, cacheTime) {
 
-      var getRequest = function(params) {
+      cacheTime = that.useCache && cacheTime;
+      var cacheKey = path + (cacheTime ? ('#'+cacheTime) : '');
+
+      var getRequestFn = function(params) {
         if (!that.started) {
           if (!that._startPromise) {
-            console.error('[ES] [http] Trying to get [{0}] before start()...'.format(path));
+            console.error('[ES] [http] Trying to get [{0}] before start(). Waiting...'.format(path));
           }
           return that.ready().then(function(start) {
             if (!start) return $q.reject('ERROR.ES_CONNECTION_ERROR');
-            return getRequest(params); // loop
+            return getRequestFn(params); // loop
           });
         }
 
-        var request = that.cache.getByPath[path];
+        var request = that.cache.getByPath[cacheKey];
         if (!request) {
-          request =  csHttp.get(that.host, that.port, path, that.useSsl);
-          that.cache.getByPath[path] = request;
+          if (cacheTime) {
+            request =  csHttp.getWithCache(that.host, that.port, path, that.useSsl, cacheTime, null, null, cachePrefix);
+          }
+          else {
+            request =  csHttp.get(that.host, that.port, path, that.useSsl);
+          }
+          that.cache.getByPath[cacheKey] = request;
         }
         return request(params);
       };
 
-      return getRequest;
+      return getRequestFn;
     };
 
     that.post = function(path) {
@@ -185,7 +198,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
           request =  csHttp.post(that.host, that.port, path, that.useSsl);
           that.cache.postByPath[path] = request;
         }
-        return request(obj, params);
+        return request(obj, params)
       };
       return postRequest;
     };
@@ -490,6 +503,10 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
                   // Send data
                   return postRequest(str, params)
                     .then(function (id){
+
+                      // Clear cache
+                      csCache.clear(cachePrefix);
+
                       return id;
                     })
                     .catch(function(err) {
@@ -676,6 +693,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
         parseAsHtml: parseAsHtml,
         findObjectInTree: findObjectInTree
       },
+      cache: csHttp.cache,
       constants: constants
     };
     exports.constants.regexp = regexp;
@@ -683,10 +701,10 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
   }
 
 
-  var service = new EsHttp();
+  var service = new EsHttp(undefined, undefined, undefined, true);
 
-  service.instance = function(host, port, useSsl) {
-    return new EsHttp(host, port, useSsl);
+  service.instance = function(host, port, useSsl, useCache) {
+    return new EsHttp(host, port, useSsl, useCache);
   };
 
   return service;
diff --git a/www/plugins/es/js/services/profile-services.js b/www/plugins/es/js/services/profile-services.js
index a2d831aa..c36f3123 100644
--- a/www/plugins/es/js/services/profile-services.js
+++ b/www/plugins/es/js/services/profile-services.js
@@ -19,10 +19,10 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
 
     that.raw = {
       getFields: esHttp.get('/user/profile/:id?&_source_exclude=avatar._content&_source=:fields'),
-      get: esHttp.get('/user/profile/:id?&_source_exclude=avatar._content'),
-      getAll: esHttp.get('/user/profile/:id'),
-      search: esHttp.post('/user/profile/_search'),
-      mixedSearch: esHttp.post('/user,page,group/profile,record/_search')
+      get: esHttp.get('/user/profile/:id?&_source_exclude=avatar._content', esHttp.cache.MEDIUM),
+      getAll: esHttp.get('/user/profile/:id', esHttp.cache.MEDIUM),
+      search: esHttp.post('/user/profile/_search', esHttp.cache.MEDIUM),
+      mixedSearch: esHttp.post('/user,page,group/profile,record/_search', esHttp.cache.MEDIUM)
     };
 
     function getAvatarAndName(pubkey) {
diff --git a/www/plugins/es/js/services/wot-services.js b/www/plugins/es/js/services/wot-services.js
index 7e8179aa..567aab08 100644
--- a/www/plugins/es/js/services/wot-services.js
+++ b/www/plugins/es/js/services/wot-services.js
@@ -1,16 +1,23 @@
 angular.module('cesium.es.wot.services', ['ngResource', 'cesium.es.http.services'])
 
-  .factory('esWot', function($q, esHttp) {
+  .factory('esWot', function($q, esHttp, csCache) {
     'ngInject';
 
     var
+      cachePrefix = 'esWot-',
+      membershipsCache = csCache.get(cachePrefix + 'memberships-', csCache.constants.MEDIUM),
       raw = {
           user: {
-            event: esHttp.post('/user/event/_search?pretty')
+            event: esHttp.post('/user/event/_search')
           }
         },
 
-      loadMemberships = function(pubkey) {
+
+      loadMemberships = function(pubkey, options) {
+        options = options || {};
+
+        var result = (options.cache !== false) ? membershipsCache.get(pubkey) : null;
+        if (result) return $q.when(result);
 
         // Get user events on membership state
         var request = {
@@ -62,6 +69,9 @@ angular.module('cesium.es.wot.services', ['ngResource', 'cesium.es.http.services
               });
             }
 
+            // Put in the cache
+            membershipsCache.put(pubkey, result);
+
             return result;
           });
       };
diff --git a/www/plugins/graph/js/controllers/account-controllers.js b/www/plugins/graph/js/controllers/account-controllers.js
index 7fcb2c61..5f5b3fa9 100644
--- a/www/plugins/graph/js/controllers/account-controllers.js
+++ b/www/plugins/graph/js/controllers/account-controllers.js
@@ -17,6 +17,15 @@ angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.se
           }
         })
 
+        .extendState('app.view_wallet_tx_by_id', {
+          points: {
+            'buttons': {
+              templateUrl: "plugins/graph/templates/account/view_wallet_tx_extend.html",
+              controller: 'GpExtendCtrl'
+            }
+          }
+        })
+
         .extendState('app.wot_identity', {
           points: {
             'buttons': {
@@ -49,6 +58,18 @@ angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.se
           }
         })
 
+        .state('app.view_wallet_stats_by_id', {
+          url: "/wallets/:id/stats?t&stepUnit&hide&scale",
+          views: {
+            'menuContent': {
+              templateUrl: "plugins/graph/templates/account/view_stats.html"
+            }
+          },
+          data: {
+            auth: true
+          }
+        })
+
         .state('app.wot_identity_stats', {
           url: "/wot/:pubkey/stats?t&stepUnit&hide&scale",
           views: {
-- 
GitLab