diff --git a/ionic.config.json b/ionic.config.json index d913a836578b69df3d02dd55390f52b847cd03cf..019c4c894379bcfb091caf82cfb0b79411a0d8af 100644 --- a/ionic.config.json +++ b/ionic.config.json @@ -1,13 +1,14 @@ { "name": "Cesium", - "type": "ionic1", "integrations": { "cordova": {} }, + "type": "ionic1", "watchPatterns": [ "www/index.html", "www/api/index.html", "www/dist/**/*", "www/css/*.css" - ] + ], + "yarn": true } diff --git a/scripts/prompts/translations.md b/scripts/prompts/translations.md new file mode 100644 index 0000000000000000000000000000000000000000..d150ad0d42b131d3c80bf6fe2c2da3f7d17655f7 --- /dev/null +++ b/scripts/prompts/translations.md @@ -0,0 +1,17 @@ +## Chat GPT prompt for translations + +I need translations for the following messages in a web application, in these languages/locales: en, en-GB, de-DE, eo-EO, es-CT, es-ES, it-IT, nl-NL, pt-PT. Please follow these constraints: maintain the input JSON format, indentation, and message keys; preserve HTML tags and case; and keep proper nouns untranslated (e.g., "Duniter" and "Cesium"). Here is the source message in French: +```json +"ORIGINAL_KEY_UNCHANGED": "<your_translation>" +``` + +Please provide the translations for each language/locale in this format: + +- <language_code> (e.g., en, en-GB, ...): +```json +{ +"ORIGINAL_KEY_UNCHANGED": "<your_translation>" +} +``` + +If you have any questions before starting, please ask for clarifications to avoid mistakes. \ No newline at end of file diff --git a/www/i18n/locale-de-DE.json b/www/i18n/locale-de-DE.json index 91b081f6f1c311286193041104b44d426167ff2d..db1b67a358ed1cefe2c7fec59c82dac2be5be167 100644 --- a/www/i18n/locale-de-DE.json +++ b/www/i18n/locale-de-DE.json @@ -847,7 +847,8 @@ "LOGOUT": "Sicher, dass du dich abmelden möchtest?", "USE_FALLBACK_NODE": "Knoten <b>{{old}}</b> ist nicht erreichbar.<br/><br/>Möchtest du temporär den Knoten <b>{{new}}</b> verwenden?", "ISSUE_524_SEND_LOG": "Die Transaktion wurde aufgrund eines bekannten Problems (issue #524) zurückgewiesen. Die Ursache ist bisher unbekannt.<br/>Akzeptierst du <b>die Übertragung deiner Logdaten</b> als Nachricht, um den Entwicklern bei der Korrektur zu helfen?<br/><small>(Dies beinhaltet keine vertraulichen Daten)</small><br/>", - "LICENCE": "Ich habe die die Lizenz des G1 gelesen und akzeptiert" + "LICENCE": "Ich habe die die Lizenz des G1 gelesen und akzeptiert", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Warnung:</b> Möchten Sie den Knoten <b>manuell ändern</b>?<br/><br/>Wenn Sie fortfahren:<ul><li> - Der <b>Expertenmodus</b> wird aktiviert;</li><li> - Sie können zur automatischen Knotenauswahl zurückkehren, indem Sie einfach den <b>Expertenmodus deaktivieren</b>.</li></ul>" }, "MODE": { "DEMO": { diff --git a/www/i18n/locale-en-GB.json b/www/i18n/locale-en-GB.json index e072b0534d8a846569259084ba44878e03e96222..36369fa5a22c9ce91f8d4c096ff07a73eae06578 100644 --- a/www/i18n/locale-en-GB.json +++ b/www/i18n/locale-en-GB.json @@ -849,7 +849,8 @@ "SAVE_BEFORE_LEAVE_TITLE": "Changes not saved", "LOGOUT": "Are you sure you want to logout?", "USE_FALLBACK_NODE": "Peer <b>{{old}}</b> unreachable or invalid address.<br/><br/>Do you want to temporarily use the <b>{{new}}</b> node?", - "ISSUE_524_SEND_LOG": "The transaction was rejected because of a known problem (issue #524) but not reproduced.<br/><br/>To help developers correct this error, do you accept <b>the transmission of your logs</b> per message?<br/><small>(No confidential data is sent)</small>" + "ISSUE_524_SEND_LOG": "The transaction was rejected because of a known problem (issue #524) but not reproduced.<br/><br/>To help developers correct this error, do you accept <b>the transmission of your logs</b> per message?<br/><small>(No confidential data is sent)</small>", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Warning:</b> Do you want to <b>manually change</b> the peer?<br/><br/>If you proceed:<ul><li> - <b>Expert mode</b> will be activated;</li><li> - You can return to automatic peer selection by simply <b>deactivating expert mode</b>.</li></ul>" }, "MODE": { "DEMO": { diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json index ec5c816180e5966cd0d55ed32ffd4b0281798493..752bc04a172483eb7fcd5aa9d236c4be93e330c2 100644 --- a/www/i18n/locale-en.json +++ b/www/i18n/locale-en.json @@ -849,7 +849,8 @@ "SAVE_BEFORE_LEAVE_TITLE": "Changes not saved", "LOGOUT": "Are you sure you want to logout?", "USE_FALLBACK_NODE": "Peer <b>{{old}}</b> unreachable or invalid address.<br/><br/>Do you want to temporarily use the <b>{{new}}</b> node?", - "ISSUE_524_SEND_LOG": "The transaction was rejected because of a known problem (issue #524) but not reproduced.<br/><br/>To help developers correct this error, do you accept <b>the transmission of your logs</b> per message?<br/><small>(No confidential data is sent)</small>" + "ISSUE_524_SEND_LOG": "The transaction was rejected because of a known problem (issue #524) but not reproduced.<br/><br/>To help developers correct this error, do you accept <b>the transmission of your logs</b> per message?<br/><small>(No confidential data is sent)</small>", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Warning:</b> Do you want to <b>manually change</b> the Duniter node?<br/><br/>If you continue:<ul><li> - <b>Expert mode</b> will be activated;</li><li> - You can return to automatic node selection by simply <b>deactivating expert mode</b>.</li></ul>" }, "MODE": { "DEMO": { diff --git a/www/i18n/locale-eo-EO.json b/www/i18n/locale-eo-EO.json index 9dceb1e94a7fce7e8e60278f8295a95fba5b4319..c504731fc046063d6850d9ec5ce997392bfe98ea 100644 --- a/www/i18n/locale-eo-EO.json +++ b/www/i18n/locale-eo-EO.json @@ -830,7 +830,8 @@ "SAVE_BEFORE_LEAVE_TITLE": "Modifoj ne registritaj", "LOGOUT": "Ĉu vi certas, ke vi volas malkonektiÄi?", "USE_FALLBACK_NODE": "Nodo <b>{{old}}</b> neatingebla aÅ adreso nevalida.<br/><br/>Ĉu vi volas provizore uzi la nodon <b>{{new}}</b> ?", - "ISSUE_524_SEND_LOG": "La spezo estis forĵetita, pro konata anomalio (petslipo #524) sed <b>ne ripetita</b>.<br/><br/>Por helpi la programistojn korekti tiun eraron, <b>ĉu vi akceptas la sendadon de viaj protokolaj dosieroj</b> per mesaÄo?<br/><small>(neniu konfidenca dateno estas sendita)</small>." + "ISSUE_524_SEND_LOG": "La spezo estis forĵetita, pro konata anomalio (petslipo #524) sed <b>ne ripetita</b>.<br/><br/>Por helpi la programistojn korekti tiun eraron, <b>ĉu vi akceptas la sendadon de viaj protokolaj dosieroj</b> per mesaÄo?<br/><small>(neniu konfidenca dateno estas sendita)</small>.", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Averto:</b> Ĉu vi volas <b>mane ÅanÄi</b> la nodon?<br/><br/>Se vi daÅrigas:<ul><li> - La <b>eksperta reÄimo</b> estos aktivigita;</li><li> - Vi povos reveni al aÅtomata nodo-selekto simple <b>malaktivigante la ekspertan reÄimon</b>.</li></ul>" }, "MODE": { "DEMO": { diff --git a/www/i18n/locale-es-CT.json b/www/i18n/locale-es-CT.json index 172f338f3ff16c98364e7c7f3346698554cdc939..816c02a84d3913c8e105d2cf43054a74dbd80e27 100644 --- a/www/i18n/locale-es-CT.json +++ b/www/i18n/locale-es-CT.json @@ -913,7 +913,8 @@ "SAVE_BEFORE_LEAVE": "¿Desea <b>guardar sus cambios</b> antes de abandonar la página?", "SAVE_BEFORE_LEAVE_TITLE": "Cambios no registrados", "LOGOUT": "¿Desea desconectarse?", - "USE_FALLBACK_NODE": "Nodo <b>{{old}}</b> inalcanzable o dirección inválida.<br/><br/>¿Desea utilizar temporalmente el nodo <b>{{new}}</b>?" + "USE_FALLBACK_NODE": "Nodo <b>{{old}}</b> inalcanzable o dirección inválida.<br/><br/>¿Desea utilizar temporalmente el nodo <b>{{new}}</b>?", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Advertència:</b> Vols <b>canviar manualment</b> el node Duniter?<br/><br/>Si continues:<ul><li> - S'activarà el <b>modo experto</b>;</li><li> - Podrà s tornar a la selecció automà tica de nodes simplement <b>desactivant el modo experto</b>.</li></ul>" }, "DOWNLOAD": { "POPUP_TITLE": "<b>Revocación del archivo</b>", diff --git a/www/i18n/locale-es-ES.json b/www/i18n/locale-es-ES.json index aabfca48b43714ed465f3832cbc37090d51cc621..1bd2ed81d1c530fb72c0591f9ccc7c9123da47cd 100644 --- a/www/i18n/locale-es-ES.json +++ b/www/i18n/locale-es-ES.json @@ -915,7 +915,8 @@ "SAVE_BEFORE_LEAVE": "¿Desea <b>guardar sus cambios</b> antes de abandonar la página?", "SAVE_BEFORE_LEAVE_TITLE": "Cambios no registrados", "LOGOUT": "¿Desea desconectarse?", - "USE_FALLBACK_NODE": "Nodo <b>{{old}}</b> inalcanzable o dirección inválida.<br/><br/>¿Desea utilizar temporalmente el nodo <b>{{new}}</b>?" + "USE_FALLBACK_NODE": "Nodo <b>{{old}}</b> inalcanzable o dirección inválida.<br/><br/>¿Desea utilizar temporalmente el nodo <b>{{new}}</b>?", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Advertencia:</b> ¿Quieres <b>cambiar manualmente</b> el nodo?<br/><br/>Si continúas:<ul><li> - Se activará el <b>modo experto</b>;</li><li> - Podrás volver a la selección automática de nodos simplemente <b>desactivando el modo experto</b>.</li></ul>" }, "DOWNLOAD": { "POPUP_TITLE": "<b>Revocación del archivo</b>", diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json index 8a013ebcf68d84f82c1166a5e0c2bcac7c05f1f9..141f513f440845ec3a7966fcfc186c255c9738ea 100644 --- a/www/i18n/locale-fr-FR.json +++ b/www/i18n/locale-fr-FR.json @@ -853,7 +853,8 @@ "SAVE_BEFORE_LEAVE_TITLE": "Modifications non enregistrées", "LOGOUT": "Êtes-vous sûr de vouloir vous déconnecter ?", "USE_FALLBACK_NODE": "NÅ“ud <b>{{old}}</b> injoignable ou adresse invalide.<br/><br/>Voulez-vous temporairement utiliser le nÅ“ud <b>{{new}}</b> ?", - "ISSUE_524_SEND_LOG": "La transaction a été rejetée, à cause d'une anomalie connue (ticket #524) mais <b>non reproduite</b>.<br/><br/>Pour aider les développeurs à corriger cette erreur, <b>acceptez-vous la transmission de vos logs</b> par message ?<br/><small>(aucune donnée confidentielle n'est envoyée)</small>." + "ISSUE_524_SEND_LOG": "La transaction a été rejetée, à cause d'une anomalie connue (ticket #524) mais <b>non reproduite</b>.<br/><br/>Pour aider les développeurs à corriger cette erreur, <b>acceptez-vous la transmission de vos logs</b> par message ?<br/><small>(aucune donnée confidentielle n'est envoyée)</small>.", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Avertissement :</b> Voulez-vous <b>changer manuellement</b> le nÅ“ud ?<br/><br/>Si vous continuez :<ul><li> - Le <b>mode expert</b> sera activé;</li><li> - Vous pourrez revenir à la sélection automatique du nÅ“ud, simplement <b>en désactivant le mode expert</b>.</li></ul>" }, "MODE": { "DEMO": { diff --git a/www/i18n/locale-it-IT.json b/www/i18n/locale-it-IT.json index 933c7f01e1e0ad52021715f9fee7474976511356..31780da81c841d223a4a36e151a6b6cb9998253b 100644 --- a/www/i18n/locale-it-IT.json +++ b/www/i18n/locale-it-IT.json @@ -814,9 +814,9 @@ "CERTIFY_RULES": "<b class=\"assertive\">Non certificare un conto</b> se credi che: <ul><li>1.) L'identità della persona potrebbe essere finta.<li>2.) La persona ha già un conto certicato.<li>3.) La persona trasgredisce la regola 1 o 2 o entrambe. (Certifica conti finti o gemelli).</ul></small><br/>Sei sicuro di voler certificare questa identità ?", "FULLSCREEN": "Aprire l'applicazione a schermo intero?", "EXIT_APP": "Chiudere l'applicazione?", -"TRANSFER": "<b>Resoconto del bonifico:</b><br/><br/><ul><li> - Inviato da: <b>{{from}}</b></li><li> - A: <b>{{to}}</b></li><li> - Importo: <b>{{amount}} {{unit}}</b></li><li> - Comemnto: <i>{{comment}}</i></li></ul><br/><b>Sei sicuro di voler procedere con questo bonifico?</b>", - "TRANSFER_ALL": "<b>Riepilogo del bonifico:</b><br/><br/><ul><li> - Da: <b>{{from}}</b></li><li> - A: <b>{{to}}</b></li><li> - Importo: <b>{{amount}} {{unit}}</b></li><li> - Commento: <i>{{comment}}</i></li><br/><li> - Resto : <b>{{restAmount}} {{unit}}</b> a <b>{{restTo}}</b></li></ul><br/><b>Sicuro di voler fare questo bonifico?</b>", - "MEMBERSHIP_OUT": "Questa operazione è <b>irreversibile</b>.<br/></br/><b>Sei sicuro di voler cancellare la tua presenza nella RdF?</b>", + "TRANSFER": "<b>Resoconto del bonifico:</b><br/><br/><ul><li> - Inviato da: <b>{{from}}</b></li><li> - A: <b>{{to}}</b></li><li> - Importo: <b>{{amount}} {{unit}}</b></li><li> - Comemnto: <i>{{comment}}</i></li></ul><br/><b>Sei sicuro di voler procedere con questo bonifico?</b>", + "TRANSFER_ALL": "<b>Riepilogo del bonifico:</b><br/><br/><ul><li> - Da: <b>{{from}}</b></li><li> - A: <b>{{to}}</b></li><li> - Importo: <b>{{amount}} {{unit}}</b></li><li> - Commento: <i>{{comment}}</i></li><br/><li> - Resto : <b>{{restAmount}} {{unit}}</b> a <b>{{restTo}}</b></li></ul><br/><b>Sicuro di voler fare questo bonifico?</b>", + "MEMBERSHIP_OUT": "Questa operazione è <b>irreversibile</b>.<br/></br/><b>Sei sicuro di voler cancellare la tua presenza nella RdF?</b>", "MEMBERSHIP_OUT_2": "Questa operazione è <b>irreversibile</b>!<br/><br/>Sei sicuro <b>di voler revocare la tua identità </b>?", "LOGIN_UNUSED_WALLET_TITLE": "Errore di battitura?", "LOGIN_UNUSED_WALLET": "Il conto sembra <b>inattivo</b>.<br/><br/>Probabilmente è un<b>errore di battitura</b> delle tue credenziali. Per favore riprova, verificando che la <b>chiave pubblica sia la tua<b/>.", @@ -831,8 +831,9 @@ "SAVE_BEFORE_LEAVE_TITLE": "Modifiche non salvate", "LOGOUT": "Sei sicuro di voler chiudere la sessione?", "USE_FALLBACK_NODE": "Nodo <b>{{old}}</b> indisponibile o indirizzo errato.<br/><br/>Vuoi utilizzare temporanemante il <b>{{new}}</b> nodo?", - "ISSUE_524_SEND_LOG": "La transazione è stata annullata a causa di un errore conosciuto (issue #524) ma non riprodotto. <br/><br/>Per aiutare gli sviluppatori a risolvere questo errore, acconsenti all'<b>invio dei tuoi logs</b> per messaggio?<br/><small>(Non viene inviato nessun dato confidenziale)</small>" - }, + "ISSUE_524_SEND_LOG": "La transazione è stata annullata a causa di un errore conosciuto (issue #524) ma non riprodotto. <br/><br/>Per aiutare gli sviluppatori a risolvere questo errore, acconsenti all'<b>invio dei tuoi logs</b> per messaggio?<br/><small>(Non viene inviato nessun dato confidenziale)</small>", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Avviso:</b> Vuoi <b>cambiare manualmente</b> il nodo?<br/><br/>Se continui:<ul><li> - Verrà attivata la <b>modalità esperto</b>;</li><li> - Potrai tornare alla selezione automatica del nodo semplicemente <b>disattivando la modalità esperto</b>.</li></ul>" + }, "MODE": { "DEMO": { "BADGE": "Demo", diff --git a/www/i18n/locale-nl-NL.json b/www/i18n/locale-nl-NL.json index 71e6acd1382883b2af992fadcdf5168a881b322f..64517eb1038ee7ff0d556ce6ca5f6565f5184063 100644 --- a/www/i18n/locale-nl-NL.json +++ b/www/i18n/locale-nl-NL.json @@ -510,7 +510,8 @@ "NOT_NEED_RENEW_MEMBERSHIP": "Je lidmaatschap hoeft niet verlengd te worden (het zal pas verlopen na {{membershipExpiresIn|formatDuration}}).<br/></br/><b>Weet je zeker</b> dat je een verlengingsaanvraag wil versturen?", "SAVE_BEFORE_LEAVE": "Wil je <b>je wijzigingen opslaan</b> voor je de pagina verlaat?", "SAVE_BEFORE_LEAVE_TITLE": "Wijzigingen niet opgeslagen", - "LICENCE": "Ik heb gelezen en geaccepteerd de voorwaarden van de vergunning G1" + "LICENCE": "Ik heb gelezen en geaccepteerd de voorwaarden van de vergunning G1", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Waarschuwing:</b> Wil je het knooppunt <b>handmatig wijzigen</b>?<br/><br/>Als je doorgaat:<ul><li> - De <b>expertmodus</b> wordt geactiveerd;</li><li> - Je kunt terugkeren naar automatische knooppuntselectie door eenvoudigweg de <b>expertmodus uit te schakelen</b>.</li></ul>" }, "DOWNLOAD": { "POPUP_TITLE": "<b>Intrekkingsdocument</b>", diff --git a/www/i18n/locale-pt-PT.json b/www/i18n/locale-pt-PT.json index 71ab7efc8c1f1888915af4c35f0d806288c0a796..cc360777b4bedc89d0249fc42414d9a1d6d4d5da 100644 --- a/www/i18n/locale-pt-PT.json +++ b/www/i18n/locale-pt-PT.json @@ -914,7 +914,8 @@ "SAVE_BEFORE_LEAVE": "Deseja <b>guardar as alterações</b> antes de abandonar a página?", "SAVE_BEFORE_LEAVE_TITLE": "Alterações não guardadas", "LOGOUT": "Deseja desconectar-se?", - "USE_FALLBACK_NODE": "Nó <b>{{old}}</b> indisponÃvel ou endereço inválido.<br/><br/>Deseja utilizar temporalmente o nó <b>{{new}}</b>?" + "USE_FALLBACK_NODE": "Nó <b>{{old}}</b> indisponÃvel ou endereço inválido.<br/><br/>Deseja utilizar temporalmente o nó <b>{{new}}</b>?", + "ENABLE_EXPERT_MODE_TO_CHANGE_NODE": "<b class=\"assertive\">Aviso:</b> Queres <b>alterar manualmente</b> o nó Duniter?<br/><br/>Se continuares:<ul><li> - Será ativado o <b>modo perito</b>;</li><li> - Poderás voltar à seleção automática do nó, simplesmente <b>desativando o modo perito</b>.</li></ul>" }, "DOWNLOAD": { "POPUP_TITLE": "<b>Revogação do arquivo</b>", diff --git a/www/index.html b/www/index.html index 2d57619a9cbb17c871060930dff5d9233535ab32..00fbb79d1740ffe1f6efa1b9fb667af4a812fcd0 100644 --- a/www/index.html +++ b/www/index.html @@ -279,6 +279,7 @@ <script src="dist/dist_js/app/directives.js"></script> <script src="dist/dist_js/app/filters.js"></script> <script src="dist/dist_js/app/platform.js"></script> + <script src="dist/dist_js/app/functions.js"></script> <!-- endbuild --> <!-- build:js config.js --> diff --git a/www/js/controllers/network-controllers.js b/www/js/controllers/network-controllers.js index 51a53bb2825f71632cf0bafcbffce02d259a49a6..35289842cbbbdd9faeada129d8c8c9e2bd12d40e 100644 --- a/www/js/controllers/network-controllers.js +++ b/www/js/controllers/network-controllers.js @@ -203,7 +203,7 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win .then(function() { $scope.updating = false; }); - } + }; $scope.updateView = function(data) { console.debug("[peers] Updating UI"); diff --git a/www/js/controllers/settings-controllers.js b/www/js/controllers/settings-controllers.js index b6e756bdec6cf0a7627e85946392ef521201812a..9292608e7ce12b01fb72ede530ae668cb61cfa5a 100644 --- a/www/js/controllers/settings-controllers.js +++ b/www/js/controllers/settings-controllers.js @@ -29,7 +29,11 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti $scope.loading = true; $scope.nodePopup = {}; $scope.bma = BMA; - + $scope.listeners = []; + $scope.platform = { + loading: !csPlatform.isStarted(), + loadingMessage: 'COMMON.LOADING' + }; $scope.keepAuthIdleLabels = { /*0: { @@ -80,7 +84,9 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti }; $scope.blockValidityWindows = _.keys($scope.blockValidityWindowLabels); - $scope.$on('$ionicView.enter', function() { + $scope.enter = function() { + $scope.addListeners(); + $q.all([ csSettings.ready(), csCurrency.parameters() @@ -91,7 +97,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti .then(function(parameters) { var avgGenTime = parameters && parameters.avgGenTime; if (!avgGenTime || avgGenTime < 0) { - console.warn("[settings] Could not not currency parameters. Using default G1 'avgGenTime' (300s)"); + console.warn('[settings] Could not not currency parameters. Using default G1 \'avgGenTime\' (300s)'); avgGenTime = 300; /* = G1 value = 5min */ } _.each($scope.blockValidityWindows, function(blockCount) { @@ -102,7 +108,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti }) ]) .then($scope.load); - }); + }; $scope.setPopupForm = function(popupForm) { $scope.popupForm = popupForm; @@ -111,6 +117,8 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti $scope.load = function() { $scope.loading = true; // to avoid the call of csWallet.store() + $scope.platform.loading = !csPlatform.isStarted(); + // Fill locales $scope.locales = angular.copy(csSettings.locales); @@ -130,6 +138,22 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti }, 100); }; + + $scope.addListeners = function() { + $scope.listeners = [ + // Listen platform start message + csPlatform.api.start.on.message($scope, function(message) { + $scope.platform.loading = !csPlatform.isStarted(); + $scope.platform.loadingMessage = message; + }) + ]; + }; + + $scope.leave = function() { + console.debug('[settings] Leaving page'); + $scope.removeListeners(); + } + $scope.reset = function() { if ($scope.actionsPopover) { $scope.actionsPopover.hide(); @@ -149,7 +173,30 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti }; // Change node - $scope.changeNode= function(node) { + $scope.changeNode = function(node, confirm) { + + // If platform not stared yet: wait then loop + if (!csPlatform.isStarted()) { + UIUtils.loading.update({template: this.loadingMessage}); + return csPlatform.ready() + .then(function() { + return $scope.changeNode(node, confirm); // Loop + }); + } + + // Ask user to confirm, before allow to change the node + if (!confirm && !$scope.formData.expertMode) { + return UIUtils.alert.confirm('CONFIRM.ENABLE_EXPERT_MODE_TO_CHANGE_NODE', 'CONFIRM.POPUP_WARNING_TITLE', { + cssClass: 'warning', + cancelText: 'COMMON.BTN_NO', + okText: 'COMMON.BTN_YES_CONTINUE', + okType: 'button-assertive' + }) + .then(function(confirm) { + if (!confirm) return; + $scope.changeNode(node, true); + }); + } var port = !!$scope.formData.node.port && $scope.formData.node.port != 80 && $scope.formData.node.port != 443 ? $scope.formData.node.port : undefined; node = node || { @@ -166,6 +213,10 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti newNode.useSsl === $scope.formData.node.useSsl && !$scope.formData.node.temporary) { return; // same node = nothing to do } + + // Change to expert mode + $scope.formData.expertMode = true; + UIUtils.loading.show(); BMA.isAlive(newNode) @@ -174,7 +225,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti UIUtils.loading.hide(); return UIUtils.alert.error('ERROR.INVALID_NODE_SUMMARY') .then(function(){ - $scope.changeNode(newNode); // loop + $scope.changeNode(newNode, true); // loop }); } UIUtils.loading.hide(); @@ -216,7 +267,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti } }) .then(function(newNode) { - $scope.changeNode(newNode); + $scope.changeNode(newNode, true); }); }; @@ -284,8 +335,12 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti } $scope.saving = true; + var now = Date.now(); + // Async - to avoid UI lock return $timeout(function() { + console.debug('[settings] Saving...'); + // Make sure to format helptip $scope.cleanupHelpTip(); @@ -301,7 +356,8 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti }, 100) .then(function() { //return $timeout(function() { - $scope.saving = false; + $scope.saving = false; + console.debug('[settings] Saving [OK] in {0}ms'.format(Date.now() - now)); //}, 100); }); }; @@ -395,4 +451,20 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti csSettings.store(); }); }; + + + + $scope.removeListeners = function() { + if ($scope.listeners.length) { + console.debug('[settings] Closing listeners'); + _.forEach($scope.listeners, function(remove){ + remove(); + }); + $scope.listeners = []; + } + }; + + $scope.$on('$ionicView.enter', $scope.enter); + $scope.$on('$ionicView.beforeLeave', $scope.leave); + } diff --git a/www/js/functions.js b/www/js/functions.js new file mode 100644 index 0000000000000000000000000000000000000000..b773f31f41c2f43c9a13417bbcef12d228e8146b --- /dev/null +++ b/www/js/functions.js @@ -0,0 +1,37 @@ + +// Workaround to add "".startsWith() if not present +if (typeof String.prototype.startsWith !== 'function') { + console.debug("Adding String.prototype.startsWith() -> was missing on this platform"); + String.prototype.startsWith = function(prefix, position) { + return this.indexOf(prefix, position) === 0; + }; +} + +// Workaround to add "".startsWith() if not present +if (typeof String.prototype.trim !== 'function') { + console.debug("Adding String.prototype.trim() -> was missing on this platform"); + // Make sure we trim BOM and NBSP + var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + String.prototype.trim = function() { + return this.replace(rtrim, ''); + }; +} + +// Workaround to add Math.trunc() if not present - fix #144 +if (Math && typeof Math.trunc !== 'function') { + console.debug("Adding Math.trunc() -> was missing on this platform"); + Math.trunc = function(number) { + return parseInt((number - 0.5).toFixed()); + }; +} + +// Workaround to add "".format() if not present +if (typeof String.prototype.format !== 'function') { + console.debug("Adding String.prototype.format() -> was missing on this platform"); + String.prototype.format = function() { + var args = arguments; + return this.replace(/{(\d+)}/g, function(match, number) { + return typeof args[number] != 'undefined' ? args[number] : match; + }); + }; +} diff --git a/www/js/platform.js b/www/js/platform.js index d1cd933c4cea62d78482e253d1ca134bcddccd3d..44720b39edda88d71b0028b41a4f1f97d820452c 100644 --- a/www/js/platform.js +++ b/www/js/platform.js @@ -197,8 +197,8 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] var askUserConfirmation = csSettings.data.expertMode; return csNetwork.getSynchronizedBmaPeers(BMA, { - timeout: Math.min(csConfig.timeout, 10000 /*10s max*/) - }) + timeout: Math.min(csConfig.timeout, 10000 /*10s max*/) + }) .then(function(peers) { if (!peers.length) return; // No peer found: exit @@ -270,7 +270,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] csSettings.data.node.temporary = true; return BMA.copy(node); - }) + }); }); } @@ -417,10 +417,13 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] addListeners(); startPromise = null; started = true; + + api.start.raise.message(''); // Reset message }) .catch(function(err) { startPromise = null; started = false; + api.start.raise.message(''); // Reset message if($state.current.name !== $rootScope.errorState) { $state.go($rootScope.errorState, {error: 'peer'}); } @@ -560,40 +563,3 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] }); }) ; - -// Workaround to add "".startsWith() if not present -if (typeof String.prototype.startsWith !== 'function') { - console.debug("Adding String.prototype.startsWith() -> was missing on this platform"); - String.prototype.startsWith = function(prefix, position) { - return this.indexOf(prefix, position) === 0; - }; -} - -// Workaround to add "".startsWith() if not present -if (typeof String.prototype.trim !== 'function') { - console.debug("Adding String.prototype.trim() -> was missing on this platform"); - // Make sure we trim BOM and NBSP - var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; - String.prototype.trim = function() { - return this.replace(rtrim, ''); - }; -} - -// Workaround to add Math.trunc() if not present - fix #144 -if (Math && typeof Math.trunc !== 'function') { - console.debug("Adding Math.trunc() -> was missing on this platform"); - Math.trunc = function(number) { - return parseInt((number - 0.5).toFixed()); - }; -} - -// Workaround to add "".format() if not present -if (typeof String.prototype.format !== 'function') { - console.debug("Adding String.prototype.format() -> was missing on this platform"); - String.prototype.format = function() { - var args = arguments; - return this.replace(/{(\d+)}/g, function(match, number) { - return typeof args[number] != 'undefined' ? args[number] : match; - }); - }; -} diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js index 1a88a9ce5d1a75c073fbf46aa92220cb0d8bce23..a7de712bf2aa3d53a9b3af782b5a7d5612d72e9d 100644 --- a/www/js/services/bma-services.js +++ b/www/js/services/bma-services.js @@ -382,7 +382,9 @@ 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) { + + // Filter to exclude the current BMA node + 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); if (same) console.debug('[BMA] Skipping fallback node [{0}]: same as current BMA node'.format(node.server)); @@ -399,7 +401,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium. else { console.error('[BMA] Unreachable (or not compatible) fallback node [{0}]: skipping'.format(node.server)); } - }) + }); })) .then(function() { return aliveNodes; diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js index b5d21dd7c8993902552c3dfd63c8daf4b4361222..4353a4a2b0c1847c798cee3e783e95cee0a9ef51 100644 --- a/www/js/services/device-services.js +++ b/www/js/services/device-services.js @@ -4,7 +4,7 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti .factory('Device', function($rootScope, $translate, $ionicPopup, $q, Api, // removeIf(no-device) - $cordovaClipboard, $cordovaBarcodeScanner, $cordovaCamera, + $cordovaClipboard, $cordovaBarcodeScanner, $cordovaCamera, $cordovaNetwork, // endRemoveIf(no-device) ionicReady) { 'ngInject'; @@ -143,6 +143,17 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti cordova.plugins.Keyboard.close(); } }; + exports.network = { + connectionType: function() { + return navigator.connection.type || 'unknown'; + }, + isOnline: function() { + return navigator.connection.type !== Connection.NONE; + }, + isOffline: function() { + return navigator.connection.type === Connection.NONE; + } + }; function getLastIntent() { var deferred = $q.defer(); @@ -271,13 +282,15 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti exports.barcode.enable = cordova && cordova.plugins && !!cordova.plugins.barcodeScanner && (!exports.isOSX() || exports.isIOS()); exports.clipboard.enable = cordova && cordova.plugins && !!cordova.plugins.clipboard; exports.intent.enable = window && !!window.plugins.launchmyapp; + exports.clipboard.enable = cordova && cordova.plugins && !!cordova.plugins.clipboard; + exports.network.enable = navigator.connection && !!navigator.connection.type; if (exports.keyboard.enable) { angular.extend(exports.keyboard, cordova.plugins.Keyboard); } - console.debug('[device] Ionic platform ready, with {camera: {0}, barcode: {1}, keyboard: {2}, clipboard: {3}, intent: {4}}' - .format(exports.camera.enable, exports.barcode.enable, exports.keyboard.enable, exports.clipboard.enable, exports.intent.enable)); + console.debug('[device] Ionic platform ready, with {camera: {0}, barcode: {1}, keyboard: {2}, clipboard: {3}, intent: {4}, network: {5}}' + .format(exports.camera.enable, exports.barcode.enable, exports.keyboard.enable, exports.clipboard.enable, exports.intent.enable, exports.network.enable)); if (cordova.InAppBrowser) { console.debug('[device] Enabling InAppBrowser'); diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js index 83da91b1891114bc951b9b461b214644f2cd6441..778fd80d12918af0169412ff1cfdc99ae6eaf550 100644 --- a/www/js/services/network-services.js +++ b/www/js/services/network-services.js @@ -15,6 +15,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', startPromise, data = { + pid: 0, // Start PID bma: null, listeners: [], loading: true, @@ -61,6 +62,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', }, resetData = function() { + data.starCounter = 0; data.bma = null; data.listeners = []; data.peers.splice(0); @@ -178,10 +180,11 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', else if (data.loading && !data.searchingPeersOnNetwork) { data.loading = false; $interval.cancel(interval); + // The peer lookup end, we can make a clean final report sortPeers(true/*update main buid*/); - console.debug('[network] {0} peers found.'.format(data.peers.length)); + console.debug('[network] {0} peer(s) found.'.format(data.peers.length)); } }, 1000); @@ -462,7 +465,6 @@ 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); @@ -512,7 +514,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', return $q.when(peer); } - const timeout = Math.max(500, remainingTime()); // >= 500ms + var timeout = Math.max(500, remainingTime()); // >= 500ms peer.api = peer.api || BMA.lightInstance(peer.getHost(), peer.getPort(), peer.isSsl(), timeout); // Get current block @@ -781,16 +783,17 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', if (startPromise) { console.warn('[network-service] Waiting previous start to be closed...'); return startPromise.then(function() { - return start(bma, options) - }) + return start(bma, options); + }); } options = options || {}; bma = bma || BMA; + var pid = data.pid; startPromise = bma.ready() .then(function() { - close(); - + close(pid); + data.pid++; 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; @@ -816,18 +819,21 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', return loadPeers() .then(function(peers){ - console.debug('[network] Started in {0}ms, {1} peers found'.format(Date.now() - now, peers.length)); + if (peers) console.debug('[network] Started in {0}ms, {1} peers found'.format(Date.now() - now, peers.length)); return data; }); }); return startPromise; }, - close = function() { - if (data.bma) { - console.info('[network] Stopping...'); - removeListeners(); - resetData(); + close = function(pid) { + if (data.bma) { + console.info('[network] Stopping...'); + removeListeners(); + resetData(); + } + if (interval && pid === data.pid) { + $interval.cancel(interval); } startPromise = null; }, @@ -849,11 +855,17 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', getMainBlockUid = function(bma, options) { var wasStarted = isStarted(); + var pid = data.pid + 1; return startIfNeed(bma, options) .then(function(data) { var buid = data.mainBlock && data.mainBlock.buid; - if (!wasStarted) close(); + if (!wasStarted) close(pid); return buid; + }) + .catch(function(err) { + console.error('[network] Failed to get main block'); + if (!wasStarted) close(pid); + throw err; }); }, @@ -865,19 +877,20 @@ 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(); + console.info('[network] Getting synchronized BMA peers...'); + var wasStarted = isStarted(); + var pid = data.pid + 1; return startIfNeed(bma, options) .then(function(data){ - var peers = _.filter(data.peers, function(peer) { + var peers = data && _.filter(data.peers, function(peer) { return peer.hasMainConsensusBlock && peer.isBma(); }); // Log - if (peers.length) { + if (peers && peers.length > 0) { console.info('[network] Found {0}/{1} BMA peers on main consensus block #{2} - in {3}ms'.format( peers.length, data.peers.length, @@ -885,12 +898,19 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services', Date.now() - now)); } else { - console.warn('[network] No synchronized BMA peers found, in {1}ms'.format(Date.now() - now)) + console.warn('[network] No synchronized BMA peers found, in {0}ms'.format(Date.now() - now)); } - if (!wasStarted) close(); + if (!wasStarted) close(pid); return peers; + }) + .catch(function(err) { + console.error('[network] Error while getting synchronized BMA peers', err); + + if (!wasStarted) close(pid); + + throw err; }); }; diff --git a/www/templates/settings/settings.html b/www/templates/settings/settings.html index c3233d5b6cc596b8192c5afd6679775585042211..eefef17e87f972180bd616ae1ea7d69278fb4119 100644 --- a/www/templates/settings/settings.html +++ b/www/templates/settings/settings.html @@ -216,28 +216,40 @@ <!-- Duniter node --> <div class="item ink item-text-wrap item-icon-right" ng-click="changeNode()"> - <div class="input-label" translate>SETTINGS.PEER</div> - - <ng-if ng-if="formData.node.temporary"> - <!-- 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> + <div class="input-label" ng-class="{'gray': !formData.expertMode}" translate>SETTINGS.PEER</div> + + <!-- node manually selected (expert mode) --> + <ng-if ng-if="formData.expertMode"> + <h4 class="gray text-wrap assertive" ng-if="formData.node.temporary"> + <span class="ion-alert-circled"></span> + <span ng-bind-html="'SETTINGS.PEER_CHANGED_TEMPORARY' | translate "></span> + </h4> + <ng-if ng-if="platform.loading"> + <h4 class="gray text-italic"> + <span ng-bind-html="platform.loadingMessage | 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" ng-class="{'badge-assertive': formData.node.temporary}">{{bma.server}}</div> + </ng-if> + + <!-- node selected automatically --> + <ng-if ng-if="!formData.expertMode"> + <h4 class="gray text-wrap"> + <span ng-bind-html="'SETTINGS.PEER_SELECTED_AUTOMATICALLY' | translate "></span> + </h4> + <ng-if ng-if="!platform.loading"> <div class="badge badge-balanced">{{bma.server}}</div> </ng-if> + <ng-if ng-if="platform.loading"> + <h4 class="gray text-italic"> + <span ng-bind-html="platform.loadingMessage | translate "></span> + </h4> + <div class="badge badge-note"> + <ion-spinner icon="android"></ion-spinner> + </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" ng-if="formData.expertMode"></i> </div> <!-- <ion-item class="ink item-icon-right visible-xs visible-sm" ng-click="changeNode()">-->