From 8d8c015011ad080b54560a173a6637c5ea323b12 Mon Sep 17 00:00:00 2001
From: Benoit Lavenier <benoit.lavenier@e-is.pro>
Date: Mon, 27 Feb 2023 22:02:01 +0100
Subject: [PATCH] [wip] Add startup progression message, in home page [fix]
 Network scan: compute a timeout using the remaining time, to force network to
 finish in 10s.

---
 app/config.json                            |   2 +-
 dist/desktop                               |   2 +-
 www/i18n/locale-fr-FR.json                 |  23 ++--
 www/js/config.js                           |  22 +---
 www/js/controllers/home-controllers.js     |  23 +++-
 www/js/controllers/network-controllers.js  |  23 ++--
 www/js/controllers/settings-controllers.js |   1 +
 www/js/entities/peer.js                    |   4 +-
 www/js/platform.js                         | 144 +++++++++++++--------
 www/js/services/bma-services.js            |  40 +++---
 www/js/services/network-services.js        | 100 ++++++++++----
 www/js/services/settings-services.js       |  25 +++-
 www/js/services/wot-services.js            |   4 +-
 www/license/license_g1-es-CT.md            |  96 ++++++++++++++
 www/templates/home/home.html               |   1 +
 www/templates/settings/popup_node.html     |   2 +-
 www/templates/settings/settings.html       |  53 +++++---
 17 files changed, 390 insertions(+), 175 deletions(-)
 create mode 100644 www/license/license_g1-es-CT.md

diff --git a/app/config.json b/app/config.json
index 442313101..89aeb8acb 100644
--- a/app/config.json
+++ b/app/config.json
@@ -41,7 +41,7 @@
       "maxContentLength": 1300
     },
     "node": {
-      "host": "g1.duniter.org",
+      "host": "g111.duniter.org",
       "port": 443
     },
     "fallbackNodes": [
diff --git a/dist/desktop b/dist/desktop
index c3688522f..6e01a54b4 160000
--- a/dist/desktop
+++ b/dist/desktop
@@ -1 +1 @@
-Subproject commit c3688522fc97557794341ab17810671380bdccc1
+Subproject commit 6e01a54b4fd3b6fd3b7a4b708643694ea45edc13
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index b0a88d903..8a013ebcf 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -144,6 +144,7 @@
     "PEER": "Nœud Duniter",
     "PEER_SHORT": "Nœud Duniter",
     "PEER_CHANGED_TEMPORARY": "Adresse utilisée temporairement",
+    "PEER_SELECTED_AUTOMATICALLY": "Sélectionné automatiquement au démarrage",
     "PERSIST_CACHE": "Conserver les données de navigation (expérimental)",
     "PERSIST_CACHE_HELP": "Permet une navigation plus rapide, en conservant localement les données reçues, pour les utiliser d'une session à l'autre.",
     "USE_LOCAL_STORAGE": "Activer le stockage local",
@@ -191,7 +192,7 @@
       "HOST": "Adresse",
       "HOST_HELP": "Adresse : serveur:port",
       "USE_SSL": "Sécurisé ?",
-      "USE_SSL_HELP": "(Chiffrement SSL)",
+      "USE_SSL_HELP": "Chiffrement SSL",
       "BTN_SHOW_LIST": "Liste des noeuds"
     }
   },
@@ -201,7 +202,7 @@
       "HEADER_TITLE": "Bloc #{{number}}-{{hash|formatHash}}",
       "TITLE_CURRENT": "Bloc courant",
       "TITLE": "Bloc #{{number|formatInteger}}",
-      "COMPUTED_BY": "Calculé par le noeud de",
+      "COMPUTED_BY": "Calculé par le nœud de",
       "SHOW_RAW": "Voir le fichier brut",
       "TECHNICAL_DIVIDER": "Informations techniques",
       "VERSION": "Version du format",
@@ -297,7 +298,6 @@
   "NETWORK": {
     "VIEW": {
       "MEDIAN_TIME": "Heure de la blockchain",
-      "LOADING_PEERS": "Chargement des noeuds...",
       "NODE_ADDRESS": "Adresse :",
       "SOFTWARE": "Logiciel",
       "WARN_PRE_RELEASE": "Pré-version (dernière version stable : <b>{{version}}</b>)",
@@ -309,11 +309,13 @@
         "BMAS": "Interface sécurisée (SSL)",
         "BMATOR": "Interface réseau TOR",
         "WS2P": "Interface WS2P",
-        "ES_USER_API": "Noeud de données Cesium+"
+        "ES_USER_API": "Nœud de données Cesium+"
       }
     },
     "INFO": {
-      "ONLY_SSL_PEERS": "Les noeuds non SSL ont un affichage dégradé, car Cesium fonctionne en mode HTTPS."
+      "CONNECTING_TO_PEER": "Connexion au nœud réseau...",
+      "CHECKING_NETWORK_STATE": "Calcul de l'état du réseau {{currency|abbreviate}}...",
+      "ONLY_SSL_PEERS": "Les nœuds non SSL ont un affichage dégradé, car Cesium fonctionne en mode HTTPS."
     }
   },
   "PEER": {
@@ -342,8 +344,8 @@
       "KNOWN_PEERS": "Nœuds connus :",
       "GENERAL_DIVIDER": "Informations générales",
       "ERROR": {
-        "LOADING_TOR_NODE_ERROR": "Récupération des informations du noeud impossible. Le délai d'attente est dépassé.",
-        "LOADING_NODE_ERROR": "Récupération des informations du noeud impossible"
+        "LOADING_TOR_NODE_ERROR": "Récupération des informations du nœud impossible. Le délai d'attente est dépassé.",
+        "LOADING_NODE_ERROR": "Récupération des informations du nœud impossible"
       }
     }
   },
@@ -524,8 +526,8 @@
       "INTRO_WARNING_SECURITY": "Vérifiez que le matériel que vous utilisez actuellement (ordinateur, tablette, téléphone) <b>est sécurisé et digne de confiance</b>.",
       "INTRO_WARNING_SECURITY_HELP": "Anti-virus à jour, pare-feu activé, session protégée par mot de passe ou code pin, etc.",
       "INTRO_HELP": "Cliquez sur <b>{{'COMMON.BTN_START'|translate}}</b> pour débuter la création de compte. Vous serez guidé étape par étape.",
-      "REGISTRATION_NODE": "Votre inscription sera enregistrée via le noeud Duniter <b>{{server}}</b>, qui le diffusera ensuite au reste du réseau de la monnaie.",
-      "REGISTRATION_NODE_HELP": "Si vous ne faites pas confiance en ce noeud, veuillez en changer <a ng-click=\"doQuickFix('settings')\">dans les paramètres</a> de Cesium.",
+      "REGISTRATION_NODE": "Votre inscription sera enregistrée via le nœud Duniter <b>{{server}}</b>, qui le diffusera ensuite au reste du réseau de la monnaie.",
+      "REGISTRATION_NODE_HELP": "Si vous ne faites pas confiance en ce nœud, veuillez en changer <a ng-click=\"doQuickFix('settings')\">dans les paramètres</a> de Cesium.",
       "SELECT_ACCOUNT_TYPE": "Choisissez le type de compte à créer :",
       "MEMBER_ACCOUNT": "Compte membre",
       "MEMBER_ACCOUNT_TITLE": "Création d'un compte membre",
@@ -822,7 +824,8 @@
     "REVOCATION_SENT": "Révocation envoyée",
     "REVOCATION_SENT_WAITING_PROCESS": "La <b>révocation de cette identité</b> a été demandée et est en attente de traitement.",
     "FEATURES_NOT_IMPLEMENTED": "Cette fonctionnalité est encore en cours de développement.<br/>Pourquoi ne pas <b>contribuer à Cesium</b>, pour l'obtenir plus rapidement ? ;)",
-    "EMPTY_TX_HISTORY": "Aucune opération à exporter"
+    "EMPTY_TX_HISTORY": "Aucune opération à exporter",
+    "LOADING_PENDING_TX": "Lecture des opérations en attente"
   },
   "CONFIRM": {
     "CAN_CONTINUE": "<b>Êtes-vous sûr</b> de vouloir continuer ?",
diff --git a/www/js/config.js b/www/js/config.js
index ebf439e7f..b1356a965 100644
--- a/www/js/config.js
+++ b/www/js/config.js
@@ -15,7 +15,7 @@ angular.module("cesium.config", [])
 	"fallbackLanguage": "en",
 	"rememberMe": true,
 	"showUDHistory": true,
-	"timeout": 3000,
+	"timeout": 40000,
 	"timeWarningExpireMembership": 5184000,
 	"timeWarningExpire": 7776000,
 	"keepAuthIdle": 600,
@@ -28,7 +28,7 @@ angular.module("cesium.config", [])
 	"helptip": {
 		"enable": true,
 		"installDocUrl": {
-			"fr-FR": "https://duniter.org/fr/miner-des-blocs/installer/",
+			"fr-FR": "https://duniter.fr/wiki/doc/installer/",
 			"en": "https://duniter.org/en/wiki/duniter/install/"
 		}
 	},
@@ -50,7 +50,7 @@ angular.module("cesium.config", [])
 		"maxContentLength": 1300
 	},
 	"node": {
-		"host": "g1.duniter.org",
+		"host": "g11.duniter.org",
 		"port": 443
 	},
 	"fallbackNodes": [
@@ -67,19 +67,11 @@ angular.module("cesium.config", [])
 			"port": 443
 		},
 		{
-			"host": "g1.le-sou.org",
+			"host": "duniter.moul.re",
 			"port": 443
 		},
 		{
-			"host": "g1.moul.re",
-			"port": 443
-		},
-		{
-			"host": "g1.cloud-libre.eu",
-			"port": 443
-		},
-		{
-			"host": "g1.texu.es",
+			"host": "g1.presles.fr",
 			"port": 443
 		},
 		{
@@ -120,8 +112,8 @@ angular.module("cesium.config", [])
 		}
 	},
 	"version": "1.7.0-rc2",
-	"build": "2023-02-27T10:05:45.363Z",
+	"build": "2023-02-27T10:33:03.091Z",
 	"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
 })
 
-;
\ No newline at end of file
+;
diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js
index e2a925de2..389e8e6da 100644
--- a/www/js/controllers/home-controllers.js
+++ b/www/js/controllers/home-controllers.js
@@ -29,18 +29,17 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
   'ngInject';
 
   $scope.loading = true;
+  $scope.loadingMessage = '';
   $scope.locales = angular.copy(csSettings.locales);
   $scope.smallscreen = UIUtils.screen.isSmall();
   $scope.showInstallHelp = false;
 
   $scope.enter = function(e, state) {
-    if (ionic.Platform.isIOS()) {
-      if(window.StatusBar) {
-        // needed to fix Xcode 9 / iOS 11 issue with blank space at bottom of webview
-        // https://github.com/meteor/meteor/issues/9041
-        StatusBar.overlaysWebView(false);
-        StatusBar.overlaysWebView(true);
-      }
+    if (ionic.Platform.isIOS() && window.StatusBar) {
+      // needed to fix Xcode 9 / iOS 11 issue with blank space at bottom of webview
+      // https://github.com/meteor/meteor/issues/9041
+      StatusBar.overlaysWebView(false);
+      StatusBar.overlaysWebView(true);
     }
 
     if (state && state.stateParams && state.stateParams.uri) {
@@ -57,16 +56,20 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
       $scope.cleanLocationHref(state);
     }
     else {
+
+
       // Wait platform to be ready
       csPlatform.ready()
         .then(function() {
           $scope.loading = false;
+          $scope.loadingMessage = '';
           $scope.loadFeeds();
         })
         .catch(function(err) {
           $scope.node =  csCurrency.data.node;
           $scope.loading = false;
           $scope.error = err;
+          $scope.loadingMessage = '';
         });
     }
   };
@@ -193,6 +196,12 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
     }
   };
 
+
+  // Listen platform messages
+  csPlatform.api.start.on.message($scope, function(message) {
+    $scope.loadingMessage = message;
+  });
+
   // For DEV ONLY
   /*$timeout(function() {
    $scope.loginAndGo();
diff --git a/www/js/controllers/network-controllers.js b/www/js/controllers/network-controllers.js
index a3ab788f6..51a53bb28 100644
--- a/www/js/controllers/network-controllers.js
+++ b/www/js/controllers/network-controllers.js
@@ -157,18 +157,18 @@ function NetworkLookupController($scope,  $state, $location, $ionicPopover, $win
   $scope.load = function() {
 
     if ($scope.search.loading){
-      $scope.refreshing = false;
+      $scope.updating = false;
 
       // Start network scan
       csNetwork.start($scope.node, $scope.computeOptions())
         .then(function() {
-          $scope.refresh();
+          $scope.onDataChanged();
         });
 
       // Catch event on new peers
       $scope.listeners.push(
         csNetwork.api.data.on.changed($scope, function(data) {
-          $scope.refresh(data);
+          $scope.onDataChanged(data);
         }));
     }
 
@@ -176,16 +176,21 @@ function NetworkLookupController($scope,  $state, $location, $ionicPopover, $win
     $scope.showHelpTip();
   };
 
-  $scope.refresh = function(data) {
+  $scope.onDataChanged = function(data) {
     data = csNetwork.data || data;
-    if (!data || $scope.refreshing /*|| !$scope.networkStarted*/) return; // Skip if no data, or already refreshing
+    if (!data || $scope.updating /*|| !$scope.networkStarted*/) return; // Skip if no data, or already updating
 
-    // Mark as refreshing
-    $scope.refreshing = true;
+    var now = Date.now();
+    console.debug("[peers] Fetching name + avatar, on {0} peers...".format(data.peers && data.peers.length || 0));
+
+    // Mark as updating
+    $scope.updating = true;
 
     // Add name+avatar to peers
     csWot.extendAll(data.peers)
       .then(function() {
+        console.debug("[peers] Fetching name + avatar on peers [OK] in {0}ms".format(Date.now() - now));
+
         // Avoid to refresh if view has been leaving
         if ($scope.networkStarted) {
           $scope.updateView(data);
@@ -196,7 +201,7 @@ function NetworkLookupController($scope,  $state, $location, $ionicPopover, $win
         // Continue
       })
       .then(function() {
-        $scope.refreshing = false;
+        $scope.updating = false;
       });
   }
 
@@ -223,7 +228,7 @@ function NetworkLookupController($scope,  $state, $location, $ionicPopover, $win
 
   $scope.sort = function() {
     $scope.search.loading = true;
-    $scope.refreshing = true;
+    $scope.updating = true;
     csNetwork.sort($scope.computeOptions());
     $scope.updateView(csNetwork.data);
   };
diff --git a/www/js/controllers/settings-controllers.js b/www/js/controllers/settings-controllers.js
index 2e7082d32..b6e756bde 100644
--- a/www/js/controllers/settings-controllers.js
+++ b/www/js/controllers/settings-controllers.js
@@ -150,6 +150,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
 
   // Change node
   $scope.changeNode= function(node) {
+
     var port = !!$scope.formData.node.port && $scope.formData.node.port != 80 && $scope.formData.node.port != 443 ? $scope.formData.node.port : undefined;
     node = node || {
         host: $scope.formData.node.host,
diff --git a/www/js/entities/peer.js b/www/js/entities/peer.js
index 6a2e1fbba..6b5afe039 100644
--- a/www/js/entities/peer.js
+++ b/www/js/entities/peer.js
@@ -123,8 +123,8 @@ Peer.prototype.getPort = function() {
   return bma.port ? parseInt(bma.port) : null;
 };
 
-Peer.prototype.getHost = function(getHost) {
-  bma = getHost || this.bma || this.getBMA();
+Peer.prototype.getHost = function(bma) {
+  bma = bma || this.bma || this.getBMA();
   return ((bma.port == 443 || bma.useSsl) && bma.dns) ? bma.dns :
     (this.hasValid4(bma) ? bma.ipv4 :
         (bma.dns ? bma.dns :
diff --git a/www/js/platform.js b/www/js/platform.js
index 4a1a105ac..d1cd933c4 100644
--- a/www/js/platform.js
+++ b/www/js/platform.js
@@ -101,7 +101,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
 
 
   .factory('csPlatform', function (ionicReady, $rootScope, $q, $state, $translate, $timeout, $ionicHistory, $window,
-                                   UIUtils, Modals, BMA, Device,
+                                   UIUtils, Modals, BMA, Device, Api,
                                    csHttp, csConfig, csCache, csSettings, csNetwork, csCurrency, csWallet) {
 
     'ngInject';
@@ -110,7 +110,9 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       started = false,
       startPromise,
       listeners = [],
-      removeChangeStateListener;
+      removeChangeStateListener,
+      api = new Api(this, 'csPlatform')
+    ;
 
     // Fix csConfig values
     csConfig.demo = csConfig.demo === true || csConfig.demo === 'true' || false;
@@ -141,57 +143,40 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       removeChangeStateListener = null;
     }
 
-    // Alert user if node not reached - fix issue #
+    // Alert user if node not reached
     function checkBmaNodeAlive(alive) {
-      if (alive) return true;
+      if (alive) return true; // Ok, current node is alive
 
+      var askUserConfirmation = checkBmaNodeAliveCounter === 0 && csSettings.data.expertMode;
       checkBmaNodeAliveCounter++;
       if (checkBmaNodeAliveCounter > 3)  throw 'ERROR.CHECK_NETWORK_CONNECTION'; // Avoid infinite loop
 
-      return BMA.filterAliveNodes(csSettings.data.fallbackNodes, Math.min(csConfig.timeout, 3000)/*3s max*/)
+      api.start.raise.message('NETWORK.INFO.CONNECTING_TO_PEER');
+      return BMA.filterAliveNodes(csSettings.data.fallbackNodes, csConfig.timeout)
         .then(function (fallbackNodes) {
-          if (!fallbackNodes.length) {
-            throw 'ERROR.CHECK_NETWORK_CONNECTION';
-          }
-          var randomIndex = Math.floor(Math.random() * fallbackNodes.length);
-          var fallbackNode = fallbackNodes[randomIndex];
-          return fallbackNode;
+          if (!fallbackNodes.length) throw 'ERROR.CHECK_NETWORK_CONNECTION';
+          return _.sample(fallbackNodes); // Random select
         })
         .then(function (fallbackNode) {
 
-          // Not expert mode: continue with the fallback node
-          if (!csSettings.data.expertMode) {
-            console.info("[platform] Switching to fallback node: {0}".format(fallbackNode.server));
-            return fallbackNode;
-          }
-
-          // If expert mode: ask user to confirm, before switching to fallback node
-          var confirmMsgParams = {old: BMA.server, new: fallbackNode.server};
-
-          // Force to show port/ssl, if this is the only difference
-          if (confirmMsgParams.old === confirmMsgParams.new) {
-            if (BMA.port != fallbackNode.port) {
-              confirmMsgParams.new += ':' + fallbackNode.port;
-            } else if (BMA.useSsl == false && (fallbackNode.useSsl || fallbackNode.port == 443)) {
-              confirmMsgParams.new += ' (SSL)';
-            }
+          // Ask user before using the fallback node
+          if (askUserConfirmation) {
+            return askUseFallbackNode(fallbackNode);
           }
 
-          return $translate('CONFIRM.USE_FALLBACK_NODE', confirmMsgParams)
-            .then(UIUtils.alert.confirm)
-            .then(function (confirm) {
-              if (!confirm) return; // Stop
-              return fallbackNode;
-            });
+          return fallbackNode;
         })
         .then(function (fallbackNode) {
           if (!fallbackNode) return; // Skip
 
-          // Only change BMA node in settings
-          csSettings.data.node = fallbackNode;
-
-          // Add a marker, for UI (only if not expert mode)
-          csSettings.data.node.temporary = !csSettings.data.expertMode;
+          console.info("[platform] Switching to fallback node: {0}".format(fallbackNode.server));
+          var node = {
+            host: fallbackNode.host,
+            port: fallbackNode.port,
+            useSsl: fallbackNode.useSsl,
+          };
+          csSettings.data.node = node;
+          csSettings.data.node.temporary = true;
 
           csHttp.cache.clear();
 
@@ -202,18 +187,32 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
     }
 
     // Make sure the BMA node is synchronized (is on the main consensus block)
-    function checkBmaNodeSynchronized() {
+    function checkBmaNodeSynchronized(alive) {
+      if (!alive) return false;
       var now = Date.now();
+
       console.info("[platform] Checking if node is synchronized...");
+      api.start.raise.message('NETWORK.INFO.CHECKING_NETWORK_STATE');
+
+      var askUserConfirmation = csSettings.data.expertMode;
 
       return csNetwork.getSynchronizedBmaPeers(BMA, {
-        timeout:  Math.min(csConfig.timeout, 3000 /*3s max*/)
+        timeout:  Math.min(csConfig.timeout, 10000 /*10s max*/)
       })
         .then(function(peers) {
-          console.info("[platform] Network scanned in {0}ms, {1} peers (UP and synchronized) found".format(Date.now() - now, peers.length));
 
           if (!peers.length) return; // No peer found: exit
 
+          // Not enough peers in network (isolated node). Should never occur. Make sure at least one known node exists
+          if (peers.length < 10) {
+            console.warn("[platform] Network scanned in {0}ms, only {1} peers (UP and synchronized) found. To few peers. Will peek another peer...".format(Date.now() - now, peers.length));
+            // Retry using another peer
+            return checkBmaNodeAlive(false)
+              .then(checkBmaNodeSynchronized); // Loop
+          }
+
+          console.info("[platform] Network scanned in {0}ms, {1} peers (UP and synchronized) found".format(Date.now() - now, peers.length));
+
           // TODO: store sync peers in storage ?
           //csSettings.data.
 
@@ -242,19 +241,28 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
                 return true;
               }
 
-              // If Expert mode: ask user to select a node
-              if (csSettings.data.expertMode) {
-                return selectBmaNode();
-              }
-
-              var randomIndex = Math.floor(Math.random() * peers.length);
-              var randomPeer = peers[randomIndex];
-              var node = {
+              var randomPeer = _.sample(peers);
+              var synchronizedNode = {
                 host: randomPeer.getHost(),
                 port: randomPeer.getPort(),
                 useSsl: randomPeer.isSsl()
               };
-              console.info("[platform] Randomly selected peer {0}".format(randomPeer.server));
+
+              // If Expert mode: ask user to select a node
+              if (askUserConfirmation) {
+                return askUseFallbackNode(synchronizedNode);
+              }
+
+              return synchronizedNode;
+            })
+            .then(function(node) {
+              if (node === true) return true;
+              if (!node) {
+                return selectBmaNode();
+              }
+
+              console.info("[platform] Switching to synchronized fallback peer {{0}:{1}}".format(node.host, node.port));
+
               // Only change BMA node in settings
               csSettings.data.node = node;
 
@@ -262,7 +270,28 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
               csSettings.data.node.temporary = true;
 
               return BMA.copy(node);
-            });
+            })
+        });
+    }
+
+    function askUseFallbackNode(fallbackNode) {
+      // Ask user to confirm, before switching to fallback node
+      var confirmMsgParams = {old: BMA.server, new: fallbackNode.server};
+
+      // Force to show port/ssl, if this is the only difference
+      if (confirmMsgParams.old === confirmMsgParams.new) {
+        if (BMA.port != fallbackNode.port) {
+          confirmMsgParams.new += ':' + fallbackNode.port;
+        } else if (BMA.useSsl == false && (fallbackNode.useSsl || fallbackNode.port == 443)) {
+          confirmMsgParams.new += ' (SSL)';
+        }
+      }
+
+      return $translate('CONFIRM.USE_FALLBACK_NODE', confirmMsgParams)
+        .then(UIUtils.alert.confirm)
+        .then(function (confirm) {
+          if (!confirm) return; // Stop
+          return fallbackNode;
         });
     }
 
@@ -324,8 +353,6 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       return $q.when();
     }
 
-
-
     function addListeners() {
       // Listen if node changed
       listeners.push(
@@ -358,6 +385,8 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       // Avoid change state
       disableChangeState();
 
+      api.start.raise.message('COMMON.LOADING');
+
       // We use 'ionicReady()' instead of '$ionicPlatform.ready()', because this one is callable many times
       startPromise = ionicReady()
 
@@ -370,7 +399,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
         ]))
 
         // Load BMA
-        .then(function(){
+        .then(function() {
           checkBmaNodeAliveCounter = 0;
           return BMA.ready()
             .then(checkBmaNodeAlive)
@@ -416,6 +445,8 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       }, 500);
     }
 
+    api.registerEvent('start', 'message');
+
     return  {
       disableChangeState: disableChangeState,
       isStarted: isStarted,
@@ -425,7 +456,8 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       stop: stop,
       version: {
         latest: getLatestRelease
-      }
+      },
+      api: api
     };
   })
 
@@ -481,7 +513,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
 
       // Ionic Platform Grade is not A, disabling views transitions
       if (ionic.Platform.grade.toLowerCase() !== 'a') {
-        console.info('[app] Disabling UI effects, because plateform\'s grade is [' + ionic.Platform.grade + ']');
+        console.info('[app] Disabling UI effects, because platform\'s grade is {{0}}'.format(ionic.Platform.grade));
         UIUtils.setEffects(false);
       }
 
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index e61558fd7..1a88a9ce5 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -97,16 +97,16 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
       that.alive = false;
 
       // Use settings as default, if exists
-      if (csSettings.data && csSettings.data.node) {
-        host = host || csSettings.data.node.host;
-        port = port || csSettings.data.node.port;
-
-        useSsl = angular.isDefined(useSsl) ? useSsl : (port == 443 || csSettings.data.node.useSsl || that.forceUseSsl);
+      var node = csSettings.data && csSettings.data.node;
+      if (node) {
+        host = host || node.host;
+        port = port || node.port;
+        useSsl = angular.isDefined(useSsl) ? useSsl : (port == 443 || node.useSsl || that.forceUseSsl);
       }
 
-      if (!host) {
-        return; // could not init yet
-      }
+      if (!host) return; // could not init yet
+
+
       that.host = host;
       that.port = port || 80;
       that.useSsl = angular.isDefined(useSsl) ? useSsl : (that.port == 443 || that.forceUseSsl);
@@ -308,7 +308,8 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
       if (that._startPromise) return that._startPromise;
       if (that.started) return $q.when(that.alive);
 
-      if (!that.host) {
+      // Load without argument: wait settings, then init using setting's data
+      if (!that.host || !csSettings.isStarted()) {
         return csSettings.ready()
           .then(function() {
             that.init();
@@ -320,32 +321,29 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
           });
       }
 
-      console.debug("[BMA] Starting {0} {ssl: {1})...".format(that.server, that.useSsl));
+      console.debug("[BMA] [{0}] Starting {ssl: {1})...".format(that.server, that.useSsl));
       var now = Date.now();
 
-      that._startPromise = $q.all([
-          csSettings.ready(),
-          that.isAlive()
-        ])
-        .then(function(res) {
-          that.alive = res[1];
+      that._startPromise = that.isAlive()
+        .then(function(alive) {
+          that.alive = alive;
           if (!that.alive) {
-            console.error("[BMA] Could not start {0} : unreachable".format(that.server));
+            console.error("[BMA] Could not start using peer {{0}}: unreachable".format(that.server));
             that.started = true;
             delete that._startPromise;
-            return false;
+            return false; // Not alive
           }
 
           // Add listeners
           if (!listeners || !listeners.length) {
             addListeners();
           }
-          console.debug('[BMA] Started in '+(Date.now()-now)+'ms');
+          console.debug('[BMA] Started in {}ms'.format(Date.now()-now));
 
           that.api.node.raise.start();
           that.started = true;
           delete that._startPromise;
-          return true;
+          return true; // Alive
         });
       return that._startPromise;
     };
@@ -383,6 +381,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
     };
 
     that.filterAliveNodes = function(fallbackNodes, timeout) {
+      timeout = timeout || csConfig.timeout;
       var fallbackNodes = _.filter(fallbackNodes || [], function(node) {
         node.server = node.server || node.host + ((!node.port && node.port != 80 && node.port != 443) ? (':' + node.port) : '');
         var same = that.node.same(node);
@@ -1017,6 +1016,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
   service.lightInstance = function(host, port, useSsl, timeout) {
     port = port || 80;
     useSsl = angular.isDefined(useSsl) ? useSsl : (port == 443);
+    timeout = timeout || csConfig.timeout;
     return {
       host: host,
       port: port,
diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js
index 64b14df49..83da91b18 100644
--- a/www/js/services/network-services.js
+++ b/www/js/services/network-services.js
@@ -42,7 +42,8 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       searchingPeersOnNetwork: false,
       difficulties: null,
       ws2pHeads: null,
-      timeout: csConfig.timeout
+      timeout: csConfig.timeout,
+      startTime: null
     },
 
     // Return the block uid
@@ -50,6 +51,15 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       return block && [block.number, block.hash].join('-');
     },
 
+    // Return the block uid
+    buidBlockNumber = function(buid) {
+      return (typeof buid === 'string') && parseInt(buid.split('-')[0]);
+    },
+
+    remainingTime = function() {
+      return Math.max(0, data.timeout - (Date.now() - data.startTime));
+    },
+
     resetData = function() {
       data.bma = null;
       data.listeners = [];
@@ -79,6 +89,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       data.difficulties = null;
       data.ws2pHeads = null;
       data.timeout = csConfig.timeout;
+      data.startTime = null;
     },
 
     hasPeers = function() {
@@ -119,7 +130,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           // When too many request, retry in 3s
           if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
             return $timeout(function() {
-              return loadW2spHeads();
+              if (remainingTime() > 0) return loadW2spHeads();
             }, 3000);
           }
           console.error(err); // can occur on duniter v1.6
@@ -140,7 +151,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           // When too many request, retry in 3s
           if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
             return $timeout(function() {
-              return loadDifficulties();
+              if (remainingTime() > 0) return loadDifficulties();
             }, 3000);
           }
           console.error(err);
@@ -207,7 +218,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
             _.forEach(res.peers, function(json) {
               // Exclude, if not UP or on a too old block
               if (json.status !== 'UP') return;
-              json.blockNumber = json.block && parseInt(json.block.split('-')[0]);
+              json.blockNumber = buidBlockNumber(json.block);
               if (json.blockNumber && json.blockNumber < data.minOnlineBlockNumber) {
                 console.debug("[network] Exclude a too old peer document, on pubkey {0}".format(json.pubkey.substring(0,6)));
                 return;
@@ -219,7 +230,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
               _.forEach(json.endpoints||[], function(ep) {
                 if (ep.startsWith('WS2P')) {
                   var key = json.pubkey + '-' + ep.split(' ')[1];
-                  if (data.ws2pHeads[key]) {
+                  if (data.ws2pHeads && data.ws2pHeads[key]) {
                     data.ws2pHeads[key].hasEndpoint = true;
                   }
                 }
@@ -234,7 +245,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
               _.forEach(privateWs2pHeads, function(head) {
 
                 if (!head.hasEndPoint) {
-                  var currentNumber = head.buid && parseInt(head.buid.split('-')[0]);
+                  var currentNumber = buidBlockNumber(head.buid);
                   // Exclude if on a too old block
                   if (currentNumber && currentNumber < data.minOnlineBlockNumber) {
                     console.debug("[network] Exclude a too old WS2P message, on pubkey {0}".format(head.pubkey.substring(0,6)));
@@ -286,6 +297,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           }
         })
         .then(function(){
+          if (!isStarted()) return; // Skip if stopped
           data.searchingPeersOnNetwork = false;
           data.loading = false;
           if (newPeers.length) {
@@ -349,7 +361,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       list = list || data.newPeers;
 
       // Analyze the peer document, and exclude using the online filter
-      json.blockNumber = json.block && parseInt(json.block.split('-')[0]);
+      json.blockNumber = buidBlockNumber(json.block);
       json.oldBlock = (json.status === 'UP' && json.blockNumber && json.blockNumber < data.minOnlineBlockNumber);
 
       var peers = createPeerEntities(json);
@@ -439,7 +451,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       peer.server = peer.getServer();
       peer.dns = peer.getDns();
       peer.buid = peer.buid || peer.block;
-      peer.blockNumber = peer.buid && parseInt(peer.buid.split('-')[0]);
+      peer.blockNumber = buidBlockNumber(peer.buid);
       peer.uid = peer.pubkey && data.uidsByPubkeys[peer.pubkey];
       peer.id = peer.keyID();
       return [peer];
@@ -450,6 +462,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       // Apply filter
       if (!applyPeerFilter(peer)) return $q.when();
 
+      var startRefreshTime = Date.now();
       if (!data.filter.online || (!data.filter.online && peer.status === 'DOWN') || !peer.getHost() /*fix #537*/) {
         peer.online = false;
         return $q.when(peer);
@@ -461,7 +474,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
         delete data.ws2pHeads[ws2pHeadKey];
         if (head) {
           peer.buid = head.buid;
-          peer.currentNumber=head.buid && parseInt(head.buid.split('-')[0]);
+          peer.currentNumber = buidBlockNumber(head.buid);
           peer.version = head.version;
           peer.powPrefix = head.powPrefix;
         }
@@ -499,7 +512,8 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
         return $q.when(peer);
       }
 
-      peer.api = peer.api ||  BMA.lightInstance(peer.getHost(), peer.getPort(), peer.isSsl(), data.timeout);
+      const timeout = Math.max(500, remainingTime()); // >= 500ms
+      peer.api = peer.api || BMA.lightInstance(peer.getHost(), peer.getPort(), peer.isSsl(), timeout);
 
       // Get current block
       return peer.api.blockchain.current(false/*no cache*/)
@@ -525,10 +539,14 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           if (!peer.secondTry) {
             var bma = peer.bma || peer.getBMA();
             if (bma.dns && peer.server.indexOf(bma.dns) === -1) {
+              var secondTryTimeout = remainingTime();
+
               // try again, using DNS instead of IPv4 / IPV6
-              peer.secondTry = true;
-              peer.api = BMA.lightInstance(bma.dns, bma.port, bma.useSsl);
-              return refreshPeer(peer); // recursive call
+              if (secondTryTimeout > 0) {
+                peer.secondTry = true;
+                peer.api = BMA.lightInstance(bma.dns, bma.port, bma.useSsl, secondTryTimeout);
+                return refreshPeer(peer); // recursive call
+              }
             }
           }
 
@@ -760,23 +778,33 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
     },
 
     start = function(bma, options) {
+      if (startPromise) {
+        console.warn('[network-service] Waiting previous start to be closed...');
+        return startPromise.then(function() {
+          return start(bma, options)
+        })
+      }
+
       options = options || {};
-      startPromise = BMA.ready()
+      bma = bma || BMA;
+      startPromise = bma.ready()
         .then(function() {
           close();
 
-          data.bma = bma || BMA;
+          data.bma = bma;
           data.filter = options.filter ? angular.merge(data.filter, options.filter) : data.filter;
           data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
           data.expertMode = angular.isDefined(options.expertMode) ? options.expertMode : data.expertMode;
           data.timeout = angular.isDefined(options.timeout) ? options.timeout : csConfig.timeout;
+          data.startTime = Date.now();
 
           // Init a min block number
-          data.minOnlineBlockNumber = data.mainBlock && data.mainBlock.buid && (parseInt(data.mainBlock.buid.split('-')[0]) - constants.MAX_BLOCK_OFFSET) || undefined;
+          var mainBlockNumber = data.mainBlock && buidBlockNumber(data.mainBlock.buid);
+          data.minOnlineBlockNumber = mainBlockNumber && Math.max(0, (mainBlockNumber - constants.MAX_BLOCK_OFFSET)) || undefined;
           if (data.minOnlineBlockNumber === undefined) {
             return csCurrency.blockchain.current(true/*use cache*/)
               .then(function(current) {
-                data.minOnlineBlockNumber = current.number - constants.MAX_BLOCK_OFFSET;
+                data.minOnlineBlockNumber = Math.max(0, current.number - constants.MAX_BLOCK_OFFSET);
               });
           }
         })
@@ -797,10 +825,11 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
 
     close = function() {
       if (data.bma) {
-        console.info('[network-service] Stopping...');
+        console.info('[network] Stopping...');
         removeListeners();
         resetData();
       }
+      startPromise = null;
     },
 
     isStarted = function() {
@@ -809,13 +838,9 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
 
     startIfNeed = function(bma, options) {
       if (startPromise) return startPromise;
+      // Start if need
       if (!isStarted()) {
-        // Start (then stop) if need
-        return start(bma, options)
-          .then(function() {
-            close();
-            return data;
-          });
+        return start(bma, options);
       }
       else {
         return $q.resolve(data);
@@ -823,9 +848,12 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
     },
 
     getMainBlockUid = function(bma, options) {
+      var wasStarted = isStarted();
       return startIfNeed(bma, options)
         .then(function(data) {
-          return data.mainBlock && data.mainBlock.buid;
+          var buid = data.mainBlock && data.mainBlock.buid;
+          if (!wasStarted) close();
+          return buid;
         });
     },
 
@@ -837,12 +865,32 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       options.filter.ssl = isHttpsMode ? true : undefined;
       options.filter.online = true;
       options.filter.expertMode = false;
+      console.info('[network] Getting synchronized BMA peers...');
+
+      var wasStarted = isStarted();
+      var now = Date.now();
 
       return startIfNeed(bma, options)
         .then(function(data){
-          return _.filter(data.peers, function(peer) {
+          var peers = _.filter(data.peers, function(peer) {
             return peer.hasMainConsensusBlock && peer.isBma();
           });
+
+          // Log
+          if (peers.length) {
+            console.info('[network] Found {0}/{1} BMA peers on main consensus block #{2} - in {3}ms'.format(
+              peers.length,
+              data.peers.length,
+              peers[0] && peers[0].buid,
+              Date.now() - now));
+          }
+          else {
+            console.warn('[network] No synchronized BMA peers found, in {1}ms'.format(Date.now() - now))
+          }
+
+          if (!wasStarted) close();
+
+          return peers;
         });
     };
 
diff --git a/www/js/services/settings-services.js b/www/js/services/settings-services.js
index 15e3db750..d962f4d3a 100644
--- a/www/js/services/settings-services.js
+++ b/www/js/services/settings-services.js
@@ -115,7 +115,11 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
       "en": "license/license_g1-en",
       "fr-FR": "license/license_g1-fr-FR",
       "es-ES": "license/license_g1-es-ES",
-      "pt-PT": "license/license_g1-pt-PT"
+      "es-CT": "license/license_g1-es-CT",
+      "eo-EO": "license/license_g1-eo-EO",
+      "pt-PT": "license/license_g1-pt-PT",
+      "it-IT": "license/license_g1-it-IT",
+      "de-DE": "license/license_g1-de-DE"
     }
   },
     fixedSettings,
@@ -174,7 +178,7 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
     var promise;
     if (data.useLocalStorage) {
       // When node is temporary (fallback node): keep previous node address - issue #476
-      if (data.node.temporary === true) {
+      if (data.node && data.node.temporary === true) {
         promise = localStorage.getObject(constants.STORAGE_KEY)
           .then(function(previousSettings) {
             var savedData = angular.copy(data);
@@ -212,6 +216,9 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
   applyData = function(newData) {
     if (!newData) return; // skip empty
 
+    // DEBUG
+    //console.debug('[settings] Applying data', newData);
+
     var localeChanged = false;
     if (newData.locale && newData.locale.id) {
       // Fix previously stored locale (could use bad format)
@@ -225,11 +232,20 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
       newData[key] = defaultSettings[key]; // This will apply fixed value (override by config.js file)
     });
 
+    // If need select a random peer, from the config
+    if (!newData.node && _.size(csConfig.fallbackNodes) > 0) {
+      newData.node = _.sample(csConfig.fallbackNodes);
+      console.info('[settings] Random selected peer: [{0}:{1}]'.format(newData.node.host, newData.node.port||80));
+      newData.node.temporary = true;
+    }
+
     // Apply new settings
     angular.merge(data, newData);
 
     // Delete temporary properties, if false
-    if (newData && newData.node && !newData.node.temporary || !data.node.temporary) delete data.node.temporary;
+    if ((newData && newData.node && !newData.node.temporary) || (data.node && !data.node.temporary)) {
+      delete data.node.temporary;
+    }
 
     // Apply the new locale (only if need)
     // will produce an event cached by onLocaleChange();
@@ -251,10 +267,11 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
             return;
           }
 
+          console.debug('[settings] Loaded from local storage in {0}ms'.format(Date.now()-now));
+
           // Apply stored data
           applyData(storedData);
 
-          console.debug('[settings] Loaded from local storage in '+(Date.now()-now)+'ms');
           emitChangedEvent();
         });
   },
diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js
index 8011f7361..8bf72a926 100644
--- a/www/js/services/wot-services.js
+++ b/www/js/services/wot-services.js
@@ -997,7 +997,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
           var idtiesByBlock = {};
           var idtiesByPubkey = {};
           _.forEach(memberships, function(ms){
-            if (ms.membership == 'IN' && !uids[ms.pubkey]) {
+            if (ms.membership === 'IN' && !uids[ms.pubkey]) {
               var idty = {
                 uid: ms.uid,
                 pubkey: ms.pubkey,
@@ -1019,7 +1019,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
               // Remove previous idty from map
               if (otherIdtySamePubkey) {
                 idtiesByBlock[otherIdtySamePubkey.block] = idtiesByBlock[otherIdtySamePubkey.block].reduce(function(res, aidty){
-                  if (aidty.pubkey == otherIdtySamePubkey.pubkey) return res; // if match idty to remove, to NOT add
+                  if (aidty.pubkey === otherIdtySamePubkey.pubkey) return res; // if match idty to remove, to NOT add
                   return (res||[]).concat(aidty);
                 }, null);
                 if (idtiesByBlock[otherIdtySamePubkey.block] === null) {
diff --git a/www/license/license_g1-es-CT.md b/www/license/license_g1-es-CT.md
new file mode 100644
index 000000000..5da80f68b
--- /dev/null
+++ b/www/license/license_g1-es-CT.md
@@ -0,0 +1,96 @@
+Licencia Äž1 - v0.2.9
+====================
+
+:Date: 2017-04-04 12:59
+:Modified: 2023-01-01 18:33
+
+**Licencia de la moneda y compromiso de responsabilidad.**
+
+Cualquier certificación de nueva membresía de Ğ1 debe ir primero acompañada por la transmisión de esta licencia de moneda Ğ1 cuyo certificador debe asegurar que ha sido estudiada, entendida y aceptada por la persona que será certificada.
+
+Cualquier evento de encuentro relacionado con Ğ1 debe ir acompañado de la transmisión de esta licencia, que puede leerse en voz alta y transmitirse por cualquier medio.
+
+Red de confianza Äž1 (RdC Äž1)
+------------------------------
+
+** Aviso de advertencia :** Certificar no significa que simplemente has visto físicamente a la persona candidata. Es asegurar a la comunidad Ğ1 que la conoces lo suficientemente bien y que sabrás cómo contactarla y localizarla fácilmente, ser capaz de identificar una posible cuenta falsa o duplicada realizada por alguien que has certificado, u otro tipo de problema, efectuando verificaciones con la comunidad que detecten el problema. La seguridad de la Red de Confianza es descentralizada.
+
+**Consejos fuertemente recomendados**
+
+Conocer bien a una persona significa que puedes comunicarte con ella por varios medios distintos (físicos, electrónicos, etc ...) pero también que conoces a varias personas que también la conocen bien y, por lo tanto, pueden contactar con ella igualmente. Además si no conoces bien a nadie de quienes ya le han certificado, es una clara indicación de que no conoces bien a la persona; una certificación de este tipo provoca una alerta hacia toda la comunidad Ğ1. En caso de conocimiento insuficiente, es importante NO certificar.
+
+Nunca certifiques solo/a, sino acompáñate por al menos otro/a miembro de la RdC Ğ1 para evitar cualquier error de manejo. En caso de error, advierte a quienes ya son miembros de la RdC Ğ1 inmediatamente.
+
+Antes de cualquier certificación, asegúrate de verificar si su cuenta (tanto si es nueva candidatura o ya miembro) ha recibido ya una o más certificaciones. Si es necesario, solicite información para ponerte en contacto con quienes ya le hayan certificado para verificar juntos/as que conocéis bien la persona en cuestión concerniente, así como su llave pública correspondiente.
+
+Comprueba que la persona a ser certificada domina bien el manejo de su cuenta: una buena manera de comprobar esto es transferir unas Ğ1 a la cuenta destino, y luego pedir la devolución a tu propia cuenta. Esto asegura el buen manejo por quien va a ser certificado, de su llave privada.
+
+Comprueba que tus contactos hayan estudiado y comprendido la licencia Äž1 actualizada.
+
+Si te percatas de que un/a certificador/a real o potencial de la cuenta candidata no conoce a la persona interesada, avisa inmediatamente a quien tenga experiencia en el tema de tus contactos en la RdC Ğ1, para que la RdC Ğ1 verifique el proceso de validación.
+
+Cuando eres miembro de la RdC Ğ1 y estás a punto de certificar una cuenta:
+
+
+** Estás seguro de: **
+
+1°) Conocer suficientemente bien a quien declara poseer esta llave pública (cuenta candidata a miembro). Lea los consejos fuertemente recomendados más arriba para asegurarte de que la "conoces bien".
+
+2°) Haber comprobado personalmente con esa persona de que se trata de esa llave pública que está a punto de certificar.
+
+3°) Haber verificado con la persona interesada, que ha generado y guardado su documento o archivo de revocación de cuenta Duniter que le permitirá, si es necesario, desactivar su estado de miembro y cocreación (en caso de robo de las contraseñas, cambio de seudónimo, cuenta creada incorrectamente, etc.).
+
+4a°) Para verificar el paso 2, para asegurarte de que la persona humana que conoces bien posee esa llave pública, es recomendable hacer esa validación in situ físicamente.
+
+4b°) O bien se puede realizar la validación de forma remota de la llave pública, comunicándola por diferentes medios, como correo ordinario, electrónico, redes sociales, foro, videoconferencia, llamada telefónica (reconociendo la voz). Si alguien hackeara una cuenta de correo electrónico o una cuenta en un foro, es mucho más difícil imaginar que pueda hackear cuatro medios de comunicación separados, e imitar la apariencia (vídeo) e incluso la voz de alguien.
+
+La 4a° es preferible a la 4b° mientras que los puntos 1°, 2° y 3° son ante todo indispensables.
+
+
+**Reglas abreviadas de la RdC :**
+
+Cada miembro tiene una reserva de 100 certificaciones posibles, que solo pueden hacerse efectivas a razón de 1 certificación cada 5 días.
+
+Válida durante 2 meses, una certificación se toma en consideración definitivamente para una nueva candidatura de membresía si esta persona recibe al menos otras 4 certificaciones que cumplen la regla de distancia durante esos 2 meses, de lo contrario, el proceso de candidatura deberá reiniciarse sin perder acceso a la cuenta.
+
+Para convertirse en nuevo/a miembro de la RdC Ğ1 es necesario obtener 5 certificaciones y estar a una distancia de <= 5 pasos con como mínimo un 80% del total de miembros referentes de la RdC.
+
+Alguien es miembro referente de la RdC Ğ1 cuando ha recibido y también emitido al menos Y[N] certificaciones donde N es el total de miembros en la RdC e Y[N] = techo N^(1/5). Ejemplos:
+
+* Para 1024 < N ≤ 3125 se obtiene que Y[N] = 5
+* Para 7776 < N ≤ 16807 se obtiene que Y[N] = 7
+* para 59049 < N ≤ 100.000 se obtiene que Y[N] = 10
+
+Una vez que alguien es nuevo/a miembro de la RdC Ğ1, sus certificaciones serán válidas durante 2 años.
+
+Para seguir siendo miembro, se debe renovar el acuerdo regularmente mediante la llave privada (cada 12 meses) y asegurarse de que siempre se tenga al menos 5 certificaciones válidas después de los 2 años.
+
+Moneda Äž1
+----------
+
+Ğ1 se genera a través de un Dividendo Universal (DU) para cada ser humano miembro de la Red de Confianza Ğ1, de la siguiente forma:
+
+* 1 DU por persona y por día.
+
+La cantidad en Ğ1 del DU diario es idéntica hasta pasado cada equinoccio(cada 6 meses) cuando se re-evaluará el DU según la fórmula :
+
+* DU diario (próximo equinoccio) = DU diario (equinoccio) + c² (M/N)(equinoccio) / (15778800 segundos)
+
+Teniendo como constantes:
+
+* c = 4,88% / equinoccio
+* DU(0) = 10,00 Äž1
+
+Y como variables :
+
+* *M* la masa monetaria total en el equinoccio
+* *N* el número de miembros el día del equinoccio
+
+Software Äž1 y licencia Äž1
+--------------------------
+
+El software Ğ1 que permita a los usuarios administrar su uso de Ğ1 debe transmitir esta licencia y todos los parámetros técnicos de la moneda Ğ1 y de la RdC Ğ1 que han sido configurados en el bloque 0 de Ğ1. El software que no cumpla con estas obligaciones de la licencia no es compatible con Ğ1.
+
+Para más detalle en los aspectos técnicos, es posible consultar directamente el código de Duniter, que es un software libre así como los datos de la cadena de bloques (blockchain) Ğ1, recuperándolos a través de una instancia (o nodo) de Duniter Ğ1.
+
+Más información en el sitio web del equipo Duniter https://www.duniter.org
diff --git a/www/templates/home/home.html b/www/templates/home/home.html
index df345cbbe..73765b5c5 100644
--- a/www/templates/home/home.html
+++ b/www/templates/home/home.html
@@ -40,6 +40,7 @@
 
         <div class="center padding" ng-if="loading">
           <ion-spinner icon="android" ></ion-spinner>
+          <p class="text-italic" translate-values=":currency:{currency: $root.currency.name}" translate>{{loadingMessage}}</p>
         </div>
 
         <div class="center padding animate-fade-in animate-show-hide ng-hide" ng-show="!loading && error">
diff --git a/www/templates/settings/popup_node.html b/www/templates/settings/popup_node.html
index d3eda1d0a..9fb9b7723 100644
--- a/www/templates/settings/popup_node.html
+++ b/www/templates/settings/popup_node.html
@@ -24,7 +24,7 @@
       <span class="input-label">
         {{'SETTINGS.POPUP_PEER.USE_SSL' | translate}}
       </span>
-      <h4>
+      <h4 class="text-wrap">
         <small class="gray" ng-bind-html="'SETTINGS.POPUP_PEER.USE_SSL_HELP' | translate">
         </small>
       </h4>
diff --git a/www/templates/settings/settings.html b/www/templates/settings/settings.html
index 450db38b3..3f66378b9 100644
--- a/www/templates/settings/settings.html
+++ b/www/templates/settings/settings.html
@@ -202,21 +202,43 @@
 
         <span class="item item-divider" translate>SETTINGS.NETWORK_SETTINGS</span>
 
+        <!-- Expert mode ?-->
+        <div class="item item-text-wrap item-toggle dark hidden-xs hidden-sm">
+          <div class="input-label" ng-bind-html="'SETTINGS.EXPERT_MODE' | translate"></div>
+          <h4 class="gray" ng-bind-html="'SETTINGS.EXPERT_MODE_HELP' | translate"></h4>
+          <label class="toggle toggle-royal">
+            <input type="checkbox" ng-model="formData.expertMode" >
+            <div class="track">
+              <div class="handle"></div>
+            </div>
+          </label>
+        </div>
+
         <!-- Duniter node -->
-        <div class="item ink item-text-wrap item-icon-right hidden-xs hidden-sm" ng-click="changeNode()">
+        <div class="item ink item-text-wrap item-icon-right" ng-click="changeNode()">
           <div class="input-label" translate>SETTINGS.PEER</div>
 
-          <!-- node temporary changed -->
           <ng-if ng-if="formData.node.temporary">
-            <h4 class="gray text-wrap assertive" >
-              <i class="icon ion-alert-circled"></i>
-              <span ng-bind-html="'SETTINGS.PEER_CHANGED_TEMPORARY' | translate "></span>
-            </h4>
-            <div class="item-note assertive text-italic">{{bma.server}}</div>
+            <!-- node temporary changed -->
+            <ng-if ng-if="formData.expertMode">
+              <h4 class="gray text-wrap assertive" >
+                <span class="ion-alert-circled"></span>
+                <span ng-bind-html="'SETTINGS.PEER_CHANGED_TEMPORARY' | translate "></span>
+              </h4>
+              <div class="badge badge-assertive">{{bma.server}}</div>
+            </ng-if>
+            <!-- node selected automatically -->
+            <ng-if ng-if="!formData.expertMode">
+              <h4 class="gray text-wrap " >
+                <span class="ion-info-circled positive"></span>
+                <span ng-bind-html="'SETTINGS.PEER_SELECTED_AUTOMATICALLY' | translate "></span>
+              </h4>
+              <div class="badge badge-balanced">{{bma.server}}</div>
+            </ng-if>
           </ng-if>
 
           <div class="badge badge-balanced" ng-if="!formData.node.temporary">{{bma.server}}</div>
-          <i class="icon ion-ios-arrow-right"></i>
+          <i class="icon ion-ios-arrow-right" ng-if="formData.expertMode"></i>
         </div>
         <ion-item class="ink item-icon-right visible-xs visible-sm" ng-click="changeNode()">
           <div class="input-label hidden-xs" translate>SETTINGS.PEER</div>
@@ -225,26 +247,15 @@
           <!-- node temporary changed -->
           <ng-if ng-if="formData.node.temporary">
             <h4 class="gray text-wrap assertive" >
-              <b class="ion-alert-circled"></b>
+              <span class="ion-alert-circled"></span>
               <span ng-bind-html="'SETTINGS.PEER_CHANGED_TEMPORARY' | translate "></span>
             </h4>
             <div class="badge badge-assertive">{{bma.server}}</div>
           </ng-if>
           <div class="badge badge-balanced" ng-if="!formData.node.temporary">{{bma.server}}</div>
-          <i class="icon ion-ios-arrow-right"></i>
+          <i class="icon ion-ios-arrow-right" ng-if="formData.expertMode"></i>
         </ion-item>
 
-        <!-- Expert mode ?-->
-        <div class="item item-text-wrap item-toggle dark hidden-xs hidden-sm">
-          <div class="input-label" ng-bind-html="'SETTINGS.EXPERT_MODE' | translate"></div>
-          <h4 class="gray" ng-bind-html="'SETTINGS.EXPERT_MODE_HELP' | translate"></h4>
-          <label class="toggle toggle-royal">
-            <input type="checkbox" ng-model="formData.expertMode" >
-            <div class="track">
-              <div class="handle"></div>
-            </div>
-          </label>
-        </div>
 
         <!-- Block validity window -->
         <label class="item item-input item-select item-text-wrap">
-- 
GitLab