From ad186824faff7fbe35f865d91deaaa8084f6a3ff Mon Sep 17 00:00:00 2001
From: Benoit Lavenier <benoit.lavenier@e-is.pro>
Date: Mon, 30 May 2016 09:10:22 +0200
Subject: [PATCH] Add transfer form validation, Fix New Account modal recall,
 Fix Wallet avatar icon, Add actions on menu, Add rememberMe on Login

---
 package.json                               |  2 +-
 www/i18n/locale-en.json                    | 14 +++++---
 www/i18n/locale-fr-FR.json                 | 14 +++++---
 www/js/config.js                           |  4 +--
 www/js/controllers/app-controllers.js      | 20 +++++++----
 www/js/controllers/home-controllers.js     |  9 +++--
 www/js/controllers/transfer-controllers.js | 18 +++++++---
 www/js/services/utils-services.js          |  2 +-
 www/js/services/wallet-services.js         | 41 ++++++++++++----------
 www/templates/home/new_account_wizard.html | 11 ++++--
 www/templates/login.html                   | 19 ++++++++--
 www/templates/menu.html                    | 26 ++++++++++++--
 www/templates/wallet/modal_transfer.html   |  2 +-
 www/templates/wallet/new_transfer.html     |  4 ++-
 www/templates/wallet/transfer_form.html    | 40 ++++++++++++++++++---
 www/templates/wallet/view_wallet.html      |  4 +--
 16 files changed, 171 insertions(+), 59 deletions(-)

diff --git a/package.json b/package.json
index fa5eca2a9..7d7062d8a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cesium",
-  "version": "0.1.4",
+  "version": "0.1.5",
   "description": "A webapp client for Duniter network",
   "dependencies": {
     "gulp": "^3.9.1",
diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json
index 7e683b611..30b71f710 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -5,6 +5,7 @@
     "APP_BUILD_DATE": "- build {{buildDate}}",
     "PUBKEY": "Public key",
     "BTN_OK": "OK",
+    "BTN_SEND": "Send",
     "BTN_SHOW": "Show",
     "BTN_SHOW_PUBKEY": "Show key",
     "BTN_RELATIVE_UNIT": "Use relative unit",
@@ -36,6 +37,8 @@
     "CURRENCY": "Currency",
     "CURRENCIES": "Currencies",
     "ACCOUNT": "My Account",
+    "TRANSFER": "Pay",
+    "SCAN": "Scan",
     "SETTINGS": "Settings"
   },
   "HOME": {
@@ -159,8 +162,8 @@
     "TO": "To",
     "AMOUNT": "Amount",
     "AMOUNT_HELP": "Amount",
-    "COMMENTS": "Reference",
-    "COMMENTS_HELP": "Reference (optional)",
+    "COMMENT": "Reference",
+    "COMMENT_HELP": "Reference (optional)",
     "BTN_SEND": "Send",
     "BTN_RELATIVE_UNIT": "Amount in relative unit ?",
     "MODAL": {
@@ -259,7 +262,9 @@
     "UNKNOWN_ERROR": "Unknown error",
     "CRYPTO_UNKNOWN_ERROR": "Your browser is not compatible with cryptographic features.",
     "FIELD_REQUIRED": "This field is required.",
-    "FIELD_TOO_SHORT": "This field is too short.",
+    "FIELD_TOO_SHORT": "This field value is too short.",
+    "FIELD_TOO_LONG": "This field value is exeeding max length.",
+    "FIELD_ACCENT": "Accent character not allowed",
     "PASSWORD_NOT_CONFIRMED": "Must match previous password.",
     "SEND_IDENTITY_FAILED": "Error while trying to register.",
     "SEND_CERTIFICATION_FAILED": "Could not certify identity.",
@@ -287,7 +292,8 @@
     "AMOUNT_REQUIRED": "Amount is required.",
     "AMOUNT_NEGATIVE": "Negative amount not allowed.",
     "NOT_ENOUGH_CREDIT": "Not enough credit.",
-    "INVALID_NODE_SUMMARY": "Unreachable node or invalid address"
+    "INVALID_NODE_SUMMARY": "Unreachable node or invalid address",
+    "INVALID_COMMENT": "Field 'reference' has a bad format."
   },
   "INFO": {
     "POPUP_TITLE": "Information",
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index da6ea34eb..25eb4ebe2 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -5,6 +5,7 @@
     "APP_BUILD_DATE": "- build {{buildDate}}",
     "PUBKEY": "Clé publique",
     "BTN_OK": "OK",
+    "BTN_SEND": "Envoyer",
     "BTN_SHOW": "Voir",
     "BTN_SHOW_PUBKEY": "Voir la clé",
     "BTN_RELATIVE_UNIT": "Afficher en unité relative ?",
@@ -36,6 +37,8 @@
     "CURRENCY": "Monnaie",
     "CURRENCIES": "Monnaies",
     "ACCOUNT": "Mon compte",
+    "TRANSFER": "Payer",
+    "SCAN": "Scanner",
     "SETTINGS": "Paramètres"
   },
   "HOME": {
@@ -59,7 +62,7 @@
     "NODE_HELP": "server.domain.com:port",
     "USE_LOCAL_STORAGE": "Activer le stockage local",
     "AUTHENTICATION_SETTINGS": "Authentification",
-    "REMEMBER_ME": "Se rappeller de moi",
+    "REMEMBER_ME": "Se souvenir de moi",
     "POPUP_NODE": {
       "TITLE" : "Noeud Duniter",
       "HELP" : "Saisissez l'adresse du noeud que vous voulez utiliser :"
@@ -159,8 +162,8 @@
     "TO": "A",
     "AMOUNT": "Montant",
     "AMOUNT_HELP": "Montant",
-    "COMMENTS": "Référence",
-    "COMMENTS_HELP": "Référence (optionnelle)",
+    "COMMENT": "Référence",
+    "COMMENT_HELP": "Référence (optionnelle)",
     "BTN_SEND": "Envoyer",
     "BTN_RELATIVE_UNIT": "Montant en unité relative ?",
     "MODAL": {
@@ -260,6 +263,8 @@
     "CRYPTO_UNKNOWN_ERROR": "Votre navigateur ne semble pas compatible avec les fonctionnalités de cryptographie.",
     "FIELD_REQUIRED": "Champ obligatoire.",
     "FIELD_TOO_SHORT": "Valeur trop courte.",
+    "FIELD_TOO_LONG": "Valeur trop longue",
+    "FIELD_ACCENT": "Caractères accentués non autorisés",
     "PASSWORD_NOT_CONFIRMED": "Ne correspond pas au mot de passe.",
     "SEND_IDENTITY_FAILED": "Erreur pendant l'inscription.",
     "SEND_CERTIFICATION_FAILED": "Erreur lors de la certification de l'identité.",
@@ -287,7 +292,8 @@
     "AMOUNT_REQUIRED": "Le montant est obligatoire.",
     "AMOUNT_NEGATIVE": "Montant négatif non autorisé.",
     "NOT_ENOUGH_CREDIT": "Crédit insufissant.",
-    "INVALID_NODE_SUMMARY": "Noeud injoignable ou adresse invalide"
+    "INVALID_NODE_SUMMARY": "Noeud injoignable ou adresse invalide.",
+    "INVALID_COMMENT": "Le champ 'référence' ne doit pas contenir de caractères accentués."
   },
   "INFO": {
     "POPUP_TITLE": "Information",
diff --git a/www/js/config.js b/www/js/config.js
index 54af8481c..9525d199a 100644
--- a/www/js/config.js
+++ b/www/js/config.js
@@ -14,8 +14,8 @@ angular.module("cesium.config", [])
 	"TIMEOUT": 4000,
 	"DEBUG": false,
 	"NATIVE_TRANSITION": false,
-	"VERSION": "0.1.4",
-	"BUILD_DATE": "2016-05-28T17:53:04.922Z"
+	"VERSION": "0.1.5",
+	"BUILD_DATE": "2016-05-30T07:08:26.918Z"
 })
 
 ;
\ No newline at end of file
diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js
index b44c534d4..2b5c0edd1 100644
--- a/www/js/controllers/app-controllers.js
+++ b/www/js/controllers/app-controllers.js
@@ -31,7 +31,9 @@ angular.module('cesium.app.controllers', ['cesium.services'])
 function LoginModalController($scope, $rootScope, $ionicModal, Wallet, CryptoUtils, UIUtils, $q, $state, $timeout, $ionicSideMenuDelegate, $ionicHistory) {
   // Login modal
   $scope.loginModal = null;
-  $scope.loginData = {};
+  $scope.loginData = {
+    rememberMe: Wallet.defaultSettings.rememberMe
+  };
   $rootScope.viewFirstEnter = false;
 
   $scope.$on('$ionicView.enter', function(e, $state) {
@@ -66,7 +68,7 @@ function LoginModalController($scope, $rootScope, $ionicModal, Wallet, CryptoUti
       }, 2000);
     }
   };
-
+  
   // Login and load wallet
   $scope.loadWallet = function() {
     return $q(function(resolve, reject){
@@ -117,7 +119,9 @@ function LoginModalController($scope, $rootScope, $ionicModal, Wallet, CryptoUti
   // Triggered in the login modal to close it
   $scope.cancelLogin = function() {
     var callback = $scope.loginData.callbacks.cancel;
-    $scope.loginData = {}; // Reset login data
+    $scope.loginData = { // Reset login data
+      rememberMe: Wallet.defaultSettings.rememberMe
+    }; 
     $scope.loginForm.$setPristine(); // Reset form
     $scope.loginModal.hide();
     if (!!callback) {
@@ -136,7 +140,13 @@ function LoginModalController($scope, $rootScope, $ionicModal, Wallet, CryptoUti
     .then(function(){
       // Call wallet login, then execute callback function
       Wallet.login($scope.loginData.username, $scope.loginData.password)
-        .then(function(){
+        .then(function(walletData){
+          walletData.settings.rememberMe = $scope.formData.rememberMe;
+          if (walletData.settings.rememberMe) {
+            walletData.settings.useLocalStorage = true;
+            Wallet.store();
+          }
+
           var callback = $scope.loginData.callbacks.success;
           $scope.loginData = {}; // Reset login data
           $scope.loginForm.$setPristine(); // Reset form
@@ -181,11 +191,9 @@ function LoginModalController($scope, $rootScope, $ionicModal, Wallet, CryptoUti
 
   // Logout
   $scope.logout = function() {
-    UIUtils.loading.show();
     Wallet.logout()
     .then(function() {
       $ionicSideMenuDelegate.toggleLeft();
-      UIUtils.loading.hide()
       $state.go('app.home');
     })
     .catch(UIUtils.onError());
diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js
index 6b0666edd..0e5d9963d 100644
--- a/www/js/controllers/home-controllers.js
+++ b/www/js/controllers/home-controllers.js
@@ -55,8 +55,9 @@ function NewAccountWizardController($scope, $ionicModal, $state, $ionicSideMenuD
     $timeout(function(){
       $scope.accountData = {};
       $scope.accountForm = {};
-      $scope.slides.slider.destroy();
-      delete $scope.slides.slider;
+      $scope.newAccountModal.remove();
+      $scope.newAccountModal = null;
+      $scope.slides.slider = null;
     }, 200);
   };
 
@@ -118,6 +119,10 @@ function NewAccountWizardController($scope, $ionicModal, $state, $ionicSideMenuD
     }
   };
 
+  //Cleanup the modal when we're done with it!
+  $scope.$on('$destroy', function() {
+  });
+
   $scope.selectCurrency = function(currency) {
     $scope.accountData.currency = currency;
     $scope.slideNext();
diff --git a/www/js/controllers/transfer-controllers.js b/www/js/controllers/transfer-controllers.js
index 5040a13b7..98f666639 100644
--- a/www/js/controllers/transfer-controllers.js
+++ b/www/js/controllers/transfer-controllers.js
@@ -66,16 +66,17 @@ function TransferController($scope, $ionicModal, $state, BMA, Wallet, UIUtils, $
 function TransferModalController($scope, $ionicModal, $state, BMA, Wallet, UIUtils, $timeout, System) {
 
   $scope.walletData = {};
-  $scope.transferForm = {};
   $scope.convertedBalance = 0;
+  //$scope.transferForm = {};
   $scope.formData = {
     destPub: null,
     amount: null,
-    comments: null,
+    comment: null,
     useRelative: Wallet.defaultSettings.useRelative
   };
   $scope.dest = null;
   $scope.udAmount = null;
+  $scope.commentPattern = Wallet.regex.COMMENT;
 
   WotLookupController.call(this, $scope, BMA, $state, UIUtils, $timeout, System);
 
@@ -182,6 +183,11 @@ function TransferModalController($scope, $ionicModal, $state, BMA, Wallet, UIUti
   };
 
   $scope.doTransfer = function() {
+    $scope.transferForm.$submitted=true;
+    if(!$scope.transferForm.$valid) {
+      return;
+    }
+
     UIUtils.loading.show();
 
     var amount = $scope.formData.amount;
@@ -191,7 +197,7 @@ function TransferModalController($scope, $ionicModal, $state, BMA, Wallet, UIUti
                amount.replace(new RegExp('[.,]'), '.');
     }
 
-    Wallet.transfer($scope.formData.destPub, amount, $scope.formData.comments)
+    Wallet.transfer($scope.formData.destPub, amount, $scope.formData.comment)
     .then(function() {
        var callback = $scope.formData.callback;
         $scope.formData = {}; // Reset form data
@@ -207,7 +213,11 @@ function TransferModalController($scope, $ionicModal, $state, BMA, Wallet, UIUti
           $state.go('app.view_wallet');
         }
     })
-    .catch(UIUtils.onError('ERROR.SEND_TX_FAILED'));
+    .catch(// TODO BLA remoive function
+      function(err) {
+        UIUtils.onError('ERROR.SEND_TX_FAILED')(err);
+      }
+    );
   };
 
   $scope.closeLookup = function() {
diff --git a/www/js/services/utils-services.js b/www/js/services/utils-services.js
index a2570fafd..624d5aea2 100644
--- a/www/js/services/utils-services.js
+++ b/www/js/services/utils-services.js
@@ -100,7 +100,7 @@ angular.module('cesium.utils.services', ['ngResource'])
       // Otherwise, log to console and display error
       else {
         console.error('>>>>>>>' , err);
-        hideLoading();
+        hideLoading(10); // timeout, to avoid bug on transfer (when error on reference)
         alertError(fullMsg, subtitle);
       }
     };
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index 5eb79b4c4..46ee24030 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -8,6 +8,10 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
 
     var
 
+    USER_ID = "[A-Za-z0-9_-]*",
+    PUBKEY  = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}",
+    COMMENT = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}",
+
     defaultSettings = {
       useRelative: true,
       timeWarningExpire: 129600 /*TODO: =1.5j est-ce suffisant ?*/,
@@ -79,6 +83,10 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
       }
     },
 
+    exact = function(regexpContent) {
+      return new RegExp("^" + regexpContent + "$");
+    },
+
     reduceTxAndPush = function(txArray, result, processedTxMap) {
       if (!txArray || txArray.length === 0) {
         return;
@@ -309,7 +317,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
       });
     },
 
-    loadSources = function(refresh) {
+    loadSources = function() {
       return $q(function(resolve, reject) {
         // Get transactions
         BMA.tx.sources({pubkey: data.pubkey})
@@ -328,9 +336,9 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
               else {
                 src.consumed = false;
               }
-              //if (!src.consumed) {
+              if (!src.consumed) {
                 balance += src.amount;
-              //}
+              }
               sources.push(src);
               sources[srcKey] = src;
             });
@@ -448,7 +456,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
       });
     },
 
-    loadData = function(refresh) {
+    loadData = function() {
         if (data.loaded) {
           return refreshData();
         }
@@ -468,7 +476,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
             loadMembers(),
 
             // Get sources
-            loadSources(false),
+            loadSources(),
 
             // Get requirements
             loadRequirements(),
@@ -499,23 +507,14 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
         return $q(function(resolve, reject){
           $q.all([
 
-            // Get the UD informations
-            BMA.blockchain.stats.ud()
-              .then(function(res){
-                if (res.result.blocks.length) {
-                  var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
-                  return BMA.blockchain.block({ block: lastBlockWithUD })
-                    .then(function(block){
-                      data.currentUD = block.dividend;
-                    });
-                  }
-              }),
+            // Get UDs
+            loadUDs(),
 
             // Get requirements
             loadRequirements(),
 
             // Get sources
-            loadSources(true),
+            loadSources(),
 
             // Get transactions
             loadTransactions()
@@ -532,6 +531,9 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
     transfer = function(destPub, amount, comments) {
         return $q(function(resolve, reject) {
 
+            if (!exact(COMMENT).test(comments)){
+              reject({message:'ERROR.INVALID_COMMENT'}); return;
+            }
             if (!isLogin()){
               reject({message:'ERROR.NEED_LOGIN_FIRST'}); return;
             }
@@ -798,7 +800,10 @@ angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', '
       // serialization
       toJson: toJson,
       fromJson: fromJson,
-      defaultSettings: defaultSettings
+      defaultSettings: defaultSettings,
+      regex: {
+        COMMENT: exact(COMMENT)
+      }
     };
   };
 
diff --git a/www/templates/home/new_account_wizard.html b/www/templates/home/new_account_wizard.html
index 2e202e3be..fe6d1e1d4 100644
--- a/www/templates/home/new_account_wizard.html
+++ b/www/templates/home/new_account_wizard.html
@@ -129,10 +129,15 @@
                     </div>
                 </div>
 
+              <div class="padding hidden-xs text-right">
+                <button class="button button-clear button-dark ink" ng-click="cancel()" type="button" translate>COMMON.BTN_CANCEL
+                </button>
+                <button class="button button-positive ink" type="submit" translate>
+                    COMMON.BTN_SEND
+                </button>
+              </div>
+
                 <div class="padding hidden-xs">
-                    <button class="button button-block button-positive"  type="submit">
-                        {{'COMMON.BTN_OK' | translate}}
-                    </button>
                 </div>
             </form>
             </ion-content>
diff --git a/www/templates/login.html b/www/templates/login.html
index 6bbdffa6c..ec8e011e2 100644
--- a/www/templates/login.html
+++ b/www/templates/login.html
@@ -1,4 +1,4 @@
-<ion-modal-view class="login modal slide-in-up ng-enter active ng-enter-active">
+<ion-modal-view class="login">
   <ion-header-bar class="bar-positive">
     <button class="button button-clear visible-xs" ng-click="cancelLogin()" translate>COMMON.BTN_CANCEL
     </button>
@@ -52,8 +52,18 @@
           </div>
         </div>
 
-        <!-- remember me
-        <ion-checkbox ng-model="isChecked">Checkbox Label</ion-checkbox>-->
+        <!-- remember me -->
+        <div class="item item-toggle dark hidden-xs">
+        <div class="input-label">
+          {{'SETTINGS.REMEMBER_ME' | translate}}
+        </div>
+        <label class="toggle toggle-royal">
+          <input type="checkbox" ng-model="loginData.rememberMe">
+          <div class="track">
+            <div class="handle"></div>
+          </div>
+        </label>
+      </div>
 
         <!-- Show public key -->
         <div class="item item-icon-left item-button-right left">
@@ -67,6 +77,9 @@
           <h3 class="gray text-no-wrap" ng-if="loginData.pubkey">
             {{loginData.pubkey}}
           </h3>
+          <h3 ng-if="loginData.computing">
+            <ion-spinner icon="android"></ion-spinner>
+          </h3>
         </div>
       </div>
 
diff --git a/www/templates/menu.html b/www/templates/menu.html
index de6f63baf..1dc0299da 100644
--- a/www/templates/menu.html
+++ b/www/templates/menu.html
@@ -69,18 +69,38 @@
           <i class="icon ion-card"></i>
           <span translate>MENU.ACCOUNT</span>
         </ion-item>
+
         <!-- <ion-item menu-close ng-click="addAccount()">
           Add account
         </ion-item> -->
         <ion-item menu-close class="item item-icon-left" href="#/app/settings">
-          <i class="icon ion-settings"></i>
+          <i class="icon ion-android-settings"></i>
           <span translate>MENU.SETTINGS</span>
         </ion-item>
-        <ion-item menu-close class="item item-assertive item-icon-left" ng-click="logout()" ng-if="isLogged()">
-          <i class="icon ion-log-out"></i>
+
+        <!-- actions -->
+        <div class="item item-divider" ng-if="isLogged()"></div>
+        <ion-item menu-close class="item item-button-right" ng-if="isLogged()">
+          <span translate>MENU.TRANSFER</span>
+          <button class="button button-energized-900" ng-click="transfer()">
+          <i class="icon ion-paper-airplane"></i>
+          </button>
+        </ion-item>
+        <!-- scan QR code 
+         ion-item menu-close class="item item-button-right" ng-if="isLogged()">
+          <span translate>Scan</span>
+          <button class="button button-energized-900" ng-click="scan()">
+            <i class="icon ion-qr-scanner"></i>
+          </button>
+        </ion-item-->
+        <ion-item menu-close class="item item-button-right" ng-if="isLogged()">
           <span translate>COMMON.BTN_LOGOUT</span>
+          <button class="button button-energized-900" ng-click="logout()">
+          <i class="icon ion-log-out"></i>
+          </button>
         </ion-item>
 
+
       </ion-list>
     </ion-content>
 
diff --git a/www/templates/wallet/modal_transfer.html b/www/templates/wallet/modal_transfer.html
index 33be62b29..2c2330569 100644
--- a/www/templates/wallet/modal_transfer.html
+++ b/www/templates/wallet/modal_transfer.html
@@ -1,4 +1,4 @@
-<ion-modal-view id="transfer">
+<ion-view id="transfer" class="modal slide-in-up ng-enter active ng-enter-active">
   <ion-header-bar class="bar-positive">
     <button class="button button-clear visible-xs" ng-click="closeTransfer()" translate>COMMON.BTN_CANCEL</button>
     <h1 class="title" translate>TRANSFER.MODAL.TITLE</h1>
diff --git a/www/templates/wallet/new_transfer.html b/www/templates/wallet/new_transfer.html
index 5bd8b9bb9..ae031d453 100644
--- a/www/templates/wallet/new_transfer.html
+++ b/www/templates/wallet/new_transfer.html
@@ -1,4 +1,6 @@
-<ion-view view-title="{{'TRANSFER.TITLE' | translate}}" left-buttons="leftButtons">
+<ion-view view-title="{{'TRANSFER.TITLE' | translate}}" 
+  left-buttons="leftButtons"
+  id="transfer">
   <ion-nav-buttons side="secondary">
       <button class="button button-icon button-clear icon ion-android-send visible-xs" ng-click="doTransfer()">
       </button>
diff --git a/www/templates/wallet/transfer_form.html b/www/templates/wallet/transfer_form.html
index 543f9ca9b..e6ad5c9b2 100644
--- a/www/templates/wallet/transfer_form.html
+++ b/www/templates/wallet/transfer_form.html
@@ -7,6 +7,12 @@
           <span class="badge badge-royal">{{dest | formatPubkey}}</span>&nbsp;
           <i class="button button-clear ion-chevron-right"></i>
       </a>
+      <div class="form-errors"
+           ng-show="transferForm.$submitted && !dest">
+        <div class="form-error">
+          <span translate="ERROR.FIELD_REQUIRED"></span>
+        </div>
+      </div>
 
       <span class="item item-text-wrap">
           <span class="gray" translate>TRANSFER.FROM</span>
@@ -20,15 +26,22 @@
 
       <div class="item item-input item-floating-label" ng-if="!formData.useRelative">
           <span class="input-label">{{'TRANSFER.AMOUNT' | translate}} ({{unit | abbreviate}}<sub>{{udUnit | abbreviate}}</sub>)</span>
-          <input type="number" placeholder="{{'TRANSFER.AMOUNT_HELP' | translate}} ({{unit | abbreviate}}{{udUnit | abbreviate}})"
+          <input type="number" name="amount" placeholder="{{'TRANSFER.AMOUNT_HELP' | translate}} ({{unit | abbreviate}}{{udUnit | abbreviate}})"
                  ng-model="formData.amount" required>
       </div>
 
       <div class="item item-input item-floating-label" ng-if="formData.useRelative">
           <span class="input-label">{{'TRANSFER.AMOUNT' | translate}} ({{unit | abbreviate}}<sub>{{udUnit | abbreviate}}</sub>)</span>
-          <input type="text" placeholder="{{'TRANSFER.AMOUNT_HELP' | translate}} ({{unit | abbreviate}}{{udUnit | abbreviate}})" ng-model="formData.amount"
+          <input type="text" name="amount" placeholder="{{'TRANSFER.AMOUNT_HELP' | translate}} ({{unit | abbreviate}}{{udUnit | abbreviate}})" ng-model="formData.amount"
                  required>
       </div>
+      <div class="form-errors"
+           ng-show="transferForm.$submitted && transferForm.amount.$error"
+           ng-messages="transferForm.amount.$error">
+        <div class="form-error" ng-message="required">
+          <span translate="ERROR.FIELD_REQUIRED"></span>
+        </div>
+      </div>
 
 
       <div class="item item-content item-toggle dark">
@@ -41,9 +54,28 @@
         </label>
       </div>
 
-      <label class="item item-input">
-          <textarea placeholder="{{'TRANSFER.COMMENTS_HELP' | translate}}" ng-model="formData.comments"></textarea>
+      <!-- Comment -->
+      <label class="item item-input"
+        ng-class="{'item-input-error': transferForm.$submitted && transferForm.comment.$invalid}">
+          <textarea placeholder="{{'TRANSFER.COMMENT_HELP' | translate}}" 
+            name="comment"
+            ng-model="formData.comment"
+            ng-maxlength="255"
+            ng-pattern="commentPattern"
+            >
+          </textarea>
       </label>
+      <div class="form-errors"
+           ng-show="transferForm.$submitted && transferForm.comment.$error"
+           ng-messages="transferForm.comment.$error">
+        <div class="form-error" ng-message="maxlength">
+          <span translate="ERROR.FIELD_TOO_LONG"></span>
+        </div>
+        <div class="form-error" ng-message="pattern">
+          <span translate="ERROR.FIELD_ACCENT"></span>
+        </div>
+      </div>
+
     </div>
 
     <div class="padding hidden-xs text-right">
diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html
index 715ec8c92..4870cde52 100644
--- a/www/templates/wallet/view_wallet.html
+++ b/www/templates/wallet/view_wallet.html
@@ -19,7 +19,7 @@
                 <div class="avatar"
                      ng-if="!!walletData.avatar"
                      style="background-image: url({{::walletData.avatar}});"></div>
-                <i class="avatar" ng-class="{'avatar-wallet': !walletData.isMember, 'avatar-member': isMember}" ng-if="!walletData.avatar"></i>
+                <i class="avatar" ng-class="{'avatar-wallet': !walletData.isMember, 'avatar-member': walletData.isMember}"></i>
                 <h3 class="light" ng-if="walletData.isMember">{{walletData.uid}}</h3>
                 <h3 class="light" ng-if="!walletData.isMember">{{::walletData.pubkey | formatPubkey}}</h3>
                 <h4 class="light">
@@ -30,7 +30,7 @@
             </div>
 
             <div class="hidden-xs hidden-sm padding" style="text-align:center">
-              <button class="button button-raised button-assertive ink-dark"
+              <button class="button button-raised button-energized-900 ink-dark"
                       ng-click="openTransfer()">
                 {{'ACCOUNT.BTN_SEND_MONEY' | translate}}
               </button>
-- 
GitLab