diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json
index b1b6cc4d1a777c0ff23053e84a83ee75688a9b3c..95c563544db5fc4c60edefb303ff199abe4fb5f6 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -94,7 +94,7 @@
     "NODE_HELP": "server.domain.com:port",
     "USE_LOCAL_STORAGE": "Enable local storage",
     "HISTORY_SETTINGS": "My Account",
-    "DISPLAY_UD_HISTORY": "Show received dividends ?",
+    "DISPLAY_UD_HISTORY": "Display produced dividends ?",
     "AUTHENTICATION_SETTINGS": "Authentication",
     "REMEMBER_ME": "Remember me",
     "PLUGINS_SETTINGS": "Extensions",
@@ -169,14 +169,14 @@
       "SUMMARY": "Received certifications",
       "LIST": "Détails of received certifications",
       "RECEIVED": "Received certifications",
-      "RECEIVED_BY": "Certifications received by {{uid}}",
+      "RECEIVED_BY": "Certifications received by {{uid}}"
     },
     "GIVEN_CERTIFICATIONS": {
       "TITLE": "{{uid}} - Certifications sent",
       "SUMMARY": "Sent certifications",
       "LIST": "Détails of sent certifications",
       "SENT": "Sent certifications",
-      "SENT_BY": "Certifications sent by {{uid}}",
+      "SENT_BY": "Certifications sent by {{uid}}"
     }
   },
   "LOGIN": {
@@ -194,6 +194,9 @@
     "BALANCE": "Balance",
     "LAST_TX": "Last transactions",
     "NO_TX": "No transaction",
+    "SHOW_MORE_TX": "Show more",
+    "SHOW_ALL_TX": "Show all",
+    "TX_FROM_DATE": "(current limit to {{fromTime|formatFromNowShort}})",
     "PENDING_TX": "Pending transactions",
     "ERROR_TX": "Transaction not executed",
     "ERROR_TX_SENT": "Sent transactions",
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index 597f3c972259955ad7a5cdbd13ce8db551c72596..70b459a0c18d71d158738ef5bc9174351fa31c83 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -59,7 +59,7 @@
     "CURRENCY": "Monnaie",
     "CURRENCIES": "Monnaies",
     "ACCOUNT": "Mon compte",
-    "TRANSFER": "Payer",
+    "TRANSFER": "Virement",
     "SCAN": "Scanner",
     "SETTINGS": "Paramètres"
   },
@@ -94,7 +94,7 @@
     "NODE_HELP": "server.domain.com:port",
     "USE_LOCAL_STORAGE": "Activer le stockage local",
     "HISTORY_SETTINGS": "Mon compte",
-    "DISPLAY_UD_HISTORY": "Voir les dividendes reçus ?",
+    "DISPLAY_UD_HISTORY": "Afficher les dividendes produits ?",
     "AUTHENTICATION_SETTINGS": "Authentification",
     "REMEMBER_ME": "Se souvenir de moi",
     "PLUGINS_SETTINGS": "Extensions",
@@ -194,6 +194,9 @@
     "BALANCE": "Solde",
     "LAST_TX": "Dernières transactions",
     "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",
     "ERROR_TX": "Transactions non executées",
     "ERROR_TX_SENT": "Transactions envoyées",
@@ -243,7 +246,7 @@
     "BTN_SEND": "Envoyer",
     "BTN_ADD_COMMENT": "Saisir un commentaire ?",
     "MODAL": {
-      "TITLE": "Paiement"
+      "TITLE": "Virement"
     }
   },
   "ERROR": {
@@ -270,7 +273,7 @@
     "GET_CURRENCY_PARAMETER": "Echec de la récupération des règles de la monnaie.",
     "GET_CURRENCIES_FAILED": "Impossible de charger la liste des monnaies. Veuillez ressayer plus tard.",
     "GET_CURRENCY_FAILED": "Chargement de la monnaie impossible.",
-    "SEND_TX_FAILED": "Echec du paiement.",
+    "SEND_TX_FAILED": "Echec du virement.",
     "ALL_SOURCES_USED": "Veuillez attendre le calcul du prochain bloc (Toutes vos sources de monnaie ont été utilisées).",
     "NOT_ENOUGH_SOURCES": "Pas assez de change pour envoyer ce montant en une seule transaction.<br/>Montant maximum : {{amount}} {{unit}}<sub>{{subUnit}}</sub>.",
     "ACCOUNT_CREATION_FAILED": "Echec de la création du compte membre.",
diff --git a/www/index.html b/www/index.html
index d7238ef14342127940998ebaee68e0fd33089b9d..081c514ab350b36613146250343b9566b059daaa 100644
--- a/www/index.html
+++ b/www/index.html
@@ -106,6 +106,7 @@
       <script src="dist/dist_js/plugins/es/js/services/social-services.js"></script>
       <script src="dist/dist_js/plugins/es/js/services/user-services.js"></script>
       <script src="dist/dist_js/plugins/es/js/services/message-services.js"></script>
+      <script src="dist/dist_js/plugins/es/js/services/modal-services.js"></script>
       <script src="dist/dist_js/plugins/es/js/controllers/common-controllers.js"></script>
       <script src="dist/dist_js/plugins/es/js/controllers/settings-controllers.js"></script>
       <script src="dist/dist_js/plugins/es/js/controllers/wot-controllers.js"></script>
diff --git a/www/js/app.js b/www/js/app.js
index 78b96e1022e008bcbd65520ca96655a43cda6ec5..21557e0eaac4a5be50a38203db4d40d06df91302 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -57,6 +57,7 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngAnimate',
     };
   })
 
+
   .filter('formatDuration', function() {
     return function(input) {
       return input ? moment(moment().utc().valueOf() + parseInt(input)*1000).startOf('minute').fromNow() : '';
@@ -67,7 +68,13 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngAnimate',
     return function(input) {
       if (!input) {return null;}
       var duration = moment(0).startOf('minute').from(moment(parseInt(input)*1000), true);
-      return duration.split(" ").slice(-1)[0]; // keep only the last word (e.g. remove "un" "a"...)
+      return duration.split(' ').slice(-1)[0]; // keep only last words (e.g. remove "un" "a"...)
+    };
+  })
+
+  .filter('formatFromNowShort', function() {
+    return function(input) {
+      return input ? moment(parseInt(input)*1000).startOf('minute').fromNow(true) : '';
     };
   })
 
diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js
index 0ef63ee87f5edbda7d8ca240df9e22f30a785406..ea092632f153c23569bd76224163bdec812124a4 100644
--- a/www/js/controllers/app-controllers.js
+++ b/www/js/controllers/app-controllers.js
@@ -163,8 +163,13 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     });
   };
 
-  // Login
-  $scope.login = function(state) {
+  // Login and go to wallet
+  $scope.login = function() {
+    $scope.loginAndGo('app.view_wallet');
+  };
+
+  // Login and go to a state
+  $scope.loginAndGo = function(state) {
     if (!Wallet.isLogin()) {
       $scope.showLoginModal()
       .then(function(walletData){
@@ -174,6 +179,9 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
         }
       });
     }
+    else {
+      $state.go(state);
+    }
   };
 
   // Show login modal
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
index 90f980ec08ac10d9d3b22d763448c3568de874ee..b1796a9786cdbc999dbee6e4656f13f1bf47a52d 100644
--- a/www/js/controllers/wallet-controllers.js
+++ b/www/js/controllers/wallet-controllers.js
@@ -32,8 +32,8 @@ angular.module('cesium.wallet.controllers', ['cesium.services', 'cesium.currency
   .controller('WalletTxErrorCtrl', WalletTxErrorController)
 ;
 
-function WalletController($scope, $q, $ionicPopup, $timeout, $state, screenmatch,
-  UIUtils, Wallet, $translate, $ionicPopover, Modals, csSettings) {
+function WalletController($scope, $q, $ionicPopup, $timeout, $state, $ionicHistory, screenmatch,
+  UIUtils, Wallet, $translate, $ionicPopover, Modals, csSettings, BMA) {
   'ngInject';
 
   $scope.walletData = null;
@@ -42,20 +42,24 @@ function WalletController($scope, $q, $ionicPopup, $timeout, $state, screenmatch
   $scope.showDetails = false;
   $scope.loading = true;
 
-  $scope.$on('$ionicView.enter', function(e, $state) {
+  $scope.$on('$ionicView.enter', function() {
     $scope.loadWallet()
       .then(function(walletData) {
-        if (!walletData) {
-          $state.go('app.home');
-        }
         $scope.walletData = walletData;
         $scope.updateView();
         $scope.loading=false;
         $scope.showFab('fab-transfer');
         $scope.showQRCode('qrcode', walletData.pubkey, 1100);
         UIUtils.loading.hide(); // loading could have be open (e.g. new account)
+      })
+      .catch(function(err){
+        if ('CANCELLED' === err) {
+          $ionicHistory.nextViewOptions({
+            historyRoot: true
+          });
+          $state.go('app.home');
+        }
       });
-
   });
 
   $ionicPopover.fromTemplateUrl('templates/wallet/popover_actions.html', {
@@ -273,7 +277,7 @@ function WalletController($scope, $q, $ionicPopup, $timeout, $state, screenmatch
   // Updating wallet data
   $scope.doUpdate = function() {
     UIUtils.loading.show();
-    Wallet.refreshData()
+    return Wallet.refreshData()
     .then(function() {
       $scope.updateView();
       UIUtils.loading.hide();
@@ -324,21 +328,40 @@ function WalletController($scope, $q, $ionicPopup, $timeout, $state, screenmatch
     });
   };
 
-   // TODO: remove auto add account when done
-   /*$timeout(function() {
+  $scope.showMoreTx = function(fromTime) {
+
+    fromTime = fromTime ||
+      ($scope.walletData.tx.fromTime - csSettings.data.walletHistoryTimeSecond) ||
+      (Math.trunc(new Date().getTime() / 1000) - 2 * csSettings.data.walletHistoryTimeSecond);
+
+    UIUtils.loading.show();
+    return Wallet.refreshData({tx: {enable: true,fromTime: fromTime}})
+      .then(function() {
+        $scope.updateView();
+        UIUtils.loading.hide();
+      })
+      .catch(function(err) {
+        // If http rest limitation: wait then retry
+        if (err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
+          $timeout(function() {
+            return $scope.showMoreTx();
+          }, 2000);
+        }
+        else {
+          UIUtils.onError('ERROR.REFRESH_WALLET_DATA')(err);
+        }
+      });
+  };
 
-     $scope.newAccount();
-   }, 400);
-   */
 }
 
 
-function WalletTxErrorController($scope, $rootScope, $ionicPopup, $timeout, UIUtils, Wallet) {
+function WalletTxErrorController($scope, $timeout, UIUtils, Wallet) {
   'ngInject';
 
   $scope.walletData = null;
 
-  $scope.$on('$ionicView.enter', function(e, $state) {
+  $scope.$on('$ionicView.enter', function(e) {
     $scope.loadWallet()
       .then(function(walletData) {
         $scope.walletData = walletData;
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index 14034d7c114a0f1d1d9a09d072e93b3e1ec226d8..5ee895cebc93e8fe761c7447715561a8dee7f957 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -14,7 +14,8 @@ angular.module('cesium.bma.services', ['ngResource', 'cesium.http.services', 'ce
         NO_MATCHING_MEMBER: 2004,
         NO_IDTY_MATCHING_PUB_OR_UID: 2021,
         MEMBERSHIP_ALREADY_SEND: 2007,
-        IDENTITY_SANDBOX_FULL: 1007
+        IDENTITY_SANDBOX_FULL: 1007,
+        HTTP_LIMITATION: 1006
       },
     regex = {
       USER_ID: "[A-Za-z0-9_-]+",
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index 52ab55ecfa6e64be455d880ca2648ccd18bc6beb..1bc807a56874a1715a254f201c6e5fa18a37ca3d 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -12,35 +12,11 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
     constants = {
       STORAGE_KEY: "CESIUM_DATA"
     },
-    data = {
-        pubkey: null,
-        keypair: {
-            signSk: null,
-            signPk: null
-        },
-        uid: null,
-        balance: 0,
-        sources: null,
-        sourcesIndexByKey: null,
-        currency: null,
-        parameters: null,
-        currentUD: null,
-        medianTime: null,
-        tx: {
-          history: [],
-          pendings: [],
-          errors: []
-        },
-        requirements: {},
-        isMember: false,
-        loaded: false,
-        blockUid: null,
-        avatar: null,
-    },
+    data = {},
 
     api = new Api(this, 'WalletService-' + id),
 
-    resetData = function() {
+    resetData = function(init) {
       data.pubkey= null;
       data.keypair ={
                 signSk: null,
@@ -63,11 +39,15 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
       data.isMember = false;
       data.loaded = false;
       data.blockUid = null;
-      data.avatar = null;
-      if (!csSettings.data.useLocalStorage) {
-        csSettings.reset();
+      if (init) {
+        api.data.raise.init(data);
+      }
+      else {
+        if (!csSettings.data.useLocalStorage) {
+          csSettings.reset();
+        }
+        api.data.raise.reset(data);
       }
-      api.data.raise.reset(data);
     },
 
     reduceTxAndPush = function(txArray, result, processedTxMap, excludePending) {
@@ -252,14 +232,6 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
       return data;
     },
 
-    isSourceEquals = function(arg1, arg2) {
-        return arg1.identifier == arg2.identifier &&
-               arg1.noffset == arg2.noffset &&
-               arg1.type == arg2.type &&
-               arg1.base == arg2.base &&
-              arg1.amount == arg2.amount;
-    },
-
     resetRequirements = function() {
       data.requirements = {
         needSelf: true,
@@ -363,7 +335,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
       });
     },
 
-    loadTransactions = function() {
+    loadTransactions = function(fromTime) {
       return $q(function(resolve, reject) {
         var txHistory = [];
         var udHistory = [];
@@ -371,14 +343,14 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
 
         var now = new Date().getTime();
         var nowInSec = Math.trunc(now / 1000);
-        var fromTime = nowInSec - csSettings.data.walletHistoryTimeSecond;
+        fromTime = fromTime || (nowInSec - csSettings.data.walletHistoryTimeSecond);
         var processedTxMap = {};
 
         var reduceTx = function(res){
           reduceTxAndPush(res.history.sent, txHistory, processedTxMap, true/*exclude pending*/);
           reduceTxAndPush(res.history.received, txHistory, processedTxMap, true/*exclude pending*/);
           reduceTxAndPush(res.history.sending, txHistory, processedTxMap, true/*exclude pending*/);
-          reduceTxAndPush(res.history.pending, txPendings, processedTxMap, false/*exclude pending*/);
+          reduceTxAndPush(res.history.pending, txPendings, processedTxMap, false/*include pending*/);
         };
 
         var jobs = [
@@ -387,17 +359,27 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
             .then(reduceTx)
         ];
 
+
         // get TX history since
-        var sliceTime = csSettings.data.walletHistorySliceSecond;
-        for(var i = fromTime - (fromTime % sliceTime); i - sliceTime < nowInSec; i += sliceTime)  {
-          jobs.push(BMA.tx.history.times({pubkey: data.pubkey, from: i, to: i+sliceTime-1})
+        if (fromTime !== -1) {
+          var sliceTime = csSettings.data.walletHistorySliceSecond;
+          for(var i = fromTime - (fromTime % sliceTime); i - sliceTime < nowInSec; i += sliceTime)  {
+            jobs.push(BMA.tx.history.times({pubkey: data.pubkey, from: i, to: i+sliceTime-1})
+              .then(reduceTx)
+            );
+          }
+
+          jobs.push(BMA.tx.history.timesNoCache({pubkey: data.pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999})
+            .then(reduceTx));
+        }
+
+        // get all TX
+        else {
+          jobs.push(BMA.tx.history.all({pubkey: data.pubkey})
             .then(reduceTx)
           );
         }
 
-        jobs.push(BMA.tx.history.timesNoCache({pubkey: data.pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999})
-          .then(reduceTx));
-
         // get UD history
         if (csSettings.data.showUDHistory) {
           jobs.push(
@@ -405,6 +387,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
             .then(function(res){
               udHistory = !res.history || !res.history.history ? [] :
                res.history.history.reduce(function(res, ud){
+                 if (ud.time < fromTime) return res; // skip to old UD
                  var amount = (ud.base > 0) ? ud.amount * Math.pow(10, ud.base) : ud.amount;
                  return res.concat({
                    time: ud.time,
@@ -626,49 +609,48 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
     refreshData = function(options) {
       if (!options) {
         options = {
-          uds: true,
+          ud: true,
           requirements: true,
           sources: true,
-          tx: true
+          tx: {
+            enable: true,
+            fromTime: data.tx ? data.tx.fromTime : undefined // keep previous time
+          },
+          sigStock: true
         };
       }
-      return $q(function(resolve, reject){
-        var jobs = [];
-        // Get UDs
-        if (options.uds) {
-          jobs.push(loadUDs());
-        }
-        // Get requirements
-        if (options.uds) {
-          jobs.push(loadRequirements()
-            .then(function() {
-              finishLoadRequirements();
-            }));
-        }
-        if (options.sources || options.tx) {
-          // Get sources
-          jobs.push(loadSources());
-          // Get transactions
-          jobs.push(loadTransactions());
-        }
 
+      var jobs = [];
 
-        // Load sigStock
-        jobs.push(loadSigStock());
+      // Get UDs
+      if (options.ud) jobs.push(loadUDs());
 
-        // API extension
-        jobs.push(api.data.raisePromise.load(data));
+      // Get requirements
+      if (options.requirements) {
+        jobs.push(loadRequirements()
+          .then(function() {
+            finishLoadRequirements();
+          }));
+      }
 
-        $q.all(jobs)
-        .then(function() {
-          // Process transactions and sources
-          processTransactionsAndSources()
-          .then(function(){
-            resolve(data);
-          })
-          .catch(function(err){reject(err);});
-        })
-        .catch(function(err){reject(err);});
+      if (options.sources || (options.tx && options.tx.enable)) {
+        // Get sources
+        jobs.push(loadSources());
+
+        // Get transactions
+        jobs.push(loadTransactions(options.tx.fromTime));
+      }
+
+      // Load sigStock
+      if (options.sigStock) jobs.push(loadSigStock());
+
+      // API extension
+      if (options.api) jobs.push(api.data.raisePromise.load(data, options));
+
+      return $q.all(jobs)
+      .then(function() {
+        // Process transactions and sources
+        return processTransactionsAndSources();
       });
     },
 
@@ -1157,11 +1139,15 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
     // Register extension points
     api.registerEvent('data', 'login');
     api.registerEvent('data', 'logout');
+    api.registerEvent('data', 'init');
     api.registerEvent('data', 'load');
     api.registerEvent('data', 'reset');
 
     csSettings.api.data.on.changed($rootScope, store);
 
+    // init data
+    resetData(true);
+
     return {
       id: id,
       data: data,
diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js
index 80e074cd6056cea74405023892debd32484ac851..77a2d847db0359175c63b0b87359e73562680407 100644
--- a/www/js/services/wot-services.js
+++ b/www/js/services/wot-services.js
@@ -457,7 +457,7 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
             }
           })
           .catch(function(err){
-            if (err && err.ucode === 1006) {
+            if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
               resolve(result);
             }
             else {
diff --git a/www/plugins/es/i18n/locale-en.json b/www/plugins/es/i18n/locale-en.json
index 42e764dc5ea381305629cb135b998e4bdba8e89b..b86fd9e3a0427631d28e5c56f3ed233be1ba4c73 100644
--- a/www/plugins/es/i18n/locale-en.json
+++ b/www/plugins/es/i18n/locale-en.json
@@ -24,25 +24,43 @@
     "POPOVER_SHARE_TITLE": "Message #{{number}}"
   },
   "MESSAGE": {
-    "BTN_RESPOND": "Respond",
+    "REMOVE_CONFIRMATION": "Are you sure you want to delete this message ?<br/><br/> This is irreversible.",
+    "REPLY_TITLE_PREFIX": "Re: ",
+    "FORWARD_TITLE_PREFIX": "Fw: ",
+    "BTN_REPLY": "Reply",
     "BTN_COMPOSE": "New message",
     "INBOX": {
-      "NO_MESSAGE": "No message",
+      "NO_MESSAGE": "No message received",
       "TITLE": "Messages"
     },
     "COMPOSE": {
       "TITLE": "New message",
+      "TITLE_REPLY": "Reply",
       "SUB_TITLE": "New message",
       "TO": "To",
       "OBJECT": "Object",
       "OBJECT_HELP": "Object",
       "ENCRYPTED_HELP": "Please note this message will by encrypt before sending. Only you and recipient could read it.",
       "MESSAGE": "Message",
-      "MESSAGE_HELP": "Message content"
+      "MESSAGE_HELP": "Message content",
+      "CONTENT_CONFIRMATION": "No message content.<br/><br/>Are your sure you want to send this message ?"
+    },
+    "VIEW": {
+      "TITLE": "Message",
+      "SENDER": "Sent by",
+      "NO_CONTENT": "Empty message"
+    },
+    "INFO": {
+      "MESSAGE_REMOVED": "Message supprimé"
     },
     "ERROR": {
       "SEND_MSG_FAILED": "Error while sending message.",
-      "SEARCH_FAILED": "Error while loading messages."
+      "SEARCH_FAILED": "Error while loading messages.",
+      "LOAD_MESSAGE_FAILED": "Error while loading message.",
+      "MESSAGE_NOT_READABLE": "Unable to read message.",
+      "USER_NOT_RECIPIENT": "You are not the recipient of this message: unable to read it.",
+      "NOT_AUTHENTICATED_MESSAGE": "The authenticity of the message is not certain or its content is corrupted.",
+      "REMOVE_MESSAGE_FAILED": "Error while deleting message"
     }
   },
   "MARKET": {
@@ -67,7 +85,7 @@
       "TITLE": "Ad",
       "MENU_TITLE": "Options",
       "POPOVER_SHARE_TITLE": "Ad {{title}}",
-      "REMOVE_CONFIRMATION" : "Are you sure you want to delete this ad ?<br/><br/> This is irrevocable."
+      "REMOVE_CONFIRMATION" : "Are you sure you want to delete this ad ?<br/><br/> This is irreversible."
     },
     "TYPE": {
       "TITLE": "New ad",
@@ -125,7 +143,7 @@
       "LOCATION": "Address:",
       "MENU_TITLE": "Options",
       "POPOVER_SHARE_TITLE": "{{title}}",
-      "REMOVE_CONFIRMATION" : "Are you sure you want to delete this record ?<br/><br/>This is irrevocable."
+      "REMOVE_CONFIRMATION" : "Are you sure you want to delete this record ?<br/><br/>This is irreversible."
     },
     "TYPE": {
       "TITLE": "New",
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index fc58c850ef6a5875028295d4d6a77d12fdf95b9d..fa98bc10d835d37c5c2e122efad7f29130d25a98 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -24,25 +24,43 @@
     "POPOVER_SHARE_TITLE": "Message #{{number}}"
   },
   "MESSAGE": {
-    "BTN_RESPOND": "Répondre",
+    "REMOVE_CONFIRMATION": "Etes-vous sur de vouloir supprimer ce message ?<br/><br/>Cette opération est irréversible.",
+    "REPLY_TITLE_PREFIX": "Rep: ",
+    "FORWARD_TITLE_PREFIX": "Tr: ",
+    "BTN_REPLY": "Répondre",
     "BTN_COMPOSE": "Nouveau message",
     "INBOX": {
-      "NO_MESSAGE": "Aucun message",
+      "NO_MESSAGE": "Aucun message reçus",
       "TITLE": "Messages"
     },
     "COMPOSE": {
       "TITLE": "Nouveau message",
+      "TITLE_REPLY": "Répondre",
       "SUB_TITLE": "Nouveau message",
       "TO": "A",
       "OBJECT": "Objet",
       "OBJECT_HELP": "Objet",
-      "ENCRYPTED_HELP": "Veuillez noter que ce message sera crypté avant son envoi. Seul le destinataire (et vous) pourrez le lire.",
+      "ENCRYPTED_HELP": "Veuillez noter que ce message sera crypté avant envoi. Seul le destinataire pourra le lire.",
       "MESSAGE": "Message",
-      "MESSAGE_HELP": "Contenu du message"
+      "MESSAGE_HELP": "Contenu du message",
+      "CONTENT_CONFIRMATION": "Le contenu du message est vide.<br/><br/>Voulez-vous néanmoins envoyer le message ?"
+    },
+    "VIEW": {
+      "TITLE": "Message",
+      "SENDER": "Envoyé par",
+      "NO_CONTENT": "Message vide"
+    },
+    "INFO": {
+      "MESSAGE_REMOVED": "Message supprimé"
     },
     "ERROR": {
       "SEND_MSG_FAILED": "Erreur lors de l'envoi du message.",
-      "SEARCH_FAILED": "Erreur lors de la récupération des messages."
+      "SEARCH_FAILED": "Erreur lors de la récupération des messages.",
+      "LOAD_MESSAGE_FAILED": "Erreur lors de la récupération du message.",
+      "MESSAGE_NOT_READABLE": "Lecture du message impossible.",
+      "USER_NOT_RECIPIENT": "Vous n'etes pas le destinataire de ce message : déchiffrement impossible.",
+      "NOT_AUTHENTICATED_MESSAGE": "L'authenticité du message est douteuse ou son contenu est corrompu.",
+      "REMOVE_MESSAGE_FAILED": "Erreur de suppression du message"
     }
   },
   "MARKET": {
@@ -67,7 +85,7 @@
       "TITLE": "Annonce",
       "MENU_TITLE": "Options",
       "POPOVER_SHARE_TITLE": "Annonce {{title}}",
-      "REMOVE_CONFIRMATION" : "Etes-vous sur de vouloir supprimer cette annonce ?<br/><br/>Cette opération est irrévocable."
+      "REMOVE_CONFIRMATION" : "Etes-vous sur de vouloir supprimer cette annonce ?<br/><br/>Cette opération est irréversible."
     },
     "TYPE": {
       "TITLE": "Nouvelle annonce",
@@ -125,7 +143,7 @@
       "LOCATION": "Adresse :",
       "MENU_TITLE": "Options",
       "POPOVER_SHARE_TITLE": "{{title}}",
-      "REMOVE_CONFIRMATION" : "Etes-vous sur de vouloir supprimer cette fiche ?<br/><br/>Cette opération est irrévocable."
+      "REMOVE_CONFIRMATION" : "Etes-vous sur de vouloir supprimer cette fiche ?<br/><br/>Cette opération est irréversible."
     },
     "TYPE": {
       "TITLE": "Nouveau référencement",
diff --git a/www/plugins/es/js/controllers/market-controllers.js b/www/plugins/es/js/controllers/market-controllers.js
index 7059300caa466bbd3e8dacb91622de22f5e87ca0..49c613b4b568a922dab414f30b1068321a298f46 100644
--- a/www/plugins/es/js/controllers/market-controllers.js
+++ b/www/plugins/es/js/controllers/market-controllers.js
@@ -545,13 +545,7 @@ function ESMarketRecordViewController($scope, $anchorScroll, $ionicPopover, $sta
       $scope.actionsPopover.hide();
     }
 
-    // translate
-    var translations;
-    $translate(['MARKET.VIEW.REMOVE_CONFIRMATION', 'MARKET.INFO.RECORD_REMOVED'])
-    .then(function(res) {
-      translations = res;
-      return UIUtils.alert.confirm(res['MARKET.VIEW.REMOVE_CONFIRMATION']);
-    })
+    UIUtils.alert.confirm('MARKET.VIEW.REMOVE_CONFIRMATION')
     .then(function(confirm) {
       if (confirm) {
         esMarket.record.remove($scope.id)
@@ -560,7 +554,7 @@ function ESMarketRecordViewController($scope, $anchorScroll, $ionicPopover, $sta
               historyRoot: true
             });
             $state.go('app.market_lookup');
-            UIUtils.toast.show(translations['MARKET.INFO.RECORD_REMOVED']);
+            UIUtils.toast.show('MARKET.INFO.RECORD_REMOVED');
           })
           .catch(UIUtils.onError('MARKET.ERROR.REMOVE_RECORD_FAILED'));
       }
diff --git a/www/plugins/es/js/controllers/message-controllers.js b/www/plugins/es/js/controllers/message-controllers.js
index 763640a3cfb1e7572cf0ec414572ef4fdedee39b..a7330dedec5a706ec80fd4125d68c178b57cf17b 100644
--- a/www/plugins/es/js/controllers/message-controllers.js
+++ b/www/plugins/es/js/controllers/message-controllers.js
@@ -6,7 +6,6 @@ angular.module('cesium.es.message.controllers', ['cesium.es.services', 'cesium.e
     $stateProvider
 
       .state('app.user_message', {
-        cache: false,
         url: "/user/message",
         views: {
           'menuContent': {
@@ -27,6 +26,17 @@ angular.module('cesium.es.message.controllers', ['cesium.es.services', 'cesium.e
         }
       })
 
+      .state('app.user_view_message', {
+        cache: false,
+        url: "/user/message/view/:id",
+        views: {
+          'menuContent': {
+            templateUrl: "plugins/es/templates/message/view_message.html",
+            controller: 'ESMessageViewCtrl'
+          }
+        }
+      })
+
     ;
   })
 
@@ -36,26 +46,36 @@ angular.module('cesium.es.message.controllers', ['cesium.es.services', 'cesium.e
 
   .controller('ESMessageComposeModalCtrl', ESMessageComposeModalController)
 
+  .controller('ESMessageViewCtrl', ESMessageViewController)
+
+
+
 ;
 
-function ESMessageInboxController($scope, $rootScope, $timeout, $q, ModalUtils, UIUtils, esMessage, CryptoUtils) {
+function ESMessageInboxController($scope, $rootScope, $state, $timeout, $translate, $ionicHistory, ModalUtils, UIUtils, esMessage) {
   'ngInject';
 
   $scope.loading = true;
   $scope.messages = [];
 
-  $scope.$on('$ionicView.enter', function(e, $state) {
+  $scope.$on('$ionicView.enter', function(e) {
 
     $scope.loadWallet()
-      .then(function(walletData) {
-        if ($scope.loading) {
+      .then(function() {
+        if (!$scope.entered) {
+          $scope.entered = true;
           $scope.load();
         }
 
         $scope.showFab('fab-add-message-record');
       })
       .catch(function(err) {
-        // TODO
+        if ('CANCELLED' === err) {
+          $ionicHistory.nextViewOptions({
+            historyRoot: true
+          });
+          $state.go('app.home');
+        }
     });
   });
 
@@ -68,27 +88,12 @@ function ESMessageInboxController($scope, $rootScope, $timeout, $q, ModalUtils,
       sort: {
         "time" : "desc"
       },
-      query: {
-        bool: {
-          filter: {
-            //term: {issuer: $rootScope.walletData.pubkey},
-            term: {recipient: $rootScope.walletData.pubkey}
-          }
-        }
-      },
+      query: {bool: {filter: {term: {recipient: $rootScope.walletData.pubkey}}}},
       from: offset,
       size: size,
       _source: esMessage.fields.commons
     };
 
-    //request.query.bool = {};
-
-    var filters = [];
-    //filters.push({match : { issuer: $rootScope.walletData.pubkey}});
-    //filters.push({match : { recipient: $rootScope.walletData.pubkey}});
-    //filters.push({match_phrase: { location: $scope.search.location}});
-    //request.query.bool.should =  filters;
-
     return $scope.doRequest(request);
 
   };
@@ -100,6 +105,17 @@ function ESMessageInboxController($scope, $rootScope, $timeout, $q, ModalUtils,
         $scope.messages = messages;
         UIUtils.loading.hide();
         $scope.loading = false;
+
+        if (messages.length > 0) {
+          // Set Motion
+          $timeout(function() {
+            UIUtils.motion.ripple({
+              startVelocity: 3000
+            });
+            // Set Ink
+            UIUtils.ink();
+          }, 10);
+        }
       })
       .catch(function(err) {
         UIUtils.onError('MESSAGE.ERROR.SEARCH_FAILED')(err);
@@ -108,31 +124,22 @@ function ESMessageInboxController($scope, $rootScope, $timeout, $q, ModalUtils,
       });
   };
 
-  /*$scope.doDecryption = function() {
-
-    return esMessage.search(request)
-      .then(function(res) {
-        if (res.hits.total === 0) {
-          $scope.messages = [];
+  $scope.delete = function(index) {
+    var message = $scope.messages[index];
+    if (!message) return;
+
+    UIUtils.alert.confirm('MESSAGE.REMOVE_CONFIRMATION')
+      .then(function(confirm) {
+        if (confirm) {
+          esMessage.remove(message.id)
+            .then(function () {
+              $scope.messages.splice(index,1); // remove from messages array
+              UIUtils.toast.show('MESSAGE.INFO.MESSAGE_REMOVED');
+            })
+            .catch(UIUtils.onError('MESSAGE.ERROR.REMOVE_MESSAGE_FAILED'));
         }
-        else {
-          var messages = res.hits.hits.reduce(function(result, hit) {
-            var message = hit._source;
-
-            // decrypt
-            return result.concat(message)
-          }, []);
-          $scope.messages = messages;
-        }
-        UIUtils.loading.hide();
-        $scope.loading = false;
-      })
-      .catch(function(err) {
-        UIUtils.onError('MESSAGE.ERROR.SEARCH_FAILED')(err);
-        $scope.messages = [];
-        $scope.loading = false;
       });
-  };*/
+  };
 
   /* -- Modals -- */
 
@@ -147,23 +154,43 @@ function ESMessageInboxController($scope, $rootScope, $timeout, $q, ModalUtils,
       });
   };
 
-  // TODO : for DEV only
-  $timeout(function() {
-    //$scope.showNewMessageModal();
+  $scope.showReplyModal = function(index) {
+    var message = $scope.messages[index];
+    if (!message) return;
+
+    $translate('MESSAGE.REPLY_TITLE_PREFIX')
+      .then(function (prefix) {
+        var content = message.content ? message.content.replace(/^/g, ' > ') : null;
+        content = content ? content.replace(/\n/g, '\n > ') : null;
+        content = content ? content +'\n' : null;
+        return esModals.showMessageCompose({
+          destPub: message.pubkey,
+          destUid: message.name||message.uid,
+          title: prefix + message.title,
+          content: content,
+          isReply: true
+        });
+      });
+  }
+
+  // for DEV only
+  /*$timeout(function() {
+    $scope.showNewMessageModal();
    }, 900);
+   */
 }
 
 
-function ESMessageComposeController($scope, $rootScope, $ionicHistory, $timeout, $focus, $q, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage) {
+function ESMessageComposeController($scope,  $ionicHistory, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage) {
   'ngInject';
 
-  ESMessageComposeModalController.call(this, $scope, $rootScope, $timeout, $focus, $q, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage);
+  ESMessageComposeModalController.call(this, $scope, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage);
 
-  $scope.$on('$ionicView.enter', function(e, $state) {
-    if (!!$state.stateParams && !!$state.stateParams.pubkey) {
-      $scope.formData.destPub = $state.stateParams.pubkey;
+  $scope.$on('$ionicView.enter', function(e, state) {
+    if (!!state.stateParams && !!state.stateParams.pubkey) {
+      $scope.formData.destPub = state.stateParams.pubkey;
       if (!!$state.stateParams.uid) {
-        $scope.destUid = $state.stateParams.uid;
+        $scope.destUid = state.stateParams.uid;
         $scope.destPub = '';
       }
       else {
@@ -175,6 +202,14 @@ function ESMessageComposeController($scope, $rootScope, $ionicHistory, $timeout,
     $scope.loadWallet()
       .then(function() {
         UIUtils.loading.hide();
+      })
+      .catch(function(err){
+        if (err === 'CANCELLED') {
+          $ionicHistory.nextViewOptions({
+            historyRoot: true
+          });
+          $state.go('app.home');
+        }
       });
   });
 
@@ -188,23 +223,35 @@ function ESMessageComposeController($scope, $rootScope, $ionicHistory, $timeout,
 
 }
 
-function ESMessageComposeModalController($scope, $rootScope, $timeout, $focus, $q, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage) {
+function ESMessageComposeModalController($scope, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage, parameters) {
   'ngInject';
 
   $scope.formData = {
-    title: null,
-    content: null,
-    destPub: null
+    title: parameters ? parameters.title : null,
+    content: parameters ? parameters.content : null,
+    destPub: parameters ? parameters.destPub : null
   };
+  $scope.destUid = parameters ? parameters.destUid : null;
+  $scope.destPub = (parameters && !parameters.destUid) ? parameters.destPub : null;
+  $scope.isResponse = parameters ? parameters.isResponse : false;
 
-  $scope.doSend = function() {
+  $scope.doSend = function(forceNoContent) {
     $scope.form.$submitted=true;
     if(!$scope.form.$valid) {
       return;
     }
 
-    UIUtils.loading.show();
+    // Ask user confirmation if no content
+    if (!forceNoContent && (!$scope.formData.content || !$scope.formData.content.trim().length)) {
+      return UIUtils.alert.confirm('MESSAGE.COMPOSE.CONTENT_CONFIRMATION')
+        .then(function(confirm) {
+          if (confirm) {
+            $scope.doSend(true);
+          }
+        });
+    }
 
+    UIUtils.loading.show();
     var data = {
       issuer: Wallet.data.pubkey,
       recipient: $scope.formData.destPub,
@@ -250,7 +297,7 @@ function ESMessageComposeModalController($scope, $rootScope, $timeout, $focus, $
 
 
   // TODO : for DEV only
-  $timeout(function() {
+  /*$timeout(function() {
     $scope.formData.destPub = 'G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU';
     $scope.formData.title = 'test';
     $scope.formData.content = 'test';
@@ -260,4 +307,107 @@ function ESMessageComposeModalController($scope, $rootScope, $timeout, $focus, $
       //$scope.doSend();
     }, 800);
   }, 100);
+  */
+}
+
+
+function ESMessageViewController($scope, $state, $timeout, $translate, $ionicHistory, UIUtils, esModals, esMessage) {
+  'ngInject';
+
+  $scope.formData = {};
+  $scope.id = null;
+  $scope.loading = true;
+
+  $scope.$on('$ionicView.enter', function (e, state) {
+    if (state.stateParams && state.stateParams.id) { // Load by id
+      if ($scope.loading) { // prevent reload if same id
+        $scope.load(state.stateParams.id);
+      }
+
+      $scope.showFab('fab-view-message-reply');
+    }
+    else {
+      $state.go('app.user_message');
+    }
+  });
+
+  $scope.load = function(id) {
+
+    $scope.loadWallet()
+      .then(function(){
+        UIUtils.loading.hide();
+
+        return esMessage.get({id: id})
+          .then(function(message) {
+
+            if (!message.valid) {
+
+              return UIUtils.alert.error(!$scope.isUserPubkey(message.pubkey) ? 'MESSAGE.ERROR.USER_NOT_RECIPIENT': 'MESSAGE.ERROR.NOT_AUTHENTICATED_MESSAGE',
+                'MESSAGE.ERROR.MESSAGE_NOT_READABLE')
+                .then(function() {
+                  $state.go('app.user_message');
+                });
+            }
+
+            $scope.formData = message;
+            $scope.canDelete = true;
+            $scope.loading = false;
+
+            // Set Motion (only direct children, to exclude .lazy-load children)
+            $timeout(function () {
+              UIUtils.motion.fadeSlideIn({
+                startVelocity: 3000
+              });
+            }, 10);
+          })
+          .catch(UIUtils.onError('MESSAGE.ERROR.LOAD_MESSAGE_FAILED'));
+      })
+      .catch(function(err){
+        if (err === 'CANCELLED') {
+          $ionicHistory.nextViewOptions({
+            historyRoot: true
+          });
+          $state.go('app.user_message');
+        }
+      });
+  };
+
+  $scope.delete = function() {
+    if ($scope.actionsPopover) {
+      $scope.actionsPopover.hide();
+    }
+
+    UIUtils.alert.confirm('MESSAGE.REMOVE_CONFIRMATION')
+      .then(function(confirm) {
+        if (confirm) {
+          esMessage.remove($scope.formData.id)
+            .then(function () {
+              $ionicHistory.nextViewOptions({
+                historyRoot: true
+              });
+              $state.go('app.user_message');
+              UIUtils.toast.show('MESSAGE.INFO.MESSAGE_REMOVED');
+            })
+            .catch(UIUtils.onError('MESSAGE.ERROR.REMOVE_MESSAGE_FAILED'));
+        }
+      });
+  };
+
+  /* -- Modals -- */
+
+  $scope.showReplyModal = function() {
+    $translate('MESSAGE.REPLY_TITLE_PREFIX')
+      .then(function (prefix) {
+        var content = $scope.formData.content ? $scope.formData.content.replace(/^/g, ' > ') : null;
+        content = content ? content.replace(/\n/g, '\n > ') : null;
+        content = content ? content +'\n' : null;
+        return esModals.showMessageCompose({
+            destPub: $scope.formData.pubkey,
+            destUid: $scope.formData.name||$scope.formData.uid,
+            title: prefix + $scope.formData.title,
+            content: content,
+            isReply: true
+          });
+      });
+  };
 }
diff --git a/www/plugins/es/js/controllers/user-controllers.js b/www/plugins/es/js/controllers/user-controllers.js
index 552a6f9d320f463d49dc965fa8f824277e249ef8..8351422b2ddae5d7e9afedcdb872a6996df558cc 100644
--- a/www/plugins/es/js/controllers/user-controllers.js
+++ b/www/plugins/es/js/controllers/user-controllers.js
@@ -18,7 +18,7 @@ angular.module('cesium.es.user.controllers', ['cesium.es.services'])
 
 ;
 
-function ProfileController($scope, $rootScope, UIUtils, $timeout, esUser, $filter, $focus, $q, SocialUtils, $translate, $ionicHistory) {
+function ProfileController($scope, $rootScope, UIUtils, $timeout, esUser, $state, $focus, $q, SocialUtils, $translate, $ionicHistory) {
   'ngInject';
 
   $scope.loading = true;
@@ -34,7 +34,7 @@ function ProfileController($scope, $rootScope, UIUtils, $timeout, esUser, $filte
     url: null
   };
 
-  $scope.$on('$ionicView.enter', function(e, $state) {
+  $scope.$on('$ionicView.enter', function(e) {
     $scope.loading = true; // to avoid the call of doSave()
     $scope.loadWallet()
       .then(function(walletData) {
@@ -65,6 +65,11 @@ function ProfileController($scope, $rootScope, UIUtils, $timeout, esUser, $filte
         // removeIf(device)
         $focus('profile-name');
         // endRemoveIf(device)
+      })
+      .catch(function(err){
+        if (err === 'CANCELLED') {
+          $state.go('app.home');
+        }
       });
   });
 
diff --git a/www/plugins/es/js/services.js b/www/plugins/es/js/services.js
index 6d937aeb30686d45ad9c2ac2966a9b6e32bcf5a5..84ff57d836c5859571ec47ad957b91ed6b655903 100644
--- a/www/plugins/es/js/services.js
+++ b/www/plugins/es/js/services.js
@@ -7,6 +7,7 @@ angular.module('cesium.es.services', [
     'cesium.es.registry.services',
     'cesium.es.market.services',
     'cesium.es.user.services',
-    'cesium.es.message.services'
+    'cesium.es.message.services',
+    'cesium.es.modal.services'
   ])
 ;
diff --git a/www/plugins/es/js/services/message-services.js b/www/plugins/es/js/services/message-services.js
index bb4b06994633f73976cd397a879729d338ad6503..6becd622e8efeae0755c95850f2b8f6649d994a9 100644
--- a/www/plugins/es/js/services/message-services.js
+++ b/www/plugins/es/js/services/message-services.js
@@ -32,6 +32,21 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
       }
     }
 
+    function onWalletInit(data) {
+      data.messages = data.messages || {};
+      data.messages.count = null;
+    }
+
+    function onWalletReset(data) {
+      if (data.keypair) {
+        delete data.keypair.boxSk;
+        delete data.keypair.boxPk;
+      };
+      if (data.messages) {
+        delete data.messages;
+      }
+    }
+
     function onWalletLoad(data, resolve, reject) {
       if (!data || !data.pubkey) {
         if (resolve) {
@@ -40,23 +55,65 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
         return;
       }
 
-      if (data.keypair) {
-        var boxKeypair = CryptoUtils.box.keypair.fromSignKeypair(data.keypair)
-        data.keypair.boxSk = boxKeypair.boxSk;
-        data.keypair.boxPk = boxKeypair.boxPk;
+      var lastMessageTime = csSettings.data && csSettings.data.plugins && csSettings.data.plugins.es ?
+        csSettings.data.plugins.es.lastMessageTime :
+        undefined;
+
+      // Count new messages
+      countNewMessages(data.pubkey, lastMessageTime)
+        .then(function(count){
+          data.messages = data.messages || {};
+          data.messages.count = count;
+          console.debug('[ES] [message] Detecting ' + count + (lastMessageTime ? ' unread' : '') + ' messages');
+          if(resolve) resolve(data);
+        })
+        .catch(function(err){
+          reject(err);
+          if(reject) reject(data);
+        });
+    }
+
+
+    function getBoxKeypair(keypair) {
+      keypair = keypair || (Wallet.isLogin() ? Wallet.data.keypair : keypair);
+      if (!keypair) {
+        throw new Error('no keypair, and user not connected.');
       }
-      resolve(data);
+      if (keypair.boxPk && keypair.boxSk) {
+        return keypair;
+      }
+      var boxKeypair = CryptoUtils.box.keypair.fromSignKeypair(keypair);
+      Wallet.data.keypair.boxSk = boxKeypair.boxSk;
+      Wallet.data.keypair.boxPk = boxKeypair.boxPk;
+      console.debug("[ES] Secret box keypair successfully computed");
+      return data.keypair;
     }
 
-    function onWalletReset(data) {
-      if (data.keypair) {
-        delete data.keypair.boxSk;
-        delete data.keypair.boxPk;
+    function countNewMessages(pubkey, fromTime) {
+      pubkey = pubkey || (Wallet.isLogin() ? Wallet.data.pubkey : pubkey);
+      if (!pubkey) {
+        throw new Error('no pubkey, and user not connected.');
+      }
+
+      var request = {
+        size: 0,
+        query: {constant_score: {filter: [{term: {recipient: pubkey}}]}}
       };
+
+      // Add time filter
+      if (fromTime) {
+        request.query.constant_score.filter.push({range: {time: {gt: fromTime}}});
+      }
+
+      return esHttp.post(host, port, '/message/record/_search')(request)
+        .then(function(res) {
+          return res.hits ? res.hits.total : 0;
+        });
     }
 
+
     function sendMessage(message, keypair) {
-      var boxKeypair = CryptoUtils.box.keypair.fromSignKeypair(keypair);
+      var boxKeypair = getBoxKeypair(keypair);
 
       // Get recipient
       var recipientPk = CryptoUtils.util.decode_base58(message.recipient);
@@ -107,12 +164,13 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
             var walletPubkey = Wallet.isLogin() ? Wallet.data.pubkey : null;
             var messages = res.hits.hits.reduce(function(result, hit) {
               var msg = hit._source;
+              msg.id = hit._id;
               msg.pubkey = msg.issuer !== walletPubkey ? msg.issuer : msg.recipient;
               msg.uid = uids[msg.pubkey];
               return result.concat(msg);
             }, []);
 
-            console.debug('[message] Loading ' + messages.length + ' messages');
+            console.debug('[ES] [message] Loading ' + messages.length + ' messages');
             return decryptMessages(messages, keypair)
               .then(function(){
                 return esUser.profile.fillAvatars(messages, 'pubkey');
@@ -121,17 +179,47 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
         });
     }
 
-    function decryptMessages(messages, recipientSignKeypair) {
+    function getAndDecrypt(params, keypair) {
+      return esHttp.get(host, port, '/message/record/:id')(params)
+        .then(function(hit) {
+          if (!hit.found) {
+            return;
+          }
+          else {
+            var walletPubkey = Wallet.isLogin() ? Wallet.data.pubkey : null;
+            var msg = hit._source;
+            msg.id = hit._id;
+            msg.pubkey = msg.issuer !== walletPubkey ? msg.issuer : msg.recipient;
+
+            // Get uid (if member)
+            return BMA.wot.member.get(msg.pubkey)
+              .then(function(user) {
+                msg.uid = user ? user.uid : user;
+                // Decrypt message
+                return decryptMessages([msg], keypair)
+              })
+              .then(function(){
+                // Fill avatar
+                return esUser.profile.fillAvatars([msg], 'pubkey');
+              })
+              .then(function() {
+                return msg;
+              });
+          }
+        })
+    }
+
+    function decryptMessages(messages, keypair) {
       var jobs = [];
       var now = new Date().getTime();
-      var recipientBoxKeypair = CryptoUtils.box.keypair.fromSignKeypair(recipientSignKeypair);
-      var issuerBoxPksCache = {};
+      var boxKeypair = getBoxKeypair(keypair);
+      var issuerBoxPks = {}; // a map used as cache
 
       var messages = messages.reduce(function(result, message) {
-        var issuerBoxPk = issuerBoxPksCache[message.issuer];
+        var issuerBoxPk = issuerBoxPks[message.issuer];
         if (!issuerBoxPk) {
           issuerBoxPk = CryptoUtils.box.keypair.pkFromSignPk(CryptoUtils.util.decode_base58(message.issuer));
-          issuerBoxPksCache[message.issuer] = issuerBoxPk; // fill box pk cache
+          issuerBoxPks[message.issuer] = issuerBoxPk; // fill box pk cache
         }
 
         var nonce = CryptoUtils.util.decode_base58(message.nonce);
@@ -139,22 +227,22 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
         message.valid = true;
 
         // title
-        jobs.push(CryptoUtils.box.open(message.title, nonce, issuerBoxPk, recipientBoxKeypair.boxSk)
+        jobs.push(CryptoUtils.box.open(message.title, nonce, issuerBoxPk, boxKeypair.boxSk)
           .then(function(title) {
             message.title = title;
           })
           .catch(function(err){
-            console.warn('[message] invalid cypher title');
+            console.warn('[ES] [message] invalid cypher title');
             message.valid = false;
           }));
 
         // content
-        jobs.push(CryptoUtils.box.open(message.content, nonce, issuerBoxPk, recipientBoxKeypair.boxSk)
+        jobs.push(CryptoUtils.box.open(message.content, nonce, issuerBoxPk, boxKeypair.boxSk)
           .then(function(content) {
             message.content = content;
           })
           .catch(function(err){
-            console.warn('[message] invalid cypher content');
+            console.warn('[ES] [message] invalid cypher content');
             message.valid = false;
           }));
         return result.concat(message);
@@ -162,41 +250,58 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
 
       return $q.all(jobs)
         .then(function() {
-          console.debug('[message] Messages decrypted in ' + (new Date().getTime() - now) + 'ms');
+          console.debug('[ES] [message] All messages decrypted in ' + (new Date().getTime() - now) + 'ms');
           return messages;
         });
     }
 
-      function addListeners() {
-        console.debug("[ES] Enable message service listeners");
+    function removeListeners() {
+      console.debug("[ES] Disable message service listeners");
 
-        // Extend Wallet.loadData() and WotService.loadData()
-        listeners = [
-          Wallet.api.data.on.load($rootScope, onWalletLoad, this),
-          Wallet.api.data.on.reset($rootScope, onWalletReset, this),
-        ];
-      }
+      _.forEach(listeners, function(remove){
+        remove();
+      });
+      listeners = [];
+    }
+
+    function addListeners() {
+      console.debug("[ES] Enable message service listeners");
 
-      function isEnable() {
-        return csSettings.data.plugins &&
-          csSettings.data.plugins.es &&
-          host && csSettings.data.plugins.es.enable;
+      // Extend Wallet.loadData() and WotService.loadData()
+      listeners = [
+        Wallet.api.data.on.login($rootScope, onWalletLoad, this),
+        Wallet.api.data.on.load($rootScope, onWalletLoad, this),
+        Wallet.api.data.on.init($rootScope, onWalletInit, this),
+        Wallet.api.data.on.reset($rootScope, onWalletReset, this),
+      ];
+    }
+
+    function isEnable() {
+      return csSettings.data.plugins &&
+        csSettings.data.plugins.es &&
+        host && csSettings.data.plugins.es.enable;
+    }
+
+    function refreshListeners() {
+      var enable = isEnable();
+      if (!enable && listeners && listeners.length > 0) {
+        removeListeners();
+      }
+      else if (enable && (!listeners || listeners.length === 0)) {
+        addListeners();
       }
+    }
 
-      function refreshListeners() {
-        var enable = isEnable();
-        if (!enable && listeners && listeners.length > 0) {
-          removeListeners();
-        }
-        else if (enable && (!listeners || listeners.length === 0)) {
-          addListeners();
-        }
+    // Listen for settings changed
+    csSettings.api.data.on.changed($rootScope, function(){
+      refreshListeners();
+      if (isEnable() && !Wallet.data.messages) {
+        onWalletLoad(Wallet.data);
       }
+    });
 
-      // Listen for settings changed
-      csSettings.api.data.on.changed($rootScope, function(){
-        refreshListeners();
-      });
+    // Default action
+    refreshListeners();
 
     return {
       copy: copy,
@@ -205,7 +310,7 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
       },
       search: esHttp.post(host, port, '/message/record/_search'),
       searchAndDecrypt: searchAndDecrypt,
-      get: esHttp.get(host, port, '/message/record/:id'),
+      get: getAndDecrypt,
       send: sendMessage,
       remove: esHttp.record.remove(host, port, 'message', 'record'),
       fields: {
diff --git a/www/plugins/es/js/services/modal-services.js b/www/plugins/es/js/services/modal-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..922e6baf62b798b398d42cbbf6a995c79be28fa8
--- /dev/null
+++ b/www/plugins/es/js/services/modal-services.js
@@ -0,0 +1,15 @@
+angular.module('cesium.es.modal.services', ['cesium.modal.services', 'cesium.es.message.services'])
+
+.factory('esModals', function(ModalUtils) {
+  'ngInject';
+
+  function showMessageCompose(parameters) {
+    return ModalUtils.show('plugins/es/templates/message/modal_compose.html','ESMessageComposeModalCtrl',
+      parameters, {focusFirstInput: true});
+  }
+
+  return {
+    showMessageCompose: showMessageCompose
+  };
+
+});
diff --git a/www/plugins/es/js/services/user-services.js b/www/plugins/es/js/services/user-services.js
index 096f5b4c13ae23e7fa2fc69dcda0c1d9d95d3ee5..4562bf6e1fb7aba1682118315e9769a1044bc299 100644
--- a/www/plugins/es/js/services/user-services.js
+++ b/www/plugins/es/js/services/user-services.js
@@ -63,6 +63,7 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
       data.avatar = null;
       data.avatarStyle = null;
       data.profile = null;
+      data.name = null;
     }
 
     function onWotLoad(data, resolve, reject) {
diff --git a/www/plugins/es/templates/menu_extend.html b/www/plugins/es/templates/menu_extend.html
index 495771e3e9af0c7cb20d2226b29efc707438f5f6..0288b43c427cc645ed97c07bb592b1040a2c089b 100644
--- a/www/plugins/es/templates/menu_extend.html
+++ b/www/plugins/es/templates/menu_extend.html
@@ -42,29 +42,21 @@
 <ng-if ng-if="enable && extensionPoint === 'menu-user'">
   <!-- user profile -->
   <ion-item menu-close class="item item-icon-left" active-link="active"
-            ng-if="$root.login"
-            href="#/app/user/profile/edit">
-    <i class="icon ion-person"></i>
-    <span translate>MENU.USER_PROFILE</span>
-  </ion-item>
-  <ion-item menu-close class="item item-icon-left item-menu-disable" active-link="active"
-            ng-if="!$root.login"
-            ng-click="login('app.user_edit_profile')">
+            active-link-path-prefix="#/app/user/profile"
+            ng-class="{'item-menu-disable': !$root.login}"
+            ng-click="loginAndGo('app.user_edit_profile')">
     <i class="icon ion-person"></i>
     <span translate>MENU.USER_PROFILE</span>
   </ion-item>
 
   <ion-item menu-close class="item item-icon-left" active-link="active"
-            ng-if="$root.login"
-            href="#/app/user/message">
-    <i class="icon ion-email"></i>
-    <span translate>MENU.USER_MESSAGE</span>
-  </ion-item>
-  <ion-item menu-close class="item item-icon-left item-menu-disable" active-link="active"
-            ng-if="!$root.login"
-            ng-click="login('app.user_message')">
+            active-link-path-prefix="#/app/user/message"
+            ng-class="{'item-menu-disable': !$root.login}"
+            ng-click="loginAndGo('app.user_message')">
     <i class="icon ion-email"></i>
     <span translate>MENU.USER_MESSAGE</span>
+    <span class="badge badge-positive"
+          ng-if="$root.walletData.messages.count">{{$root.walletData.messages.count}}</span>
   </ion-item>
 
 </ng-if>
diff --git a/www/plugins/es/templates/message/compose.html b/www/plugins/es/templates/message/compose.html
index e0ffa5432aa971840d0563a507df974de5516d52..561a693d0f8489e0b2434c6eb905770d72df69fa 100644
--- a/www/plugins/es/templates/message/compose.html
+++ b/www/plugins/es/templates/message/compose.html
@@ -1,7 +1,8 @@
 <ion-view left-buttons="leftButtons"
           id="composeMessage">
   <ion-nav-title>
-    <span class="visible-xs visible-sm" translate>MESSAGE.COMPOSE.TITLE</span>
+    <span class="visible-xs visible-sm" nf=if="!isReply" translate>MESSAGE.COMPOSE.TITLE</span>
+    <span class="visible-xs visible-sm" nf=if="isReply" translate>MESSAGE.COMPOSE.TITLE_REPLY</span>
   </ion-nav-title>
 
   <ion-nav-buttons side="secondary">
diff --git a/www/plugins/es/templates/message/inbox.html b/www/plugins/es/templates/message/inbox.html
index 3888d9007e7ec9984695709aa8d1d8ad2dc83459..a551a83a4b482586e79f328d936554431aba1466 100644
--- a/www/plugins/es/templates/message/inbox.html
+++ b/www/plugins/es/templates/message/inbox.html
@@ -11,10 +11,8 @@
 
   <ion-content class="padding no-padding-xs">
 
-
-
+    <!-- Buttons bar-->
     <ion-list>
-      <!-- Buttons bar-->
       <div class="item large-button-bar hidden-xs hidden-sm">
         <button class="button button-small button-calm icon ion-compose"
                 ng-click="showNewMessageModal()">
@@ -25,14 +23,18 @@
       <div class="center" ng-if="loading">
         <ion-spinner icon="android"></ion-spinner>
       </div>
+    </ion-list>
 
-      <div class="padding gray" ng-if="!loading && messages.length===0 " translate>
+    <ion-list class="animate-ripple" ng-if="!loading">
+
+      <div class="padding gray" ng-if="messages.length===0 " translate>
         MESSAGE.INBOX.NO_MESSAGE
       </div>
 
       <ion-item
-        class="item item-border-large item-avatar ink"
-        ng-repeat="msg in messages">
+        class="item item-border-large item-avatar item-icon-right ink"
+        ng-repeat="msg in messages"
+        ui-sref="app.user_view_message({id:msg.id})">
         <img ng-if="msg.avatar" class="item-image" ng-src="{{::msg.avatar.src}}">
         <i ng-if="!msg.avatar && msg.uid" class="item-image icon ion-person"></i>
 
@@ -41,19 +43,25 @@
           <a class="positive"
              ng-if="msg.name||msg.uid"
              ui-sref="app.wot_view_identity({pubkey:msg.pubkey, uid:msg.name||msg.uid})">
-            <i class="icon ion-person"></i>
+            <i class="ion-person"></i>
             {{msg.name||msg.uid}}
           </a>
           <a class="gray" ng-if="!msg.name && !msg.uid"
              ui-sref="app.wot_view_identity({pubkey:msg.pubkey})">
-            <i class="icon ion-key"></i>
+            <i class="ion-key"></i>
             {{msg.pubkey|formatPubkey}}
           </a>
         </h3>
         <h2 >{{msg.title}}</h2>
         <p>{{msg.content}}</p>
-        <ion-option-button class="button-positive"
-                           translate>MESSAGE.BTN_RESPOND</ion-option-button>
+        <i class="icon ion-ios-arrow-right "></i>
+        <ion-option-button class="button-stable"
+                           ng-click="showResponseModal($index)"
+                           translate>MESSAGE.BTN_REPLY</ion-option-button>
+        <ion-option-button class="button-assertive"
+                           ng-click="delete($index)"
+                           translate>COMMON.BTN_DELETE</ion-option-button>
+
       </ion-item>
     </ion-list>
   </ion-content>
diff --git a/www/plugins/es/templates/message/modal_compose.html b/www/plugins/es/templates/message/modal_compose.html
index bad72d80856b42340414213d2f42ca069d69a3bd..72fbbc95ceea63f90f5c909ecb821d3c908fe3af 100644
--- a/www/plugins/es/templates/message/modal_compose.html
+++ b/www/plugins/es/templates/message/modal_compose.html
@@ -1,7 +1,8 @@
 <ion-modal-view id="transfer" class="modal-full-height">
   <ion-header-bar class="bar-positive">
     <button class="button button-clear visible-xs" ng-click="closeModal()" translate>COMMON.BTN_CANCEL</button>
-    <h1 class="title" translate>MESSAGE.COMPOSE.TITLE</h1>
+    <h1 class="title" nf=if="!isReply" translate>MESSAGE.COMPOSE.TITLE</h1>
+    <h1 class="title" nf=if="isReply" translate>MESSAGE.COMPOSE.TITLE_REPLY</h1>
 
     <button class="button button-icon button-clear icon ion-android-send visible-xs" ng-click="doSend()">
     </button>
diff --git a/www/plugins/es/templates/message/view_message.html b/www/plugins/es/templates/message/view_message.html
new file mode 100644
index 0000000000000000000000000000000000000000..39e3e0b9c67afe5c107ddc05c423dd1e4ad8fbe5
--- /dev/null
+++ b/www/plugins/es/templates/message/view_message.html
@@ -0,0 +1,92 @@
+<ion-view left-buttons="leftButtons">
+  <ion-nav-title>
+    <span translate>MESSAGE.VIEW.TITLE</span>
+  </ion-nav-title>
+
+  <ion-nav-buttons side="secondary">
+    <!--<button class="button button-bar button-icon button-clear visible-xs visible-sm" ng-click="edit()" ng-if="canEdit">
+      <i class="icon ion-android-create"></i>
+    </button>-->
+    <button class="button button-bar button-icon button-clear icon ion-android-more-vertical visible-xs visible-sm"
+            ng-click="actionsPopover.show($event)">
+    </button>
+  </ion-nav-buttons>
+
+  <ion-content scroll="true">
+
+    <div class="row no-padding">
+      <div class="col col-20 hidden-xs hidden-sm">&nbsp;</div>
+
+      <div class="col no-padding">
+
+        <div class="center padding" ng-if="loading">
+          <ion-spinner icon="android"></ion-spinner>
+        </div>
+
+        <ion-list class="animate-fade-slide-in item-text-wrap">
+
+
+          <div class="item" ng-class="{'item-avatar': formData.avatar}">
+            <img ng-if="formData.avatar" class="item-image" ng-src="{{::formData.avatar.src}}">
+            <h1 ng-bind-html="formData.title"></h1>
+            <h4>
+              <i class="ion-clock"></i>
+              <span translate>MESSAGE.VIEW.SENDER</span>
+              <a class="positive" ui-sref="app.wot_view_identity({pubkey:formData.pubkey, uid:formData.uid})">
+                <span ng-if="formData.uid">
+                  <i class="ion-person"></i>
+                  {{::formData.name||formData.uid}}
+                </span>
+                <span ng-if="!formData.uid">
+                  <i class="ion-key"></i>
+                  {{::formData.pubkey|formatPubkey}}
+                </span>
+              </a>
+              <span>
+                {{formData.time|formatFromNow}}
+                <span class="gray hidden-xs">|
+                  {{formData.time | formatDate}}
+                </span>
+              </span>
+            </h4>
+          </div>
+
+          <!-- content -->
+          <ion-item>
+            <h2>
+              <span class="text-keep-lines" ng-bind-html="formData.content"></span>
+            </h2>
+
+            <div class="padding gray" ng-if="!formData.content" translate>
+              MESSAGE.VIEW.NO_CONTENT
+            </div>
+          </ion-item>
+
+          <!-- Buttons bar-->
+          <div class="item large-button-bar hidden-xs hidden-sm">
+            <button class="button button-small button-stable icon-left ink-dark"
+                    ng-click="delete()">
+              <i class="icon ion-trash-a assertive"></i>
+              <span class="assertive"> {{'COMMON.BTN_DELETE' | translate}}</span>
+            </button>
+            <button class="button button-small button-stable icon ion-reply"
+                    ng-click="showReplyModal()">
+              {{'MESSAGE.BTN_REPLY' | translate}}
+            </button>
+            <!--<button class="button button-small button-stable icon ion-reply"
+                    ng-click="showForwardModal()">
+              {{'MESSAGE.BTN_FORWARD' | translate}}
+            </button>-->
+          </div>
+        </ion-list>
+      </div>
+
+      <div class="col col-20 hidden-xs hidden-sm">&nbsp;</div>
+    </div>
+  </ion-content>
+
+  <button id="fab-view-message-reply"
+          class="button button-fab button-fab-bottom-right button-calm icon ion-reply visible-xs visible-sm spin"
+          ng-click="showReplyModal()">
+  </button>
+</ion-view>
diff --git a/www/templates/menu.html b/www/templates/menu.html
index 1041f3fda54b571f2f49661db70a7302eabd8515..4ec5122a6779e1191ba9e4ce54a2a037f0e5799e 100644
--- a/www/templates/menu.html
+++ b/www/templates/menu.html
@@ -69,11 +69,10 @@
         <!-- USER Section -->
         <div class="item item-divider"></div>
 
-        <ion-item menu-close class="item item-icon-left item-menu-disable" ng-click="login('app.view_wallet')" ng-if="!$root.login">
-          <i class="icon ion-card"></i>
-          <span translate>MENU.ACCOUNT</span>
-        </ion-item>
-        <ion-item menu-close class="item item-icon-left" active-link="active" href="#/app/wallet" ng-if="$root.login">
+        <ion-item menu-close class="item item-icon-left" active-link="active"
+                  active-link-path-prefix="#/app/wallet"
+                  ng-click="loginAndGo('app.view_wallet')"
+                  ng-class="{'item-menu-disable': !$root.login}">
           <i class="icon ion-card"></i>
           <span translate>MENU.ACCOUNT</span>
         </ion-item>
@@ -90,7 +89,7 @@
         <div class="item item-divider" ng-if="$root.login"></div>
         <ion-item menu-close class="item item-button-right" ng-if="$root.login">
           <span translate>MENU.TRANSFER</span>
-          <button class="button button-energized-900 ink-dark" ng-click="showTransferModal()">
+          <button class="button button-positive ink-dark" ng-click="showTransferModal()">
           <i class="icon ion-paper-airplane"></i>
           </button>
         </ion-item>
diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html
index ae8aa37b1900154b02ddc67a3d93f0a2fd33fd2d..a24a74b05364531996ef3d2ce1b95eebe2bdd009 100644
--- a/www/templates/wallet/view_wallet.html
+++ b/www/templates/wallet/view_wallet.html
@@ -226,6 +226,15 @@
             <span ng-if="$root.settings.useRelative">{{::tx.amount/walletData.currentUD | formatDecimal}}</span>
           </div >
         </span>
+
+        <div class="item" ng-if="walletData.tx.fromTime > 0">
+          <p>
+            <a ng-click="showMoreTx()" translate>ACCOUNT.SHOW_MORE_TX</a>
+            <span class="gray" translate="ACCOUNT.TX_FROM_DATE" translate-values="{fromTime: walletData.tx.fromTime}"></span>
+            <span class="gray">|</span>
+            <a ng-click="showMoreTx(-1)" translate>ACCOUNT.SHOW_ALL_TX</a>
+          </p>
+        </div>
       </div>
 
       <div class="col col-20 hidden-xs hidden-sm">&nbsp;