Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • chrome-manifest-v3
  • develop
  • feat/force-migration-check
  • feat/improve-network-scan
  • feature/android_api_19
  • feature/encrypted_comment
  • feature/migrate-cordova-13
  • gitlab_migration_1
  • master
  • rml8
  • 0.0.1.ES.alpha1
  • 0.0.2
  • 0.1.13
  • 0.1.14
  • 0.1.15
  • 0.1.16
  • 0.1.17
  • 0.1.18
  • 0.1.19
  • 0.1.20
  • 0.1.21
  • 0.1.22
  • 0.1.23
  • 0.1.24
  • 0.1.25
  • 0.1.26
  • 0.1.27
  • 0.1.28
  • 0.1.4
  • 0.1.7
  • 0.1.8
  • 0.2.0
  • 0.2.1
  • v0.10.0
  • v0.10.1
  • v0.10.2
  • v0.11.0
  • v0.11.1
  • v0.11.2
  • v0.11.3
  • v0.11.4
  • v0.11.5
  • v0.11.6
  • v0.11.7
  • v0.11.8
  • v0.12.0
  • v0.12.1
  • v0.12.2
  • v0.12.3
  • v0.12.4
  • v0.12.5
  • v0.12.6
  • v0.12.7
  • v0.12.8
  • v0.12.9
  • v0.13.0
  • v0.14.0
  • v0.14.1
  • v0.15.0
  • v0.15.1
  • v0.15.2
  • v0.15.3
  • v0.15.4
  • v0.15.5
  • v0.15.6
  • v0.15.7
  • v0.16.0
  • v0.16.1
  • v0.17.0
  • v0.17.1
  • v0.17.2
  • v0.17.3
  • v0.17.4
  • v0.17.5
  • v0.17.6
  • v0.18.0
  • v0.18.1
  • v0.18.2
  • v0.18.3
  • v0.19.0
  • v0.19.1
  • v0.19.2
  • v0.19.3
  • v0.19.4
  • v0.19.5
  • v0.19.6
  • v0.2.10
  • v0.2.12
  • v0.2.13
  • v0.2.14
  • v0.2.3
  • v0.2.4
  • v0.2.5
  • v0.2.6
  • v0.2.7
  • v0.2.8
  • v0.2.9
  • v0.3.0
  • v0.3.1
  • v0.3.10
  • v0.3.11
  • v0.3.12
  • v0.3.13
  • v0.3.14
  • v0.3.15
  • v0.3.16
  • v0.3.17
  • v0.3.2
  • v0.3.3
  • v0.3.4
110 results

Target

Select target project
  • cordeliaze/cesium
  • pfouque06/cesium
  • wellno1/cesium
  • 1000i100/cesium
  • vincentux/cesium
  • calbasi/cesium
  • thomasbromehead/cesium
  • matograine/cesium
  • clients/cesium-grp/cesium
  • cedricmenec/cesium
  • Pamplemousse/cesium
  • etienneleba/cesium
  • tnntwister/cesium
  • scanlegentil/cesium
  • morvanc/cesium
  • yyy/cesium
  • Axce/cesium
  • Bertrandbenj/cesium
  • Lupus/cesium
  • elmau/cesium
  • MartinDelille/cesium
  • tykayn/cesium
  • numeropi/cesium
  • Vivakvo/cesium
  • pokapow/cesium
  • pini-gh/cesium
  • anam/cesium
  • RavanH/cesium
  • bpresles/cesium
  • am97/cesium
  • tuxmain/cesium
  • jytou/cesium
  • oliviermaurice/cesium
  • 666titi999/cesium
  • Yvv/cesium
35 results
Select Git revision
  • Vivakvo/cesium-patch-8
  • dev
  • develop
  • feature/android_api_19
  • feature/cesium_plus_pod_2
  • feature/encrypted_comment
  • feature/startup_node_selection
  • gitlab_migration_1
  • hugo/startup_node_selection
  • master
  • rml8
  • 0.0.1.ES.alpha1
  • 0.0.2
  • 0.1.13
  • 0.1.14
  • 0.1.15
  • 0.1.16
  • 0.1.17
  • 0.1.18
  • 0.1.19
  • 0.1.20
  • 0.1.21
  • 0.1.22
  • 0.1.23
  • 0.1.24
  • 0.1.25
  • 0.1.26
  • 0.1.27
  • 0.1.28
  • 0.1.4
  • 0.1.7
  • 0.1.8
  • 0.2.0
  • 0.2.1
  • v0.10.0
  • v0.10.1
  • v0.10.2
  • v0.11.0
  • v0.11.1
  • v0.11.2
  • v0.11.3
  • v0.11.4
  • v0.11.5
  • v0.11.6
  • v0.11.7
  • v0.11.8
  • v0.12.0
  • v0.12.1
  • v0.12.2
  • v0.12.3
  • v0.12.4
  • v0.12.5
  • v0.12.6
  • v0.12.7
  • v0.12.8
  • v0.12.9
  • v0.13.0
  • v0.14.0
  • v0.14.1
  • v0.15.0
  • v0.15.1
  • v0.15.2
  • v0.15.3
  • v0.15.4
  • v0.15.5
  • v0.15.6
  • v0.15.7
  • v0.16.0
  • v0.16.1
  • v0.17.0
  • v0.17.1
  • v0.17.2
  • v0.17.3
  • v0.17.4
  • v0.17.5
  • v0.17.6
  • v0.18.0
  • v0.18.1
  • v0.18.2
  • v0.18.3
  • v0.19.0
  • v0.19.1
  • v0.19.2
  • v0.19.3
  • v0.19.4
  • v0.19.5
  • v0.19.6
  • v0.2.10
  • v0.2.12
  • v0.2.13
  • v0.2.14
  • v0.2.3
  • v0.2.4
  • v0.2.5
  • v0.2.6
  • v0.2.7
  • v0.2.8
  • v0.2.9
  • v0.3.0
  • v0.3.1
  • v0.3.10
  • v0.3.11
  • v0.3.12
  • v0.3.13
  • v0.3.14
  • v0.3.15
  • v0.3.16
  • v0.3.17
  • v0.3.2
  • v0.3.3
  • v0.3.4
111 results
Show changes
Showing
with 1504 additions and 499 deletions
www/img/flag-pt.png

975 B

www/img/logo_256px.png

31.3 KiB

www/img/logo_512px.png

70.6 KiB

<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#444" d="M16 0.5c-8.563 0-15.5 6.938-15.5 15.5s6.938 15.5 15.5 15.5c8.563 0 15.5-6.938 15.5-15.5s-6.938-15.5-15.5-15.5zM23.613 11.119l-2.544 11.988c-0.188 0.85-0.694 1.056-1.4 0.656l-3.875-2.856-1.869 1.8c-0.206 0.206-0.381 0.381-0.781 0.381l0.275-3.944 7.181-6.488c0.313-0.275-0.069-0.431-0.482-0.156l-8.875 5.587-3.825-1.194c-0.831-0.262-0.85-0.831 0.175-1.231l14.944-5.763c0.694-0.25 1.3 0.169 1.075 1.219z"/>
</svg>
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
<meta property="og:locale:alternate" content="it_IT" /> <meta property="og:locale:alternate" content="it_IT" />
<meta property="og:locale:alternate" content="nl_NL" /> <meta property="og:locale:alternate" content="nl_NL" />
<meta property="og:locale:alternate" content="eo_EO" /> <meta property="og:locale:alternate" content="eo_EO" />
<meta property="og:locale:alternate" content="de_DE" />
<!--endRemoveIf(device)--> <!--endRemoveIf(device)-->
...@@ -58,9 +59,8 @@ ...@@ -58,9 +59,8 @@
ng-class="{'nobackdrop': $root.tour, 'expert-mode': $root.settings.expertMode, 'demo': $root.config.demo && !$root.config.readonly, 'readonly': $root.config.readonly}" ng-class="{'nobackdrop': $root.tour, 'expert-mode': $root.settings.expertMode, 'demo': $root.config.demo && !$root.config.readonly, 'readonly': $root.config.readonly}"
style="background-color: #1a237e;"> style="background-color: #1a237e;">
<ion-nav-view> <ion-nav-view>
<div class="loader"> <div class="loader center">
<h3 style="text-align: center; padding-top: 25px; color: whitesmoke;"><b>Cesium</b></h3> <div class="logo"></div>
<h4 style="text-align: center; color: whitesmoke;"><i class="icon ion-load-a"></i></h4>
</div> </div>
</ion-nav-view> </ion-nav-view>
...@@ -68,12 +68,9 @@ ...@@ -68,12 +68,9 @@
<!-- vendor js --> <!-- vendor js -->
<script src="lib/numeral/numeral.js"></script> <script src="lib/numeral/numeral.js"></script>
<script src="lib/numeral/languages/fr.js"></script> <script src="lib/numeral/languages.js"></script>
<script src="lib/numeral/languages/en-gb.js"></script>
<script src="lib/numeral/languages/nl-nl.js"></script>
<script src="lib/numeral/languages/es.js"></script>
<script src="lib/numeral/languages/it.js"></script>
<script src="js/vendor/numeral.eo.js"></script> <script src="js/vendor/numeral.eo.js"></script>
<script src="js/vendor/numeral.ca.js"></script>
<script src="lib/qrcode-generator/js/qrcode.js"></script> <script src="lib/qrcode-generator/js/qrcode.js"></script>
<script src="lib/aes-js/index.js"></script> <script src="lib/aes-js/index.js"></script>
<script src="lib/moment/min/moment-with-locales.min.js"></script> <script src="lib/moment/min/moment-with-locales.min.js"></script>
...@@ -108,15 +105,10 @@ ...@@ -108,15 +105,10 @@
<script src="lib/angular-fullscreen-toggle/dist/angular-fullscreen-toggle.min.js"></script> <script src="lib/angular-fullscreen-toggle/dist/angular-fullscreen-toggle.min.js"></script>
<script src="js/vendor/base58.js" async></script> <script src="js/vendor/base58.js" async></script>
<!--removeIf(android)-->
<!--removeIf(ios)-->
<script src="lib/js-nacl/lib/nacl_factory.js" async></script> <script src="lib/js-nacl/lib/nacl_factory.js" async></script>
<script src="lib/js-scrypt/browser/scrypt.js" async></script> <script src="lib/js-scrypt/browser/scrypt.js" async></script>
<script src="js/vendor/base64.js" async></script> <script src="js/vendor/base64.js" async></script>
<!--endRemoveIf(ios)-->
<!--endRemoveIf(android)-->
<!--removeIf(no-device)--> <!--removeIf(no-device)-->
<script src="js/vendor/sha256.min.js" async></script>
<script src="lib/ngCordova/dist/ng-cordova.min.js"></script> <script src="lib/ngCordova/dist/ng-cordova.min.js"></script>
<!--endRemoveIf(no-device)--> <!--endRemoveIf(no-device)-->
...@@ -167,6 +159,7 @@ ...@@ -167,6 +159,7 @@
<!-- controllers --> <!-- controllers -->
<script src="dist/dist_js/app/controllers/app-controllers.js"></script> <script src="dist/dist_js/app/controllers/app-controllers.js"></script>
<script src="dist/dist_js/app/controllers/home-controllers.js"></script> <script src="dist/dist_js/app/controllers/home-controllers.js"></script>
<script src="dist/dist_js/app/controllers/feed-controllers.js"></script>
<script src="dist/dist_js/app/controllers/join-controllers.js"></script> <script src="dist/dist_js/app/controllers/join-controllers.js"></script>
<script src="dist/dist_js/app/controllers/login-controllers.js"></script> <script src="dist/dist_js/app/controllers/login-controllers.js"></script>
<script src="dist/dist_js/app/controllers/help-controllers.js"></script> <script src="dist/dist_js/app/controllers/help-controllers.js"></script>
...@@ -282,6 +275,7 @@ ...@@ -282,6 +275,7 @@
<script src="dist/dist_js/app/directives.js"></script> <script src="dist/dist_js/app/directives.js"></script>
<script src="dist/dist_js/app/filters.js"></script> <script src="dist/dist_js/app/filters.js"></script>
<script src="dist/dist_js/app/platform.js"></script> <script src="dist/dist_js/app/platform.js"></script>
<script src="dist/dist_js/app/functions.js"></script>
<!-- endbuild --> <!-- endbuild -->
<!-- build:js config.js --> <!-- build:js config.js -->
......
...@@ -46,38 +46,45 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngSanitize', ...@@ -46,38 +46,45 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngSanitize',
// removeIf(android) // removeIf(android)
// removeIf(ios) // removeIf(ios)
// removeIf(firefoxos)
// -- Automatic redirection to large state (if define) (keep this code for platforms web and ubuntu build) // -- Automatic redirection to large state (if define) (keep this code for platforms web and ubuntu build)
if (next.data.large && !UIUtils.screen.isSmall()) { if (next.data.large && !UIUtils.screen.isSmall()) {
event.preventDefault(); event.preventDefault();
$state.go(next.data.large, nextParams); $state.go(next.data.large, nextParams);
return; return;
} }
// endRemoveIf(firefoxos)
// endRemoveIf(ios) // endRemoveIf(ios)
// endRemoveIf(android) // endRemoveIf(android)
var wallet = nextParams.wallet && nextParams.wallet != "default" ? csWallet.children.get(nextParams.wallet) : csWallet; var wallet = nextParams.wallet && nextParams.wallet !== 'default' ? csWallet.children.get(nextParams.wallet) : csWallet;
if (nextParams.wallet && !wallet) { if (nextParams.wallet && !wallet) {
console.warn("[app] Unable to find the children wallet: " + nextParams.wallet); console.warn('[app] Unable to find the children wallet: ' + nextParams.wallet);
} }
var goNextState = function() {
preventStateChange = false;
return $state.go(next.name, nextParams);
};
var processError = function(err) {
preventStateChange = false;
// If user cancel
if (err === 'CANCELLED') {
// Redirect to home, if no current state
if (!$state.current.name) {
return $state.go('app.home');
}
return; // Stay on the existing state
}
// Show Error
UIUtils.onError('ERROR.LOAD_WALLET_DATA_ERROR')(err);
};
// If state need auth // If state need auth
if (next.data.auth && !wallet.isAuth()) { if (next.data.auth && !wallet.isAuth()) {
event.preventDefault(); event.preventDefault();
options = next.data.minData ? {minData: true} : undefined; options = next.data.minData ? {minData: true} : undefined;
preventStateChange = true; preventStateChange = true;
console.debug("[app] State need auth...");
return csWallet.auth(options) return csWallet.auth(options)
.then(function() { .then(goNextState)
preventStateChange = false; .catch(processError);
return $state.go(next.name, nextParams);
})
.catch(function(err) {
preventStateChange = false;
// If cancel, redirect to home, if no current state
if (err === 'CANCELLED' && !$state.current.name) {
return $state.go('app.home');
}
});
} }
// If state need login // If state need login
...@@ -85,18 +92,10 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngSanitize', ...@@ -85,18 +92,10 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngSanitize',
event.preventDefault(); event.preventDefault();
options = next.data.minData ? {minData: true} : undefined; options = next.data.minData ? {minData: true} : undefined;
preventStateChange = true; preventStateChange = true;
console.debug('[app] State need login...');
return csWallet.login(options) return csWallet.login(options)
.then(function() { .then(goNextState)
preventStateChange = false; .catch(processError);
return $state.go(next.name, nextParams);
})
.catch(function(err) {
preventStateChange = false;
// If cancel, redirect to home, if no current state
if (err === 'CANCELLED' && !$state.current.name) {
return $state.go('app.home');
}
});
} }
// If state need login or auth, make sure to load wallet data // If state need login or auth, make sure to load wallet data
...@@ -106,11 +105,11 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngSanitize', ...@@ -106,11 +105,11 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'ngSanitize',
event.preventDefault(); event.preventDefault();
// Show loading message, when full load // Show loading message, when full load
if (!options || !options.minData) UIUtils.loading.show(); if (!options || !options.minData) UIUtils.loading.show();
console.debug('[app] State load wallet data...');
return wallet.loadData(options) return wallet.loadData(options)
.then(function() { .then(goNextState)
preventStateChange = false; .catch(processError);
return $state.go(next.name, nextParams);
});
} }
} }
}); });
......
/******
* !! WARNING: This is a generated file !!
*
* PLEASE DO NOT MODIFY DIRECTLY
*
* => Changes should be done on file 'app/config.json'.
******/
angular.module("cesium.config", [])
.constant("csConfig", {
"cacheTimeMs": 300000,
"demo": false,
"readonly": false,
"fallbackLanguage": "en",
"rememberMe": true,
"showUDHistory": true,
"timeout": 30000,
"timeWarningExpireMembership": 5184000,
"timeWarningExpire": 7776000,
"minConsensusPeerCount": -1,
"useLocalStorage": true,
"useRelative": false,
"expertMode": true,
"decimalCount": 2,
"httpsMode": false,
"shareBaseUrl": "https://g1-test.cesium.app",
"helptip": {
"enable": false,
"installDocUrl": {
"fr-FR": "https://duniter.fr/wiki/doc/installer/",
"en": "https://duniter.org/wiki/doc/install/"
}
},
"node": {
"host": "g1-test.duniter.org",
"port": 443
},
"fallbackNodes": [
{
"host": "gt.moul.re",
"port": 10902
},
{
"host": "g1-test.duniter.org",
"port": 443
}
],
"developers": [
{
"name": "Benoit Lavenier",
"pubkey": "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE"
},
{
"name": "Cédric Moreau",
"pubkey": "2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ"
},
{
"name": "Kapis",
"pubkey": "24jaf8XhYZyDyUb7hMcy5qsanaHBC11AwPefcCQRBQNA"
},
{
"name": "Matograine",
"pubkey": "CmFKubyqbmJWbhyH2eEPVSSs4H4NeXGDfrETzEnRFtPd"
}
],
"plugins": {
"es": {
"enable": false,
"askEnable": false,
"useRemoteStorage": false,
"host": "g1-test.data.e-is.pro",
"port": 443,
"notifications": {
"txSent": true,
"txReceived": true,
"certSent": true,
"certReceived": true
},
"defaultCountry": "France"
}
},
"version": "1.7.13",
"build": "2024-01-03T17:45:14.686Z",
"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
})
;
\ No newline at end of file
...@@ -15,9 +15,10 @@ angular.module("cesium.config", []) ...@@ -15,9 +15,10 @@ angular.module("cesium.config", [])
"fallbackLanguage": "en", "fallbackLanguage": "en",
"rememberMe": true, "rememberMe": true,
"showUDHistory": true, "showUDHistory": true,
"timeout": 40000, "timeout": 30000,
"timeWarningExpireMembership": 5184000, "timeWarningExpireMembership": 5184000,
"timeWarningExpire": 7776000, "timeWarningExpire": 7776000,
"minConsensusPeerCount": 10,
"keepAuthIdle": 600, "keepAuthIdle": 600,
"useLocalStorage": true, "useLocalStorage": true,
"useRelative": false, "useRelative": false,
...@@ -28,50 +29,61 @@ angular.module("cesium.config", []) ...@@ -28,50 +29,61 @@ angular.module("cesium.config", [])
"helptip": { "helptip": {
"enable": true, "enable": true,
"installDocUrl": { "installDocUrl": {
"fr-FR": "https://duniter.org/fr/miner-des-blocs/installer/", "fr-FR": "https://duniter.fr/wiki/doc/installer/",
"en": "https://duniter.org/en/wiki/duniter/install/" "en": "https://duniter.org/wiki/doc/install/"
} }
}, },
"license": { "license": {
"ca": "license/license_g1-ca",
"de-DE": "license/license_g1-de-DE",
"en": "license/license_g1-en", "en": "license/license_g1-en",
"fr-FR": "license/license_g1-fr-FR", "en-GB": "license/license_g1-en",
"eo-EO": "license/license_g1-eo-EO",
"es-ES": "license/license_g1-es-ES", "es-ES": "license/license_g1-es-ES",
"eo-EO": "license/license_g1-eo-EO" "fr-FR": "license/license_g1-fr-FR",
"it-IT": "license/license_g1-it-IT",
"pt-PT": "license/license_g1-pt-PT"
}, },
"feed": { "feed": {
"jsonFeed": { "jsonFeed": {
"fr-FR": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-fr.json", "ca": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-ca.json",
"en": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-en.json" "de-DE": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-de-DE.json",
"en": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-en.json",
"en-GB": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-en-GB.json",
"eo-EO": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-eo-EO.json",
"es-ES": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-es-ES.json",
"fr-FR": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-fr-FR.json",
"it-IT": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-it-IT.json",
"nl-NL": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-nl-NL.json",
"pt-PT": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-pt-PT.json"
}, },
"maxContentLength": 1300 "maxContentLength": 1300,
}, "maxAgeInMonths": 3,
"node": { "maxCount": 3
"host": "g1.duniter.org",
"port": 443
}, },
"fallbackNodes": [ "fallbackNodes": [
{ {
"host": "g1.cgeek.fr", "host": "g1.e-is.pro",
"port": 443 "port": 443
}, },
{ {
"host": "g1.librelois.fr", "host": "vit.fdn.org",
"port": 443 "port": 443
}, },
{ {
"host": "g1.e-is.pro", "host": "g1.cgeek.fr",
"port": 443 "port": 443
}, },
{ {
"host": "duniter.moul.re", "host": "g1.mithril.re",
"port": 443 "port": 443
}, },
{ {
"host": "g1.presles.fr", "host": "g1.duniter.org",
"port": 443 "port": 443
}, },
{ {
"host": "g1.duniter.org", "host": "g1.le-sou.org",
"port": 443 "port": 443
} }
], ],
...@@ -79,6 +91,18 @@ angular.module("cesium.config", []) ...@@ -79,6 +91,18 @@ angular.module("cesium.config", [])
{ {
"name": "Benoit Lavenier", "name": "Benoit Lavenier",
"pubkey": "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE" "pubkey": "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE"
},
{
"name": "Cédric Moreau",
"pubkey": "2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ"
},
{
"name": "Kapis",
"pubkey": "24jaf8XhYZyDyUb7hMcy5qsanaHBC11AwPefcCQRBQNA"
},
{
"name": "Matograine",
"pubkey": "CmFKubyqbmJWbhyH2eEPVSSs4H4NeXGDfrETzEnRFtPd"
} }
], ],
"plugins": { "plugins": {
...@@ -93,9 +117,25 @@ angular.module("cesium.config", []) ...@@ -93,9 +117,25 @@ angular.module("cesium.config", [])
"host": "g1.data.presles.fr", "host": "g1.data.presles.fr",
"port": 443 "port": 443
}, },
{
"host": "g1.data.le-sou.org",
"port": 443
},
{
"host": "g1.data.brussels.ovh",
"port": 443
},
{
"host": "g1.data.pini.fr",
"port": 443
},
{ {
"host": "g1.data.mithril.re", "host": "g1.data.mithril.re",
"port": 443 "port": 443
},
{
"host": "g1.data.e-is.pro",
"port": 443
} }
], ],
"notifications": { "notifications": {
...@@ -107,8 +147,8 @@ angular.module("cesium.config", []) ...@@ -107,8 +147,8 @@ angular.module("cesium.config", [])
"defaultCountry": "France" "defaultCountry": "France"
} }
}, },
"version": "1.7.0-rc1", "version": "1.7.14",
"build": "2022-05-19T22:57:43.774Z", "build": "2024-07-18T16:14:19.519Z",
"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new" "newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
}) })
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
angular.module('cesium.controllers', [ angular.module('cesium.controllers', [
'cesium.app.controllers', 'cesium.app.controllers',
'cesium.home.controllers', 'cesium.home.controllers',
'cesium.feed.controllers',
'cesium.join.controllers', 'cesium.join.controllers',
'cesium.login.controllers', 'cesium.login.controllers',
'cesium.help.controllers', 'cesium.help.controllers',
...@@ -13,5 +14,4 @@ angular.module('cesium.controllers', [ ...@@ -13,5 +14,4 @@ angular.module('cesium.controllers', [
'cesium.settings.controllers', 'cesium.settings.controllers',
'cesium.network.controllers', 'cesium.network.controllers',
'cesium.blockchain.controllers' 'cesium.blockchain.controllers'
]) ]);
;
...@@ -19,7 +19,7 @@ angular.module('cesium.app.controllers', ['cesium.platform', 'cesium.services']) ...@@ -19,7 +19,7 @@ angular.module('cesium.app.controllers', ['cesium.platform', 'cesium.services'])
cache: false, cache: false,
url: "/lock", url: "/lock",
views: { views: {
'menuContent': { menuContent: {
templateUrl: "templates/common/view_passcode.html", templateUrl: "templates/common/view_passcode.html",
controller: 'PassCodeCtrl' controller: 'PassCodeCtrl'
} }
...@@ -204,8 +204,8 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ ...@@ -204,8 +204,8 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
if (csConfig.httpsMode && $window.location && $window.location.protocol !== 'https:') { if (csConfig.httpsMode && $window.location && $window.location.protocol !== 'https:') {
var href = $window.location.href; var href = $window.location.href;
var hashIndex = href.indexOf('#'); var hashIndex = href.indexOf('#');
var rootPath = (hashIndex != -1) ? href.substr(0, hashIndex) : href; var rootPath = (hashIndex !== -1) ? href.substring(0, hashIndex) : href;
rootPath = 'https' + rootPath.substr(4); rootPath = 'https' + rootPath.substring(4);
href = rootPath + $state.href(state); href = rootPath + $state.href(state);
if (csConfig.httpsModeDebug) { if (csConfig.httpsModeDebug) {
// Debug mode: just log, then continue // Debug mode: just log, then continue
...@@ -403,19 +403,37 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ ...@@ -403,19 +403,37 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
console.info('[app] Trying to parse as uri: ', uri); console.info('[app] Trying to parse as uri: ', uri);
var fromHomeState = $state.current && $state.current.name === 'app.home'; var fromHomeState = $state.current && $state.current.name === 'app.home';
return (!csPlatform.isStarted() ? csPlatform.ready() : $q.when())
// Parse the URI // Parse the URI
return BMA.uri.parse(uri) .then(function() {
return BMA.uri.parse(uri);
})
.then(function(res) { .then(function(res) {
if (!res) throw {message: 'ERROR.UNKNOWN_URI_FORMAT'}; // Continue if (!res) throw {message: 'ERROR.UNKNOWN_URI_FORMAT'}; // Continue
if (res.pubkey) { if (res.pubkey) {
$state.go('app.wot_identity', var action = res.params && (angular.isDefined(res.params.amount) || res.params.comment) ? 'transfer' : undefined;
console.info('[app] Redirecting from URI to identity {{0}} {1} {2}'.format(
res.pubkey.substring(0,8),
action ? ('with action ' + action) : '',
res.params ? JSON.stringify(res.params) : ''
), uri);
// Redirect to an owned wallet
if (!action && (csWallet.isUserPubkey(res.pubkey) || csWallet.children.hasPubkey(res.pubkey))) {
var wallet = csWallet.getByPubkey(res.pubkey);
return $state.go('app.view_wallet_by_id', {id: wallet.id});
}
else {
return $state.go('app.wot_identity',
angular.merge({ angular.merge({
pubkey: res.pubkey, pubkey: res.pubkey,
action: res.params && (res.params.amount || res.params.comment) ? 'transfer' : undefined action: action
}, res.params), }, res.params),
{reload: true}); {reload: true});
} }
}
else if (res.uid) { else if (res.uid) {
return $state.go('app.wot_identity_uid', return $state.go('app.wot_identity_uid',
angular.merge({ angular.merge({
...@@ -454,7 +472,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ ...@@ -454,7 +472,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
reject(err); reject(err);
return; return;
} }
console.error("[home] Error while handle uri {" + uri + "': ", err); console.error("[home] Error while handle uri '{0}'".format(uri), JSON.stringify(err));
return UIUtils.onError(uri)(err); return UIUtils.onError(uri)(err);
}); });
}; };
...@@ -511,6 +529,8 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ ...@@ -511,6 +529,8 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
}; };
$scope.registerProtocolHandlers = function() { $scope.registerProtocolHandlers = function() {
if (csConfig.demo) return; // Skip if demo
var protocols = ['web+june']; var protocols = ['web+june'];
_.each(protocols, function(protocol) { _.each(protocols, function(protocol) {
...@@ -549,19 +569,41 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $ ...@@ -549,19 +569,41 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
var skip = $scope.fullscreen || !UIUtils.screen.isSmall() || !Device.isWeb(); var skip = $scope.fullscreen || !UIUtils.screen.isSmall() || !Device.isWeb();
if (skip) return; if (skip) return;
// Already ask
if (csSettings.data.useFullscreen === false) {
$scope.toggleFullscreen(false);
return;
}
// User already say 'yes' => need o ask again (chrome will avoid changed if no gesture has been done)
//if (csSettings.data.useFullscreen === true) {
// $scope.toggleFullscreen(true);
// return;
//}
return UIUtils.alert.confirm('CONFIRM.FULLSCREEN', undefined, { return UIUtils.alert.confirm('CONFIRM.FULLSCREEN', undefined, {
cancelText: 'COMMON.BTN_NO', cancelText: 'COMMON.BTN_NO',
okText: 'COMMON.BTN_YES' okText: 'COMMON.BTN_YES'
}) })
.then(function(confirm) { .then(function(confirm) {
if (!confirm) return; $scope.toggleFullscreen(confirm);
$scope.toggleFullscreen();
}); });
}; };
$scope.toggleFullscreen = function() { $scope.toggleFullscreen = function(enable, options) {
$scope.fullscreen = !UIUtils.screen.fullscreen.isEnabled(); enable = angular.isDefined(enable) ? enable : !UIUtils.screen.fullscreen.isEnabled();
$scope.fullscreen = enable;
if (enable !== UIUtils.screen.fullscreen.isEnabled()) {
UIUtils.screen.fullscreen.toggleAll(); UIUtils.screen.fullscreen.toggleAll();
}
// Save into settings
if ((csSettings.data.useFullscreen !== enable) && (!options || options.emitEvent !== false)) {
csSettings.data.useFullscreen = enable;
return $timeout(csSettings.store, 2000);
}
}; };
// removeIf(no-device) // removeIf(no-device)
......
...@@ -30,7 +30,7 @@ angular.module('cesium.blockchain.controllers', ['cesium.services']) ...@@ -30,7 +30,7 @@ angular.module('cesium.blockchain.controllers', ['cesium.services'])
}) })
.state('app.server_blockchain', { .state('app.server_blockchain', {
url: "/network/peer/:server/blockchain?ssl&tor", url: "/network/peer/:server/blockchain?ssl&tor&path",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "templates/blockchain/lookup.html", templateUrl: "templates/blockchain/lookup.html",
...@@ -43,7 +43,7 @@ angular.module('cesium.blockchain.controllers', ['cesium.services']) ...@@ -43,7 +43,7 @@ angular.module('cesium.blockchain.controllers', ['cesium.services'])
}) })
.state('app.server_blockchain_lg', { .state('app.server_blockchain_lg', {
url: "/network/peer/:server/blockchain/lg?ssl&tor", url: "/network/peer/:server/blockchain/lg?ssl&tor&path",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "templates/blockchain/lookup_lg.html", templateUrl: "templates/blockchain/lookup_lg.html",
...@@ -73,7 +73,7 @@ angular.module('cesium.blockchain.controllers', ['cesium.services']) ...@@ -73,7 +73,7 @@ angular.module('cesium.blockchain.controllers', ['cesium.services'])
}) })
.state('app.view_server_block_hash', { .state('app.view_server_block_hash', {
url: "/network/peer/:server/block/:number/:hash?ssl&tor", url: "/network/peer/:server/block/:number/:hash?ssl&tor&path",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "templates/blockchain/view_block.html", templateUrl: "templates/blockchain/view_block.html",
...@@ -127,18 +127,23 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho ...@@ -127,18 +127,23 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho
if (state && state.stateParams && state.stateParams.server) { if (state && state.stateParams && state.stateParams.server) {
var useSsl = state.stateParams.ssl == "true"; var useSsl = state.stateParams.ssl == "true";
var useTor = state.stateParams.tor == "true"; var useTor = state.stateParams.tor == "true";
var path = state.stateParams.path || '';
var node = { var node = {
server: state.stateParams.server, server: state.stateParams.server,
host: state.stateParams.server, host: state.stateParams.server,
path: path,
useSsl: useSsl, useSsl: useSsl,
useTor: useTor useTor: useTor
}; };
var serverParts = state.stateParams.server.split(':'); var serverParts = state.stateParams.server.split(':', 2);
if (serverParts.length === 2) { if (serverParts.length === 2) {
node.host = serverParts[0]; node.host = serverParts[0];
node.port = serverParts[1]; node.port = serverParts[1];
} }
else {
node.port = node.port || (node.useSsl ? 443 : 80);
}
if (BMA.node.same(node)) { if (BMA.node.same(node)) {
$scope.node = BMA; $scope.node = BMA;
...@@ -146,8 +151,8 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho ...@@ -146,8 +151,8 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho
else { else {
$scope.node = useTor ? $scope.node = useTor ?
// For TOR, use a web2tor to access the endpoint // For TOR, use a web2tor to access the endpoint
BMA.instance(node.host + ".to", 443, true/*ssl*/, 600000 /*long timeout*/) : BMA.instance(node.host + ".to", 443, node.path, true/*ssl*/, 600000 /*long timeout*/) :
BMA.instance(node.host, node.port, node.useSsl); BMA.instance(node.host, node.port, node.path, node.useSsl);
return $scope.node.blockchain.parameters() return $scope.node.blockchain.parameters()
.then(function(json) { .then(function(json) {
$scope.currency = json.currency; $scope.currency = json.currency;
...@@ -521,18 +526,23 @@ function BlockViewController($scope, $ionicPopover, $state, UIUtils, BMA, csCurr ...@@ -521,18 +526,23 @@ function BlockViewController($scope, $ionicPopover, $state, UIUtils, BMA, csCurr
if (state.stateParams && state.stateParams.server) { if (state.stateParams && state.stateParams.server) {
var useSsl = state.stateParams.ssl == "true"; var useSsl = state.stateParams.ssl == "true";
var useTor = state.stateParams.tor == "true"; var useTor = state.stateParams.tor == "true";
var path = state.stateParams.path || '';
var node = { var node = {
server: state.stateParams.server, server: state.stateParams.server,
host: state.stateParams.server, host: state.stateParams.server,
path: path,
useSsl: useSsl, useSsl: useSsl,
useTor: useTor useTor: useTor
}; };
var serverParts = state.stateParams.server.split(':'); var serverParts = state.stateParams.server.split(':', 2);
if (serverParts.length == 2) { if (serverParts.length == 2) {
node.host = serverParts[0]; node.host = serverParts[0];
node.port = serverParts[1]; node.port = serverParts[1];
} }
else {
node.port = node.port || (node.useSsl ? 443 : 80);
}
if (BMA.node.same(node)) { if (BMA.node.same(node)) {
$scope.node = BMA; $scope.node = BMA;
...@@ -540,8 +550,8 @@ function BlockViewController($scope, $ionicPopover, $state, UIUtils, BMA, csCurr ...@@ -540,8 +550,8 @@ function BlockViewController($scope, $ionicPopover, $state, UIUtils, BMA, csCurr
else { else {
$scope.node = useTor ? $scope.node = useTor ?
// For TOR, use a web2tor to access the endpoint // For TOR, use a web2tor to access the endpoint
BMA.instance(node.host + ".to", 443, true/*ssl*/, 600000 /*long timeout*/) : BMA.instance(node.host + ".to", 443, node.path, true/*ssl*/, 600000 /*long timeout*/) :
BMA.instance(node.host, node.port, node.useSsl); BMA.instance(node.host, node.port, node.path, node.useSsl);
return $scope.node.blockchain.parameters() return $scope.node.blockchain.parameters()
.then(function (json) { .then(function (json) {
$scope.currency = json.currency; $scope.currency = json.currency;
......
angular.module('cesium.feed.controllers', ['cesium.services'])
.controller('FeedCtrl', FeedController)
;
function FeedController($scope, $timeout, $http, $translate, $q, csConfig, csHttp, csCache, csSettings) {
'ngInject';
$scope.search = {
loading: true,
maxCount: (csConfig.feed && csConfig.feed.maxCount) || 3, // 3 month by default
maxContentLength: (csConfig.feed && csConfig.feed.maxContentLength) || 1300,
maxAgeInMonths: (csConfig.feed && csConfig.feed.maxAgeInMonths) || 3, // 3 month by default
minTime: undefined, // unix time
loadCount: 0,
maxLoadCount: 2 // = 1 redirection max
};
$scope.enter = function(e, state) {
// Wait platform to be ready
csSettings.ready()
.then(function() {
return $scope.load();
})
.catch(function() {
// Continue
return null;
})
.then(function(feed) {
$scope.loading = false;
$scope.feed = feed;
var showFeed = feed && feed.items && feed.items.length > 0 || false;
$scope.$parent.toggleFeed(showFeed);
});
};
$scope.$on('$ionicParentView.enter', $scope.enter);
$scope.load = function() {
var feedUrl = csSettings.getFeedUrl();
if (!feedUrl || typeof feedUrl !== 'string') return; // Skip
// Min unix time, to exclude old topics
var maxAgeInMonths = $scope.search.maxAgeInMonths;
var minDate = maxAgeInMonths > 0 ? moment().subtract(maxAgeInMonths, 'month').startOf('day').utc() : undefined;
$scope.search.minTime = minDate && minDate.unix();
// Reset load counter
$scope.search.loadCount = 0;
var now = Date.now();
console.debug("[feed] Loading from {url: {0}, minTime: '{1}'}".format(feedUrl, minDate && minDate.toISOString() || 'all'));
return $scope.loadJsonFeed(feedUrl)
.then(function (feed) {
console.debug('[feed] {0} items loaded in {0}ms'.format(feed && feed.items && feed.items.length || 0, Date.now() - now));
if (!feed || !feed.items) return null; // skip
// Clean title (remove duplicated titles)
$scope.cleanDuplicatedTitles(feed);
return feed;
})
.catch(function(err) {
console.error('[feed] Failed to load.', err);
return null;
});
};
/**
* Load a JSON file, from the given URL, and convert into the JSON Feed format
* @param feedUrl
* @param maxCount
* @returns {*}
*/
$scope.loadJsonFeed = function(feedUrl, maxItemCount) {
var locale = $translate.use();
var minTime = $scope.search.minTime;
maxItemCount = maxItemCount || $scope.search.maxCount;
// Increment load counter (to avoid infinite loop)
$scope.search.loadCount++;
return $scope.getJson(feedUrl)
.then(function(json) {
// Parse JSON from discourse
if ($scope.isJsonDiscourse(json)) {
return $scope.parseJsonDiscourse(feedUrl, json);
}
// Return a copy (to avoid any change in cached data)
return angular.copy(json);
})
.then(function(feed) {
if (!feed || !feed.items && !feed.next_url) return null; // skip
// SKip if incompatible language
if (feed.language && !$scope.isCompatibleLanguage(locale, feed.language)) {
console.debug("[feed] Skip feed item '{0}' - Expected language: '{1}', actual: '{2}'".format(feed.title, locale, feed.language));
return null;
}
// Migrate from old version 1.0 to 1.1
if (feed.version === 'https://jsonfeed.org/version/1') {
if (feed.author && !feed.authors) {
feed.authors = [feed.author];
delete feed.author;
}
(feed.items || []).forEach(function (item) {
if (item.author && !item.authors) {
item.authors = [item.author];
delete item.author;
}
});
feed.version = 'https://jsonfeed.org/version/1.1';
}
feed.items = (feed.items || []).reduce(function (res, item) {
// Skip if empty (missing title and content)
if ($scope.isEmptyFeedItem(item)) return res;
item = $scope.prepareJsonFeedItem(item, feed);
// Skip if too old items
if (minTime > 0 && item.creationTime && (item.creationTime < minTime)) return res;
// Skip if not same language
if (item.language && !$scope.isCompatibleLanguage(locale, item.language)) {
console.debug("[feed] Feed item '{0}' EXCLUDED - expected locale: {1}, actual language: {2}".format(item.title || feed.title, locale, item.language));
return res;
}
return res.concat(item);
}, []);
return feed;
})
.then(function(feed) {
if (!feed) return null; // skip
feed.items = feed.items || [];
// Slice to keep last (more recent) items
if (feed.items.length > maxItemCount) {
feed.items = feed.items.slice(feed.items.length - maxItemCount);
return feed;
}
// Not enough items: try to fetch more
var canFetchMore = feed.next_url && feed.next_url !== feedUrl && $scope.search.loadCount < $scope.search.maxLoadCount;
if (canFetchMore && feed.items.length < maxItemCount) {
console.debug("[feed] Loading from {next_url: '{0}'}".format(feed.next_url));
// Fetch more
return $scope.loadJsonFeed(feed.next_url, maxItemCount - feed.items.length)
.then(function(moreFeed) {
// Append new items
if (moreFeed && moreFeed.items && moreFeed.items.length) {
feed.items = feed.items.concat(moreFeed.items.slice(0, maxItemCount - feed.items.length));
}
return feed;
});
}
return feed;
});
};
/**
* Fetch a JSON file, from a URL. Use a cache (of 1 hour) to avoid to many network request
* @param url
* @returns {*}
*/
$scope.getJson = function(url) {
return $q(function(resolve, reject) {
$http.get(url, {
timeout: csConfig.timeout,
responseType: 'json',
cache: csCache.get('csFeed-', csCache.constants.LONG)
})
.success(resolve)
.error(reject);
});
};
$scope.isEmptyFeedItem = function(item) {
return (!item || (!item.title && !item.content_text && !item.content_html));
};
/**
* Prepare a feed for the template :
* - set 'time' with a unix timestamp
* - set 'content' with the HTML or text content (truncated if too long)
* - fill authors if not exists, using feed authors
* @param item
* @param feed
* @returns {{content}|*}
*/
$scope.prepareJsonFeedItem = function(item, feed) {
if ($scope.isEmptyFeedItem(item)) throw Error('Empty feed item');
var maxContentLength = $scope.search.maxContentLength;
// Convert UTC time
if (item.date_published) {
item.creationTime = moment.utc(item.date_published).unix();
}
if (item.date_modified) {
item.time = moment.utc(item.date_modified).unix();
}
// Convert content to HTML
if (item.content_html) {
item.content = item.content_html;
}
else {
item.content = (item.content_text||'').replace(/\n/g, '<br/>');
}
// Trunc content, if need
if (maxContentLength > 0 && item.content && item.content.length > maxContentLength) {
var endIndex = Math.max(item.content.lastIndexOf(" ", maxContentLength), item.content.lastIndexOf("<", maxContentLength));
item.content = item.content.substring(0, endIndex) + ' (...)';
item.truncated = true;
}
// If author is missing, copy the main author
item.authors = item.authors || feed.authors;
return item;
};
/**
* Prepare feed (e.g. clean duplicated title, when feed URL is a discourse topic, all item will have the same title)
* @param feed
* @returns {*}
*/
$scope.cleanDuplicatedTitles = function(feed) {
if (!feed || !feed.items) return feed;
_.forEach(feed.items, function(item, index) {
if (item.title && index > 0 && (item.title === feed.items[0].title)) {
delete item.title;
}
});
return feed;
};
/**
* Detect this the given JSON is from Discourse
* @param json
* @returns {*}
*/
$scope.isJsonDiscourse = function(json) {
return $scope.isDiscourseCategory(json) || $scope.isDiscourseTopic(json);
};
/**
* Transform a JSON from Discourse into JSON feed
* @param feedUrl
* @param json
* @returns {*}
*/
$scope.parseJsonDiscourse = function(feedUrl, json) {
// Detect if category category
if ($scope.isDiscourseCategory(json)) {
// Convert category to feed
return $scope.parseDiscourseCategory(feedUrl, json);
}
// Convert topic to feed
return $scope.parseDiscourseTopic(feedUrl, json);
};
$scope.isDiscourseCategory = function(category) {
return category && category.topic_list && Array.isArray(category.topic_list.topics) &&
!!category.topic_list.more_topics_url || false;
};
$scope.parseDiscourseCategory = function(url, category, locale) {
// Check is a discourse category
if (!$scope.isDiscourseCategory(category)) throw new Error('Not a discourse category');
locale = locale || $translate.use();
var uri = csHttp.uri.parse(url);
var baseUrl = uri.protocol + '//' + uri.host + (uri.port != 443 && uri.port != 80 ? uri.port : '');
var pageUrl = baseUrl + category.topic_list.more_topics_url.replace(/\?page=[0-9]+/, '');
var feed = {
version: "https://jsonfeed.org/version/1.1", // fixed value
home_page_url: pageUrl,
feed_url: url,
title: 'HOME.FEEDS_TITLE'
};
return $q.all(
category.topic_list.topics.reduce(function(res, topic) {
if (!topic.pinned || !topic.visible) return res; // Skip not pinned topic
// Exclude category description (=tag 'about-category')
if (topic.tags && topic.tags.includes('about-category')) return res;
// Detect language, from the title. Then skip if not compatible with expected locale
var topicLanguage = $scope.getLanguageFromTitle(topic.title);
if (!$scope.isCompatibleLanguage(locale, topicLanguage)) return res;
// Compute the URL to load the topic
var topicUrl = [baseUrl, 't', topic.slug, topic.id].join('/') + '.json';
// Load topic JSON
return res.concat($scope.getJson(topicUrl)
.catch(function(err) {
console.error("[feed] Failed to load discourse topic from '{}'".format(topicUrl), err);
return null; // continue
})
);
}, []))
.then(function(topics) {
feed.items = topics.reduce(function(res, topic) {
if (!$scope.isDiscourseTopic(topic)) return res; // Not a topic: skip
var feedTopic = $scope.parseDiscourseTopic(baseUrl, topic, feed);
if (!feedTopic.items || !feedTopic.items.length) return res; // Topic is empty: skip
return res.concat(feedTopic.items[0]);
}, []);
return feed;
});
};
$scope.isDiscourseTopic = function(topic) {
return topic && topic.title && topic.post_stream && Array.isArray(topic.post_stream.posts);
};
$scope.parseDiscourseTopic = function(url, topic, feed) {
// Make sure this is a valid topic
if (!$scope.isDiscourseTopic(topic)) throw new Error('Not a discourse topic');
var uri = csHttp.uri.parse(url);
var baseUrl = uri.protocol + '//' + uri.host + (uri.port != 443 && uri.port != 80 ? uri.port : '');
// Clean title (e.g. remove '(fr)' or '(en)' or '(es)')
// Prefer unicode title (e.g. emoji are replaced)
var title = $scope.cleanTitle(topic.unicode_title || topic.title);
var language = $scope.getLanguageFromTitle(topic.title);
// Prepare root feed, if not yet exists
feed = feed || {
version: "https://jsonfeed.org/version/1.1", // fixed value
home_page_url: [baseUrl, 't', topic.slug, topic.id].join('/'),
feed_url: url,
title: 'HOME.FEEDS_TITLE',
language: language
};
feed.language = feed.language || language;
feed.items = topic.post_stream.posts.reduce(function(res, post) {
if (!post.cooked || post.cooked.trim() === '') return res; // Skip if empty
// SKip if hidden, or deleted post
if (post.hidden || post.deleted_at) return res;
var author = {
name: post.display_username,
url: [baseUrl, 'u', post.username].join('/'),
avatar: post.avatar_template ? (baseUrl + post.avatar_template.replace('{size}', '60')) : undefined
};
// Try to resolve author pubkey, to replace author url by a link to wot identity
var developer = _.find(csConfig.developers || [], function(developer) {
return developer.name && (
(post.display_username && developer.name.toLowerCase() === post.display_username.toLowerCase()) ||
(post.username && developer.name.toLowerCase() === post.username.toLowerCase())
);
});
if (developer && developer.pubkey) {
author.url = '@' + developer.pubkey;
}
// Fill parent feed defaults
feed.authors = feed.authors || [author];
return res.concat({
id: post.id,
url: [baseUrl, 't', post.topic_slug, post.topic_id, post.post_number].join('/'),
title: title,
date_published: post.created_at,
date_modified: post.updated_at,
content_html: post.cooked,
authors: [author],
language: language,
tags: post.tags || topic.tags
});
}, []);
return feed;
};
/**
* Clean a title : remove locale string at the end (e.g. '(fr)' or '(en)' or '(es)')
* @param title
*/
$scope.cleanTitle = function(title) {
if (!title) return undefined;
return title.replace(/\s\([a-z]{2}(:?-[A-Z]{2})?\)$/, '');
};
/**
* Clean a title : remove locale string at the end (e.g. '(fr)' or '(en)' or '(es)')
* @param title
*/
$scope.getLanguageFromTitle = function(title) {
if (!title) return undefined;
var matches = /\s\(([a-z]{2}(:?-[A-Z]{2})?)\)$/.exec(title);
return matches && matches[1];
};
$scope.isCompatibleLanguage = function(expectedLocale, language) {
if (!expectedLocale || !language || expectedLocale === language) return true;
// Extract the language from the locale, then compare
// E.g. 'fr-FR' => 'fr'
var expectedLanguage = expectedLocale.split('-', 2)[0];
return expectedLanguage.toLowerCase() === language.toLowerCase();
};
csSettings.api.locale.on.changed($scope, function() {
if ($scope.loading) return;
console.debug("[feed] Locale changed. Reload feed...");
$scope.enter();
});
}
...@@ -7,7 +7,7 @@ angular.module('cesium.home.controllers', ['cesium.platform', 'cesium.services'] ...@@ -7,7 +7,7 @@ angular.module('cesium.home.controllers', ['cesium.platform', 'cesium.services']
.state('app.home', { .state('app.home', {
url: "/home?error&uri", url: "/home?error&uri&login",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "templates/home/home.html", templateUrl: "templates/home/home.html",
...@@ -24,27 +24,27 @@ angular.module('cesium.home.controllers', ['cesium.platform', 'cesium.services'] ...@@ -24,27 +24,27 @@ angular.module('cesium.home.controllers', ['cesium.platform', 'cesium.services']
.controller('HomeCtrl', HomeController) .controller('HomeCtrl', HomeController)
; ;
function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $http, $q, $location, function HomeController($scope, $state, $timeout, $interval, $ionicHistory, $translate, $http, $q, $location,
UIUtils, BMA, csConfig, csCache, csPlatform, csCurrency, csSettings) { UIUtils, BMA, Device, csConfig, csHttp, csCache, csPlatform, csNetwork, csCurrency, csSettings, csWallet) {
'ngInject'; 'ngInject';
$scope.loading = true; $scope.loading = true;
$scope.loadingPct = 0;
$scope.loadingMessage = '';
$scope.locales = angular.copy(csSettings.locales); $scope.locales = angular.copy(csSettings.locales);
$scope.smallscreen = UIUtils.screen.isSmall(); $scope.smallscreen = UIUtils.screen.isSmall();
$scope.showInstallHelp = false; $scope.showInstallHelp = false;
$scope.showFeed = false;
$scope.enter = function(e, state) { $scope.enter = function(e, state) {
if (ionic.Platform.isIOS()) { if (ionic.Platform.isIOS() && window.StatusBar) {
if(window.StatusBar) {
// needed to fix Xcode 9 / iOS 11 issue with blank space at bottom of webview // needed to fix Xcode 9 / iOS 11 issue with blank space at bottom of webview
// https://github.com/meteor/meteor/issues/9041 // https://github.com/meteor/meteor/issues/9041
StatusBar.overlaysWebView(false); StatusBar.overlaysWebView(false);
StatusBar.overlaysWebView(true); StatusBar.overlaysWebView(true);
} }
}
if (state && state.stateParams && state.stateParams.uri) { if (state && state.stateParams && state.stateParams.uri) {
return $scope.handleUri(state.stateParams.uri) return $scope.handleUri(state.stateParams.uri)
.then(function() { .then(function() {
$scope.loading = false; $scope.loading = false;
...@@ -55,18 +55,52 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht ...@@ -55,18 +55,52 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
$scope.node = csCurrency.data.node; $scope.node = csCurrency.data.node;
$scope.loading = false; $scope.loading = false;
$scope.cleanLocationHref(state); $scope.cleanLocationHref(state);
return $q.when();
} }
else { else {
// Loading progress percent
var startTime = Date.now();
var interval = $interval(function(){
var duration = Date.now() - startTime;
var timeout = Math.max(csNetwork.data.timeout, duration);
// Waiting to start
if (!$scope.loadingMessage) {
$scope.loadingPct = Math.min($scope.loadingPct + 0.5, 98);
}
else if (duration < timeout) {
var loadingPct = Math.min(duration / timeout * 100, 98);
if (loadingPct > $scope.loadingPct) {
$scope.loadingPct = loadingPct;
}
}
$scope.$broadcast('$$rebind::loading'); // force rebind loading
}, 200);
var hasLoginParam = state && state.stateParams && state.stateParams.login || false;
// Wait platform to be ready // Wait platform to be ready
csPlatform.ready() return csPlatform.ready()
.then(function() {
$scope.loading = false;
$scope.loadFeeds();
})
.catch(function(err) { .catch(function(err) {
$scope.node = csCurrency.data.node; $scope.node = csCurrency.data.node;
$scope.loading = false;
$scope.error = err; $scope.error = err;
})
.then(function() {
// Stop progression
$interval.cancel(interval);
// Mark as loaded
$scope.loading = false;
$scope.loadingMessage = '';
$scope.loadingPct = 100;
$scope.$broadcast('$$rebind::loading'); // force rebind loading
$scope.$broadcast('$$rebind::feed'); // force rebind feed
// Open the login modal
if (hasLoginParam && !csWallet.isLogin() && !$scope.error) {
$scope.cleanLocationHref(state);
return csWallet.login();
}
}); });
} }
}; };
...@@ -74,61 +108,13 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht ...@@ -74,61 +108,13 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
$scope.reload = function() { $scope.reload = function() {
$scope.loading = true; $scope.loading = true;
$scope.loadingPct = 0;
$scope.loadingMessage = '';
delete $scope.error; delete $scope.error;
$timeout($scope.enter, 200); $timeout($scope.enter, 200);
}; };
$scope.loadFeeds = function() {
var feedUrl = csSettings.getFeedUrl();
if (!feedUrl || typeof feedUrl !== 'string') return; // Skip
var maxContentLength = (csConfig.feed && csConfig.feed.maxContentLength) || 650;
var now = Date.now();
console.debug("[home] Loading feeds from {0}...".format(feedUrl));
$http.get(feedUrl, {responseType: 'json', cache: csCache.get(null, csCache.constants.LONG)})
.success(function(feed) {
console.debug('[home] Feeds loaded in {0}ms'.format(Date.now()-now));
if (!feed || !feed.items || !feed.items.length) return; // skip if empty
feed.items = feed.items.reduce(function(res, item) {
if (!item || (!item.title && !item.content_text && !item.content_html)) return res; // Skip
// Convert UTC time
if (item.date_published) {
item.time = moment.utc(item.date_published).unix();
}
// Convert content to HTML
if (item.content_html) {
item.content = item.content_html;
}
else {
item.content = (item.content_text||'').replace(/\n/g, '<br/>');
}
// Trunc content, if need
if (maxContentLength !== -1 && item.content && item.content.length > maxContentLength) {
var endIndex = Math.max(item.content.lastIndexOf(" ", maxContentLength), item.content.lastIndexOf("<", maxContentLength));
item.content = item.content.substr(0, endIndex) + ' (...)';
item.truncated = true;
}
// If author is missing, copy the main author
item.author = item.author || feed.author;
return res.concat(item);
}, []);
$scope.feed = feed;
})
.error(function(data, status) {
console.error('[home] Failed to load feeds.');
$scope.feed = null;
});
};
/** /**
* Catch click for quick fix * Catch click for quick fix
* @param action * @param action
...@@ -147,7 +133,14 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht ...@@ -147,7 +133,14 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
$scope.hideLocalesPopover(); $scope.hideLocalesPopover();
csSettings.data.locale = _.findWhere($scope.locales, {id: langKey}); csSettings.data.locale = _.findWhere($scope.locales, {id: langKey});
csSettings.store(); csSettings.store();
$scope.loadFeeds(); };
$scope.toggleFeed = function(show) {
$scope.showFeed = (show !== undefined) ? show : !$scope.showFeed;
if (!this.loading) {
$scope.$broadcast('$$rebind::feed'); // force rebind feed
}
}; };
/* -- show/hide locales popup -- */ /* -- show/hide locales popup -- */
...@@ -172,10 +165,13 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht ...@@ -172,10 +165,13 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
// remove '?uri&error' from the location URI, and inside history // remove '?uri&error' from the location URI, and inside history
$scope.cleanLocationHref = function(state) { $scope.cleanLocationHref = function(state) {
state = state || {stateName: 'app.home'};
state.stateParams = state.stateParams || {};
if (state && state.stateParams) { if (state && state.stateParams) {
var stateParams = angular.copy(state.stateParams); var stateParams = angular.copy(state.stateParams);
delete stateParams.uri; delete stateParams.uri;
delete stateParams.error; delete stateParams.error;
delete stateParams.login;
$location.search(stateParams).replace(); $location.search(stateParams).replace();
...@@ -187,12 +183,32 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht ...@@ -187,12 +183,32 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
}); });
return $state.go(state.stateName, stateParams, { return $state.go(state.stateName, stateParams, {
reload: false, reload: false,
inherit: true, inherit: false,
notify: false notify: false
}); });
} }
}; };
// Listen platform messages
csPlatform.api.start.on.message($scope, function(message) {
$scope.loadingMessage = message;
});
// Listen network offline/online
Device.api.network.on.offline($scope, function() {
csPlatform.stop();
$scope.loadingMessage = '';
$scope.loading = false;
$scope.node = csCurrency.data.node;
$scope.error = true;
});
Device.api.network.on.online($scope, function() {
if (!$scope.loading && $scope.error) {
delete $scope.error;
$scope.reload();
}
});
// For DEV ONLY // For DEV ONLY
/*$timeout(function() { /*$timeout(function() {
$scope.loginAndGo(); $scope.loginAndGo();
......
...@@ -396,7 +396,7 @@ function JoinModalController($scope, $state, $interval, $q, $timeout, Device, UI ...@@ -396,7 +396,7 @@ function JoinModalController($scope, $state, $interval, $q, $timeout, Device, UI
csSettings.data.wallet.alertIfUnusedWallet = false; // do not alert if empty csSettings.data.wallet.alertIfUnusedWallet = false; // do not alert if empty
var needSelf = angular.isUndefined(parameters.uid) || angular.isUndefined(parameters.blockUid) || var needSelf = angular.isUndefined(parameters.uid) || angular.isUndefined(parameters.blockUid) ||
(parameters.uid.toUpperCase() !== $scope.formData.pseudo.toUpperCase()); (parameters.uid !== $scope.formData.pseudo);
if (!needSelf) { if (!needSelf) {
wallet.setSelf(parameters.uid, parameters.blockUid); wallet.setSelf(parameters.uid, parameters.blockUid);
} }
...@@ -518,11 +518,11 @@ function JoinModalController($scope, $state, $interval, $q, $timeout, Device, UI ...@@ -518,11 +518,11 @@ function JoinModalController($scope, $state, $interval, $q, $timeout, Device, UI
return; return;
} }
var uid = $scope.formData.pseudo.toUpperCase(); var uid = $scope.formData.pseudo;
$scope.formData.computing=true; $scope.formData.computing=true;
// Same has given uid + self block: skip control // Same has given uid + self block: skip control
if (parameters.uid && uid === parameters.uid.toUpperCase()) { if (parameters.uid && uid === parameters.uid) {
$scope.formData.computing=false; $scope.formData.computing=false;
$scope.uiAlreadyUsed = false; $scope.uiAlreadyUsed = false;
return; return;
...@@ -533,7 +533,7 @@ function JoinModalController($scope, $state, $interval, $q, $timeout, Device, UI ...@@ -533,7 +533,7 @@ function JoinModalController($scope, $state, $interval, $q, $timeout, Device, UI
.then(function(res) { .then(function(res) {
$scope.uiAlreadyUsed = (res.results || []).some(function(pub){ $scope.uiAlreadyUsed = (res.results || []).some(function(pub){
return (pub.uids || []).some(function(idty) { return (pub.uids || []).some(function(idty) {
return (idty.uid.toUpperCase() === uid); // same Uid return (idty.uid === uid); // same Uid
}); });
}); });
$scope.formData.computing=false; $scope.formData.computing=false;
......
angular.module('cesium.login.controllers', ['cesium.services']) angular.module('cesium.login.controllers', ['cesium.services'])
.config(function($stateProvider) {
'ngInject';
$stateProvider
.state('app.login', {
url: "/login",
views: {
'menuContent': {
templateUrl: "templates/home/home.html",
controller: 'LoginCtrl'
}
}
})
;
})
.controller('LoginCtrl', LoginController)
.controller('LoginModalCtrl', LoginModalController) .controller('LoginModalCtrl', LoginModalController)
.controller('AuthCtrl', AuthController) .controller('AuthCtrl', AuthController)
...@@ -26,23 +8,6 @@ angular.module('cesium.login.controllers', ['cesium.services']) ...@@ -26,23 +8,6 @@ angular.module('cesium.login.controllers', ['cesium.services'])
; ;
function LoginController($scope, $timeout, $controller, csWallet) {
'ngInject';
// Initialize the super class and extend it.
angular.extend(this, $controller('HomeCtrl', {$scope: $scope}));
$scope.showLoginModal = function() {
if ($scope.loading) return $timeout($scope.showLoginModal, 500); // recursive call
if (!csWallet.isLogin() && !$scope.error) {
return $timeout(csWallet.login, 300);
}
};
$scope.$on('$ionicView.enter', $scope.showLoginModal);
}
function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, CryptoUtils, csCrypto, ionicReady, function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, CryptoUtils, csCrypto, ionicReady,
UIUtils, BMA, Modals, csConfig, csSettings, Device, parameters) { UIUtils, BMA, Modals, csConfig, csSettings, Device, parameters) {
'ngInject'; 'ngInject';
...@@ -57,6 +22,8 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp ...@@ -57,6 +22,8 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp
$scope.computing = false; $scope.computing = false;
$scope.pubkey = null; $scope.pubkey = null;
$scope.formData = {}; $scope.formData = {};
$scope.showSalt = false;
$scope.showPassword = false;
$scope.showPubkey = false; $scope.showPubkey = false;
$scope.showComputePubkeyButton = false; $scope.showComputePubkeyButton = false;
$scope.autoComputePubkey = false; $scope.autoComputePubkey = false;
...@@ -136,10 +103,13 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp ...@@ -136,10 +103,13 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp
if (!$scope.formData.username || !$scope.formData.password) return; if (!$scope.formData.username || !$scope.formData.password) return;
var scryptPrams = $scope.formData.scrypt && $scope.formData.scrypt.params; var scryptPrams = $scope.formData.scrypt && $scope.formData.scrypt.params;
UIUtils.loading.show(); UIUtils.loading.show();
promise = CryptoUtils.scryptKeypair($scope.formData.username, $scope.formData.password, scryptPrams) promise = csCrypto.ready()
.then(function() {
return csCrypto.scrypt.keypair($scope.formData.username, $scope.formData.password, scryptPrams);
})
.then(function(keypair) { .then(function(keypair) {
if (!keypair) return UIUtils.loading.hide(10); if (!keypair) return UIUtils.loading.hide(10);
var pubkey = CryptoUtils.util.encode_base58(keypair.signPk); var pubkey = csCrypto.util.encode_base58(keypair.signPk);
// Check pubkey // Check pubkey
if (parameters.expectedPubkey && parameters.expectedPubkey != pubkey) { if (parameters.expectedPubkey && parameters.expectedPubkey != pubkey) {
$scope.pubkey = pubkey; $scope.pubkey = pubkey;
...@@ -175,7 +145,7 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp ...@@ -175,7 +145,7 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp
}) })
.then(function(keypair) { .then(function(keypair) {
if (!keypair) return UIUtils.loading.hide(10); if (!keypair) return UIUtils.loading.hide(10);
var pubkey = CryptoUtils.util.encode_base58(keypair.signPk); var pubkey = csCrypto.util.encode_base58(keypair.signPk);
// Check pubkey // Check pubkey
if (parameters.expectedPubkey && parameters.expectedPubkey != pubkey) { if (parameters.expectedPubkey && parameters.expectedPubkey != pubkey) {
...@@ -313,15 +283,18 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp ...@@ -313,15 +283,18 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp
var salt = $scope.formData.username; var salt = $scope.formData.username;
var pwd = $scope.formData.password; var pwd = $scope.formData.password;
var scryptPrams = $scope.formData.scrypt && $scope.formData.scrypt.params; var scryptPrams = $scope.formData.scrypt && $scope.formData.scrypt.params;
return CryptoUtils.scryptSignPk(salt, pwd, scryptPrams) return csCrypto.ready()
.then(function (signPk) { .then(function() {
return csCrypto.scrypt.pubkey(salt, pwd, scryptPrams);
})
.then(function (pubkey) {
// If model has changed before the response, then retry // If model has changed before the response, then retry
if (salt !== $scope.formData.username || pwd !== $scope.formData.password) { if (salt !== $scope.formData.username || pwd !== $scope.formData.password) {
return $scope.computePubkey(); return $scope.computePubkey();
} }
$scope.pubkey = CryptoUtils.util.encode_base58(signPk); $scope.pubkey = pubkey;
if ($scope.expectedPubkey && $scope.expectedPubkey != $scope.pubkey) { if ($scope.expectedPubkey && $scope.expectedPubkey != $scope.pubkey) {
$scope.pubkeyError = true; $scope.pubkeyError = true;
} }
...@@ -356,6 +329,11 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp ...@@ -356,6 +329,11 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, Cryp
return Modals.showHelp(parameters); return Modals.showHelp(parameters);
}; };
$scope.toggleShowSalt = function() {
console.debug('[login] Toggle showSalt');
$scope.showSalt = !$scope.showSalt;
};
$scope.doScan = function() { $scope.doScan = function() {
if ($scope.computing) return; if ($scope.computing) return;
......
...@@ -21,7 +21,7 @@ angular.module('cesium.network.controllers', ['cesium.services']) ...@@ -21,7 +21,7 @@ angular.module('cesium.network.controllers', ['cesium.services'])
}) })
.state('app.view_peer', { .state('app.view_peer', {
url: "/network/peer/:server?ssl&tor", url: "/network/peer/:server?ssl&tor&path",
cache: false, cache: false,
views: { views: {
'menuContent': { 'menuContent': {
...@@ -48,12 +48,13 @@ angular.module('cesium.network.controllers', ['cesium.services']) ...@@ -48,12 +48,13 @@ angular.module('cesium.network.controllers', ['cesium.services'])
; ;
function NetworkLookupController($scope, $state, $location, $ionicPopover, $window, $translate, function NetworkLookupController($scope, $state, $location, $ionicPopover, $window, $translate,
BMA, UIUtils, csConfig, csSettings, csCurrency, csNetwork, csWot) { BMA, Device, UIUtils, csConfig, csSettings, csCurrency, csNetwork, csWot) {
'ngInject'; 'ngInject';
$scope.networkStarted = false; $scope.networkStarted = false;
$scope.ionItemClass = ''; $scope.ionItemClass = '';
$scope.expertMode = csSettings.data.expertMode && !UIUtils.screen.isSmall(); $scope.expertMode = csSettings.data.expertMode && !UIUtils.screen.isSmall();
$scope.timeout = csSettings.data.timeout;
$scope.isHttps = ($window.location.protocol === 'https:'); $scope.isHttps = ($window.location.protocol === 'https:');
$scope.search = { $scope.search = {
text: '', text: '',
...@@ -94,7 +95,7 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win ...@@ -94,7 +95,7 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win
if (currency) { if (currency) {
var isDefaultNode = BMA.node.same(currency.node); var isDefaultNode = BMA.node.same(currency.node);
$scope.node = isDefaultNode ? BMA : $scope.node = isDefaultNode ? BMA :
BMA.instance(currency.node.host, currency.node.port); BMA.instance(currency.node.host, currency.node.port, currency.node.path || '');
if (state && state.stateParams) { if (state && state.stateParams) {
if (state.stateParams.type && ['mirror', 'member', 'offline'].indexOf(state.stateParams.type) != -1) { if (state.stateParams.type && ['mirror', 'member', 'offline'].indexOf(state.stateParams.type) != -1) {
$scope.search.type = state.stateParams.type; $scope.search.type = state.stateParams.type;
...@@ -148,8 +149,8 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win ...@@ -148,8 +149,8 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win
asc : $scope.search.asc asc : $scope.search.asc
}, },
expertMode: $scope.expertMode, expertMode: $scope.expertMode,
// larger timeout when on expert mode timeout: angular.isDefined($scope.timeout) ? $scope.timeout : Device.network.timeout(),
timeout: csConfig.timeout && ($scope.expertMode ? (csConfig.timeout / 10) : (csConfig.timeout / 100)) withSandboxes: $scope.expertMode && Device.isDesktop() // SKip sandboxes if mobile
}; };
return options; return options;
}; };
...@@ -157,29 +158,52 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win ...@@ -157,29 +158,52 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win
$scope.load = function() { $scope.load = function() {
if ($scope.search.loading){ if ($scope.search.loading){
$scope.updating = false;
// Start network scan // Start network scan
csNetwork.start($scope.node, $scope.computeOptions()); csNetwork.start($scope.node, $scope.computeOptions())
.then(function() {
$scope.onDataChanged();
});
// Catch event on new peers // Catch event on new peers
$scope.refreshing = false;
$scope.listeners.push( $scope.listeners.push(
csNetwork.api.data.on.changed($scope, function(data) { csNetwork.api.data.on.changed($scope, function(data) {
if (!$scope.refreshing) { $scope.onDataChanged(data);
$scope.refreshing = true; }));
}
// Show help tip
$scope.showHelpTip();
};
$scope.onDataChanged = function(data) {
data = csNetwork.data || data;
if (!data || $scope.updating /*|| !$scope.networkStarted*/) return; // Skip if no data, or already updating
var now = Date.now();
console.debug("[peers] Fetching name + avatar, on {0} peers...".format(data.peers && data.peers.length || 0));
// Mark as updating
$scope.updating = true;
// Add name+avatar to peers
csWot.extendAll(data.peers) csWot.extendAll(data.peers)
.then(function() { .then(function() {
console.debug("[peers] Fetching name + avatar on peers [OK] in {0}ms".format(Date.now() - now));
// Avoid to refresh if view has been leaving // Avoid to refresh if view has been leaving
if ($scope.networkStarted) { if ($scope.networkStarted) {
$scope.updateView(data); $scope.updateView(data);
} }
$scope.refreshing = false; })
.catch(function(err) {
console.error(err);
// Continue
})
.then(function() {
$scope.updating = false;
}); });
}
}));
}
// Show help tip
$scope.showHelpTip();
}; };
$scope.updateView = function(data) { $scope.updateView = function(data) {
...@@ -205,7 +229,7 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win ...@@ -205,7 +229,7 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win
$scope.sort = function() { $scope.sort = function() {
$scope.search.loading = true; $scope.search.loading = true;
$scope.refreshing = true; $scope.updating = true;
csNetwork.sort($scope.computeOptions()); csNetwork.sort($scope.computeOptions());
$scope.updateView(csNetwork.data); $scope.updateView(csNetwork.data);
}; };
...@@ -272,6 +296,9 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win ...@@ -272,6 +296,9 @@ function NetworkLookupController($scope, $state, $location, $ionicPopover, $win
if (peer.isTor()) { if (peer.isTor()) {
stateParams.tor = true; stateParams.tor = true;
} }
if (peer.getPath()) {
stateParams.path = peer.getPath();
}
$state.go('app.view_peer', stateParams); $state.go('app.view_peer', stateParams);
}; };
...@@ -399,10 +426,13 @@ function NetworkLookupModalController($scope, $controller, parameters) { ...@@ -399,10 +426,13 @@ function NetworkLookupModalController($scope, $controller, parameters) {
$scope.search.ssl = angular.isDefined(parameters.ssl) ? parameters.ssl : $scope.search.ssl; $scope.search.ssl = angular.isDefined(parameters.ssl) ? parameters.ssl : $scope.search.ssl;
$scope.search.ws2p = angular.isDefined(parameters.ws2p) ? parameters.ws2p : $scope.search.ws2p; $scope.search.ws2p = angular.isDefined(parameters.ws2p) ? parameters.ws2p : $scope.search.ws2p;
$scope.expertMode = angular.isDefined(parameters.expertMode) ? parameters.expertMode : $scope.expertMode; $scope.expertMode = angular.isDefined(parameters.expertMode) ? parameters.expertMode : $scope.expertMode;
$scope.timeout = angular.isDefined(parameters.timeout) ? parameters.timeout : $scope.timeout;
$scope.ionItemClass = parameters.ionItemClass || 'item-border-large'; $scope.ionItemClass = parameters.ionItemClass || 'item-border-large';
$scope.enableLocationHref = false; $scope.enableLocationHref = false;
$scope.helptipPrefix = ''; $scope.helptipPrefix = '';
//$scope.compactMode = false; // Always false, because no toggle button in the modal
$scope.selectPeer = function(peer) { $scope.selectPeer = function(peer) {
$scope.closeModal(peer); $scope.closeModal(peer);
}; };
...@@ -502,7 +532,7 @@ function PeerInfoPopoverController($scope, $q, csSettings, csCurrency, csHttp, B ...@@ -502,7 +532,7 @@ function PeerInfoPopoverController($scope, $q, csSettings, csCurrency, csHttp, B
]) ])
.then(function() { .then(function() {
// Compare, to check if newer // Compare, to check if newer
if ($scope.formData.latestRelease && $scope.formData.software == 'duniter') { if ($scope.formData.latestRelease && $scope.formData.software === 'duniter') {
var compare = csHttp.version.compare($scope.formData.version, $scope.formData.latestRelease.version); var compare = csHttp.version.compare($scope.formData.version, $scope.formData.latestRelease.version);
$scope.formData.isPreRelease = compare > 0; $scope.formData.isPreRelease = compare > 0;
$scope.formData.hasNewRelease = compare < 0; $scope.formData.hasNewRelease = compare < 0;
...@@ -550,10 +580,11 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) { ...@@ -550,10 +580,11 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) {
$scope.$on('$ionicView.enter', function(e, state) { $scope.$on('$ionicView.enter', function(e, state) {
var isDefaultNode = !state.stateParams || !state.stateParams.server; var isDefaultNode = !state.stateParams || !state.stateParams.server;
var server = state.stateParams && state.stateParams.server || BMA.server; var server = state.stateParams && state.stateParams.server || BMA.server;
var path = state.stateParams && state.stateParams.path || (isDefaultNode ? BMA.path : '');
var useSsl = state.stateParams && state.stateParams.ssl == "true" || (isDefaultNode ? BMA.useSsl : false); var useSsl = state.stateParams && state.stateParams.ssl == "true" || (isDefaultNode ? BMA.useSsl : false);
var useTor = state.stateParams.tor == "true" || (isDefaultNode ? BMA.useTor : false); var useTor = state.stateParams.tor == "true" || (isDefaultNode ? BMA.useTor : false);
return $scope.load(server, useSsl, useTor) return $scope.load(server, path, useSsl, useTor)
.then(function() { .then(function() {
return $scope.$broadcast('$csExtension.enter', e, state); return $scope.$broadcast('$csExtension.enter', e, state);
}) })
...@@ -565,24 +596,31 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) { ...@@ -565,24 +596,31 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) {
}); });
}); });
$scope.load = function(server, useSsl, useTor) { $scope.load = function(server, path, useSsl, useTor) {
var port, host;
var serverParts = server.split(':', 2);
if (serverParts.length === 2) {
host = serverParts[0];
port = serverParts[1];
}
else {
host = server;
port = useSsl ? 443 : 80;
}
var node = { var node = {
server: server, server: server,
host: server, host: host,
port: port,
path: path,
useSsl: useSsl, useSsl: useSsl,
useTor: useTor useTor: useTor
}; };
var serverParts = server.split(':');
if (serverParts.length === 2) {
node.host = serverParts[0];
node.port = serverParts[1];
}
angular.merge($scope.node, angular.merge($scope.node,
useTor ? useTor ?
// For TOR, use a web2tor to access the endpoint // For TOR, use a web2tor to access the endpoint
BMA.lightInstance(node.host + ".to", 443, true/*ssl*/, 60000 /*long timeout*/) : BMA.lightInstance(node.host + ".to", 443, node.path, true/*ssl*/, 60000 /*long timeout*/) :
BMA.lightInstance(node.host, node.port, node.useSsl), BMA.lightInstance(node.host, node.port, node.path, node.useSsl),
node); node);
$scope.isReachable = !$scope.isHttps || useSsl; $scope.isReachable = !$scope.isHttps || useSsl;
...@@ -593,10 +631,13 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) { ...@@ -593,10 +631,13 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) {
// find the current peer // find the current peer
var peers = (res && res.peers || []).reduce(function(res, json) { var peers = (res && res.peers || []).reduce(function(res, json) {
var peer = new Peer(json); var peer = new Peer(json);
return (peer.getEndpoints('BASIC_MERKLED_API') || []).reduce(function(res, ep) { return (peer.getEndpoints('BASIC_MERKLED_API') || [])
.concat((peer.getEndpoints('BMAS') || []))
.reduce(function(res, ep) {
var bma = BMA.node.parseEndPoint(ep); var bma = BMA.node.parseEndPoint(ep);
if((bma.dns === node.host || bma.ipv4 === node.host || bma.ipv6 === node.host) && ( if ((bma.dns === node.host || bma.ipv4 === node.host || bma.ipv6 === node.host) &&
bma.port == node.port)) { (bma.port == node.port) &&
(bma.path == node.path)) {
peer.bma = bma; peer.bma = bma;
return res.concat(peer); return res.concat(peer);
} }
...@@ -682,6 +723,9 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) { ...@@ -682,6 +723,9 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) {
if (peer.isTor()) { if (peer.isTor()) {
stateParams.tor = true; stateParams.tor = true;
} }
if (peer.getPath()) {
stateParams.path = peer.getPath();
}
$state.go('app.view_peer', stateParams); $state.go('app.view_peer', stateParams);
}; };
......
...@@ -20,7 +20,7 @@ angular.module('cesium.settings.controllers', ['cesium.services', 'cesium.curren ...@@ -20,7 +20,7 @@ angular.module('cesium.settings.controllers', ['cesium.services', 'cesium.curren
.controller('SettingsCtrl', SettingsController) .controller('SettingsCtrl', SettingsController)
; ;
function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $timeout, $translate, $ionicPopover, function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $timeout, $translate, $ionicPopover, $ionicScrollDelegate,
UIUtils, Modals, BMA, csHttp, csConfig, csCurrency, csSettings, csPlatform) { UIUtils, Modals, BMA, csHttp, csConfig, csCurrency, csSettings, csPlatform) {
'ngInject'; 'ngInject';
...@@ -29,8 +29,12 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -29,8 +29,12 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
$scope.loading = true; $scope.loading = true;
$scope.nodePopup = {}; $scope.nodePopup = {};
$scope.bma = BMA; $scope.bma = BMA;
$scope.listeners = [];
$scope.platform = {
loading: !csPlatform.isStarted(),
loadingMessage: 'COMMON.LOADING'
};
$scope.timeouts = csSettings.timeouts;
$scope.keepAuthIdleLabels = { $scope.keepAuthIdleLabels = {
/*0: { /*0: {
labelKey: 'SETTINGS.KEEP_AUTH_OPTION.NEVER' labelKey: 'SETTINGS.KEEP_AUTH_OPTION.NEVER'
...@@ -80,7 +84,9 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -80,7 +84,9 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
}; };
$scope.blockValidityWindows = _.keys($scope.blockValidityWindowLabels); $scope.blockValidityWindows = _.keys($scope.blockValidityWindowLabels);
$scope.$on('$ionicView.enter', function() { $scope.enter = function() {
$scope.addListeners();
$q.all([ $q.all([
csSettings.ready(), csSettings.ready(),
csCurrency.parameters() csCurrency.parameters()
...@@ -91,7 +97,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -91,7 +97,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
.then(function(parameters) { .then(function(parameters) {
var avgGenTime = parameters && parameters.avgGenTime; var avgGenTime = parameters && parameters.avgGenTime;
if (!avgGenTime || avgGenTime < 0) { if (!avgGenTime || avgGenTime < 0) {
console.warn("[settings] Could not not currency parameters. Using default G1 'avgGenTime' (300s)"); console.warn('[settings] Could not not currency parameters. Using default G1 \'avgGenTime\' (300s)');
avgGenTime = 300; /* = G1 value = 5min */ avgGenTime = 300; /* = G1 value = 5min */
} }
_.each($scope.blockValidityWindows, function(blockCount) { _.each($scope.blockValidityWindows, function(blockCount) {
...@@ -102,7 +108,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -102,7 +108,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
}) })
]) ])
.then($scope.load); .then($scope.load);
}); };
$scope.setPopupForm = function(popupForm) { $scope.setPopupForm = function(popupForm) {
$scope.popupForm = popupForm; $scope.popupForm = popupForm;
...@@ -111,9 +117,13 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -111,9 +117,13 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
$scope.load = function() { $scope.load = function() {
$scope.loading = true; // to avoid the call of csWallet.store() $scope.loading = true; // to avoid the call of csWallet.store()
$scope.platform.loading = !csPlatform.isStarted();
// Fill locales // Fill locales
$scope.locales = angular.copy(csSettings.locales); $scope.locales = angular.copy(csSettings.locales);
// Apply settings // Apply settings
angular.merge($scope.formData, csSettings.data); angular.merge($scope.formData, csSettings.data);
...@@ -130,6 +140,22 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -130,6 +140,22 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
}, 100); }, 100);
}; };
$scope.addListeners = function() {
$scope.listeners = [
// Listen platform start message
csPlatform.api.start.on.message($scope, function(message) {
$scope.platform.loading = !csPlatform.isStarted();
$scope.platform.loadingMessage = message;
})
];
};
$scope.leave = function() {
console.debug('[settings] Leaving page');
$scope.removeListeners();
};
$scope.reset = function() { $scope.reset = function() {
if ($scope.actionsPopover) { if ($scope.actionsPopover) {
$scope.actionsPopover.hide(); $scope.actionsPopover.hide();
...@@ -148,23 +174,72 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -148,23 +174,72 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
$translate.use(langKey); $translate.use(langKey);
}; };
$scope.changeExpertMode = function(expertMode) {
// Restart platform, to auto select node
if (!expertMode) {
csPlatform.restart();
}
};
// Change node // Change node
$scope.changeNode= function(node) { $scope.changeNode = function(node, confirm) {
// If platform not stared yet: wait then loop
if (!csPlatform.isStarted()) {
UIUtils.loading.update({template: this.loadingMessage});
return csPlatform.ready()
.then(function() {
return $scope.changeNode(node, confirm); // Loop
});
}
// Ask user to confirm, before allow to change the node
if (!confirm && !$scope.formData.expertMode) {
return UIUtils.alert.confirm('CONFIRM.ENABLE_EXPERT_MODE_TO_CHANGE_NODE', 'CONFIRM.POPUP_WARNING_TITLE', {
cssClass: 'warning',
cancelText: 'COMMON.BTN_NO',
okText: 'COMMON.BTN_YES_CONTINUE',
okType: 'button-assertive'
})
.then(function(confirm) {
if (!confirm) return;
$scope.changeNode(node, true);
});
}
// If not given, get node from settings data
if (!node || !node.host) {
var host = $scope.formData.node.host;
if (!host) return; // Should never occur
var useSsl = angular.isDefined($scope.formData.node.useSsl) ?
$scope.formData.node.useSsl :
($scope.formData.node.port == 443);
var port = !!$scope.formData.node.port && $scope.formData.node.port != 80 && $scope.formData.node.port != 443 ? $scope.formData.node.port : undefined; var port = !!$scope.formData.node.port && $scope.formData.node.port != 80 && $scope.formData.node.port != 443 ? $scope.formData.node.port : undefined;
node = node || { var path = $scope.formData.node.path || (host.indexOf('/') !== -1 ? host.substring(host.indexOf('/')) : '');
host: $scope.formData.node.host, if (path.endsWith('/')) path = path.substring(0, path.length - 1); // Remove trailing slash
host = host.indexOf('/') !== -1 ? host.substring(0, host.indexOf('/')) : host; // Remove path from host
node = {
host: host,
port: port, port: port,
useSsl: angular.isDefined($scope.formData.node.useSsl) ? path: path,
$scope.formData.node.useSsl : useSsl: useSsl
($scope.formData.node.port == 443)
}; };
}
$scope.showNodePopup(node) $scope.showNodePopup(node)
.then(function(newNode) { .then(function(newNode) {
if (newNode.host === $scope.formData.node.host && if (newNode.host === $scope.formData.node.host &&
newNode.port === $scope.formData.node.port && newNode.port == $scope.formData.node.port &&
newNode.useSsl === $scope.formData.node.useSsl && !$scope.formData.node.temporary) { newNode.path === $scope.formData.node.path &&
newNode.useSsl === $scope.formData.node.useSsl &&
!$scope.formData.node.temporary) {
return; // same node = nothing to do return; // same node = nothing to do
} }
// Change to expert mode
$scope.formData.expertMode = true;
UIUtils.loading.show(); UIUtils.loading.show();
BMA.isAlive(newNode) BMA.isAlive(newNode)
...@@ -173,7 +248,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -173,7 +248,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
UIUtils.loading.hide(); UIUtils.loading.hide();
return UIUtils.alert.error('ERROR.INVALID_NODE_SUMMARY') return UIUtils.alert.error('ERROR.INVALID_NODE_SUMMARY')
.then(function(){ .then(function(){
$scope.changeNode(newNode); // loop $scope.changeNode(newNode, true); // loop
}); });
} }
UIUtils.loading.hide(); UIUtils.loading.hide();
...@@ -206,24 +281,31 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -206,24 +281,31 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
.then(function (peer) { .then(function (peer) {
if (peer) { if (peer) {
var bma = peer.getBMA(); var bma = peer.getBMA();
var host = (bma.dns ? bma.dns :
(peer.hasValid4(bma) ? bma.ipv4 : bma.ipv6));
var useSsl = bma.useSsl || bma.port == 443;
var port = bma.port || (useSsl ? 443 : 80);
return { return {
host: (bma.dns ? bma.dns : host: host,
(peer.hasValid4(bma) ? bma.ipv4 : bma.ipv6)), port: port,
port: bma.port || 80, path: bma.path || '',
useSsl: bma.useSsl || bma.port == 443 useSsl: useSsl
}; };
} }
}) })
.then(function(newNode) { .then(function(newNode) {
$scope.changeNode(newNode); $scope.changeNode(newNode, true);
}); });
}; };
// Show node popup // Show node popup
$scope.showNodePopup = function(node) { $scope.showNodePopup = function(node) {
return $q(function(resolve, reject) { return $q(function(resolve, reject) {
$scope.popupData.newNode = node.port ? [node.host, node.port].join(':') : node.host; var useSsl = node.useSsl || node.port == 443;
$scope.popupData.useSsl = node.useSsl; var host = (node.port && node.port != 80 && node.port != 443) ? [node.host, node.port].join(':') : node.host;
if (node.path && node.path.length && node.path !== '/') host += node.path;
$scope.popupData.newNode = host;
$scope.popupData.useSsl = useSsl;
if (!!$scope.popupForm) { if (!!$scope.popupForm) {
$scope.popupForm.$setPristine(); $scope.popupForm.$setPristine();
} }
...@@ -246,7 +328,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -246,7 +328,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
e.preventDefault(); e.preventDefault();
} else { } else {
return { return {
server: $scope.popupData.newNode, host: $scope.popupData.newNode,
useSsl: $scope.popupData.useSsl useSsl: $scope.popupData.useSsl
}; };
} }
...@@ -255,16 +337,23 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -255,16 +337,23 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
] ]
}) })
.then(function(res) { .then(function(res) {
if (!res) { // user cancel if (!res || !res.host) { // user cancel
UIUtils.loading.hide(); UIUtils.loading.hide();
reject('CANCELLED');
return; return;
} }
var parts = res.server.split(':'); var host = res.host;
parts[1] = parts[1] ? parts[1] : 80; var path = host.indexOf('/') !== -1 ? host.substring(host.indexOf('/')) : '';
host = host.indexOf('/') !== -1 ? host.substring(0, host.indexOf('/')) : host;
var parts = host.split(':', 2);
host = parts[0];
var port = parts[1] ? parts[1] : (res.useSsl ? 443 : 80);
var useSsl = res.useSsl || port == 443;
resolve({ resolve({
host: parts[0], host: host,
port: parts[1], port: port,
useSsl: res.useSsl path: path,
useSsl: useSsl
}); });
}); });
}); });
...@@ -283,8 +372,12 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -283,8 +372,12 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
} }
$scope.saving = true; $scope.saving = true;
var now = Date.now();
// Async - to avoid UI lock // Async - to avoid UI lock
return $timeout(function() { return $timeout(function() {
console.debug('[settings] Saving...');
// Make sure to format helptip // Make sure to format helptip
$scope.cleanupHelpTip(); $scope.cleanupHelpTip();
...@@ -301,6 +394,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -301,6 +394,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
.then(function() { .then(function() {
//return $timeout(function() { //return $timeout(function() {
$scope.saving = false; $scope.saving = false;
console.debug('[settings] Saving [OK] in {0}ms'.format(Date.now() - now));
//}, 100); //}, 100);
}); });
}; };
...@@ -387,6 +481,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -387,6 +481,7 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
var helptipScope = $scope.createHelptipScope(tour); var helptipScope = $scope.createHelptipScope(tour);
if (!helptipScope) return; // could be undefined, if a global tour already is already started if (!helptipScope) return; // could be undefined, if a global tour already is already started
$ionicScrollDelegate.scrollTop(true);
return helptipScope.startSettingsTour(index, false) return helptipScope.startSettingsTour(index, false)
.then(function(endIndex) { .then(function(endIndex) {
helptipScope.$destroy(); helptipScope.$destroy();
...@@ -394,4 +489,18 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti ...@@ -394,4 +489,18 @@ function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $ti
csSettings.store(); csSettings.store();
}); });
}; };
$scope.removeListeners = function() {
if ($scope.listeners.length) {
console.debug('[settings] Closing listeners');
_.forEach($scope.listeners, function(remove){
remove();
});
$scope.listeners = [];
}
};
$scope.$on('$ionicView.enter', $scope.enter);
$scope.$on('$ionicView.beforeLeave', $scope.leave);
} }
...@@ -129,16 +129,24 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc ...@@ -129,16 +129,24 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc
$scope.destPub = parameters.pubkey; $scope.destPub = parameters.pubkey;
} }
if (parameters.amount) { if (parameters.amount) {
$scope.formData.amount = parameters.amount; var amount = Number(parameters.amount);
// Trunc at 2 decimals
$scope.formData.amount = !isNaN(amount) ? Math.trunc(parseFloat(parameters.amount) * 100) / 100 : null;
$scope.formData.useRelative=false; $scope.formData.useRelative=false;
} }
else if (parameters.udAmount) { else if (parameters.udAmount) {
$scope.formData.amount = parameters.udAmount; var udAmount = Number(parameters.udAmount);
$scope.formData.amount = !isNaN(udAmount) ? udAmount : null;
$scope.formData.useRelative=true; $scope.formData.useRelative=true;
} }
if (parameters.comment) { if (parameters.comment) {
var cleanComment = parameters.comment.trim()
.replaceAll(new RegExp(BMA.constants.regexp.INVALID_COMMENT_CHARS, 'g'), ' ')
.replaceAll(/\s+/g, ' ');
if (cleanComment.length > 0) {
$scope.formData.useComment = true; $scope.formData.useComment = true;
$scope.formData.comment = parameters.comment; $scope.formData.comment = cleanComment;
}
} }
if (parameters.restPub || parameters.all) { if (parameters.restPub || parameters.all) {
$scope.restUid = ''; $scope.restUid = '';
...@@ -156,6 +164,7 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc ...@@ -156,6 +164,7 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc
$scope.formData.walletId = parameters.wallet; $scope.formData.walletId = parameters.wallet;
} }
}; };
// Read default parameters // Read default parameters
$scope.setParameters(parameters); $scope.setParameters(parameters);
...@@ -164,7 +173,7 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc ...@@ -164,7 +173,7 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc
wallet = $scope.enableSelectWallet && ($scope.formData.walletId ? csWallet.children.get($scope.formData.walletId) : csWallet) || csWallet; wallet = $scope.enableSelectWallet && ($scope.formData.walletId ? csWallet.children.get($scope.formData.walletId) : csWallet) || csWallet;
if (!wallet.isDefault()) { if (!wallet.isDefault()) {
console.debug("[transfer] Using wallet {" + wallet.id + "}"); console.debug("[transfer] Using wallet {{0}}".format(wallet.id));
} }
// Make to sure to load full wallet data (balance) // Make to sure to load full wallet data (balance)
return wallet.login({sources: true, silent: true}) return wallet.login({sources: true, silent: true})
...@@ -226,7 +235,8 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc ...@@ -226,7 +235,8 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc
}; };
$scope.onAmountChanged = function() { $scope.onAmountChanged = function() {
if ($scope.sending) return; // skip if sending TX if (!$scope.form || !$scope.form.amount) return; // skip if modal has been destroyed
if ($scope.sending || !$scope.form.amount) return; // skip if sending TX
var amount = $scope.formData.amount; var amount = $scope.formData.amount;
if (amount && typeof amount === "string") { if (amount && typeof amount === "string") {
...@@ -322,7 +332,7 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc ...@@ -322,7 +332,7 @@ function TransferModalController($scope, $q, $translate, $timeout, $filter, $foc
amount = amount.toFixed(2) * 100; // remove 2 decimals on quantitative mode amount = amount.toFixed(2) * 100; // remove 2 decimals on quantitative mode
} }
// convert comment: trim, then null if empty // Trim comment to null
var comment = $scope.formData.comment && $scope.formData.comment.trim(); var comment = $scope.formData.comment && $scope.formData.comment.trim();
if (comment && !comment.length) { if (comment && !comment.length) {
comment = null; comment = null;
......
...@@ -687,6 +687,8 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -687,6 +687,8 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
'ngInject'; 'ngInject';
$scope.loading = true; $scope.loading = true;
$scope.loadingMore = false;
$scope.lastMoreTxTime = null;
$scope.settings = csSettings.data; $scope.settings = csSettings.data;
$scope.listeners = []; $scope.listeners = [];
$scope.qrcodeId = 'qrcode-wallet-tx-' + $scope.$id; $scope.qrcodeId = 'qrcode-wallet-tx-' + $scope.$id;
...@@ -727,12 +729,14 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -727,12 +729,14 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
if (!wallet) return $q.reject('Missing wallet'); if (!wallet) return $q.reject('Missing wallet');
var hasMinData = wallet.isDataLoaded({minData: true}); var hasMinData = wallet.isDataLoaded({minData: true});
var fromTime = csHttp.date.now() - csSettings.data.walletHistoryTimeSecond;
var options = { var options = {
requirements: !hasMinData, // load requirements (=minData) once requirements: !hasMinData, // load requirements (=minData) once
minData: !hasMinData, minData: !hasMinData,
sources: true, sources: true,
tx: { tx: {
enable: true enable: true,
fromTime: fromTime
} }
}; };
...@@ -773,7 +777,14 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -773,7 +777,14 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
// Update view // Update view
$scope.updateView = function() { $scope.updateView = function() {
if (!$scope.formData || $scope.loading) return; if (!$scope.formData || !$scope.formData.tx || $scope.loading) return;
$scope.minScrollMoreTime = moment().utc()
.subtract(csSettings.data.walletHistoryScrollMaxTimeSecond, 'second')
.startOf('day')
.unix();
$scope.canScrollMore = $scope.formData.tx.fromTime > $scope.minScrollMoreTime;
$scope.$broadcast('$$rebind::balance'); // force rebind balance $scope.$broadcast('$$rebind::balance'); // force rebind balance
$scope.$broadcast('$$rebind::rebind'); // force rebind $scope.$broadcast('$$rebind::rebind'); // force rebind
$scope.motion.show({selector: '.view-wallet-tx .item', ink: false}); $scope.motion.show({selector: '.view-wallet-tx .item', ink: false});
...@@ -783,7 +794,19 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -783,7 +794,19 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
options = options || {}; options = options || {};
options.fromTime = options.fromTime || -1; // default: full history options.fromTime = options.fromTime || -1; // default: full history
var pubkey = $scope.formData.pubkey; var pubkey = $scope.formData.pubkey;
csTx.downloadHistoryFile(pubkey, options);
$scope.hideActionsPopover();
UIUtils.toast.show('COMMON.DOWNLOADING_DOTS');
return csTx.downloadHistoryFile(pubkey, options)
.then(UIUtils.toast.hide)
.then(function() {
UIUtils.toast.show('INFO.FILE_DOWNLOADED', 1000);
})
.catch(function(err){
if (err && err === 'CANCELLED') return;
UIUtils.onError('ERROR.DOWNLOAD_TX_HISTORY_FAILED')(err);
});
}; };
// Updating wallet data // Updating wallet data
...@@ -798,6 +821,7 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -798,6 +821,7 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
}, },
api: false api: false
}; };
return (silent ? return (silent ?
// If silent: just refresh // If silent: just refresh
wallet.refreshData(options) : wallet.refreshData(options) :
...@@ -813,7 +837,7 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -813,7 +837,7 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
}; };
$scope.showQRCode = function(timeout) { $scope.showQRCode = function(timeout) {
if (!wallet || !$scope.qrcodeId) return; // Skip if (!wallet || !$scope.qrcodeId) return; // Skip
// Get the DIV element // Get the DIV element
var element = angular.element(document.querySelector('#' + $scope.qrcodeId + ' .content')); var element = angular.element(document.querySelector('#' + $scope.qrcodeId + ' .content'));
...@@ -904,27 +928,43 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -904,27 +928,43 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
}; };
$scope.showMoreTx = function(fromTime) { $scope.showMoreTx = function(fromTime) {
if ($scope.loadingMore) return; // Skip
// Add a delay if previous load has been done recently
var waitDelayMs = $scope.lastMoreTxTime ? Date.now() - $scope.lastMoreTxTime : BMA.constants.LIMIT_REQUEST_DELAY;
if (waitDelayMs > 0 && waitDelayMs < BMA.constants.LIMIT_REQUEST_DELAY) {
return $timeout(function() {
return $scope.showMoreTx(fromTime);
}, waitDelayMs);
}
$scope.loadingMore = true;
$scope.loadingMoreTime = Date.now();
fromTime = fromTime || fromTime = fromTime ||
($scope.formData.tx.fromTime - csSettings.data.walletHistoryTimeSecond) || ($scope.formData.tx.fromTime - csSettings.data.walletHistoryTimeSecond) ||
(csHttp.date.now() - 2 * csSettings.data.walletHistoryTimeSecond); (csHttp.date.now() - 2 * csSettings.data.walletHistoryTimeSecond);
UIUtils.loading.show(); console.info('[wallet-tx] Fetching more TX, since: ' + fromTime);
return wallet.refreshData({tx: {enable: true, fromTime: fromTime}})
return wallet.refreshData({sources: true, tx: {enable: true, fromTime: fromTime}})
.then(function() { .then(function() {
$scope.loadingMore = false;
$scope.updateView(); $scope.updateView();
UIUtils.loading.hide(); $scope.$broadcast('scroll.infiniteScrollComplete');
}) })
.catch(function(err) { .catch(function(err) {
// If http rest limitation: wait then retry // If http rest limitation: wait then retry
if (err.ucode == BMA.errorCodes.HTTP_LIMITATION) { if (err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
$timeout(function() { return $timeout(function() {
return $scope.showMoreTx(fromTime); return $scope.showMoreTx(fromTime); // Loop
}, 2000); }, 2000);
} }
else {
$scope.loadingMore = false;
$scope.canScrollMore = false;
$scope.$broadcast('scroll.infiniteScrollComplete');
UIUtils.onError('ERROR.REFRESH_WALLET_DATA')(err); UIUtils.onError('ERROR.REFRESH_WALLET_DATA')(err);
}
}); });
}; };
...@@ -951,6 +991,24 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location, ...@@ -951,6 +991,24 @@ function WalletTxController($scope, $ionicPopover, $state, $timeout, $location,
/* -- popover -- */ /* -- popover -- */
$scope.showActionsPopover = function(event) {
UIUtils.popover.show(event, {
templateUrl: 'templates/wallet/popover_tx_actions.html',
scope: $scope,
autoremove: true,
afterShow: function(popover) {
$scope.actionsPopover = popover;
}
});
};
$scope.hideActionsPopover = function() {
if ($scope.actionsPopover) {
$scope.actionsPopover.hide();
$scope.actionsPopover = null;
}
};
var paddingIndent = 10; var paddingIndent = 10;
$scope.toUnlockUIArray = function(unlockTreeItem, leftPadding, operator) { $scope.toUnlockUIArray = function(unlockTreeItem, leftPadding, operator) {
...@@ -1382,11 +1440,20 @@ function WalletSecurityModalController($scope, UIUtils, csConfig, csWallet, $tra ...@@ -1382,11 +1440,20 @@ function WalletSecurityModalController($scope, UIUtils, csConfig, csWallet, $tra
return wallet.getCryptedId(record) return wallet.getCryptedId(record)
.then(function(record){ .then(function(record){
wallet.downloadSaveId(record);
$scope.closeModal(); $scope.closeModal();
return wallet.downloadSaveId(record)
.then(function() {
UIUtils.toast.show('INFO.FILE_DOWNLOADED', 1000);
})
.catch(function(err){
if (err && err === 'CANCELLED') return;
UIUtils.onError('ERROR.DOWNLOAD_SAVE_ID_FAILED')(err);
}); });
}) })
; ;
})
;
}; };
$scope.isRequired = function(){ $scope.isRequired = function(){
...@@ -1417,15 +1484,15 @@ function WalletSecurityModalController($scope, UIUtils, csConfig, csWallet, $tra ...@@ -1417,15 +1484,15 @@ function WalletSecurityModalController($scope, UIUtils, csConfig, csWallet, $tra
return wallet.downloadRevocation(); return wallet.downloadRevocation();
}) })
.then(UIUtils.loading.hide)
.then(function() { .then(function() {
UIUtils.loading.hide(); UIUtils.toast.show('INFO.FILE_DOWNLOADED', 1000);
}) })
.catch(function(err){ .catch(function(err){
if (err && err === 'CANCELLED') return; if (err && err === 'CANCELLED') return;
UIUtils.onError('ERROR.DOWNLOAD_REVOCATION_FAILED')(err); UIUtils.onError('ERROR.DOWNLOAD_REVOCATION_FAILED')(err);
}) });
;
}; };
......
...@@ -43,7 +43,7 @@ angular.module('cesium.wot.controllers', ['cesium.services']) ...@@ -43,7 +43,7 @@ angular.module('cesium.wot.controllers', ['cesium.services'])
}) })
.state('app.wot_identity', { .state('app.wot_identity', {
url: "/wot/:pubkey/:uid?action&block&amount&comment", url: "/wot/:pubkey/:uid?action&block&amount&comment&udAmount&restPub&wallet",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "templates/wot/view_identity.html", templateUrl: "templates/wot/view_identity.html",
...@@ -157,6 +157,8 @@ angular.module('cesium.wot.controllers', ['cesium.services']) ...@@ -157,6 +157,8 @@ angular.module('cesium.wot.controllers', ['cesium.services'])
.controller('WotCertificationsViewCtrl', WotCertificationsViewController) .controller('WotCertificationsViewCtrl', WotCertificationsViewController)
.controller('WotCertificationChecklistCtrl', WotCertificationChecklistController)
.controller('WotSelectPubkeyIdentityModalCtrl', WotSelectPubkeyIdentityModalController) .controller('WotSelectPubkeyIdentityModalCtrl', WotSelectPubkeyIdentityModalController)
; ;
...@@ -295,18 +297,21 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i ...@@ -295,18 +297,21 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
$scope.search.type = 'text'; $scope.search.type = 'text';
// If checksum is correct, search on simple pubkey // If checksum is correct, search on simple pubkey
let pubkeyWithCk; var pubkeyWithCk;
if (BMA.regexp.PUBKEY_WITH_CHECKSUM.test(text)) { if (BMA.regexp.PUBKEY_WITH_CHECKSUM.test(text)) {
console.debug("[wot] Validating pubkey checksum... "); console.debug("[wot] Validating pubkey checksum... ");
let matches = BMA.regexp.PUBKEY_WITH_CHECKSUM.exec(text); var matches = BMA.regexp.PUBKEY_WITH_CHECKSUM.exec(text);
console.log(matches)
// DEBUG only
//console.debug(matches)
pubkey = matches[1]; pubkey = matches[1];
let checksum = matches[2]; var checksum = matches[2];
let expectedChecksum = csCrypto.util.pkChecksum(pubkey); var expectedChecksum = csCrypto.util.pkChecksum(pubkey);
if (checksum === expectedChecksum) { if (checksum === expectedChecksum) {
console.debug("[wot] checksum {" + checksum + "} valid for pubkey {" + pubkey + "}") console.debug("[wot] checksum {" + checksum + "} valid for pubkey {" + pubkey + "}");
text = pubkey text = pubkey;
pubkeyWithCk = pubkey + ':' + checksum pubkeyWithCk = pubkey + ':' + checksum;
} }
} }
...@@ -439,7 +444,7 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i ...@@ -439,7 +444,7 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
var offset = $scope.search.results ? $scope.search.results.length : 0; var offset = $scope.search.results ? $scope.search.results.length : 0;
$scope.search.loadingMore = true; $scope.search.loadingMore = true;
var searchFunction = ($scope.search.type == 'newcomers') ? var searchFunction = ($scope.search.type === 'newcomers') ?
$scope.doGetNewcomers : $scope.doGetNewcomers :
$scope.doGetPending; $scope.doGetPending;
...@@ -689,11 +694,16 @@ function WotLookupModalController($scope, $controller, $focus, csWallet, paramet ...@@ -689,11 +694,16 @@ function WotLookupModalController($scope, $controller, $focus, csWallet, paramet
/** /**
* Abtract controller that load identity, that expose some useful methods in $scope, like 'certify()' * Abtract controller that load identity, that expose some useful methods in $scope, like 'certify()'
* @param $scope * @param $scope
* @param $rootScope
* @param $state * @param $state
* @param $timeout * @param $translate
* @param $ionicHistory
* @param $q
* @param UIUtils * @param UIUtils
* @param Modals * @param Modals
* @param csConfig * @param csConfig
* @param csSettings
* @param csCurrency
* @param csWot * @param csWot
* @param csWallet * @param csWallet
* @constructor * @constructor
...@@ -773,43 +783,14 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -773,43 +783,14 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
return; return;
} }
// Check identity not expired if (!$scope.commonCertificationVerifications($scope.formData, wallet)) {
if ($scope.formData.requirements.expired) {
UIUtils.alert.error('ERROR.IDENTITY_EXPIRED');
return;
}
// Check not already certified
var previousCert = _.find($scope.formData.received_cert, function(cert) {
return cert.pubkey === wallet.data.pubkey && cert.valid && cert.expiresIn > csSettings.data.timeWarningExpire;
});
if (previousCert) {
$translate('ERROR.IDENTITY_ALREADY_CERTIFY', previousCert)
.then(function(message) {
UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
});
return; return;
} }
// Check no pending certification return $scope.displayConfirmationModalOrLicenseQuestions($scope.formData, wallet);
previousCert = _.findWhere($scope.formData.received_cert_pending, { pubkey: wallet.data.pubkey, valid: true});
if (previousCert) {
$translate('ERROR.IDENTITY_ALREADY_CERTIFY_PENDING', previousCert)
.then(function(message) {
UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
});
return;
}
UIUtils.alert.confirm('CONFIRM.CERTIFY_RULES', 'CONFIRM.POPUP_SECURITY_WARNING_TITLE', {
cssClass: 'warning',
okText: 'WOT.BTN_YES_CERTIFY',
okType: 'button-assertive'
}) })
.then(function(confirm){ .then(function(confirm){
if (!confirm) { if (!confirm) return;
return;
}
UIUtils.loading.show(); UIUtils.loading.show();
wallet.certify($scope.formData.uid, wallet.certify($scope.formData.uid,
$scope.formData.pubkey, $scope.formData.pubkey,
...@@ -829,7 +810,6 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -829,7 +810,6 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
} }
}) })
.catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED')); .catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED'));
});
}) })
.catch(function(err) { .catch(function(err) {
if (err === 'CANCELLED') return; if (err === 'CANCELLED') return;
...@@ -838,6 +818,89 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -838,6 +818,89 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
}); });
}; };
$scope.commonCertificationVerifications = function(identity, wallet) {
// Check it is no self-certification
if (identity.pubkey === wallet.data.pubkey) {
UIUtils.alert.error('ERROR.SELF_CERTIFICATION');
return false;
}
// Check identity not expired
if (identity.requirements.expired) {
UIUtils.alert.error('ERROR.IDENTITY_EXPIRED');
return false;
}
// Check not already certified
var previousCert = _.find(identity.received_cert, function (cert) {
return cert.pubkey === wallet.data.pubkey && cert.valid && cert.expiresIn > csSettings.data.timeWarningExpire;
});
if (previousCert) {
$translate('ERROR.IDENTITY_ALREADY_CERTIFY', previousCert)
.then(function (message) {
UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
});
return false;
}
// Check no pending certification
previousCert = _.findWhere(identity.received_cert_pending, { pubkey: wallet.data.pubkey, valid: true });
if (previousCert) {
$translate('ERROR.IDENTITY_ALREADY_CERTIFY_PENDING', previousCert)
.then(function (message) {
UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
});
return false;
}
return true;
};
$scope.showLicenseReminder = function() {
return UIUtils.alert.confirm(
'ACCOUNT.CERTIFICATION_MODAL.SHORT_LICENSE_REMINDER',
'ACCOUNT.CERTIFICATION_MODAL.REMINDER_TITLE',
{
cssClass: 'confirm large',
okText: 'COMMON.BTN_OK',
okType: 'button-positive'
}
);
};
$scope.displayConfirmationModalOrLicenseQuestions = function(identity, wallet) {
// Renew: simple confirmation modal
if (isCertificationRenewal(identity.received_cert, wallet.data.pubkey)) {
return $scope.certRenewalConfirmationModal();
}
// New certification: show questions modal
return Modals.showCertificationCheckList({
identity: identity
})
.then(function(confirm) {
if (!confirm) return false;
// Show license reminder modal
return $scope.showLicenseReminder();
})
.catch(function(err) {
if (err === 'CANCELLED') return false;
UIUtils.onError('ACCOUNT.CERTIFICATION_MODAL.CHECKLIST_CONDITIONS_NOT_MET')(err);
return false;
});
};
$scope.certRenewalConfirmationModal = function() {
return UIUtils.alert.confirm('CONFIRM.CERTIFY_RULES', 'CONFIRM.CERTIFY_RULES_TITLE_UID', {
cssClass: 'warning',
okText: 'WOT.BTN_YES_CERTIFY',
okType: 'button-assertive'
});
};
// Select an identity and certify // Select an identity and certify
$scope.selectAndCertify = function() { $scope.selectAndCertify = function() {
...@@ -856,21 +919,19 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -856,21 +919,19 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
// Open Wot lookup modal // Open Wot lookup modal
return Modals.showWotLookup(); return Modals.showWotLookup();
}) })
.then(function (idty) { .then(function(identity) {
if (!idty || !idty.pubkey) { if (!identity || !identity.pubkey) return; // cancelled
return; // cancelled if (!identity.uid) { // not a member
}
if (!idty.uid) { // not a member
UIUtils.alert.error('ERROR.IDENTITY_TO_CERTIFY_HAS_NO_SELF'); UIUtils.alert.error('ERROR.IDENTITY_TO_CERTIFY_HAS_NO_SELF');
return; return;
} }
UIUtils.loading.show(); UIUtils.loading.show();
var options = {cache: false, blockUid: idty.blockUid}; var options = {cache: false, blockUid: identity.blockUid};
// load selected identity // load selected identity
return csWot.load(idty.pubkey, idty.uid, options); return csWot.load(identity.pubkey, identity.uid, options);
}) })
.then(function (identity) { .then(function (identity) {
...@@ -881,43 +942,20 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -881,43 +942,20 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
return; return;
} }
// Check identity not expired if (!$scope.commonCertificationVerifications(identity, wallet)) {
if (identity.requirements.expired) {
UIUtils.alert.error('ERROR.IDENTITY_EXPIRED');
return; return;
} }
// Check not already certified // Prepare actions after user confirmation
var previousCert = _.findWhere(identity.received_cert, {pubkey: wallet.data.pubkey, valid: true}); return $scope.displayConfirmationModalOrLicenseQuestions(identity, wallet)
if (previousCert) { .then(function(confirm) {
$translate('ERROR.IDENTITY_ALREADY_CERTIFY', previousCert) if (!confirm) return;
.then(function (message) { return identity;
UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
});
return;
}
// Check not pending certification
previousCert = _.findWhere(identity.received_cert_pending, {pubkey: wallet.data.pubkey, valid: true});
if (previousCert) {
$translate('ERROR.IDENTITY_ALREADY_CERTIFY_PENDING', previousCert)
.then(function (message) {
UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
}); });
return;
}
// Ask confirmation
$translate('CONFIRM.CERTIFY_RULES_TITLE_UID', {uid: identity.uid})
.then(function (confirmTitle) {
return UIUtils.alert.confirm('CONFIRM.CERTIFY_RULES', confirmTitle);
}) })
.then(function (confirm) { .then(function(identity){
if (!confirm) { if (!identity) return;
return;
}
UIUtils.loading.show(); UIUtils.loading.show();
// Send certification // Send certification
wallet.certify(identity.uid, wallet.certify(identity.uid,
identity.pubkey, identity.pubkey,
...@@ -942,7 +980,6 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -942,7 +980,6 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
if (err === 'CANCELLED') return; if (err === 'CANCELLED') return;
UIUtils.onError('ERROR.LOAD_IDENTITY_FAILED')(err); UIUtils.onError('ERROR.LOAD_IDENTITY_FAILED')(err);
}); });
});
}; };
// Add wallet's data to a new cert // Add wallet's data to a new cert
...@@ -1051,7 +1088,7 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ ...@@ -1051,7 +1088,7 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
/** /**
* Identity view controller - should extend WotIdentityAbstractCtrl * Identity view controller - should extend WotIdentityAbstractCtrl
*/ */
function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $state, UIUtils, Modals, csWallet) { function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $state, UIUtils, Modals) {
'ngInject'; 'ngInject';
// Initialize the super class and extend it. // Initialize the super class and extend it.
angular.extend(this, $controller('WotIdentityAbstractCtrl', {$scope: $scope})); angular.extend(this, $controller('WotIdentityAbstractCtrl', {$scope: $scope}));
...@@ -1073,18 +1110,23 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s ...@@ -1073,18 +1110,23 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s
$scope.$on('$ionicView.enter', function(e, state) { $scope.$on('$ionicView.enter', function(e, state) {
var onLoadSuccess = function() { var doAction = function() {
$scope.doMotion();
if (state.stateParams && state.stateParams.action) { if (state.stateParams && state.stateParams.action) {
$timeout(function() { $timeout(function() {
$scope.doAction(state.stateParams.action.trim(), state.stateParams); $scope.doAction(state.stateParams.action.trim(), state.stateParams);
}, 100); }, 100);
}
$scope.removeActionParamInLocationHref(state); $scope.removeActionParamInLocationHref(state);
};
var onLoadSuccess = function() {
$scope.doMotion();
doAction();
// Need by like controller // Need by like controller
$scope.likeData.id = $scope.formData.pubkey; $scope.likeData.id = $scope.formData.pubkey;
}
$scope.showQRCode(); $scope.showQRCode();
}; };
...@@ -1096,12 +1138,17 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s ...@@ -1096,12 +1138,17 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s
if (state.stateParams && if (state.stateParams &&
state.stateParams.pubkey && state.stateParams.pubkey &&
state.stateParams.pubkey.trim().length > 0) { state.stateParams.pubkey.trim().length > 0) {
if ($scope.loading) { // load once
// First time: load identity data
if ($scope.loading) {
return $scope.load(state.stateParams.pubkey.trim(), state.stateParams.uid, options) return $scope.load(state.stateParams.pubkey.trim(), state.stateParams.uid, options)
.then(onLoadSuccess) .then(onLoadSuccess)
.catch(UIUtils.onError("ERROR.LOAD_IDENTITY_FAILED")); .catch(UIUtils.onError("ERROR.LOAD_IDENTITY_FAILED"));
} }
// Do action
else {
doAction();
}
} }
else if (state.stateParams && else if (state.stateParams &&
...@@ -1111,6 +1158,9 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s ...@@ -1111,6 +1158,9 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s
return $scope.load(null, state.stateParams.uid, options) return $scope.load(null, state.stateParams.uid, options)
.then(onLoadSuccess); .then(onLoadSuccess);
} }
else {
doAction();
}
} }
// Redirect to home // Redirect to home
...@@ -1243,7 +1293,19 @@ function WotIdentityTxViewController($scope, $timeout, $q, BMA, csSettings, csWo ...@@ -1243,7 +1293,19 @@ function WotIdentityTxViewController($scope, $timeout, $q, BMA, csSettings, csWo
$scope.downloadHistoryFile = function(options) { $scope.downloadHistoryFile = function(options) {
options = options || {}; options = options || {};
options.fromTime = options.fromTime || -1; // default: full history options.fromTime = options.fromTime || -1; // default: full history
csTx.downloadHistoryFile($scope.pubkey, options);
UIUtils.loading.show();
UIUtils.toast.show('INFO.DOWNLOADING_DOTS');
return csTx.downloadHistoryFile($scope.pubkey, options)
.then(UIUtils.toast.hide)
.then(function() {
UIUtils.toast.show('INFO.FILE_DOWNLOADED', 1000);
})
.catch(function(err){
if (err && err === 'CANCELLED') return;
UIUtils.onError('ERROR.DOWNLOAD_TX_HISTORY_FAILED')(err);
});
}; };
$scope.showMoreTx = function(fromTime) { $scope.showMoreTx = function(fromTime) {
...@@ -1261,7 +1323,7 @@ function WotIdentityTxViewController($scope, $timeout, $q, BMA, csSettings, csWo ...@@ -1261,7 +1323,7 @@ function WotIdentityTxViewController($scope, $timeout, $q, BMA, csSettings, csWo
}) })
.catch(function(err) { .catch(function(err) {
// If http rest limitation: wait then retry // If http rest limitation: wait then retry
if (err.ucode == BMA.errorCodes.HTTP_LIMITATION) { if (err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
$timeout(function() { $timeout(function() {
return $scope.showMoreTx(fromTime); return $scope.showMoreTx(fromTime);
}, 2000); }, 2000);
...@@ -1272,6 +1334,25 @@ function WotIdentityTxViewController($scope, $timeout, $q, BMA, csSettings, csWo ...@@ -1272,6 +1334,25 @@ function WotIdentityTxViewController($scope, $timeout, $q, BMA, csSettings, csWo
}); });
}; };
/* -- popover -- */
$scope.showActionsPopover = function(event) {
UIUtils.popover.show(event, {
templateUrl: 'templates/wot/popover_tx_actions.html',
scope: $scope,
autoremove: true,
afterShow: function(popover) {
$scope.actionsPopover = popover;
}
});
};
$scope.hideActionsPopover = function() {
if ($scope.actionsPopover) {
$scope.actionsPopover.hide();
$scope.actionsPopover = null;
}
};
} }
...@@ -1298,8 +1379,8 @@ function WotCertificationsViewController($scope, $rootScope, $controller, csSett ...@@ -1298,8 +1379,8 @@ function WotCertificationsViewController($scope, $rootScope, $controller, csSett
$scope.$on('$ionicView.enter', function(e, state) { $scope.$on('$ionicView.enter', function(e, state) {
if (state.stateParams && state.stateParams.type) { if (state.stateParams && state.stateParams.type) {
$scope.motions.receivedCertifications.enable = (state.stateParams.type != 'given'); $scope.motions.receivedCertifications.enable = (state.stateParams.type !== 'given');
$scope.motions.givenCertifications.enable = (state.stateParams.type == 'given'); $scope.motions.givenCertifications.enable = (state.stateParams.type === 'given');
$scope.motions.avatar.enable = false; $scope.motions.avatar.enable = false;
} }
...@@ -1447,6 +1528,75 @@ function WotCertificationsViewController($scope, $rootScope, $controller, csSett ...@@ -1447,6 +1528,75 @@ function WotCertificationsViewController($scope, $rootScope, $controller, csSett
}; };
} }
/**
* Certification checklist controller
* @param $scope
* @param $controller
* @param parameters
*/
function WotCertificationChecklistController($scope, $controller, parameters){
// allow to display license
$controller('CurrencyViewCtrl', {$scope: $scope});
$scope.identity = parameters.identity;
$scope.prepare_cert_checklist = function() {
var original_cert_checklist = [
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.WELL_KNOWN',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.REVOCATION',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.CONTACT',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.MASTER_ACCOUNT',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.LICENSE',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.CREDENTIALS',
expected_answer: true,
answer: false
},
// questions with negative answers
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.DOUBLE_IDENTITY',
expected_answer: false,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.PUBLIC_KEY_DIFFERENT',
expected_answer: false,
answer: false
},
];
return shuffle(original_cert_checklist).slice(0, 5);
};
$scope.cert_checklist = $scope.prepare_cert_checklist();
$scope.verifyAnswers = function() {
var ok = _.every($scope.cert_checklist, function(question) {
return (question.answer === question.expected_answer);
});
$scope.closeModal(ok);
};
}
/** /**
* Select identities from a pubkey (useful when many self on the same pubkey) * Select identities from a pubkey (useful when many self on the same pubkey)
...@@ -1491,3 +1641,19 @@ function WotSelectPubkeyIdentityModalController($scope, $q, csWot, parameters) { ...@@ -1491,3 +1641,19 @@ function WotSelectPubkeyIdentityModalController($scope, $q, csWot, parameters) {
}; };
$scope.$on('modal.shown', $scope.load); $scope.$on('modal.shown', $scope.load);
} }
function isCertificationRenewal(identity_current_certs, certifier_pubkey) {
return _.find(identity_current_certs, function(certification) {
return certification.pubkey === certifier_pubkey;
});
}
// Fisher-Yates shuffle
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
var t = array[i]; array[i] = array[j]; array[j] = t;
}
return array;
}