diff --git a/gulpfile.js b/gulpfile.js index 672c8fe31c188c3a5491cd7f4a9970daf01667da..364e9f9aa947a1cb980dd2c19fe4ca05af6dfef6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -36,7 +36,7 @@ const gulp = require('gulp'), argv = require('yargs').argv, sriHash = require('gulp-sri-hash'), sort = require('gulp-sort'), - gulpfile = this; + map = require('map-stream'); // Workaround because @ioni/v1-toolkit use gulp v3.9.2 instead of gulp v4 let jsonlint; @@ -62,7 +62,7 @@ const paths = { ng_annotate_plugin: ['./www/plugins/*/**/*.js', '!./www/plugins/*/js/vendor/*.js'] }; -const uglifyOptions = { +const uglifyBaseOptions = { toplevel: true, warnings: true, mangle: { @@ -80,6 +80,13 @@ const uglifyOptions = { max_line_len: 120000 } }; +const debugBaseOptions = { + title: 'Processing', + minimal: true, + showFiles: argv.debug || false, + showCount: argv.debug || false, + logger: m => log(colors.grey(m)) +}; function appAndPluginWatch(done) { @@ -102,6 +109,15 @@ function appAndPluginWatch(done) { if (done) done(); } + +function appAndPluginClean() { + return del([ + './www/dist', + './www/css/ionic.app*.css', + './www/css/leaflet.app*.css' + ]); +} + /** * Generate App CSS (using SASS) * @returns {*} @@ -157,8 +173,25 @@ function appConfig() { .pipe(gulp.dest('www/js')); } +function appAndPluginLint(done) { + log(colors.green('Linting JS files...')); + + // Copy Js (and remove unused code) + return gulp.src(paths.ng_annotate.concat(paths.ng_annotate_plugin)) + .pipe(debug({...debugBaseOptions, title: 'Linting'})) + .pipe(jshint()) + .pipe(jshint.reporter(require('jshint-stylish'))) + .pipe(map( (file, cb) => { + if (!file.jshint.success) { + console.error('jshint failed'); + process.exit(1); + } + })) + .on('end', done); +} + function appNgTemplate() { - log(colors.green('Building App template file...')); + log(colors.green('Building template file...')); return gulp.src(paths.templatecache) .pipe(sort()) @@ -188,7 +221,7 @@ function appNgAnnotate(event) { } function appNgTranslate() { - log(colors.green('Building App translation file...')); + log(colors.green('Building translation file...')); return gulp.src('www/i18n/locale-*.json') .pipe(jsonlint()) @@ -281,7 +314,7 @@ function pluginSass() { return merge( // Copy plugins CSS - gulp.src(paths.css_plugin, {read: false}) + gulp.src(paths.css_plugin) .pipe(gulp.dest('www/dist/dist_css/plugins')), // Leaflet images @@ -481,8 +514,8 @@ function webPluginNgTemplate() { return gulp.src(targetPath + '/plugins/**/*.html') .pipe(sort()) .pipe(templateCache({ - standalone:true, - module:"cesium.plugins.templates", + standalone: true, + module: "cesium.plugins.templates", root: "plugins/" })) .pipe(gulp.dest(targetPath + '/dist/dist_js/plugins')); @@ -514,7 +547,7 @@ function webUglify() { // Process JS .pipe(jsFilter) - .pipe(uglify(uglifyOptions)) // Minify javascript files + .pipe(uglify(uglifyBaseOptions)) // Minify javascript files .pipe(jsFilter.restore) // Process CSS @@ -585,7 +618,7 @@ function webApiUglify() { // Process JS .pipe(jsFilter) - .pipe(uglify(uglifyOptions)) // Minify any javascript sources + .pipe(uglify(uglifyBaseOptions)) // Minify any javascript sources .pipe(jsFilter.restore) // Process CSS @@ -856,8 +889,8 @@ function cdvAddPlatformToBodyTag() { } } -function cdvCopyFiles() { - log(colors.green('Copying files... ')); +function cdvNgAnnotate() { + log(colors.green('Building JS files... ')); const projectRoot = argv.root || '.'; const platform = argv.platform || 'android'; @@ -882,17 +915,17 @@ function cdvCopyFiles() { gulp.src(wwwPath + '/plugins/**/*.js') .pipe(ngAnnotate({single_quotes: true})) .pipe(gulp.dest(wwwPath + '/dist/dist_js/plugins')), - +/* // Copy plugin CSS - gulp.src(wwwPath + '/plugins/*/css/**/*.css') + gulp.src(wwwPath + '/plugins/!*!/css/!**!/!*.css') .pipe(gulp.dest(wwwPath + '/dist/dist_css/plugins')), // Copy Leaflet images pluginLeafletImages(wwwPath + '/img'), // Copy Leaflet CSS - gulp.src('./www/css/**/leaflet.*') - .pipe(gulp.dest(wwwPath + '/css')) + gulp.src('./www/css/!**!/leaflet.*') + .pipe(gulp.dest(wwwPath + '/css'))*/ ); } @@ -1099,7 +1132,7 @@ function cdvUglify() { const jsLibFilter = filter(['*/lib/**/*.js', '*/js/vendor/**/*.js'], {restore: true}); // External libs only const cssFilter = filter("**/*.css", {restore: true}); const cdvUglifyOptions = { - ...uglifyOptions, + ...uglifyBaseOptions, ecma: '5' }; const debugOptions = { @@ -1238,11 +1271,12 @@ function cdvAndroidManifest() { const projectRoot = argv.root || '.'; const platform = argv.platform || 'android'; + if (platform !== 'android') return Promise.resolve(); // Skip const srcMainPath = path.join(projectRoot, 'platforms', platform, 'app', 'src', 'main'); const androidManifestFile = path.join(srcMainPath, 'AndroidManifest.xml'); - log(colors.green(' Updating Android manifest... ') + colors.grey(androidManifestFile)); + log(colors.green('Patch Android manifest... ') + colors.grey(androidManifestFile)); if (!fs.existsSync(androidManifestFile)) { throw Error("Missing required file " + androidManifestFile); @@ -1276,6 +1310,8 @@ function cdvAndroidCheckSigning() { const projectRoot = argv.root || '.'; const platform = argv.platform || 'android'; + if (platform !== 'android') return Promise.resolve(); // Skip + const targetPath = path.join(projectRoot, 'platforms', platform); const signingFile = path.join(targetPath, 'release-signing.properties'); @@ -1288,28 +1324,18 @@ function cdvAndroidCheckSigning() { return Promise.resolve(); } -function cdvAfterPrepare(done, projectRoot, platform) { +function cdvAsHook(wrapper) { - projectRoot = (typeof projectRoot === 'string' && projectRoot) || argv.root || '.'; - platform = ((typeof platform === 'string' && platform) || argv.platform || 'android').toLowerCase(); + return (done, projectRoot, platform) => { + projectRoot = (typeof projectRoot === 'string' && projectRoot) || argv.root || '.'; + platform = ((typeof platform === 'string' && platform) || argv.platform || 'android').toLowerCase(); - // Override arguments, to pass it to other tasks - argv.root = projectRoot; - argv.platform = platform; + // Override arguments, to pass it to other tasks + argv.root = projectRoot; + argv.platform = platform; - let wrapper = gulp.series( - gulp.parallel(cdvCopyFiles, cdvAddPlatformToBodyTag), - cdvRemoveCode, - gulp.parallel(cdvNgTemplate, cdvNgTranslate), - cdvUglify, - gulp.parallel(cdvCleanUnusedDirectories,cdvCopyBuildFiles) - ); - - if (platform === 'android') { - wrapper = gulp.series(wrapper, cdvAndroidManifest, cdvAndroidCheckSigning); + wrapper(done); } - - wrapper(done); } function help() { @@ -1372,15 +1398,6 @@ const webExtBuild = gulp.series( ); -exports.cdvRemoveCode = cdvRemoveCode; -exports.cdvNgTemplate = cdvNgTemplate; -exports.cdvNgTranslate = cdvNgTranslate; -exports.cdvUglify = cdvUglify; -exports.cdvCleanUnusedDirectories = cdvCleanUnusedDirectories; -exports.cdvCopyBuildFiles = cdvCopyBuildFiles; -exports.cdvAndroidManifest = cdvAndroidManifest; -exports.cdvAndroidCheckSigning = cdvAndroidCheckSigning; -exports.cdvAfterPrepare = cdvAfterPrepare; /* -------------------------------------------------------------------------- -- Define public tasks @@ -1392,20 +1409,36 @@ exports.license = appLicense; exports.sass = appAndPluginSass; exports.translate = translate; exports.template = template; -exports.annotate = appNgAnnotate; +exports.clean = appAndPluginClean; +exports.lint = appAndPluginLint; +exports.annotate = gulp.series(appNgAnnotate, pluginNgAnnotate); exports.watch = appAndPluginWatch; exports.build = build; +// Web exports.webCompile = webCompile; exports.webBuild = webBuild; exports['build:web'] = exports.webBuild; // Alias +// Web extension exports.webExtClean = webExtClean; exports.webExtCompile = webExtCompile; exports.webExtBuild = webExtBuild; exports.webExtCopyFiles = webExtCopyFiles; exports['build:webExt'] = exports.webBuild; // Alias +// Cordova (hooks) +const cdvAfterPrepare = gulp.series( + gulp.parallel(cdvNgAnnotate, cdvAddPlatformToBodyTag), + cdvRemoveCode, + gulp.parallel(cdvNgTemplate, cdvNgTranslate), + cdvUglify, + gulp.parallel(cdvCleanUnusedDirectories, cdvCopyBuildFiles), + // Android tasks + gulp.parallel(cdvAndroidManifest, cdvAndroidCheckSigning), +); +exports.cdvAfterPrepare = cdvAsHook(cdvAfterPrepare); + exports.default = gulp.series(appConfig, build); exports.serveBefore = gulp.series(build, appAndPluginWatch); exports['ionic:serve:before'] = exports.serveBefore; // Alias need need by @ionic/cli diff --git a/package.json b/package.json index d5feb51e5c0b18b36b0fdba14231959ff7af6ecd..96899ea4b7fda974d19650b2fee8dd3e90b4ad87 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "clean": "trash www/dist/** dist/web/* dist/desktop/**/*.deb platforms/android/**/*.apk", "postinstall": "node scripts/node/postinstall.js", - "lint": "node scripts/node/jshint.js", + "lint": "gulp lint", "install-platforms": "ionic cordova prepare", "start": "ionic serve", "start:webExt": "gulp webExtCompile && web-ext run --source-dir ./dist/web/ext/", @@ -151,8 +151,10 @@ "gulp-useref": "^4.0.1", "gulp-zip": "^5.0.2", "ionic-plugin-keyboard": "^2.2.1", - "jshint": "^2.10.3", + "jshint": "^2.12.0", + "jshint-stylish": "^2.2.1", "lazypipe": "^1.0.2", + "map-stream": "0.0.7", "merge2": "^1.3.0", "mv": "^2.1.1", "node-sass": "^4.14.1", diff --git a/scripts/hooks/before_prepare.js b/scripts/hooks/before_prepare.js index 811e08822faeb44b3b136b9a33e0bd76021c9d44..6384ee6c98221fb610dc998d3dd5e7318c94d33f 100644 --- a/scripts/hooks/before_prepare.js +++ b/scripts/hooks/before_prepare.js @@ -1,8 +1,10 @@ #!/usr/bin/env node -const log = require('fancy-log'), +const gulp = require('gulp'), + path = require("path"), colors = require('ansi-colors'), - jshint = require('../node/jshint-utils'); + jshint = require('../node/jshint-utils'), + log = require('fancy-log'); module.exports = function(context) { const now = Date.now(); @@ -10,10 +12,13 @@ module.exports = function(context) { const projectRoot = context && context.opts && context.opts.projectRoot || '.'; const platforms = context && context.opts && context.opts.platforms || ['android']; - if (!projectRoot || !platforms) return; // Skip + + const gulpFile = require(path.join(projectRoot, 'gulpfile')); + + if (!projectRoot || !platforms || !gulpFile) return; // Skip // Run JS Lint - return jshint.validate(projectRoot) + return jshint.validate(projectRoot) // new Promise(done => gulpFile.lint(done)) .then(() => { log(colors.grey("Hook 'before_prepare' finished in " + (Date.now() - now) + 'ms')); }); diff --git a/scss/ionic.app.scss b/scss/ionic.app.scss index 0a544a5fc507fa65100f8598820e0e656bccee5d..cf11d75726e1c47926a877e8d2b15fa937542a4e 100644 --- a/scss/ionic.app.scss +++ b/scss/ionic.app.scss @@ -241,12 +241,6 @@ $screen-menu: $screen-sm; } } -@media screen and (max-width: $screen-sm-max) { - body { - cursor: url('http://ionicframework.com/img/finger.png'), auto; - } -} - @media screen and (min-width: $screen-md) { body { cursor: inherit; diff --git a/www/js/config.js b/www/js/config.js index 8f3722b07ec6873b302141795194e783db6f5fa1..045782e1d0462df71ddd8264e1174543f9a23bf1 100644 --- a/www/js/config.js +++ b/www/js/config.js @@ -120,8 +120,8 @@ angular.module("cesium.config", []) } }, "version": "1.6.7", - "build": "2020-05-06T09:07:27.415Z", + "build": "2020-08-11T08:40:38.369Z", "newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new" }) -; +; \ No newline at end of file diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js index 8fa7643ef9240be2fd2433eb97a0192edf44fe0f..9e3c703f99837fcb7ebb868ff73171a2fe1307db 100644 --- a/www/js/controllers/app-controllers.js +++ b/www/js/controllers/app-controllers.js @@ -67,81 +67,6 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ .then(UIUtils.loading.hide); }; - // removeIf(no-device) - //////////////////////////////////////// - // Device only methods - // (code removed when NO device) - //////////////////////////////////////// - - $scope.scanQrCodeAndGo = function() { - - if (!Device.barcode.enable) return; - - // Run scan cordova plugin, on device - return Device.barcode.scan() - .then(function(data) { - if (!data) return; - - // Try to parse as an URI - return BMA.uri.parse(data) - .then(function(res){ - if (!res || !res.pubkey) throw {message: 'ERROR.SCAN_UNKNOWN_FORMAT'}; - // If pubkey: open the identity - return $state.go('app.wot_identity', { - pubkey: res.pubkey, - node: res.host ? res.host: null} - ); - }) - - // Not an URI: try WIF or EWIF format - .catch(function(err) { - console.debug("[app] Scan data is not an URI (get error: " + (err && err.message || err) + "). Trying to decode as a WIF or EWIF format..."); - - // Try to read as WIF format - return csCrypto.keyfile.parseData(data) - .then(function(keypair) { - if (!keypair || !keypair.signPk || !keypair.signSk) throw err; // rethrow the first error (e.g. Bad URI) - - var pubkey = CryptoUtils.base58.encode(keypair.signPk); - console.debug("[app] Detected WIF/EWIF format. Will login to wallet {" + pubkey.substring(0, 8) + "}"); - - // Create a new wallet (if default wallet is already used) - var wallet = !csWallet.isLogin() ? csWallet : csWallet.children.create({store: false}); - - // Login using keypair - return wallet.login({ - silent: true, - forceAuth: true, - minData: false, - authData: { - pubkey: pubkey, - keypair: keypair - } - }) - .then(function () { - - // Open transfer all wallet - $ionicHistory.nextViewOptions({ - historyRoot: true - }); - return $state.go('app.new_transfer', { - all: true, // transfer all sources - wallet: !wallet.isDefault() ? wallet.id : undefined - }); - }); - }) - // Unknown format (nor URI, nor WIF/EWIF) - .catch(UIUtils.onError('ERROR.SCAN_UNKNOWN_FORMAT')); - }); - }) - .catch(UIUtils.onError('ERROR.SCAN_FAILED')); - }; - - //////////////////////////////////////// - // End of device only methods - //////////////////////////////////////// - // endRemoveIf(no-device) - //////////////////////////////////////// // Show Help tour //////////////////////////////////////// @@ -465,6 +390,83 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ return false; }; + /** + * Parse an external URI (see g1lien), and open the expected state + * @param uri + * @returns {*} + */ + $scope.handleUri = function(uri) { + if (!uri) return $q.when(); // Skip + + console.info('[app] Parsing external uri: ', uri); + var fromHomeState = $state.current && $state.current.name === 'app.home'; + + // Parse the URI + return BMA.uri.parse(uri) + .then(function(res) { + if (!res) throw {message: 'ERROR.UNKNOWN_URI_FORMAT'}; // Continue + + if (res.pubkey) { + $state.go('app.wot_identity', + angular.merge({ + pubkey: res.pubkey, + action: res.params && res.params.amount ? 'transfer' : undefined + }, res.params), + {reload: true}); + } + else if (res.uid) { + return $state.go('app.wot_identity_uid', + angular.merge({ + uid: res.uid, + action: res.params && res.params.amount ? 'transfer' : undefined + }, res.params), + {reload: true}); + } + else if (angular.isDefined(res.block)) { + return $state.go('app.view_block', + angular.merge(res.block, res.params), + {reload: true}); + } + // Default: wot lookup + else { + console.warn('[app] TODO implement state redirection from URI result: ', res, uri); + return $state.go('app.wot_lookup.tab_search', + {q: uri}, + {reload: true}); + } + }) + + // After state change + .then(function() { + if (fromHomeState) { + // Wait 500ms, then remove /app/home?uri from the history + // to make sure the back button will work fine + return $timeout(function () { + if ($ionicHistory.backView()) $ionicHistory.removeBackView(); + }, 500); + } + }) + + .catch(function(err) { + console.error("[home] Error while handle uri {" + uri + "': ", err); + return UIUtils.onError(uri)(err); + }); + }; + + $scope.registerProtocolHandlers = function() { + var protocols = ['web+june', /*'june' - NOT yet accepted by web browser */]; + + _.each(protocols, function(protocol) { + console.debug("[app] Registering protocol '%s'...".format(protocol)); + try { + navigator.registerProtocolHandler(protocol, "#/app/home?uri=%s", "Cesium"); + } + catch(err) { + console.error("[app] Error while registering protocol '%s'".format(protocol), err); + } + }); + }; + //////////////////////////////////////// // Layout Methods //////////////////////////////////////// @@ -505,8 +507,114 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ UIUtils.screen.fullscreen.toggleAll(); }; + // removeIf(no-device) + //////////////////////////////////////// + // Device only methods + // (code removed when NO device) + //////////////////////////////////////// + + $scope.scanQrCodeAndGo = function() { + + if (!Device.barcode.enable) return; + + // Run scan cordova plugin, on device + return Device.barcode.scan() + .then(function(data) { + if (!data) return; + + // Try to parse as an URI + return BMA.uri.parse(data) + .then(function(res){ + if (!res || !res.pubkey) throw {message: 'ERROR.SCAN_UNKNOWN_FORMAT'}; + // If pubkey: open the identity + return $state.go('app.wot_identity', { + pubkey: res.pubkey, + node: res.host ? res.host: null} + ); + }) + + // Not an URI: try WIF or EWIF format + .catch(function(err) { + console.debug("[app] Scan data is not an URI (get error: " + (err && err.message || err) + "). Trying to decode as a WIF or EWIF format..."); + + // Try to read as WIF format + return csCrypto.keyfile.parseData(data) + .then(function(keypair) { + if (!keypair || !keypair.signPk || !keypair.signSk) throw err; // rethrow the first error (e.g. Bad URI) + + var pubkey = CryptoUtils.base58.encode(keypair.signPk); + console.debug("[app] Detected WIF/EWIF format. Will login to wallet {" + pubkey.substring(0, 8) + "}"); + + // Create a new wallet (if default wallet is already used) + var wallet = !csWallet.isLogin() ? csWallet : csWallet.children.create({store: false}); + + // Login using keypair + return wallet.login({ + silent: true, + forceAuth: true, + minData: false, + authData: { + pubkey: pubkey, + keypair: keypair + } + }) + .then(function () { + + // Open transfer all wallet + $ionicHistory.nextViewOptions({ + historyRoot: true + }); + return $state.go('app.new_transfer', { + all: true, // transfer all sources + wallet: !wallet.isDefault() ? wallet.id : undefined + }); + }); + }) + // Unknown format (nor URI, nor WIF/EWIF) + .catch(UIUtils.onError('ERROR.SCAN_UNKNOWN_FORMAT')); + }); + }) + .catch(UIUtils.onError('ERROR.SCAN_FAILED')); + }; + + /** + * Process launch intent, as it could have been triggered BEFORE addListeners() + * @returns {*} + */ + $scope.processLaunchUri = function() { + return Device.intent.last() + .then(function(intent) { + if (intent) { + Device.intent.clear(); + return $scope.handleUri(intent); + } + }); + }; + + // Listen for new intent + Device.api.intent.on.new($scope, $scope.handleUri); + $scope.processLaunchUri(); + + //////////////////////////////////////// + // End of device only methods + //////////////////////////////////////// + // endRemoveIf(no-device) + + // removeIf(device) + //////////////////////////////////////// + // NOT-Device only methods (web or desktop) + // (code removed when build for device - eg. Android, iOS) + //////////////////////////////////////// + // Ask switching fullscreen $scope.askFullscreen(); + + // Register protocol handlers + $scope.registerProtocolHandlers(); + + //////////////////////////////////////// + // End of NOT-device only methods + //////////////////////////////////////// // endRemoveIf(device) } diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js index a2362a37a3905a861e77842cf52f465affecda29..2632b18f12ab76068a5aa6f8036489e3ab9fd8bf 100644 --- a/www/js/controllers/home-controllers.js +++ b/www/js/controllers/home-controllers.js @@ -47,7 +47,7 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht if (state && state.stateParams && state.stateParams.uri) { - return csPlatform.uri.open(state.stateParams.uri) + return $scope.handleUri(state.stateParams.uri) .then(function() { $scope.loading = false; }); diff --git a/www/js/platform.js b/www/js/platform.js index 0ef30a900e2448c9f1eb6f5dcf168e0119bb6a3a..640b7e0f9011260d3ea1b766fe7328b7e75b1f02 100644 --- a/www/js/platform.js +++ b/www/js/platform.js @@ -227,96 +227,12 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] return $q.when(); } - /** - * Parse an external URI (see g1lien), and open the expected state - * @param uri - * @returns {*} - */ - function openUri(uri) { - if (!uri) return $q.when(); // Skip - - console.info('[platform] Detecting external uri: ', uri); - - var parts = csHttp.uri.parse(uri), - state, stateParams; - - // G1 lien - if (parts.protocol === 'june:') { - console.debug("[home] Applying uri...", parts); - - // Pubkey - if (parts.hostname && BMA.regexp.PUBKEY.test(parts.hostname)) { - state = 'app.wot_identity'; - stateParams = {pubkey: parts.hostname}; - } else if ((parts.hostname === 'wallet' || parts.hostname === 'pubkey') && BMA.regexp.PUBKEY.test(parts.pathSegments[0])) { - state = 'app.wot_identity'; - stateParams = {pubkey: parts.pathSegments[0]}; - } - - // Block - else if (parts.hostname === 'block') { - state = 'app.view_block'; - stateParams = {number: parts.pathSegments[0]}; - } - - // Otherwise, try to a wot lookup - else if (parts.hostname && BMA.regexp.USER_ID.test(parts.hostname)) { - state = 'app.wot_lookup.tab_search'; - stateParams = {q: parts.hostname}; - } - } - - if (state === 'app.wot_identity' && parts.searchParams && (angular.isDefined(parts.searchParams.action) || angular.isDefined(parts.searchParams.amount))) { - stateParams = angular.merge(stateParams, - // Add default actions - {action: 'transfer'}, - // Add path params - parts.searchParams); - } - - if (state) { - - var fromHomeState = $state.current && $state.current.name === 'app.home'; - - // Open the state, after cleaning current location URI - return $state.go(state, stateParams, { - reload: true - }) - .then(function () { - if (fromHomeState) { - // This is need to make back button working again - return $timeout(function () { - if ($ionicHistory.backView()) $ionicHistory.removeBackView(); - }, 400); - } - }); - } else { - console.error("[home] Unknown URI format: " + uri); - return UIUtils.alert.error("ERROR.UNKNOWN_URI_FORMAT", uri); - } - } - function registerProtocolHandlers() { - var protocols = ['web+june'/*, 'web+g1', 'g1'*/]; - - _.each(protocols, function(protocol) { - console.debug("[platform] Registering protocol '%s'...".format(protocol)); - try { - navigator.registerProtocolHandler(protocol, "#/app/home?uri=%s", "Cesium"); - } - catch(err) { - console.error("[platform] Error while registering protocol '%s'".format(protocol), err); - } - }); - } function addListeners() { listeners = [ // Listen if node changed - BMA.api.node.on.restart($rootScope, restart, this), - - // Listen for new intent - Device.api.intent.on.new($rootScope, openUri, this) + BMA.api.node.on.restart($rootScope, restart, this) ]; } @@ -345,7 +261,6 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] // Avoid change state disableChangeState(); - registerProtocolHandlers(); // We use 'ionicReady()' instead of '$ionicPlatform.ready()', because this one is callable many times startPromise = ionicReady() @@ -372,7 +287,6 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] .then(function(){ enableChangeState(); addListeners(); - processLaunchUri(); startPromise = null; started = true; }) @@ -403,20 +317,6 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] }, 500); } - /** - * Process launch intent, as it could have been triggered BEFORE addListeners() - * @returns {*} - */ - function processLaunchUri() { - return Device.intent.last() - .then(function(intent) { - if (intent) { - Device.intent.clear(); - return openUri(intent); - } - }); - } - return { disableChangeState: disableChangeState, isStarted: isStarted, @@ -426,9 +326,6 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services'] stop: stop, version: { latest: getLatestRelease - }, - uri: { - open: openUri } }; }) diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js index e045aeb89e01e792f2af0d9e75e32ff6c89efdeb..ffacc98f31e8b2d258929557bdfc0f5e12ebea9f 100644 --- a/www/js/services/bma-services.js +++ b/www/js/services/bma-services.js @@ -838,115 +838,78 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium. }; exports.uri.parse = function(uri) { - return $q(function(resolve, reject) { - var pubkey; + if (!uri) return $q.reject("Missing required argument 'uri'"); - // If pubkey: not need to parse - if (exact(regexp.PUBKEY).test(uri)) { + return $q(function(resolve, reject) { + // Pubkey or pubkey+checksum + if (exports.regexp.PUBKEY.test(uri) || exports.regexp.PUBKEY_WITH_CHECKSUM.test(uri)) { resolve({ pubkey: uri }); } - // If pubkey+checksum - else if (exact(regexp.PUBKEY_WITH_CHECKSUM).test(uri)) { - console.debug("[BMA.parse] Detecting a pubkey with checksum: " + uri); - var matches = exports.regexp.PUBKEY_WITH_CHECKSUM.exec(uri); - pubkey = matches[1]; - var checksum = matches[2]; - console.debug("[BMA.parse] Detecting a pubkey {"+pubkey+"} with checksum {" + checksum + "}"); - var expectedChecksum = csCrypto.util.pkChecksum(pubkey); - console.debug("[BMA.parse] Expecting checksum for pubkey is {" + expectedChecksum + "}"); - if (checksum != expectedChecksum) { - reject( {message: 'ERROR.PUBKEY_INVALID_CHECKSUM'}); - } - else { - resolve({ - pubkey: pubkey - }); - } + + // Uid + else if (uri.startsWith('@') && exports.regexp.USER_ID.test(uid.substr(1))) { + resolve({ + uid: uid.substr(1) + }); } - else if(uri.startsWith('duniter://')) { - var parser = csHttp.uri.parse(uri), - uid, - currency = parser.host.indexOf('.') === -1 ? parser.host : null, - host = parser.host.indexOf('.') !== -1 ? parser.host : null; - if (parser.username) { - if (parser.password) { - uid = parser.username; - pubkey = parser.password; + + // G1 protocols + else if(uri.startsWith('june:') || uri.startsWith('web+june:')) { + var parser = csHttp.uri.parse(uri); + + // Pubkey (explicit path) + var pubkey; + if (parser.hostname === 'wallet' || parser.hostname === 'pubkey') { + if (exports.regexp.PUBKEY.test(parser.pathSegments[0]) || exports.regexp.PUBKEY_WITH_CHECKSUM.test(parser.pathSegments[0])) { + pubkey = parser.pathSegments[0]; + parser.pathSegments = parser.pathSegments.slice(1); } else { - pubkey = parser.username; + reject({message: 'ERROR.INVALID_PUBKEY'}); + return; } } - if (parser.pathname) { - var paths = parser.pathname.split('/'); - var pathCount = !paths ? 0 : paths.length; - var index = 0; - if (!currency && pathCount > index) { - currency = paths[index++]; - } - if (!pubkey && pathCount > index) { - pubkey = paths[index++]; - } - if (!uid && pathCount > index) { - uid = paths[index++]; - } - if (pathCount > index) { - reject( {message: 'Bad Duniter URI format. Invalid path (incomplete or redundant): '+ parser.pathname}); return; - } + else if (parser.hostname && + (exports.regexp.PUBKEY.test(parser.hostname) || exports.regexp.PUBKEY_WITH_CHECKSUM.test(parser.hostname))) { + pubkey = parser.hostname; } - if (!currency){ - if (host) { - csHttp.get(host + '/blockchain/parameters')() - .then(function(parameters){ - resolve({ - uid: uid, - pubkey: pubkey, - host: host, - currency: parameters.currency - }); - }) - .catch(function(err) { - console.error(err); - reject({message: 'Could not get node parameter. Currency could not be retrieve'}); - }); - } - else { - reject({message: 'Bad Duniter URI format. Missing currency name (or node address).'}); return; - } + if (pubkey) { + resolve({ + pubkey: pubkey, + pathSegments: parser.pathSegments, + params: parser.searchParams + }); } - else { - if (!host) { - resolve({ - uid: uid, - pubkey: pubkey, - currency: currency - }); - } - // Check if currency are the same (between node and uri) - return csHttp.get(host + '/blockchain/parameters')() - .then(function(parameters){ - if (parameters.currency !== currency) { - reject( {message: "Node's currency ["+parameters.currency+"] does not matched URI's currency ["+currency+"]."}); return; - } - resolve({ - uid: uid, - pubkey: pubkey, - host: host, - currency: currency - }); - }) - .catch(function(err) { - console.error(err); - reject({message: 'Could not get node parameter. Currency could not be retrieve'}); - }); + // UID + else if (parser.hostname && parser.hostname.startsWith('@') && exports.regexp.USER_ID.test(parser.hostname.substr(1))) { + resolve({ + uid: parser.hostname.substr(1), + pathSegments: parser.pathSegments, + params: parser.searchParams + }); + } + + // Block + else if (parser.hostname === 'block') { + resolve({ + block: {number: parser.pathSegments[0]}, + pathSegments: parser.pathSegments.slice(1), + params: parser.searchParams + }); + } + + // Other case + else { + console.debug("[BMA.parse] Unknown URI format: " + uri); + reject({message: 'ERROR.UNKNOWN_URI_FORMAT'}); } } else { - console.debug("[BMA.parse] Could not parse URI: " + uri); + console.debug("[BMA.parse] Unknown URI format: " + uri); reject({message: 'ERROR.UNKNOWN_URI_FORMAT'}); } }) @@ -954,14 +917,20 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium. // Check values against regex .then(function(result) { if (!result) return; - if (result.pubkey && !(exact(regexp.PUBKEY).test(result.pubkey))) { - throw {message: "Invalid pubkey format [" + result.pubkey + "]"}; - } - if (result.uid && !(exact(regexp.USER_ID).test(result.uid))) { - throw {message: "Invalid uid format [" + result.uid + "]"}; - } - if (result.currency && !(exact(regexp.CURRENCY).test(result.currency))) { - throw {message: "Invalid currency format ["+result.currency+"]"}; + + // Validate checksum + if (result.pubkey && exports.regexp.PUBKEY_WITH_CHECKSUM.test(result.pubkey)) { + console.debug("[BMA.parse] Validating pubkey checksum... "); + var matches = exports.regexp.PUBKEY_WITH_CHECKSUM.exec(uri); + pubkey = matches[1]; + var checksum = matches[2]; + var expectedChecksum = csCrypto.util.pkChecksum(pubkey); + if (checksum !== expectedChecksum) { + console.warn("[BMA.parse] Detecting a pubkey {"+pubkey+"} with checksum {" + checksum + "}, but expecting checksum is {" + expectedChecksum + "}"); + throw {message: 'ERROR.PUBKEY_INVALID_CHECKSUM'}; + } + result.pubkey = pubkey; + result.pubkeyChecksum = checksum; } return result; }); diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js index 5479c3b1988c615fd100e8997893e8b8078644d6..a756a437d8d7cebc61b62fe65b6cd09aa61dc729 100644 --- a/www/js/services/device-services.js +++ b/www/js/services/device-services.js @@ -153,27 +153,25 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti return deferred.promise; } - function handleOpenUri(intent) { - if (intent) { - console.info('[platform] Received new intent: ', intent); - cache.lastIntent = intent; // Remember, for last() - api.intent.raise.new(intent); - } + // WARN: Need by cordova-plugin-customurlscheme + window.handleOpenURL = function(intent) { + if (intent) { + console.info('[device] Received new intent: ', intent); + cache.lastIntent = intent; // Remember, for last() + api.intent.raise.new(intent); } + }; - exports.intent = { + exports.intent = { enable: false, last: function() { return $q.when(cache.lastIntent); }, - handle: handleOpenUri, clear: function() { cache.lastIntent = undefined; } }; - window.handleOpenURL = handleOpenUri; // Need by cordova-plugin-customurlscheme - // Numerical keyboard - fix #30 exports.keyboard.digit = { settings: { diff --git a/yarn.lock b/yarn.lock index f0b798ae545cf7ac3d04b0e6015128f3478a9d5c..1cdf837d80719716bd4609c571b62b2825cbc10b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5747,6 +5747,11 @@ ipaddr.js@1.9.1: resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +irregular-plurals@^1.0.0: + version "1.4.0" + resolved "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" + integrity sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y= + irregular-plurals@^2.0.0: version "2.0.0" resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872" @@ -6214,9 +6219,21 @@ jsesc@2.5.2: resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jshint@^2.10.3: +jshint-stylish@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/jshint-stylish/-/jshint-stylish-2.2.1.tgz#242082a2c035ae03fd81044e0570cc4208cf6e61" + integrity sha1-JCCCosA1rgP9gQROBXDMQgjPbmE= + dependencies: + beeper "^1.1.0" + chalk "^1.0.0" + log-symbols "^1.0.0" + plur "^2.1.0" + string-length "^1.0.0" + text-table "^0.2.0" + +jshint@^2.12.0: version "2.12.0" - resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/jshint/-/jshint-2.12.0.tgz#52e75bd058d587ef81a0e2f95e5cf18eb5dc5c37" + resolved "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz#52e75bd058d587ef81a0e2f95e5cf18eb5dc5c37" integrity sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA== dependencies: cli "~1.0.0" @@ -7024,6 +7041,13 @@ lodash@^4.0.0, lodash@^4.12.0, lodash@^4.14.0, lodash@^4.14.1, lodash@^4.15.0, l resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +log-symbols@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= + dependencies: + chalk "^1.0.0" + log-update@^4.0.0: version "4.0.0" resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" @@ -8512,6 +8536,13 @@ plugin-log@^0.1.0: chalk "^1.1.1" dateformat "^1.0.11" +plur@^2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" + integrity sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo= + dependencies: + irregular-plurals "^1.0.0" + plur@^3.0.0: version "3.1.1" resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/plur/-/plur-3.1.1.tgz#60267967866a8d811504fe58f2faaba237546a5b" @@ -9975,6 +10006,13 @@ streamfilter@^3.0.0: dependencies: readable-stream "^3.0.6" +string-length@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" + integrity sha1-VpcPscOFWOnnC3KL894mmsRa36w= + dependencies: + strip-ansi "^3.0.0" + string-template@~0.2.0, string-template@~0.2.1: version "0.2.1" resolved "https://nexus.e-is.pro/nexus/content/repositories/npmjs/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"