From 95f85f416eca9857f864d35f71a5698d77e96856 Mon Sep 17 00:00:00 2001 From: blavenie <benoit.lavenier@e-is.pro> Date: Thu, 9 Mar 2017 18:14:20 +0100 Subject: [PATCH] - Add cross-domain local storage - need for HTTPS/HTTP mode (when config has httpsMode=clever) - Change default config to use G1 --- app/config.json | 14 +- gulpfile.js | 22 +- www/https-storage.html | 11 + www/js/app.js | 27 +- www/js/config.js | 21 +- www/js/controllers/settings-controllers.js | 27 +- www/js/services/bma-services.js | 123 +++- www/js/services/cache-services.js | 141 ++--- www/js/services/crypto-services.js | 11 +- www/js/services/currency-services.js | 6 +- www/js/services/device-services.js | 51 +- www/js/services/http-services.js | 8 +- www/js/services/network-services.js | 33 +- www/js/services/plugin-services.js | 2 +- www/js/services/settings-services.js | 138 +++-- www/js/services/storage-services.js | 224 +++++-- www/js/services/wallet-services.js | 68 +- .../es/js/controllers/app-controllers.js | 7 +- .../es/js/controllers/network-controllers.js | 7 +- .../es/js/controllers/wot-controllers.js | 7 +- www/plugins/es/js/services/http-services.js | 65 +- .../es/js/services/settings-services.js | 584 +++++++++--------- www/sync-storage.html | 9 - www/templates/settings/settings.html | 2 +- 24 files changed, 934 insertions(+), 674 deletions(-) create mode 100644 www/https-storage.html delete mode 100644 www/sync-storage.html diff --git a/app/config.json b/app/config.json index b26417bc..792fe7ec 100644 --- a/app/config.json +++ b/app/config.json @@ -7,26 +7,26 @@ "timeout": 10000, "timeWarningExpireMembership": 5184000, "timeWarningExpire": 7776000, - "useLocalStorage": true, - "useRelative": true, + "useLocalStorage": false, + "useRelative": false, "initPhase": false, "expertMode": false, - "decimalCount": 4, + "decimalCount": 2, "httpsMode": false, "helptip": { "enable": true, "installDocUrl": "https://github.com/duniter/duniter/blob/master/doc/install-a-node.md" }, "node": { - "host": "gtest.duniter.org", - "port": "10900" + "host": "g1.duniter.org", + "port": "443" }, "plugins":{ "es": { "enable": true, "askEnable": false, - "host": "data.gtest.duniter.fr", - "port": "80", + "host": "g1.data.duniter.fr", + "port": "443", "notifications": { "txSent": true, "txReceived": true, diff --git a/gulpfile.js b/gulpfile.js index 85d1be9c..da6f6224 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -31,7 +31,6 @@ var rev = require('gulp-rev'); var revReplace = require('gulp-rev-replace'); var clean = require('gulp-clean'); var htmlmin = require('gulp-htmlmin'); -var deleteEmpty = require('delete-empty'); var jshint = require('gulp-jshint'); var sourcemaps = require('gulp-sourcemaps'); @@ -261,6 +260,11 @@ gulp.task('copy-files:web', ['clean:tmp', 'clean:web', 'sass', 'config'], functi .pipe(rename("debug.html")) .pipe(gulp.dest(tmpPath)), + // Copy https-storage.html + gulp.src('./www/https-storage.html') + .pipe(htmlmin()) + .pipe(gulp.dest(tmpPath)), + // Copy fonts gulp.src('./www/fonts/**/*.*') .pipe(gulp.dest(tmpPath + '/fonts')), @@ -357,12 +361,23 @@ gulp.task('debug-files:web', ['ng_annotate:web', 'ng_annotate-plugin:web'], func .on('end', done); }); -gulp.task('optimize-files:web', ['debug-files:web'], function(done) { +gulp.task('https-storage-files:web', ['debug-files:web'], function(done) { + var tmpPath = './platforms/web/www'; + + // Process https-storage.html file + gulp.src(tmpPath + '/https-storage.html') + .pipe(useref()) // Concatenate with gulp-useref + .pipe(gulp.dest(tmpPath)) + .on('end', done); +}); + +gulp.task('optimize-files:web', ['https-storage-files:web'], function(done) { var tmpPath = './platforms/web/www'; var jsFilter = filter(["**/*.js", '!**/config.js'], { restore: true }); var cssFilter = filter("**/*.css", { restore: true }); var revFilesFilter = filter(['**/*', '!**/index.html', '!**/config.js'], { restore: true }); + // Process index.html gulp.src(tmpPath + '/index.html') .pipe(useref()) // Concatenate with gulp-useref // Process JS @@ -380,8 +395,7 @@ gulp.task('optimize-files:web', ['debug-files:web'], function(done) { .pipe(rev()) // Rename the concatenated files .pipe(revFilesFilter.restore) - // Substitute in new filenames - .pipe(revReplace()) + .pipe(revReplace()) // Substitute in new filenames .pipe(gulp.dest(tmpPath)) .on('end', done); }); diff --git a/www/https-storage.html b/www/https-storage.html new file mode 100644 index 00000000..56f67c12 --- /dev/null +++ b/www/https-storage.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> + <!-- build:js dist_js/https-storage.js --> + <script src="lib/ionic/js/angular/angular-xdLocalStoragePostMessageApi.min.js"></script> + <!-- endbuild --> +</head> +<body> + This is the frame used for Cross-Domain local storage, when using config with httpsMode='clever' +</body> +</html> diff --git a/www/js/app.js b/www/js/app.js index 8670bf0d..d44f3488 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -302,7 +302,7 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht $ionicConfigProvider.views.maxCache(5); }) -.run(function($rootScope, $translate, $state, $window, xdLocalStorage, Device, UIUtils, $ionicConfig, PluginService, csWallet, csSettings, csConfig) { +.run(function($rootScope, $translate, $state, $window, xdLocalStorage, ionicReady, Device, UIUtils, $ionicConfig, PluginService, csWallet, csSettings, csConfig) { 'ngInject'; $rootScope.config = csConfig; @@ -377,22 +377,8 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht }); } - if (csConfig.httpsMode === 'clever' && $window.location.protocol !== 'https:') { - var href = $window.location.href; - var hashIndex = href.indexOf('#'); - var rootPath = (hashIndex != -1) ? href.substr(0, hashIndex) : href; - var httpsFrame = (csConfig.httpsModeDebug ? 'http' : 'https') + rootPath.substr(4) + 'sync-storage.html'; - - xdLocalStorage.init({iframeUrl: httpsFrame}) - .then(function () { - //an option function to be called once the iframe was loaded and ready for action - console.debug('[app] Cross-domain local storage started'); - csSettings.start(); - }); - } - - // We use 'Device.ready()' instead of '$ionicPlatform.ready()', because this one is callable many times - Device.ready() + // We use 'ionicReady()' instead of '$ionicPlatform.ready()', because this one is callable many times + ionicReady() .then(function() { // Keyboard @@ -413,13 +399,14 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht console.log('Disable UI effects - plateform\'s grade is not [a] but [' + ionic.Platform.grade + ']'); UIUtils.setEffects(false); } - }) - // Status bar style - .then(function() { + + // Status bar style if (window.StatusBar) { // org.apache.cordova.statusbar required StatusBar.styleDefault(); } + + return csSettings.ready(); }); // Update some translations, when locale changed diff --git a/www/js/config.js b/www/js/config.js index 0b3a8289..a1841eee 100644 --- a/www/js/config.js +++ b/www/js/config.js @@ -16,27 +16,26 @@ angular.module("cesium.config", []) "timeout": 10000, "timeWarningExpireMembership": 5184000, "timeWarningExpire": 7776000, - "useLocalStorage": true, - "useRelative": true, + "useLocalStorage": false, + "useRelative": false, "initPhase": false, "expertMode": false, - "decimalCount": 4, - "httpsMode": "clever", - "httpsModeDebug": true, + "decimalCount": 2, + "httpsMode": false, "helptip": { "enable": true, "installDocUrl": "https://github.com/duniter/duniter/blob/master/doc/install-a-node.md" }, "node": { - "host": "gtest.duniter.org", - "port": "10900" + "host": "g1.duniter.org", + "port": "443" }, "plugins": { "es": { "enable": true, "askEnable": false, - "host": "data.gtest.duniter.fr", - "port": "80", + "host": "g1.data.duniter.fr", + "port": "443", "notifications": { "txSent": true, "txReceived": true, @@ -46,8 +45,8 @@ angular.module("cesium.config", []) } }, "version": "0.11.1", - "build": "2017-03-08T14:25:14.195Z", + "build": "2017-03-09T16:46:53.787Z", "newIssueUrl": "https://github.com/duniter/cesium/issues/new?labels=bug" }) -; +; \ No newline at end of file diff --git a/www/js/controllers/settings-controllers.js b/www/js/controllers/settings-controllers.js index 85291c56..2c4627a2 100644 --- a/www/js/controllers/settings-controllers.js +++ b/www/js/controllers/settings-controllers.js @@ -28,10 +28,11 @@ function SettingsController($scope, $q, $ionicPopup, $timeout, $translate, csHtt $scope.popupData = {}; // need for the node popup $scope.loading = true; $scope.nodePopup = {}; + $scope.bma = BMA; $scope.$on('$ionicView.enter', function() { - $scope.load(); + csSettings.ready().then($scope.load); }); $scope.setPopupForm = function(popupForm) { @@ -81,13 +82,12 @@ function SettingsController($scope, $q, $ionicPopup, $timeout, $translate, csHtt $scope.changeNode= function(node) { $scope.showNodePopup(node || $scope.formData.node) .then(function(newNode) { - console.log(newNode); if (newNode.host === $scope.formData.node.host && newNode.port === $scope.formData.node.port) { return; // same node = nothing to do } UIUtils.loading.show(); - var nodeBMA = BMA.instance(newNode.host, newNode.port); + var nodeBMA = BMA.instance(newNode.host, newNode.port, undefined, true /*cache*/); nodeBMA.isAlive() .then(function(alive) { if (!alive) { @@ -190,10 +190,29 @@ function SettingsController($scope, $q, $ionicPopup, $timeout, $translate, csHtt $scope.saving = false; }, 100); }; - $scope.$watch('formData', $scope.save, true); + + $scope.onDataChanged = function(oldValue, newValue, scope) { + if ($scope.loading || $scope.pendingSaving) return $q.when(); + if ($scope.saving) { + $scope.pendingSaving = true; + // Retry later + return $timeout(function() { + $scope.pendingSaving = false; + return $scope.onDataChanged(oldValue, newValue, scope); + }, 500); + } + + var updated = !angular.equals(oldValue, newValue); + if (updated) { + //console.debug('Detected settings update: will save it'); + $scope.save(); + } + }; + $scope.$watch('formData', $scope.onDataChanged, true); $scope.getServer = function() { + if (!$scope.formData.node || !$scope.formData.node.host) return ''; return csHttp.getServer($scope.formData.node.host, $scope.formData.node.port); }; diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js index 1ebeb051..22909ff1 100644 --- a/www/js/services/bma-services.js +++ b/www/js/services/bma-services.js @@ -2,7 +2,7 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.services', 'cesium.settings.services']) -.factory('BMA', function($q, Api, Device, csSettings, csHttp, csCache, $rootScope, $timeout) { +.factory('BMA', function($q, Api, Device, csSettings, csHttp, $rootScope, $timeout) { 'ngInject'; function BMA(host, port, useSsl, useCache) { @@ -41,24 +41,37 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi regexp: regexp }, listeners, - that = this; + that = this, + startPromise, + started = false; that.date = {now: csHttp.date.now}; that.api = new Api(this, 'BMA-' + that.server); + that.init = init; - init(host, port, useSsl, useCache); + if (host) { + init(host, port, useSsl, useCache); + } function init(host, port, useSsl, useCache) { - if (!host) { - throw new Error('new BMA() need mandatory argument [host]'); - } that.alive = false; that.cache = _emptyCache(); + + // 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 : (csSettings.data.node.port == 443 || csSettings.data.node.useSsl); + useCache = angular.isDefined(useCache) ? useCache : true; + } + + if (!host) { + return; // could not init yet + } that.host = host; that.port = port || 80; that.useSsl = angular.isDefined(useSsl) ? useSsl : (that.port == 443); that.useCache = angular.isDefined(useCache) ? useCache : false; - that.server = csHttp.getServer(host, port); that.url = csHttp.getUrl(host, port, useSsl); } @@ -89,9 +102,19 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi }; get = function (path, cacheTime) { + cacheTime = that.useCache && cacheTime; var cacheKey = path + (cacheTime ? ('#'+cacheTime) : ''); - return function(params) { + + var getRequest = function(params) { + + if (!started) { + console.debug('[BMA] get waiting start finished...'); + return that.ready().then(function() { + return getRequest(params); + }); + } + var request = that.cache.getByPath[cacheKey]; if (!request) { if (cacheTime) { @@ -104,10 +127,19 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi } return request(params); }; + + return getRequest; }; post = function(path) { - return function(obj, params) { + postRequest = function(obj, params) { + if (!started) { + console.debug('[BMA] post waiting start finished...'); + return that.ready().then(function() { + return postRequest(obj, params); + }); + } + var request = that.cache.postByPath[path]; if (!request) { request = csHttp.post(that.host, that.port, path, that.useSsl); @@ -115,6 +147,8 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi } return request(obj, params); }; + + return postRequest; }; ws = function(path) { @@ -129,7 +163,7 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi }; that.isAlive = function() { - return that.node.summary() + return csHttp.get(that.host, that.port, '/node/summary', that.useSsl)() .then(function(json) { var isDuniter = json && json.duniter && json.duniter.software == 'duniter' && json.duniter.version; var isCompatible = isDuniter && csHttp.version.isCompatible(csSettings.data.minVersion, json.duniter.version); @@ -167,17 +201,44 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi } } + that.isStarted = function() { + return started; + }; + + that.ready = function() { + if (started) return $q.when(); + return startPromise || that.start(); + }; + that.start = function() { + if (startPromise) return startPromise; + + if (!that.host) { + return csSettings.ready() + .then(function() { + that.init(); + + // Always enable cache + that.useCache = true; + + return that.start(); // recursive call + }); + } console.debug('[BMA] Starting [{0}]...'.format(that.server)); var now = new Date().getTime(); - return that.isAlive() + startPromise = $q.all([ + csSettings.ready, + that.isAlive() + ]) .then(function(alive) { that.alive = alive; if (!alive) { // TODO : alert user ? console.error('[BMA] Could not start [{0}]: node unreachable'.format(that.server)); + started = true; + startPromise = null; return false; } @@ -188,8 +249,11 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi console.debug('[BMA] Started in '+(new Date().getTime()-now)+'ms'); that.api.node.raise.start(); + started = true; + startPromise = null; return true; }); + return startPromise; }; that.stop = function() { @@ -201,6 +265,7 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi }; that.restart = function() { + csHttp.cache.clear(); that.stop(); return $timeout(function() { that.start(); @@ -223,10 +288,6 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi BMAS_ENDPOINT: exact(regexp.BMAS_ENDPOINT) }, node: { - server: that.server, - url: that.url, - host: host, - port: port, summary: get('/node/summary', csHttp.cache.LONG), same: function(host2, port2) { return host2 == host && ((!port && !port2) || (port == port2)); @@ -286,13 +347,26 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi }; exports.node.parseEndPoint = function(endpoint) { + // Try BMA var matches = exports.regex.BMA_ENDPOINT.exec(endpoint); + if (matches) { + return { + "dns": matches[2] || '', + "ipv4": matches[4] || '', + "ipv6": matches[6] || '', + "port": matches[8] || 80, + "useSsl": matches[8] && matches[8] == 443 + }; + } + // Try BMAS + matches = exports.regex.BMAS_ENDPOINT.exec(endpoint);; if (!matches) return; return { "dns": matches[2] || '', "ipv4": matches[4] || '', "ipv6": matches[6] || '', - "port": matches[8] || 80 + "port": matches[8] || 80, + "useSsl": true }; }; @@ -528,14 +602,12 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi angular.merge(that, exports); } - var service = new BMA( - csSettings.data.node.host, - csSettings.data.node.port, - csSettings.data.node.port == 443 || csSettings.data.node.useSsl, - true /*cache*/); + var service = new BMA(); service.instance = function(host, port, useSsl, useCache) { - return new BMA(host, port, useSsl, useCache); + var bma = new BMA(); + bma.init(host, port, useSsl, useCache); + return bma; }; service.lightInstance = function(host, port, useSsl) { @@ -559,10 +631,11 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi } }; }; - /*Device.ready().then(function() { + + // default action + Device.ready().then(function() { return service.start(); - });*/ - service.start(); + }); return service; }) diff --git a/www/js/services/cache-services.js b/www/js/services/cache-services.js index 83bc2947..24836804 100644 --- a/www/js/services/cache-services.js +++ b/www/js/services/cache-services.js @@ -3,89 +3,84 @@ angular.module('cesium.cache.services', ['ngResource', 'angular-cache']) .factory('csCache', function($http, csSettings, CacheFactory) { 'ngInject'; - function factory() { + var + constants = { + LONG: 1 * 60 * 60 * 1000 /*5 min*/, + SHORT: csSettings.defaultSettings.cacheTimeMs + }, + cacheNames = [] + ; - var - constants = { - LONG: 1 * 60 * 60 * 1000 /*5 min*/, - SHORT: csSettings.data.cacheTimeMs - }, - cacheNames = [] - ; - - function getOrCreateCache(prefix, maxAge, onExpire){ - prefix = prefix || 'csCache-'; - maxAge = maxAge || constants.cache.SHORT; - var cacheName = prefix + maxAge; - if (!onExpire) { - if (!cacheNames[cacheName]) { - cacheNames[cacheName] = true; - } - return CacheFactory.get(cacheName) || - CacheFactory.createCache(cacheName, { - maxAge: maxAge, - deleteOnExpire: 'aggressive', - //cacheFlushInterval: 60 * 60 * 1000, // clear itself every hour - recycleFreq: Math.max(maxAge - 1000, 5 * 60 * 1000 /*5min*/), - storageMode: 'memory' - // FIXME : enable this when cache is cleaning on rollback - //csSettings.data.useLocalStorage ? 'localStorage' : 'memory' - }); + function getOrCreateCache(prefix, maxAge, onExpire){ + prefix = prefix || 'csCache-'; + maxAge = maxAge || constants.SHORT; + var cacheName = prefix + maxAge; + if (!onExpire) { + if (!cacheNames[cacheName]) { + cacheNames[cacheName] = true; } - else { - var counter = 1; - while(CacheFactory.get(cacheName + counter)) { - counter++; - } - cacheName = cacheName + counter; - if (!cacheNames[cacheName]) { - cacheNames[cacheName] = true; - } - return CacheFactory.createCache(cacheName, { - maxAge: maxAge, - deleteOnExpire: 'aggressive', - //cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour - recycleFreq: maxAge, - onExpire: onExpire, - storageMode: 'memory' - // FIXME : enable this when cache is cleaning on rollback - //csSettings.data.useLocalStorage ? 'localStorage' : 'memory' - }); + return CacheFactory.get(cacheName) || + CacheFactory.createCache(cacheName, { + maxAge: maxAge, + deleteOnExpire: 'aggressive', + //cacheFlushInterval: 60 * 60 * 1000, // clear itself every hour + recycleFreq: Math.max(maxAge - 1000, 5 * 60 * 1000 /*5min*/), + storageMode: 'memory' + // FIXME : enable this when cache is cleaning on rollback + //csSettings.data.useLocalStorage ? 'localStorage' : 'memory' + }); + } + else { + var counter = 1; + while(CacheFactory.get(cacheName + counter)) { + counter++; + } + cacheName = cacheName + counter; + if (!cacheNames[cacheName]) { + cacheNames[cacheName] = true; } + return CacheFactory.createCache(cacheName, { + maxAge: maxAge, + deleteOnExpire: 'aggressive', + //cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour + recycleFreq: maxAge, + onExpire: onExpire, + storageMode: 'memory' + // FIXME : enable this when cache is cleaning on rollback + //csSettings.data.useLocalStorage ? 'localStorage' : 'memory' + }); } + } + + function clearAllCaches() { + console.debug("[cache] cleaning all caches"); + _.forEach(_.keys(cacheNames), function(cacheName) { + var cache = CacheFactory.get(cacheName); + if (cache) { + cache.removeAll(); + } + }); + } - function clearAllCaches() { - console.debug("[http] cleaning all caches"); - _.forEach(_.keys(cacheNames), function(cacheName) { - var cache = CacheFactory.get(cacheName); + function clearFromPrefix(cachePrefix) { + _.forEach(_.keys(cacheNames), function(cacheName) { + if (cacheName.startsWith(cachePrefix)) { + var cache = CacheFactory.get(cacheNames); if (cache) { cache.removeAll(); } - }); - } - - function clearFromPrefix(cachePrefix) { - _.forEach(_.keys(cacheNames), function(cacheName) { - if (cacheName.startsWith(cachePrefix)) { - var cache = CacheFactory.get(cacheNames); - if (cache) { - cache.removeAll(); - } - } - }); - } - - return { - get: getOrCreateCache, - clear: clearFromPrefix, - clearAll: clearAllCaches, - constants: { - LONG : constants.LONG, - SHORT: constants.SHORT } - }; + }); } - return factory(); + return { + get: getOrCreateCache, + clear: clearFromPrefix, + clearAll: clearAllCaches, + constants: { + LONG : constants.LONG, + SHORT: constants.SHORT + } + }; }) ; diff --git a/www/js/services/crypto-services.js b/www/js/services/crypto-services.js index c710479e..630f7ba4 100644 --- a/www/js/services/crypto-services.js +++ b/www/js/services/crypto-services.js @@ -2,7 +2,7 @@ angular.module('cesium.crypto.services', ['ngResource', 'cesium.device.services']) - .factory('CryptoUtils', function($q, $timeout, Device) { + .factory('CryptoUtils', function($q, $timeout, ionicReady) { 'ngInject'; /** @@ -606,13 +606,18 @@ angular.module('cesium.crypto.services', ['ngResource', 'cesium.device.services' var service = new CryptoAbstractService(); - Device.ready().then(function() { + var isDevice = true; + // removeIf(device) + isDevice = false; + // endRemoveIf(device) + + ionicReady().then(function() { var now = new Date().getTime(); var serviceImpl; // Use Cordova plugin implementation, when exists - if (Device.enable && window.plugins && window.plugins.MiniSodium && crypto && crypto.getRandomValues) { + if (isDevice && window.plugins && window.plugins.MiniSodium && crypto && crypto.getRandomValues) { console.debug('[crypto] Loading Cordova MiniSodium implementation...'); serviceImpl = new CordovaServiceFactory(); } diff --git a/www/js/services/currency-services.js b/www/js/services/currency-services.js index ba0b5809..6f7b2dcf 100644 --- a/www/js/services/currency-services.js +++ b/www/js/services/currency-services.js @@ -26,9 +26,9 @@ angular.module('cesium.currency.services', ['ngResource', 'ngApi', 'cesium.bma.s data.currencies.push({ name: res.currency, peer: { - host: BMA.node.host, - port: BMA.node.port, - server: BMA.node.server + host: BMA.host, + port: BMA.port, + server: BMA.server }, parameters: res }); diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js index 8f0df0a2..6e2598e6 100644 --- a/www/js/services/device-services.js +++ b/www/js/services/device-services.js @@ -1,12 +1,25 @@ -angular.module('cesium.device.services', ['ngResource', 'cesium.utils.services']) +angular.module('cesium.device.services', ['ngResource', 'cesium.utils.services', 'cesium.settings.services']) + + // Replace the '$ionicPlatform.ready()', to enable multiple calls + // See http://stealthcode.co/multiple-calls-to-ionicplatform-ready/ + .factory('ionicReady', function($ionicPlatform) { + var readyPromise; + + return function () { + if (!readyPromise) { + readyPromise = $ionicPlatform.ready(); + } + return readyPromise; + }; + }) .factory('Device', function($translate, $ionicPopup, $q, // removeIf(no-device) $cordovaClipboard, $cordovaBarcodeScanner, $cordovaCamera, // endRemoveIf(no-device) - $ionicPlatform) { + ionicReady, csSettings) { 'ngInject'; var @@ -14,7 +27,6 @@ angular.module('cesium.device.services', ['ngResource', 'cesium.utils.services'] MAX_HEIGHT: 400, MAX_WIDTH: 400 }, - readyPromise, exports = { // workaround to quickly no is device or not (even before the ready() event) enable: true @@ -118,13 +130,14 @@ angular.module('cesium.device.services', ['ngResource', 'cesium.utils.services'] return deferred.promise; } - // Replace the '$ionicPlatform.ready()', to enable multiple calls - readyPromise = $ionicPlatform.ready(); - function ready() { - return readyPromise; - } + exports.clipboard = {copy: copy}; + exports.camera = { + getPicture : getPicture, + scan: scan + }; - readyPromise.then(function(){ + var started = false; + var startPromise = ionicReady().then(function(){ var enableCamera = !!navigator.camera; exports.enable = enableCamera; @@ -141,16 +154,24 @@ angular.module('cesium.device.services', ['ngResource', 'cesium.utils.services'] else { console.debug('[device] Ionic platform ready - no device detected.'); } + + started = true; + startPromise = null; }); - exports.ready = function() { + var readyPromise; + + // backward compat + exports.ready = function () { + if (!readyPromise) { + readyPromise = $q.all([ + ionicReady(), + csSettings.ready() + ]); + } return readyPromise; }; - exports.clipboard = {copy: copy}; - exports.camera = { - getPicture : getPicture, - scan: scan - }; + return exports; }) diff --git a/www/js/services/http-services.js b/www/js/services/http-services.js index 92948c43..1edcf835 100644 --- a/www/js/services/http-services.js +++ b/www/js/services/http-services.js @@ -258,6 +258,12 @@ angular.module('cesium.http.services', ['ngResource', 'cesium.cache.services']) return true; } + var cache = angular.copy(csCache.constants); + cache.clear = function() { + console.debug('[http] Cleaning cache...'); + csCache.clear(cachePrefix); + }; + return { get: getResource, getWithCache: getResourceWithCache, @@ -275,7 +281,7 @@ angular.module('cesium.http.services', ['ngResource', 'cesium.cache.services']) version: { isCompatible: isVersionCompatible }, - cache: csCache.constants + cache: cache }; }) ; diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js index 56ff6a1e..86e44091 100644 --- a/www/js/services/network-services.js +++ b/www/js/services/network-services.js @@ -485,21 +485,24 @@ angular.module('cesium.network.services', ['ngResource', 'ngApi', 'cesium.bma.se start = function(bma, options) { options = options || {}; - return $q(function(resolve, reject) { - close(); - data.bma = bma ? 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; - console.info('[network] Starting network from [{0}]'.format(bma.node.server)); - var now = new Date(); - startListeningOnSocket(resolve, reject); - loadPeers() - .then(function(peers){ - resolve(peers); - console.debug('[network] Started in '+(new Date().getTime() - now.getTime())+'ms'); - }); - }); + return BMA.ready() + .then(function() { + close(); + data.bma = bma ? 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; + console.info('[network] Starting network from [{0}]'.format(bma.server)); + var now = new Date(); + + startListeningOnSocket(); + + return loadPeers() + .then(function(peers){ + console.debug('[network] Started in '+(new Date().getTime() - now.getTime())+'ms'); + return peers; + }); + }); }, close = function() { diff --git a/www/js/services/plugin-services.js b/www/js/services/plugin-services.js index ad88ff3b..05e94d74 100644 --- a/www/js/services/plugin-services.js +++ b/www/js/services/plugin-services.js @@ -32,7 +32,7 @@ angular.module('cesium.plugin.services', []) return this; }; - this.$get = ['$injector', '$state', function pluginFactory($injector, $state) { + this.$get = ['$injector', '$state', function($injector, $state) { var currentExtensionPointName; diff --git a/www/js/services/settings-services.js b/www/js/services/settings-services.js index c6f39b47..7f632d5a 100644 --- a/www/js/services/settings-services.js +++ b/www/js/services/settings-services.js @@ -1,7 +1,7 @@ -angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.config', 'cesium.device.services']) +angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.config']) -.factory('csSettings', function($rootScope, $q, Api, localStorage, $translate, csConfig, Device) { +.factory('csSettings', function($rootScope, $q, Api, localStorage, $translate, csConfig) { 'ngInject'; // Define app locales @@ -54,10 +54,10 @@ angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.confi useRelative: false, timeWarningExpireMembership: 2592000 * 2 /*=2 mois*/, timeWarningExpire: 2592000 * 3 /*=3 mois*/, - useLocalStorage: Device.enable, // on mobile device, use local storage by default + useLocalStorage: true, // override to false if no device walletHistoryTimeSecond: 30 * 24 * 60 * 60 /*30 days*/, walletHistorySliceSecond: 5 * 24 * 60 * 60 /*download using 5 days slice*/, - rememberMe: Device.enable, // on mobile device, remember me by default + rememberMe: true, // override to false if no device showUDHistory: true, showLoginSalt: false, initPhase: false, // For currency start (when block #0 not written) @@ -89,11 +89,21 @@ angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.confi } }, csConfig), + + data = {}, previousData, + started = false, + startPromise, + api = new Api(this, "csSettings"); - api = new Api(this, "csSettings"), + // Change some defaults, when no device + // removeIf(device) + defaultSettings.useLocalStorage = false; + defaultSettings.rememberMe = false; + // endRemoveIf(device) + var reset = function() { _.keys(data).forEach(function(key){ delete data[key]; @@ -127,15 +137,30 @@ angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.confi }, store = function() { + if (!started) { + console.debug('[setting] Waiting start finished...'); + return startPromise.then(store); + } + + var promise; if (data.useLocalStorage) { - localStorage.setObject(constants.STORAGE_KEY, data); + promise = localStorage.setObject(constants.STORAGE_KEY, data); } else { - localStorage.setObject(constants.STORAGE_KEY, null); + promise = localStorage.setObject(constants.STORAGE_KEY, null); } - // Emit event on store - return api.data.raisePromise.store(data) + return promise + .then(function() { + if (data.useLocalStorage) { + console.debug('[setting] Saved'); + } + + // Emit event on store + return api.data.raisePromise.store(data); + }) + + // Emit event on store .then(emitChangedEvent); }, @@ -166,54 +191,22 @@ angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.confi restore = function() { var now = new Date().getTime(); - return $q(function(resolve, reject){ - console.debug("[settings] Loading from local storage..."); - var storedData = localStorage.getObject(constants.STORAGE_KEY); - - var finishRestore = function() { - emitChangedEvent(); - resolve(); - }; - - // No settings stored - if (!storedData) { - console.debug("[settings] No settings in local storage. Using defaults."); - applyData(defaultSettings); - finishRestore(); - return; - } - - // Workaround to get node info from Cesium < 0.2.0 - if (storedData.DUNITER_NODE) { - var nodePart = storedData.DUNITER_NODE.split(':'); - if (nodePart.length == 1 || nodePart.length == 2) { - storedData.node = { - host: nodePart[0], - port: nodePart[1] // could be undefined, but that's fine - }; - } - delete storedData.DUNITER_NODE; - } - if (storedData.DUNITER_NODE_ES) { - var esNodePart = storedData.DUNITER_NODE_ES.split(':'); - if (esNodePart.length == 1 || esNodePart.length == 2) { - storedData.plugins = { - es: { - enable: true, - host: esNodePart[0], - port: esNodePart[1] // could be undefined, but that's fine - } - }; - } - delete storedData.DUNITER_NODE_ES; - } - - // Apply stored data - applyData(storedData); - - console.debug('[settings] Loaded from local storage in '+(new Date().getTime()-now)+'ms'); - finishRestore(); - }); + return localStorage.getObject(constants.STORAGE_KEY) + .then(function(storedData) { + // No settings stored + if (!storedData) { + console.debug("[settings] No settings in local storage. Using defaults."); + applyData(defaultSettings); + emitChangedEvent(); + return; + } + + // Apply stored data + applyData(storedData); + + console.debug('[settings] Loaded from local storage in '+(new Date().getTime()-now)+'ms'); + emitChangedEvent(); + }); }, // Detect locale sucessuf changes, then apply to vendor libs @@ -243,8 +236,30 @@ angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.confi api.locale.raise.changed(locale); }, + + ready = function() { + if (started) return $q.when(); + return startPromise || start(); + }, + start = function() { - restore().then(api.data.raise.ready); + console.debug('[settings] Starting...'); + + startPromise = localStorage.ready() + + // Restore + .then(restore) + + // Emit ready event + .then(function() { + console.debug('[settings] Started'); + started = true; + startPromise = null; + // Emit event + api.data.raise.ready(data); + }); + + return startPromise; }; $rootScope.$on('$translateChangeSuccess', onLocaleChange); @@ -256,10 +271,13 @@ angular.module('cesium.settings.services', ['ngResource', 'ngApi', 'cesium.confi api.registerEvent('locale', 'changed'); // Apply default settings. This is required on some browser (web or mobile - see #361) - applyData(defaultSettings); + //applyData(defaultSettings); + + // Default action + start(); return { - start : start, + ready: ready, data: data, getByPath: getByPath, reset: reset, diff --git a/www/js/services/storage-services.js b/www/js/services/storage-services.js index 45574bdd..5451b647 100644 --- a/www/js/services/storage-services.js +++ b/www/js/services/storage-services.js @@ -1,61 +1,107 @@ -angular.module('cesium.storage.services', ['ngResource', 'ngResource', 'xdLocalStorage', - 'cesium.device.services', 'cesium.config']) +angular.module('cesium.storage.services', ['ngResource', 'ngResource', 'xdLocalStorage', 'ngApi', 'cesium.config']) -.factory('localStorage', function($window, $q, $rootScope, Device, csConfig, xdLocalStorage) { +.factory('localStorage', function($window, $q, $rootScope, $timeout, ionicReady, csConfig, Api, xdLocalStorage) { 'ngInject'; var appName = "Cesium", - localStorage = $window.localStorage, + api = new Api(this, "localStorage"), + started = false, + startPromise, + tryInitSecureStorage = true, // default for device (override later) exports = { + api: api, useHttpsFrame: false, - unsecure: {}, - https: {}, + standard: { + storage: null + }, + xd: { + enable: false + }, secure: { storage: null } }; - /* -- Use default default browser implementation -- */ + // removeIf(device) + // Use this workaround to avoid the use of Device service (could cause a circular reference) + tryInitSecureStorage = false; + // endRemoveIf(device) + + /* -- Use standard browser implementation -- */ - exports.unsecure.put = function(key, value) { - localStorage[key] = value; + exports.standard.put = function(key, value) { + exports.standard.storage[key] = value; + return $q.when(); }; - exports.unsecure.get = function(key, defaultValue) { - return localStorage[key] || defaultValue; + exports.standard.get = function(key, defaultValue) { + return $q.when(exports.standard.storage[key] || defaultValue); }; - exports.unsecure.setObject = function(key, value) { - localStorage[key] = JSON.stringify(value); + exports.standard.setObject = function(key, value) { + exports.standard.storage[key] = JSON.stringify(value); + return $q.when(); }; - exports.unsecure.getObject = function(key) { - return JSON.parse(localStorage[key] || '{}'); + exports.standard.getObject = function(key) { + return $q.when(JSON.parse(exports.standard.storage[key] || '{}')); }; - /* -- Use of HTTPS frame -- */ + /* -- Use of cross-domain HTTPS iframe -- */ // See https://github.com/ofirdagan/cross-domain-local-storage - exports.https.put = function(key, value) { - console.log('TODO: setting [{0}] into https frame'.format(key)); - }; + exports.xd.put = function(key, value) { + if (!started) { + console.debug('[storage] Waiting start finished...'); + return startPromise.then(function(){ + return exports.xd.put(key, value); + }); + } - exports.https.get = function(key, defaultValue) { - console.log('TODO: getting [{0}] from https frame'.format(key)); - return localStorage[key] || defaultValue; + xdLocalStorage.setItem(key, value); }; - exports.https.setObject = function(key, value) { - console.log('TODO: setting object [{0}] into https frame'.format(key)); + exports.xd.get = function(key, defaultValue) { + if (!started) { + console.debug('[storage] Waiting start finished...'); + return startPromise.then(function(){ + return exports.xd.get(key, defaultValue); + }); + } + + return xdLocalStorage.getItem(key).then(function(response){ + return (response && response.value) || defaultValue; + }); }; - exports.https.getObject = function(key) { - console.log('TODO: getting object [{0}] from https frame'.format(key)); - return JSON.parse(localStorage[key] || '{}'); + exports.xd.setObject = function(key, value) { + if (!started) { + console.debug('[storage] Waiting start finished...'); + return startPromise.then(function(){ + return exports.xd.setObject(key, value); + }); + } + + if (!value) { + return xdLocalStorage.removeItem(key); + } + return xdLocalStorage.setItem(key, JSON.stringify(value)); }; + exports.xd.getObject = function(key, defaultObject) { + if (!started) { + console.debug('[storage] Waiting start finished...'); + return startPromise.then(function(){ + return exports.xd.getObject(key, defaultObject); + }); + } + + return xdLocalStorage.getItem(key).then(function(response){ + return response && response.value && JSON.parse(response.value) || defaultObject; + }); + }; /* -- Use secure storage (using a cordova plugin) -- */ @@ -104,48 +150,128 @@ angular.module('cesium.storage.services', ['ngResource', 'ngResource', 'xdLocalS exports.secure.getObject = function(key) { return exports.secure.get(key) .then(function(value) { - return JSON.parse(localStorage[key] || '{}'); + return (value && JSON.parse(value)) || {}; }); }; - // Copy HTTPS functions as default function - if (csConfig.httpsMode === 'clever' && $window.location.protocol !== 'https:') { - _.forEach(_.keys(exports.https), function(key) { - exports[key] = exports.https[key]; + function initStandardStorage() { + console.debug('[storage] Starting [standard mode]...'); + exports.standard.storage = $window.localStorage; + // Set standard storage as default + _.forEach(_.keys(exports.standard), function(key) { + exports[key] = exports.standard[key]; }); + + return $q.when(); } - else { - // Copy unsecure function as default function - _.forEach(_.keys(exports.unsecure), function(key) { - exports[key] = exports.unsecure[key]; + function initXdStorage() { + // Compute the HTTPS iframe url + var href = $window.location.href; + var hashIndex = href.indexOf('#'); + var rootPath = (hashIndex != -1) ? href.substr(0, hashIndex) : href; + var iframeUrl = 'https' + rootPath.substr(4); + if (iframeUrl.charAt(iframeUrl.length-1) != '/') iframeUrl += '/'; // end slash + iframeUrl += 'https-storage.html'; + + console.debug('[storage] Starting [cross-domain mode] using iframe [{0}]'.format(iframeUrl)); + + // Set cross-domain storage as default + _.forEach(_.keys(exports.xd), function(key) { + exports[key] = exports.xd[key]; }); - } + var isOK = false; + var deferred = $q.defer(); + + // Timeout, in case the frame could not be loaded + $timeout(function() { + if (!isOK) { + // TODO: alert user ? + console.error('[storage] https frame not loaded (timeout). Trying standard mode...'); + deferred.resolve(initStandardStorage()); + } + }, csConfig.timeout); - Device.ready().then(function() { + xdLocalStorage.init({iframeUrl: iframeUrl}) + .then(function() { + isOK = true; + deferred.resolve(); + }) + .catch(function(err) { + console.error('[storage] Could not init cross-domain storage. Trying standard mode...', err); + deferred.resolve(initStandardStorage()); + }); + return deferred.promise; + } - function replaceSecureStorage() { - exports.secure = exports.unsecure; - exports.secure.storage = null; - } + function initSecureStorage() { + console.debug('[storage] Starting [secure mode]...'); + // Set secure storage as default + _.forEach(_.keys(exports.secure), function(key) { + exports[key] = exports.secure[key]; + }); - if (Device.enable) { + var deferred = $q.defer(); + + ionicReady().then(function() { + if (!cordova.plugins || !cordova.plugins.SecureStorage) { + deferred.resolve(initStandardStorage()); + return; + } exports.secure.storage = new cordova.plugins.SecureStorage( function () { - console.log('[storage] Secure storage initialized.'); + deferred.resolve(); }, - function (error) { - console.error('[storage] Could not use secure storage: ' + error); - replaceSecureStorage(); + function (err) { + console.error('[storage] Could not use secure storage. Will use standard.', err); + deferred.resolve(initStandardStorage()); }, appName); + }); + + return deferred.promise; + } + + exports.isStarted = function() { + return started; + }; + + exports.ready = function() { + if (started) return $q.when(); + return startPromise || start(); + }; + + function start() { + if (startPromise) return startPromise; + + var now = new Date().getTime(); + + // Is site on both HTTPS and HTTP: Need to use cross-domain storage + if (csConfig.httpsMode === 'clever' && $window.location.protocol !== 'https:') { + startPromise = initXdStorage(); } + + // Use Cordova secure storage plugin + else if (tryInitSecureStorage) { + startPromise = initSecureStorage(); + } + + // Use default browser local storage else { - replaceSecureStorage(); + startPromise = initStandardStorage(); } - }); + return startPromise + .then(function() { + console.debug('[storage] Started in ' + (new Date().getTime() - now) + 'ms'); + started = true; + startPromise = null; + }); + } + + // default action + start(); return exports; }) diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js index 3d1b9566..28900f88 100644 --- a/www/js/services/wallet-services.js +++ b/www/js/services/wallet-services.js @@ -257,45 +257,41 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser }, restore = function() { - return $q(function(resolve, reject){ - var dataStr = localStorage.get(constants.STORAGE_KEY); - if (!dataStr) { - resolve(); - return; - } - fromJson(dataStr, false) - .then(function(storedData){ - if (storedData && storedData.keypair && storedData.pubkey) { - data.keypair = storedData.keypair; - data.pubkey = storedData.pubkey; - if (storedData.tx && storedData.tx.pendings) { - data.tx.pendings = storedData.tx.pendings; - } - data.loaded = false; + return localStorage.get(constants.STORAGE_KEY) + .then(function(dataStr) { + if (!dataStr) return; + return fromJson(dataStr, false) + .then(function(storedData){ + if (storedData && storedData.keypair && storedData.pubkey) { + data.keypair = storedData.keypair; + data.pubkey = storedData.pubkey; + if (storedData.tx && storedData.tx.pendings) { + data.tx.pendings = storedData.tx.pendings; + } + data.loaded = false; - return $q.all([ - // Call extend api - api.data.raisePromise.login(data), + return $q.all([ + // Call extend api + api.data.raisePromise.login(data), - // Load parameters - // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5) - loadParameters(), + // Load parameters + // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5) + loadParameters(), - // Load current UD is need by features tour - loadCurrentUD() - ]); - } - else { - // Load parameters - // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5) - return loadParameters(); - } - }) - .then(function(){ - resolve(data); - }) - .catch(function(err){reject(err);}); - }); + // Load current UD is need by features tour + loadCurrentUD() + ]); + } + else { + // Load parameters + // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5) + return loadParameters(); + } + }) + .then(function(){ + return data; + }); + }); }, getData = function() { diff --git a/www/plugins/es/js/controllers/app-controllers.js b/www/plugins/es/js/controllers/app-controllers.js index 7f6b5f15..2c148eb4 100644 --- a/www/plugins/es/js/controllers/app-controllers.js +++ b/www/plugins/es/js/controllers/app-controllers.js @@ -126,11 +126,8 @@ function ESMenuExtendController($scope, $state, PluginService, csSettings, UIUti !!csSettings.data.plugins.host; }; - csSettings.api.data.on.changed($scope, function() { - $scope.updateView(); - }); - - $scope.updateView(); + csSettings.api.data.on.changed($scope, $scope.updateView); + csSettings.api.data.on.ready($scope, $scope.updateView); } diff --git a/www/plugins/es/js/controllers/network-controllers.js b/www/plugins/es/js/controllers/network-controllers.js index 63152e02..c9562af2 100644 --- a/www/plugins/es/js/controllers/network-controllers.js +++ b/www/plugins/es/js/controllers/network-controllers.js @@ -33,9 +33,6 @@ function ESNetworkViewExtendController($scope, PluginService, csSettings) { !!csSettings.data.plugins.host; }; - csSettings.api.data.on.changed($scope, function() { - $scope.updateView(); - }); - - $scope.updateView(); + csSettings.api.data.on.changed($scope, $scope.updateView); + csSettings.api.data.on.ready($scope, $scope.updateView); } diff --git a/www/plugins/es/js/controllers/wot-controllers.js b/www/plugins/es/js/controllers/wot-controllers.js index 3c77fbc0..2770a739 100644 --- a/www/plugins/es/js/controllers/wot-controllers.js +++ b/www/plugins/es/js/controllers/wot-controllers.js @@ -53,11 +53,8 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, UIUtils, Modals, !!csSettings.data.plugins.host; }; - csSettings.api.data.on.changed($scope, function() { - $scope.updateView(); - }); - - $scope.updateView(); + csSettings.api.data.on.changed($scope, $scope.updateView); + csSettings.api.data.on.ready($scope, $scope.updateView); /* -- modals -- */ diff --git a/www/plugins/es/js/services/http-services.js b/www/plugins/es/js/services/http-services.js index 436c9930..c68cd548 100644 --- a/www/plugins/es/js/services/http-services.js +++ b/www/plugins/es/js/services/http-services.js @@ -17,13 +17,26 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic }; that.cache = _emptyCache(); - that.alive = false; - that.host = host; - that.port = port || 80; - that.wsPort = wsPort || port || 80; - that.useSsl = angular.isDefined(useSsl) ? useSsl : false; - that.server = csHttp.getServer(host, port); that.api = new Api(this, "esHttp"); + that.init = init; + + init(host, port, wsPort, useSsl); + + function init(host, port, wsPort, useSsl) { + // Use settings as default + if (csSettings.data) { + host = host || (csSettings.data.plugins && csSettings.data.plugins.es ? csSettings.data.plugins.es.host : null); + port = port || (host ? csSettings.data.plugins.es.port : null); + wsPort = wsPort || (host ? csSettings.data.plugins.es.wsPort : null); + } + + that.alive = false; + that.host = host; + that.port = port || 80; + that.wsPort = wsPort || port || 80; + that.useSsl = angular.isDefined(useSsl) ? useSsl : false; + that.server = csHttp.getServer(host, port); + } function exact(regexpContent) { return new RegExp('^' + regexpContent + '$'); @@ -50,11 +63,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic }; that.copy = function(otherNode) { - that.host = otherNode.host; - that.port = otherNode.port; - that.wsPort = otherNode.wsPort || otherNode.port; - that.useSsl = otherNode.useSsl; - that.server = csHttp.getServer(host, port); + that.init(otherNode.host, otherNode.port, otherNode.wsPort, otherNode.useSsl); return that.restart(); }; @@ -110,19 +119,22 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic that.start = function() { - console.debug('[ES] [http] Starting on [{0}]...'.format(that.server)); - var now = new Date().getTime(); - - return that.isAlive() - .then(function(alive) { - that.alive = alive; - if (!alive) { - console.error('[ES] [http] Could not start [{0}]: node unreachable'.format(that.server)); - return false; - } - console.debug('[ES] [http] Started in '+(new Date().getTime()-now)+'ms'); - that.api.node.raise.start(); - return true; + return csSettings.ready() + .then(service.init) + .then(function() { + console.debug('[ES] [http] Starting on [{0}]...'.format(that.server)); + var now = new Date().getTime(); + return that.isAlive() + .then(function(alive) { + that.alive = alive; + if (!alive) { + console.error('[ES] [http] Could not start [{0}]: node unreachable'.format(that.server)); + return false; + } + console.debug('[ES] [http] Started in '+(new Date().getTime()-now)+'ms'); + that.api.node.raise.start(); + return true; + }); }); }; @@ -423,11 +435,8 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic angular.merge(that, exports); } - var host = csSettings.data.plugins && csSettings.data.plugins.es ? csSettings.data.plugins.es.host : null; - var port = host ? csSettings.data.plugins.es.port : null; - var wsPort = host ? csSettings.data.plugins.es.wsPort : null; - var service = new Factory(host, port, wsPort); + var service = new Factory(); service.instance = function(host, port, wsPort) { return new Factory(host, port, wsPort); diff --git a/www/plugins/es/js/services/settings-services.js b/www/plugins/es/js/services/settings-services.js index d35307e0..aeba31e1 100644 --- a/www/plugins/es/js/services/settings-services.js +++ b/www/plugins/es/js/services/settings-services.js @@ -14,340 +14,336 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt csConfig, csSettings, CryptoUtils, Device, UIUtils, csWallet) { 'ngInject'; - function Factory() { - - var - SETTINGS_SAVE_SPEC = { - includes: ['locale', 'showUDHistory', 'useRelative', 'useLocalStorage', 'expertMode'], - excludes: ['time'], - plugins: { - es: { - excludes: ['enable', 'host', 'port', 'wsPort'] - } - }, - helptip: { - excludes: ['installDocUrl'] + var + SETTINGS_SAVE_SPEC = { + includes: ['locale', 'showUDHistory', 'useRelative', 'useLocalStorage', 'expertMode'], + excludes: ['time'], + plugins: { + es: { + excludes: ['enable', 'host', 'port', 'wsPort'] } }, - defaultSettings = angular.merge({ - plugins: { - es: { - askEnable: false, - notifications: { - readTime: true, - txSent: true, - txReceived: true, - certSent: true, - certReceived: true - }, - invitations: { - readTime: true - } + helptip: { + excludes: ['installDocUrl'] + } + }, + defaultSettings = angular.merge({ + plugins: { + es: { + askEnable: false, + notifications: { + readTime: true, + txSent: true, + txReceived: true, + certSent: true, + certReceived: true + }, + invitations: { + readTime: true } } - }, {plugins: {es: csConfig.plugins && csConfig.plugins.es || {}}}), - that = this, - previousRemoteData, - listeners, - ignoreSettingsChanged = false - ; + } + }, {plugins: {es: csConfig.plugins && csConfig.plugins.es || {}}}), + that = this, + previousRemoteData, + listeners, + ignoreSettingsChanged = false + ; + + that.get = esHttp.get('/user/settings/:id'); + that.add = esHttp.record.post('/user/settings'); + that.update = esHttp.record.post('/user/settings/:id/_update'); + + that.isEnable = function() { + return csSettings.data.plugins && + csSettings.data.plugins.es && + csSettings.data.plugins.es.enable && + !!csSettings.data.plugins.es.host; + }; + + function copyUsingSpec(data, copySpec) { + var result = {}; + + // Add implicit includes + if (copySpec.includes) { + _.forEach(_.keys(copySpec), function(key) { + if (key != "includes" && key != "excludes") { + copySpec.includes.push(key); + } + }); + } - that.get = esHttp.get('/user/settings/:id'); - that.add = esHttp.record.post('/user/settings'); - that.update = esHttp.record.post('/user/settings/:id/_update'); - - that.isEnable = function() { - return csSettings.data.plugins && - csSettings.data.plugins.es && - csSettings.data.plugins.es.enable && - !!csSettings.data.plugins.es.host; - }; - - function copyUsingSpec(data, copySpec) { - var result = {}; - - // Add implicit includes - if (copySpec.includes) { - _.forEach(_.keys(copySpec), function(key) { - if (key != "includes" && key != "excludes") { - copySpec.includes.push(key); - } - }); + _.forEach(_.keys(data), function(key) { + if ((!copySpec.includes || _.contains(copySpec.includes, key)) && + (!copySpec.excludes || !_.contains(copySpec.excludes, key))) { + if (data[key] && (typeof data[key] == 'object') && + copySpec[key] && (typeof copySpec[key] == 'object')) { + result[key] = copyUsingSpec(data[key], copySpec[key]); + } + else { + result[key] = data[key]; + } } + }); + return result; + } - _.forEach(_.keys(data), function(key) { - if ((!copySpec.includes || _.contains(copySpec.includes, key)) && - (!copySpec.excludes || !_.contains(copySpec.excludes, key))) { - if (data[key] && (typeof data[key] == 'object') && - copySpec[key] && (typeof copySpec[key] == 'object')) { - result[key] = copyUsingSpec(data[key], copySpec[key]); - } - else { - result[key] = data[key]; - } + // Load settings + function loadSettings(pubkey, keypair) { + var now = new Date().getTime(); + return $q.all([ + CryptoUtils.box.keypair.fromSignKeypair(keypair), + that.get({id: pubkey}) + .catch(function(err){ + if (err && err.ucode && err.ucode == 404) { + return null; // not found + } + else { + throw err; + } + })]) + .then(function(res) { + var boxKeypair = res[0]; + res = res[1]; + if (!res || !res._source) { + return; + } + var record = res._source; + // Do not apply if same version + if (record.time === csSettings.data.time) { + console.debug('[ES] [settings] Loaded user settings in '+ (new Date().getTime()-now) +'ms (no update need)'); + return; } + var nonce = CryptoUtils.util.decode_base58(record.nonce); + // Decrypt settings content + return CryptoUtils.box.open(record.content, nonce, boxKeypair.boxPk, boxKeypair.boxSk) + .then(function(json) { + var settings = JSON.parse(json || '{}'); + settings.time = record.time; + console.debug('[ES] [settings] Loaded user settings in '+ (new Date().getTime()-now) +'ms'); + return settings; + }) + // if error: skip stored content + .catch(function(err){ + console.error('[ES] [settings] Could not read stored settings: ' + (err && err.message || 'decryption error')); + // make sure to remove time, to be able to save it again + delete csSettings.data.time; + return null; + }); }); - return result; - } + } - // Load settings - function loadSettings(pubkey, keypair) { - var now = new Date().getTime(); - return $q.all([ - CryptoUtils.box.keypair.fromSignKeypair(keypair), - that.get({id: pubkey}) - .catch(function(err){ - if (err && err.ucode && err.ucode == 404) { - return null; // not found - } - else { - throw err; - } - })]) - .then(function(res) { - var boxKeypair = res[0]; - res = res[1]; - if (!res || !res._source) { - return; - } - var record = res._source; - // Do not apply if same version - if (record.time === csSettings.data.time) { - console.debug('[ES] [settings] Loaded user settings in '+ (new Date().getTime()-now) +'ms (no update need)'); - return; - } - var nonce = CryptoUtils.util.decode_base58(record.nonce); - // Decrypt settings content - return CryptoUtils.box.open(record.content, nonce, boxKeypair.boxPk, boxKeypair.boxSk) - .then(function(json) { - var settings = JSON.parse(json || '{}'); - settings.time = record.time; - console.debug('[ES] [settings] Loaded user settings in '+ (new Date().getTime()-now) +'ms'); - return settings; - }) - // if error: skip stored content - .catch(function(err){ - console.error('[ES] [settings] Could not read stored settings: ' + (err && err.message || 'decryption error')); - // make sure to remove time, to be able to save it again - delete csSettings.data.time; - return null; - }); - }); - } + function onSettingsReset(data, deferred) { + deferred = deferred || $q.defer(); + angular.merge(data, defaultSettings); + deferred.resolve(data); + return deferred.promise; + } - function onSettingsReset(data, deferred) { - deferred = deferred || $q.defer(); - angular.merge(data, defaultSettings); - deferred.resolve(data); + function onWalletLogin(data, deferred) { + deferred = deferred || $q.defer(); + if (!data || !data.pubkey || !data.keypair) { + deferred.resolve(); return deferred.promise; } - function onWalletLogin(data, deferred) { - deferred = deferred || $q.defer(); - if (!data || !data.pubkey || !data.keypair) { - deferred.resolve(); - return deferred.promise; - } - - // Waiting to load crypto libs - if (!CryptoUtils.isLoaded()) { - console.debug('[ES] [settings] Waiting crypto lib loading...'); - return $timeout(function() { - return onWalletLogin(data, deferred); - }, 50); - } + // Waiting to load crypto libs + if (!CryptoUtils.isLoaded()) { + console.debug('[ES] [settings] Waiting crypto lib loading...'); + return $timeout(function() { + return onWalletLogin(data, deferred); + }, 50); + } - console.debug('[ES] [settings] Loading user settings...'); + console.debug('[ES] [settings] Loading user settings...'); - // Load settings - loadSettings(data.pubkey, data.keypair) - .then(function(settings) { - if (!settings) return; // not found or up to date - angular.merge(csSettings.data, settings); + // Load settings + loadSettings(data.pubkey, data.keypair) + .then(function(settings) { + if (!settings) return; // not found or up to date + angular.merge(csSettings.data, settings); - // Remember for comparison - previousRemoteData = settings; + // Remember for comparison + previousRemoteData = settings; - console.debug('[ES] [settings] Successfully load settings from ES'); - return storeSettingsLocally(); - }) - .then(function() { - deferred.resolve(data); + console.debug('[ES] [settings] Successfully load settings from ES'); + return storeSettingsLocally(); }) - .catch(function(err){ - deferred.reject(err); - }); + .then(function() { + deferred.resolve(data); + }) + .catch(function(err){ + deferred.reject(err); + }); - return deferred.promise; - } + return deferred.promise; + } - // Listen for settings changed - function onSettingsChanged(data) { - // avoid recursive call, because storeSettingsLocally() could emit event again - if (ignoreSettingsChanged) return; + // Listen for settings changed + function onSettingsChanged(data) { + // avoid recursive call, because storeSettingsLocally() could emit event again + if (ignoreSettingsChanged) return; - var wasEnable = listeners && listeners.length > 0; + var wasEnable = listeners && listeners.length > 0; - refreshState(); + refreshState(); - var isEnable = that.isEnable(); - if (csWallet.isLogin()) { - if (!wasEnable && isEnable) { + var isEnable = that.isEnable(); + if (csWallet.isLogin()) { + if (!wasEnable && isEnable) { - onWalletLogin(csWallet.data); - } - else { - storeSettingsRemotely(data); - } + onWalletLogin(csWallet.data); + } + else { + storeSettingsRemotely(data); } } + } - function storeSettingsLocally() { - if (ignoreSettingsChanged) return $q.when(); - ignoreSettingsChanged = true; - return csSettings.store() - .then(function(){ - ignoreSettingsChanged = false; - }) - .catch(function(err) { - ignoreSettingsChanged = false; - throw err; - }); - } - - function storeSettingsRemotely(data) { - if (!csWallet.isLogin()) return $q.when(); - - var filteredData = copyUsingSpec(data, SETTINGS_SAVE_SPEC); - if (previousRemoteData && angular.equals(filteredData, previousRemoteData)) { - return $q.when(); - } + function storeSettingsLocally() { + if (ignoreSettingsChanged) return $q.when(); + ignoreSettingsChanged = true; + return csSettings.store() + .then(function(){ + ignoreSettingsChanged = false; + }) + .catch(function(err) { + ignoreSettingsChanged = false; + throw err; + }); + } - // Waiting to load crypto libs - if (!CryptoUtils.isLoaded()) { - console.debug('[ES] [settings] Waiting crypto lib loading...'); - return $timeout(function() { - return storeSettingsRemotely(); - }, 50); - } + function storeSettingsRemotely(data) { + if (!csWallet.isLogin()) return $q.when(); - var time = esHttp.date.now(); - console.debug('[ES] [settings] Saving user settings... at time ' + time); - - return $q.all([ - CryptoUtils.box.keypair.fromSignKeypair(csWallet.data.keypair), - CryptoUtils.util.random_nonce() - ]) - .then(function(res) { - var boxKeypair = res[0]; - var nonce = res[1]; - - var record = { - issuer: csWallet.data.pubkey, - nonce: CryptoUtils.util.encode_base58(nonce), - time: time - }; - - var json = JSON.stringify(filteredData); - - return CryptoUtils.box.pack(json, nonce, boxKeypair.boxPk, boxKeypair.boxSk) - .then(function(cypherText) { - record.content = cypherText; - // create or update - return !data.time ? - that.add(record) : - that.update(record, {id: record.issuer}); - }); - }) - .then(function() { - // Update settings version, then store (on local store only) - csSettings.data.time = time; - previousRemoteData = filteredData; - console.debug('[ES] [settings] Saved user settings in ' + (esHttp.date.now() - time) + 'ms'); - return storeSettingsLocally(); - }) - .catch(function(err) { - console.error(err); - throw err; - }) - ; + var filteredData = copyUsingSpec(data, SETTINGS_SAVE_SPEC); + if (previousRemoteData && angular.equals(filteredData, previousRemoteData)) { + return $q.when(); } - function removeListeners() { - _.forEach(listeners, function(remove){ - remove(); - }); - listeners = []; + // Waiting to load crypto libs + if (!CryptoUtils.isLoaded()) { + console.debug('[ES] [settings] Waiting crypto lib loading...'); + return $timeout(function() { + return storeSettingsRemotely(); + }, 50); } - function addListeners() { - // Extend csWallet.login() - listeners = [ - csSettings.api.data.on.reset($rootScope, onSettingsReset, this), - csWallet.api.data.on.login($rootScope, onWalletLogin, this) - ]; - } + var time = esHttp.date.now(); + console.debug('[ES] [settings] Saving user settings... at time ' + time); + + return $q.all([ + CryptoUtils.box.keypair.fromSignKeypair(csWallet.data.keypair), + CryptoUtils.util.random_nonce() + ]) + .then(function(res) { + var boxKeypair = res[0]; + var nonce = res[1]; + + var record = { + issuer: csWallet.data.pubkey, + nonce: CryptoUtils.util.encode_base58(nonce), + time: time + }; + + var json = JSON.stringify(filteredData); + + return CryptoUtils.box.pack(json, nonce, boxKeypair.boxPk, boxKeypair.boxSk) + .then(function(cypherText) { + record.content = cypherText; + // create or update + return !data.time ? + that.add(record) : + that.update(record, {id: record.issuer}); + }); + }) + .then(function() { + // Update settings version, then store (on local store only) + csSettings.data.time = time; + previousRemoteData = filteredData; + console.debug('[ES] [settings] Saved user settings in ' + (esHttp.date.now() - time) + 'ms'); + return storeSettingsLocally(); + }) + .catch(function(err) { + console.error(err); + throw err; + }) + ; + } + + function removeListeners() { + _.forEach(listeners, function(remove){ + remove(); + }); + listeners = []; + } - function refreshState() { - var enable = that.isEnable(); + function addListeners() { + // Extend csWallet.login() + listeners = [ + csSettings.api.data.on.reset($rootScope, onSettingsReset, this), + csWallet.api.data.on.login($rootScope, onWalletLogin, this) + ]; + } - // Disable - if (!enable && listeners && listeners.length > 0) { - console.debug("[ES] [settings] Disable"); - removeListeners(); - return esHttp.stop(); - } + function refreshState() { + var enable = that.isEnable(); - // Enable - else if (enable && (!listeners || listeners.length === 0)) { - return esHttp.start() - .then(function(started) { - if (!started) { - // TODO : alert user ? - console.error('[ES] node could not be started !!'); - } - else { - console.debug("[ES] [settings] Enable"); - addListeners(); - if (csWallet.isLogin()) { - return onWalletLogin(csWallet.data); - } + // Disable + if (!enable && listeners && listeners.length > 0) { + console.debug("[ES] [settings] Disable"); + removeListeners(); + return esHttp.stop(); + } + + // Enable + else if (enable && (!listeners || listeners.length === 0)) { + return esHttp.start() + .then(function(started) { + if (!started) { + // TODO : alert user ? + console.error('[ES] node could not be started !!'); + } + else { + console.debug("[ES] [settings] Enable"); + addListeners(); + if (csWallet.isLogin()) { + return onWalletLogin(csWallet.data); } - }); - } + } + }); } + } - Device.ready().then(function() { + csSettings.ready().then(function() { - csSettings.api.data.on.changed($rootScope, onSettingsChanged, this); - esHttp.api.node.on.stop($rootScope, function() { - previousRemoteData = null; - }, this); - return refreshState(); - }) + csSettings.api.data.on.changed($rootScope, onSettingsChanged, this); + esHttp.api.node.on.stop($rootScope, function() { + previousRemoteData = null; + }, this); + return refreshState(); + }) - .then(function() { - // Ask (once) user to enable ES plugin - if (csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.askEnable && // if config ask enable - csSettings.data.plugins.es && !csSettings.data.plugins.es.enable && // AND user settings has disable plugin - csSettings.data.plugins.es.askEnable // AND user has not yet answer 'NO' - ) { - return UIUtils.alert.confirm('ES_SETTINGS.CONFIRM.ASK_ENABLE', 'ES_SETTINGS.CONFIRM.ASK_ENABLE_TITLE', - { - cancelText: 'COMMON.BTN_NO', - okText: 'COMMON.BTN_YES' - }) - .then(function (confirm) { - if (confirm) { - csSettings.data.plugins.es.enable = true; - } - csSettings.data.plugins.es.askEnable = false; - return csSettings.store(); - }); - } - }); - } + .then(function() { + // Ask (once) user to enable ES plugin + if (csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.askEnable && // if config ask enable + csSettings.data.plugins.es && !csSettings.data.plugins.es.enable && // AND user settings has disable plugin + csSettings.data.plugins.es.askEnable // AND user has not yet answer 'NO' + ) { + return UIUtils.alert.confirm('ES_SETTINGS.CONFIRM.ASK_ENABLE', 'ES_SETTINGS.CONFIRM.ASK_ENABLE_TITLE', + { + cancelText: 'COMMON.BTN_NO', + okText: 'COMMON.BTN_YES' + }) + .then(function (confirm) { + if (confirm) { + csSettings.data.plugins.es.enable = true; + } + csSettings.data.plugins.es.askEnable = false; + return csSettings.store(); + }); + } + }); - return new Factory(); -}) -; + return that; +}); diff --git a/www/sync-storage.html b/www/sync-storage.html deleted file mode 100644 index a1b1a840..00000000 --- a/www/sync-storage.html +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <script src="lib/ionic/js/angular/angular-xdLocalStoragePostMessageApi.min.js"></script> -</head> -<body> - This is the HTTPS Frame content -</body> -</html> diff --git a/www/templates/settings/settings.html b/www/templates/settings/settings.html index a07236ed..4d8208cd 100644 --- a/www/templates/settings/settings.html +++ b/www/templates/settings/settings.html @@ -125,7 +125,7 @@ <div class="input-label"> {{'SETTINGS.PEER' | translate}} </div> - <span class="item-note dark">{{getServer()}}</ng-if></span> + <span class="item-note dark">{{bma.server}}</ng-if></span> </div> <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> -- GitLab