diff --git a/www/i18n/locale-en-GB.json b/www/i18n/locale-en-GB.json index ce1488a92e67a179c3c7f71df1652cf97619c81b..d63ff55904228ef387a18eb26e09950ebb2a865c 100644 --- a/www/i18n/locale-en-GB.json +++ b/www/i18n/locale-en-GB.json @@ -329,7 +329,9 @@ "LOOKUP": { "TITLE": "Registry", "NEWCOMERS": "New members:", + "NEWCOMERS_COUNT": "{{count}} members", "PENDING": "Pending registrations:", + "PENDING_COUNT": "{{count}} pending registrations", "REGISTERED": "Registered {{sigDate | formatFromNow}}", "MEMBER_FROM": "Member since {{memberDate|formatFromNowShort}}", "BTN_NEWCOMERS": "Latest members", @@ -436,15 +438,17 @@ "ERROR_TX_SENT": "Sent transactions", "PENDING_TX_RECEIVED": "Transactions awaiting receipt", "EVENTS": "Events", - "WAITING_MEMBERSHIP": "Membership request sent. Waiting validation.", + "WAITING_MEMBERSHIP": "Membership application sent. Waiting validation.", "WAITING_CERTIFICATIONS": "You need {{needCertificationCount}} certification(s) to become a member", "WILL_MISSING_CERTIFICATIONS": "You will <b>lack certifications</b> soon (at least {{willNeedCertificationCount}} more are needed)", "WILL_NEED_RENEW_MEMBERSHIP": "Your membership <b>will expire {{membershipExpiresIn|formatDurationTo}}</b>. Remember to <a ng-click=\"doQuickFix('renew')\">renew your membership</a> before then.", "NEED_RENEW_MEMBERSHIP": "You are no longer a member because your membership <b>has expired</b>. Remember to <a ng-click=\"doQuickFix('renew')\">renew your membership</a>.", + "NO_WAITING_MEMBERSHIP": "No membership application pending. If you'd like to <b>become a member</ b>, please <a ng-click=\"doQuickFix('membership')\">send the membership application</a>.", "CERTIFICATION_COUNT": "Received certifications", "CERTIFICATION_COUNT_SHORT": "Certifications", "SIG_STOCK": "Stock of certifications to give", "BTN_RECEIVE_MONEY": "Receive", + "BTN_SELECT_ALTERNATIVES_IDENTITIES": "Switch to another identity...", "BTN_MEMBERSHIP_IN_DOTS": "Register as member...", "BTN_MEMBERSHIP_RENEW": "Renew membership", "BTN_MEMBERSHIP_RENEW_DOTS": "Renew membership...", @@ -498,6 +502,10 @@ "TITLE": "Enter a pseudonym", "HELP": "A pseudonym is needed to let other members find you." }, + "SELECT_IDENTITY_MODAL": { + "TITLE": "Identity selection", + "HELP": "Several <b>different identities</b> have been sent, for the public key <span class=\"gray\"> <i class=\"ion-key\"></i> {{pubkey | formatPubkey}}</span>.<br/>Please select the identity to use:" + }, "SECURITY":{ "ADD_QUESTION" : "Add custom question", "BTN_CLEAN" : "Clean", @@ -665,6 +673,7 @@ "MEMBERSHIP_OUT_SENT": "Membership revocation sent", "NOT_NEED_MEMBERSHIP": "Already a member.", "IDENTITY_WILL_MISSING_CERTIFICATIONS": "This identity will soon lack certification (at least {{willNeedCertificationCount}}).", + "IDENTITY_NEED_MEMBERSHIP": "This identity did not send a membership request. She will have to if she wishes to become a member.", "REVOCATION_SENT": "Revocation sent successfully", "REVOCATION_SENT_WAITING_PROCESS": "Revocation <b>has been sent successfully</b>. It is awaiting processing.", "FEATURES_NOT_IMPLEMENTED": "This features is not implemented yet.<br/><br/>Why not to contribute to get it faster? ;)", @@ -683,6 +692,7 @@ "LOGIN_UNUSED_WALLET": "The account seems to be <b>inactive</b>.<br/><br/>It's probably a <b>typing error</b> when sign in. Please try again, checking that <b>public key is yours<b/>.", "FIX_IDENTITY": "The pseudonym <b>{{uid}}</b> will be published again, replacing the old publication that has expired.<br/></br/><b>Are you sure</b> you want to continue?", "FIX_MEMBERSHIP": "Your application for membership will be sent.<br/></br/><b>Are you sure?</b>", + "MEMBERSHIP": "Your membership request will be sent. <br/></br/><b>Are you sure?</b>", "RENEW_MEMBERSHIP": "Your membership will be renewed.<br/></br/><b>Are you sure?</b>", "REVOKE_IDENTITY": "You will <b>definitely revoke this identity</b>.<br/><br/>The public key and the associated pseudonym <b>will never be used again</b> (for a member account).<br/></br/><b>Are you sure</b> you want to revoke this identity?", "REVOKE_IDENTITY_2": "This operation is <b>irreversible</b>!<br/><br/>Are you sure you want to <b>revoke this identity</b>?", diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json index 4a3a5d1b2a039289d3739e497e2f951118b73f49..3068738159514369f8ee8d33cb33b6489eee5515 100644 --- a/www/i18n/locale-en.json +++ b/www/i18n/locale-en.json @@ -329,7 +329,9 @@ "LOOKUP": { "TITLE": "Registry", "NEWCOMERS": "New members:", + "NEWCOMERS_COUNT": "{{count}} members", "PENDING": "Pending registrations:", + "PENDING_COUNT": "{{count}} pending registrations", "REGISTERED": "Registered {{sigDate | formatFromNow}}", "MEMBER_FROM": "Member since {{memberDate|formatFromNowShort}}", "BTN_NEWCOMERS": "Latest members", @@ -436,15 +438,17 @@ "ERROR_TX_SENT": "Sent transactions", "PENDING_TX_RECEIVED": "Transactions awaiting receipt", "EVENTS": "Events", - "WAITING_MEMBERSHIP": "Membership request sent. Waiting validation.", + "WAITING_MEMBERSHIP": "Membership application sent. Waiting validation.", "WAITING_CERTIFICATIONS": "You need {{needCertificationCount}} certification(s) to become a member", "WILL_MISSING_CERTIFICATIONS": "You will <b>lack certifications</b> soon (at least {{willNeedCertificationCount}} more are needed)", "WILL_NEED_RENEW_MEMBERSHIP": "Your membership <b>will expire {{membershipExpiresIn|formatDurationTo}}</b>. Remember to <a ng-click=\"doQuickFix('renew')\">renew your membership</a> before then.", "NEED_RENEW_MEMBERSHIP": "You are no longer a member because your membership <b>has expired</b>. Remember to <a ng-click=\"doQuickFix('renew')\">renew your membership</a>.", + "NO_WAITING_MEMBERSHIP": "No membership application pending. If you'd like to <b>become a member</ b>, please <a ng-click=\"doQuickFix('membership')\">send the membership application</a>.", "CERTIFICATION_COUNT": "Received certifications", "CERTIFICATION_COUNT_SHORT": "Certifications", "SIG_STOCK": "Stock of certifications to give", "BTN_RECEIVE_MONEY": "Receive", + "BTN_SELECT_ALTERNATIVES_IDENTITIES": "Switch to another identity...", "BTN_MEMBERSHIP_IN_DOTS": "Register as member...", "BTN_MEMBERSHIP_RENEW": "Renew membership", "BTN_MEMBERSHIP_RENEW_DOTS": "Renew membership...", @@ -498,6 +502,10 @@ "TITLE": "Enter a pseudonym", "HELP": "A pseudonym is needed to let other members find you." }, + "SELECT_IDENTITY_MODAL": { + "TITLE": "Identity selection", + "HELP": "Several <b>different identities</b> have been sent, for the public key <span class=\"gray\"> <i class=\"ion-key\"></i> {{pubkey | formatPubkey}}</span>.<br/>Please select the identity to use:" + }, "SECURITY":{ "ADD_QUESTION" : "Add custom question", "BTN_CLEAN" : "Clean", @@ -665,6 +673,7 @@ "MEMBERSHIP_OUT_SENT": "Membership revocation sent", "NOT_NEED_MEMBERSHIP": "Already a member.", "IDENTITY_WILL_MISSING_CERTIFICATIONS": "This identity will soon lack certification (at least {{willNeedCertificationCount}}).", + "IDENTITY_NEED_MEMBERSHIP": "This identity did not send a membership request. She will have to if she wishes to become a member.", "REVOCATION_SENT": "Revocation sent successfully", "REVOCATION_SENT_WAITING_PROCESS": "Revocation <b>has been sent successfully</b>. It is awaiting processing.", "FEATURES_NOT_IMPLEMENTED": "This features is not implemented yet.<br/><br/>Why not to contribute to get it faster? ;)", @@ -683,6 +692,7 @@ "LOGIN_UNUSED_WALLET": "The account seems to be <b>inactive</b>.<br/><br/>It's probably a <b>typing error</b> when sign in. Please try again, checking that <b>public key is yours<b/>.", "FIX_IDENTITY": "The pseudonym <b>{{uid}}</b> will be published again, replacing the old publication that has expired.<br/></br/><b>Are you sure</b> you want to continue?", "FIX_MEMBERSHIP": "Your application for membership will be sent.<br/></br/><b>Are you sure?</b>", + "MEMBERSHIP": "Your membership request will be sent. <br/></br/><b>Are you sure?</b>", "RENEW_MEMBERSHIP": "Your membership will be renewed.<br/></br/><b>Are you sure?</b>", "REVOKE_IDENTITY": "You will <b>definitely revoke this identity</b>.<br/><br/>The public key and the associated pseudonym <b>will never be used again</b> (for a member account).<br/></br/><b>Are you sure</b> you want to revoke this identity?", "REVOKE_IDENTITY_2": "This operation is <b>irreversible</b>!<br/><br/>Are you sure you want to <b>revoke this identity</b>?", diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json index 4ae6729a01b212fdeff9cfcbbc0cb95f9853150e..7afcaf03575eff00b5fd6d08d8552af58615f7b2 100644 --- a/www/i18n/locale-fr-FR.json +++ b/www/i18n/locale-fr-FR.json @@ -446,10 +446,12 @@ "WILL_MISSING_CERTIFICATIONS": "Vous allez bientôt <b>manquer de certification</b> (au moins {{willNeedCertificationCount}} sont requises)", "WILL_NEED_RENEW_MEMBERSHIP": "Votre adhésion comme membre <b>va expirer {{membershipExpiresIn|formatDurationTo}}</b>. Pensez à <a ng-click=\"doQuickFix('renew')\">renouveler votre adhésion</a> d'ici là.", "NEED_RENEW_MEMBERSHIP": "Vous n'êtes plus membre, car votre adhésion <b>a expiré</b>. Pensez à <a ng-click=\"doQuickFix('renew')\">renouveler votre adhésion</a>.", + "NO_WAITING_MEMBERSHIP": "Aucune demande d'adhésion en attente. Si vous souhaitez <b>devenir membre</b>, pensez à <a ng-click=\"doQuickFix('membership')\">envoyer la demande d'adhésion</a>.", "CERTIFICATION_COUNT": "Certifications reçues", "CERTIFICATION_COUNT_SHORT": "Certifications", "SIG_STOCK": "Certifications envoyées", "BTN_RECEIVE_MONEY": "Encaisser", + "BTN_SELECT_ALTERNATIVES_IDENTITIES": "Basculer vers une autre identité...", "BTN_MEMBERSHIP_IN_DOTS": "Devenir membre...", "BTN_MEMBERSHIP_RENEW": "Renouveler l'adhésion", "BTN_MEMBERSHIP_RENEW_DOTS": "Renouveler l'adhésion...", @@ -503,6 +505,10 @@ "TITLE": "Choisissez un pseudonyme", "HELP": "Un pseudonyme est obligatoire pour devenir membre." }, + "SELECT_IDENTITY_MODAL": { + "TITLE": "Sélection de l'identité", + "HELP": "Plusieurs <b>identités différentes</b> ont été envoyées, pour la clé publique <span class=\"gray\"><i class=\"ion-key\"></i> {{pubkey|formatPubkey}}</span>.<br/>Veuillez sélectionner le dossier à utiliser :" + }, "SECURITY": { "ADD_QUESTION": "Ajouter une question personnalisée ", "BTN_CLEAN": "Vider", @@ -670,6 +676,7 @@ "MEMBERSHIP_OUT_SENT": "Résiliation envoyée", "NOT_NEED_MEMBERSHIP": "Vous êtes déjà membre.", "IDENTITY_WILL_MISSING_CERTIFICATIONS": "Cette identité va bientôt manquer de certification (au moins {{willNeedCertificationCount}}).", + "IDENTITY_NEED_MEMBERSHIP": "Cette identité n'a pas envoyée de demande d'adhésion. Elle devra si elle souhaite devenir membre.", "REVOCATION_SENT": "Revocation envoyée", "REVOCATION_SENT_WAITING_PROCESS": "La <b>révocation de cette identité</b> a été demandée et est en attente de traitement.", "FEATURES_NOT_IMPLEMENTED": "Cette fonctionnalité est encore en cours de développement.<br/>Pourquoi ne pas <b>contribuer à Cesium</b>, pour l'obtenir plus rapidement ? ;)", @@ -688,6 +695,7 @@ "LOGIN_UNUSED_WALLET": "Le compte connecté semble <b>inactif</b>.<br/><br/>Il s'agit probablement d'une <b>erreur de saisie</b> dans vos identifiants de connexion. Veuillez recommencer, en vérifiant que <b>la clé publique est celle de votre compte</b>.", "FIX_IDENTITY": "Le pseudonyme <b>{{uid}}</b> va être publiée à nouveau, en remplacement de l'ancienne publication qui a expirée.<br/></br/><b>Etes-vous sûr</b> de vouloir continuer ?", "FIX_MEMBERSHIP": "Votre demande d'adhésion comme membre va être renvoyée.<br/></br/><b>Etes-vous sûr</b> de vouloir continuer ?", + "MEMBERSHIP": "Votre demande d'adhésion comme membre va être envoyée.<br/></br/><b>Etes-vous sûr</b> de vouloir continuer ?", "RENEW_MEMBERSHIP": "Votre adhésion comme membre va être renouvellée.<br/></br/><b>Etes-vous sûr</b> de vouloir continuer ?", "REVOKE_IDENTITY": "Vous allez <b>revoquer définitivement cette identité</b>.<br/><br/>La clé publique et le pseudonyme associés <b>ne pourront plus jamais être utilisés</b> (pour un compte membre). <br/></br/><b>Etes-vous sûr</b> de vouloir révoquer définitivement ce compte ?", "REVOKE_IDENTITY_2": "Cette opération est <b>irreversible</b> !<br/><br/>Etes-vous vraiment sûr de vouloir <b>révoquer définitivement</b> ce compte ?", diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js index 5b86b7da2abbb5ed08dcc0e7326374e4e55c803b..1c71d3139edfeff11953c96b5cf70c3358bc8504 100644 --- a/www/js/controllers/wallet-controllers.js +++ b/www/js/controllers/wallet-controllers.js @@ -55,11 +55,10 @@ angular.module('cesium.wallet.controllers', ['cesium.services', 'cesium.currency .controller('WalletTxErrorCtrl', WalletTxErrorController) .controller('WalletSecurityModalCtrl', WalletSecurityModalController) - ; function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, $translate, $ionicPopover, - UIUtils, Modals, csConfig, csSettings, csWallet, csHelp) { + UIUtils, Modals, BMA, csConfig, csSettings, csCurrency, csWallet, csHelp) { 'ngInject'; $scope.loading = true; @@ -71,6 +70,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, } else { // update view (to refresh avatar + plugin data, such as profile, subscriptions...) + UIUtils.loading.hide(); $timeout($scope.updateView, 300); } }); @@ -186,56 +186,64 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, UIUtils.loading.hide(); }) .catch(function(err) { + if (err == 'CANCELLED') throw err; + if (err && err.ucode != BMA.errorCodes.MEMBERSHIP_ALREADY_SEND) return; if (!retryCount || retryCount <= 2) { - $timeout(function() { + return $timeout(function() { $scope.doMembershipIn(retryCount ? retryCount+1 : 1); }, 1000); } - else { - UIUtils.onError('ERROR.SEND_MEMBERSHIP_IN_FAILED')(err) - .then(function() { - $scope.membershipIn(); // loop - }); - } + throw err; }); }; // Send membership IN - $scope.membershipIn = function() { + $scope.membershipIn = function(keepSelf) { $scope.hideActionsPopover(); - if ($scope.formData.isMember) { + if (csWallet.isMember()) { return UIUtils.alert.info("INFO.NOT_NEED_MEMBERSHIP"); } - return $scope.showUidPopup() - .then(function (uid) { - UIUtils.loading.show(); - // If uid changed, or self blockUid not retrieve : do self() first - if (!$scope.formData.blockUid || uid != $scope.formData.uid) { - $scope.formData.blockUid = null; - $scope.formData.uid = uid; - csWallet.self(uid, false/*do NOT load membership here*/) - .then(function() { - $scope.doMembershipIn(); - }) - .catch(function(err){ - UIUtils.onError('ERROR.SEND_IDENTITY_FAILED')(err) - .then(function() { - $scope.membershipIn(); // loop - }); - }); - } - else { - $scope.doMembershipIn(); - } - }) - .catch(function(err){ - UIUtils.loading.hide(); - UIUtils.alert.info(err); - $scope.membershipIn(); // loop - }); + // Select uid (or reuse it) + return ((keepSelf && !!$scope.formData.blockUid) ? + $q.when($scope.formData.uid) : + $scope.showUidPopup()) + + // Ask user confirmation + .then(function(uid) { + return UIUtils.alert.confirm("CONFIRM.MEMBERSHIP") + .then(function(confirm) { + if (!confirm) throw 'CANCELLED'; + return uid; + }); + }) + + // Send self (identity) - if need + .then(function (uid) { + UIUtils.loading.show(); + + // If uid changed, or self blockUid not retrieve : do self() first + if (!$scope.formData.blockUid || uid != $scope.formData.uid) { + $scope.formData.blockUid = null; + $scope.formData.uid = uid; + + return csWallet.self(uid, false/*do NOT load membership here*/); + } + }) + + // Send membership + .then($scope.doMembershipIn) + .catch(function(err) { + if (err == 'CANCELLED') return; + if (!csWallet.data.uid) { + UIUtils.onError('ERROR.SEND_IDENTITY_FAILED')(err); + } + else { + UIUtils.onError('ERROR.SEND_MEMBERSHIP_IN_FAILED')(err); + } + }); }; // Send membership OUT @@ -319,9 +327,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, .catch(function(err){ if (err == 'CANCELLED') return; UIUtils.loading.hide(); - UIUtils.alert.error(err) - // loop - .then($scope.renewMembership); + UIUtils.alert.error(err); }); }; @@ -332,27 +338,27 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, $scope.fixIdentity = function() { if (!$scope.formData.uid) return; - return $translate('CONFIRM.FIX_IDENTITY', {uid: $scope.formData.uid}) - .then(function(message) { - return UIUtils.alert.confirm(message); + return $q.all([ + csWallet.auth(), + $translate('CONFIRM.FIX_IDENTITY', {uid: $scope.formData.uid}) + ]) + .then(function(res) { + return UIUtils.alert.confirm(res[1]); }) .then(function(confirm) { if (!confirm) return; UIUtils.loading.show(); - // Reset membership data + // Reset self data $scope.formData.blockUid = null; + // Reset membership data $scope.formData.sigDate = null; return csWallet.self($scope.formData.uid); }) - .then(function() { - return $scope.doMembershipIn(); - }) + .then($scope.doMembershipIn) .catch(function(err){ + if (err == 'CANCELLED') return; UIUtils.loading.hide(); - UIUtils.alert.error(err) - .then(function() { - $scope.fixIdentity(); // loop - }); + UIUtils.alert.error(err); }); }; @@ -362,22 +368,28 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, $scope.fixMembership = function() { if (!$scope.formData.uid) return; - return UIUtils.alert.confirm("CONFIRM.FIX_MEMBERSHIP") + if (csWallet.isMember()) { + return UIUtils.alert.info("INFO.NOT_NEED_MEMBERSHIP"); + } + + return csWallet.auth() + .then(function() { + UIUtils.alert.confirm("CONFIRM.FIX_MEMBERSHIP"); + }) .then(function(confirm) { if (!confirm) return; UIUtils.loading.show(); - // Reset membership data + // Reset self data $scope.formData.blockUid = null; + // Reset membership data $scope.formData.sigDate = null; - return Wallet.self($scope.formData.uid, false/*do NOT load membership here*/); - }) - .then(function() { - return $scope.doMembershipIn(); + return csWallet.self($scope.formData.uid, false/*do NOT load membership here*/); }) + .then($scope.doMembershipIn) .catch(function(err){ + if (err == 'CANCELLED') return; UIUtils.loading.hide(); - UIUtils.alert.info(err); - $scope.fixMembership(); // loop + UIUtils.alert.error(err); }); }; @@ -389,6 +401,9 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, if (event == 'renew') { $scope.renewMembership(); } + else if (event == 'membership') { + $scope.membershipIn(true/*keep self*/); + } else if (event == 'fixMembership') { $scope.fixMembership(); } @@ -419,29 +434,10 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, $scope.startWalletTour = function() { $scope.hideActionsPopover(); return csHelp.wallet.tour(); - //return $scope.showHelpTip(0, true); }; - $scope.showHelpTip = function(index, isTour) { + $scope.showHelpTip = function() { return csHelp.wallet.helptip(); - /*index = angular.isDefined(index) ? index : csSettings.data.helptip.wallet; - isTour = angular.isDefined(isTour) ? isTour : false; - - if (index < 0 || index > 3/!*max step*!/) return; - - // Create a new scope for the tour controller - var helptipScope = $scope.createHelptipScope(isTour); - if (!helptipScope) return; // could be undefined, if a global tour already is already started - helptipScope.tour = isTour; - - return helptipScope.startWalletTour(index, false) - .then(function(endIndex) { - helptipScope.$destroy(); - if (!isTour) { - csSettings.data.helptip.wallet = endIndex; - csSettings.store(); - } - });*/ }; $scope.showQRCode = function(id, text, timeout) { @@ -534,7 +530,28 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, $scope.showSecurityModal = function(){ $scope.hideActionsPopover(); - Modals.showAccountSecurity(); + return Modals.showAccountSecurity(); + }; + + $scope.showSelectIdentitiesModal = function(){ + $scope.hideActionsPopover(); + + return Modals.showSelectPubkeyIdentity({ + identities: [$scope.formData.requirements].concat($scope.formData.requirements.alternatives) + }) + .then(function(idty) { + if (!idty || !idty.uid) return; + + $scope.loading = true; + + // Set self (= uid + blockUid) + return csWallet.setSelf(idty.uid, idty.blockUid) + .then(function() { + $scope.loading=false; + $scope.updateView(); + UIUtils.loading.hide(); + }); + }); }; } @@ -777,7 +794,7 @@ function WalletTxErrorController($scope, UIUtils, csWallet) { } -function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, CryptoUtils){ +function WalletSecurityModalController($scope, UIUtils, csWallet, $translate){ $scope.slides = { slider: null, @@ -1132,3 +1149,5 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr }; } + + diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js index 52ea3821d70cb08b3a1867f43b9006bcea0d4bf5..730557fca9d419342d1e61257405db2d9cd83ff8 100644 --- a/www/js/controllers/wot-controllers.js +++ b/www/js/controllers/wot-controllers.js @@ -146,6 +146,7 @@ angular.module('cesium.wot.controllers', ['cesium.services']) .controller('WotCertificationsViewCtrl', WotCertificationsViewController) + .controller('WotSelectPubkeyIdentityModalCtrl', WotSelectPubkeyIdentityModalController) ; @@ -945,7 +946,7 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $ /** * Identity view controller - should extend WotIdentityAbstractCtrl */ -function WotIdentityViewController($scope, $rootScope, $controller, $timeout, UIUtils, csWallet, csTx) { +function WotIdentityViewController($scope, $rootScope, $controller, $timeout, UIUtils, csWallet) { 'ngInject'; // Initialize the super class and extend it. angular.extend(this, $controller('WotIdentityAbstractCtrl', {$scope: $scope})); @@ -1264,3 +1265,46 @@ function WotCertificationsViewController($scope, $rootScope, $controller, csSett } +/** + * Select identities from a pubkey (yusfull when many self on the same pubkey) + * @param $scope + * @param $q + * @param csWot + * @param parameters + * @constructor + */ +function WotSelectPubkeyIdentityModalController($scope, $q, csWot, parameters) { + + $scope.loading = true; + + $scope.load = function() { + // If list of identities given by parameters: use it + if (parameters && parameters.identities) { + $scope.identities = parameters.identities; + $scope.pubkey = $scope.identities[0].pubkey; + $scope.loading = false; + return $q.when(); + } + + // Or load from pubkey + $scope.pubkey = parameters && parameters.pubkey; + if (!pubkey) { + return $q.reject('Missing parameters: [pubkey] or [identities]'); + } + + return csWot.loadRequirements({pubkey: pubkey, uid: uid}) + .then(function(data) { + if (data && data.requirements) { + $scope.identities = data.requirements; + if (data.requirements.alternatives) { + $scope.identities = [data.requirements].concat(data.requirements.alternatives); + } + else { + $scope.identities = [data.requirements]; + } + } + $scope.loading = false; + }); + }; + $scope.$on('modal.shown', $scope.load); +} diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js index b0d86f152c3fc0817b201d1b58f0b0fc98678a0b..5ffe61da021a01d0ac810b47b61d6980af32f2b1 100644 --- a/www/js/services/device-services.js +++ b/www/js/services/device-services.js @@ -157,7 +157,6 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti var property = paths.length && paths[paths.length-1]; paths.reduce(function(res, path) { if (path == property) { - console.log("setting value"); res[property] = value; return; } diff --git a/www/js/services/modal-services.js b/www/js/services/modal-services.js index 9f2deaf32f45635ccbfd0cfe6cbab5aa6ef3abbd..3ad9f7cb029e44b487afafcf8d607f28ad3e4cc9 100644 --- a/www/js/services/modal-services.js +++ b/www/js/services/modal-services.js @@ -131,7 +131,7 @@ angular.module('cesium.modal.services', []) }; }) -.factory('Modals', function(ModalUtils, UIUtils) { +.factory('Modals', function($rootScope, ModalUtils, UIUtils) { 'ngInject'; function showTransfer(parameters) { @@ -204,6 +204,11 @@ angular.module('cesium.modal.services', []) parameters); } + function showSelectPubkeyIdentity(parameters) { + return ModalUtils.show('templates/wot/modal_select_pubkey_identity.html', 'WotSelectPubkeyIdentityModalCtrl', + parameters); + } + return { showTransfer: showTransfer, showLogin: showLogin, @@ -216,7 +221,8 @@ angular.module('cesium.modal.services', []) showJoinWallet: showJoinWallet, showHelp: showHelp, showAccountSecurity: showAccountSecurity, - showLicense: showLicense + showLicense: showLicense, + showSelectPubkeyIdentity: showSelectPubkeyIdentity }; }); diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js index 17474709e19f15288a61c2e8eb7bd8f6884221b2..fbe78518b08e9fa53191c8f76b4eaaf81548819b 100644 --- a/www/js/services/wallet-services.js +++ b/www/js/services/wallet-services.js @@ -15,6 +15,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se // @Deprecated OLD_STORAGE_KEY: 'CESIUM_DATA', STORAGE_PUBKEY: 'pubkey', + STORAGE_UID: 'uid', STORAGE_SECKEY: 'seckey', /* Need for compat with old currencies (test_net and sou) */ TX_VERSION: csConfig.compatProtocol_0_80 ? 3 : BMA.constants.PROTOCOL_VERSION, @@ -72,8 +73,8 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se resetTxAndSources = function(){ // reset sources data - data.sources = []; - data.sourcesIndexByKey = {}; + data.sources = undefined; + data.sourcesIndexByKey = undefined; data.balance = 0; // reset TX data data.tx = data.tx || {}; @@ -93,6 +94,8 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se }, addSources = function(sources) { + data.sources = data.sources || []; + data.sourcesIndexByKey = data.sourcesIndexByKey || {}; _(sources).forEach(function(src) { addSource(src, data.sources, data.sourcesIndexByKey); }); @@ -176,7 +179,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se // Load data if need // If user just login, force data full load (even if min data asked) // because the user can wait (after the login modal) - var loadOptions = !needLogin && options && options.minData ? {minData: true} : undefined; + var loadOptions = !needLogin && options && options.minData ? {minData: true} : undefined/*=load all*/; if (!isDataLoaded(loadOptions)) { return loadData(loadOptions); } @@ -264,7 +267,6 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se return !!(data.pubkey && data.keypair && data.keypair.signSk); }, - getKeypair = function(options) { if (!started) { return (startPromise || start()) @@ -295,18 +297,20 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se isNeverUsed = function() { if (!data.loaded) return undefined; // undefined if not full loaded - return !data.pubkey || ( + return !data.pubkey || !( // Check registration - !data.isMember && - !data.requirements.pendingMembership && - !data.requirements.wasMember && + data.isMember || + data.requirements.pendingMembership || + !data.requirements.needSelf || + data.requirements.wasMember || // Check TX history - !data.tx.history.length && - !data.tx.pendings.length && + data.tx.history.length || + data.tx.pendings.length || // Check extended data (name+avatar) - !data.name && !data.avatar + data.name || + data.avatar ); }, @@ -334,6 +338,15 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se // Use local storage for pubkey jobs.push(localStorage.put(constants.STORAGE_PUBKEY, data.pubkey)); + + // Use local storage for uid - fix #625 + if (data.uid) { + jobs.push(localStorage.put(constants.STORAGE_UID, data.uid)); + } + else { + jobs.push(localStorage.put(constants.STORAGE_UID, null)); + } + // Clean old storage jobs.push(localStorage.put(constants.OLD_STORAGE_KEY, null)); @@ -345,6 +358,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se return $q.all([ sessionStorage.put(constants.STORAGE_SECKEY, null), localStorage.put(constants.STORAGE_PUBKEY, null), + localStorage.put(constants.STORAGE_UID, null), // Clean old storage localStorage.put(constants.OLD_STORAGE_KEY, null) ]); @@ -354,6 +368,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se return $q.all([ sessionStorage.put(constants.STORAGE_SECKEY, null), localStorage.put(constants.STORAGE_PUBKEY, null), + localStorage.put(constants.STORAGE_UID, null), // Clean old storage localStorage.put(constants.OLD_STORAGE_KEY, null) ]); @@ -364,11 +379,13 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se restore = function() { return $q.all([ sessionStorage.get(constants.STORAGE_SECKEY), - localStorage.get(constants.STORAGE_PUBKEY) + localStorage.get(constants.STORAGE_PUBKEY), + localStorage.get(constants.STORAGE_UID) ]) .then(function(res) { var seckey = res[0]; var pubkey = res[1]; + var uid = res[2]; if (!pubkey || pubkey == 'null') return; var keypair; @@ -386,6 +403,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se } data.pubkey = pubkey; + data.uid = uid; data.keypair = keypair || {signPk: undefined, signSk: undefined}; console.debug('[wallet] Restore \'{0}\' from local storage.'.format(pubkey.substring(0,8))); @@ -402,93 +420,18 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se return data; }, - resetRequirements = function() { - data.requirements = { - needSelf: true, - needMembership: true, - canMembershipOut: false, - needRenew: false, - pendingMembership: false, - wasMember: false, - certificationCount: 0, - needCertifications: false, - needCertificationCount: 0, - willNeedCertificationCount: 0 - }; - data.blockUid = null; - data.isMember = false; - data.sigDate = null; - cleanEventsByContext('requirements'); - }, - loadRequirements = function() { - return $q(function(resolve, reject) { + // Clean existing events + cleanEventsByContext('requirements'); - // Clean existing events - cleanEventsByContext('requirements'); + // Get requirements + return csWot.loadRequirements(data) + .then(function(){ - // Get requirements - BMA.wot.requirements({pubkey: data.pubkey}) - .then(function(res){ - if (!res.identities || res.identities.length === 0) { - resetRequirements(); - resolve(); - return; - } - // Sort to select the best identity - if (res.identities.length > 1) { - // Select the best identity, by sorting using this order - // - same wallet uid - // - is member - // - has a pending membership - // - is not expired (in sandbox) - // - is not outdistanced - // - if has certifications - // max(count(certification) - // else - // max(membershipPendingExpiresIn) = must recent membership - res.identities = _.sortBy(res.identities, function(idty) { - var score = 0; - score += (10000000000 * ((data.uid && idty.uid === data.uid) ? 1 : 0)); - score += (1000000000 * (idty.membershipExpiresIn > 0 ? 1 : 0)); - score += (100000000 * (idty.membershipPendingExpiresIn > 0 ? 1 : 0)); - score += (10000000 * (!idty.expired ? 1 : 0)); - score += (1000000 * (!idty.outdistanced ? 1 : 0)); - score += (100000 * (idty.wasMember ? 1 : 0)); - var certCount = !idty.expired && idty.certifications ? idty.certifications.length : 0; - score += (1 * (certCount ? certCount : 0)); - score += (1 * (!certCount && idty.membershipPendingExpiresIn > 0 ? idty.membershipPendingExpiresIn/1000 : 0)); - return -score; - }); - console.debug('Found {0} identities. Will selected the best one'.format(res.identities.length)); - } + if (!data.requirements.uid) return; - // Select the first identity - var idty = res.identities[0]; - - // Compute useful fields - idty.needSelf = false; - idty.wasMember = angular.isDefined(idty.wasMember) ? idty.wasMember : false; // Compat with Duniter 0.9 - idty.needMembership = (idty.membershipExpiresIn <= 0 && idty.membershipPendingExpiresIn <= 0 && !idty.wasMember); - idty.needRenew = (!idty.needMembership && - idty.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership && - idty.membershipPendingExpiresIn <= 0) || - (idty.wasMember && idty.membershipExpiresIn === 0 && - idty.membershipPendingExpiresIn === 0); - idty.canMembershipOut = (idty.membershipExpiresIn > 0); - idty.pendingMembership = (idty.membershipExpiresIn <= 0 && idty.membershipPendingExpiresIn > 0); - idty.certificationCount = (idty.certifications) ? idty.certifications.length : 0; - idty.willExpireCertificationCount = idty.certifications ? idty.certifications.reduce(function(count, cert){ - return count + (cert.expiresIn <= csSettings.data.timeWarningExpire ? 1 : 0); - }, 0) : 0; - idty.pendingRevocation = !idty.revoked && !!idty.revocation_sig; - - data.requirements = idty; - data.uid = idty.uid; - data.blockUid = idty.meta.timestamp; - data.isMember = (idty.membershipExpiresIn > 0); - - var blockParts = idty.meta.timestamp.split('-', 2); + // Get sigDate + var blockParts = data.requirements.blockUid.split('-', 2); var blockNumber = parseInt(blockParts[0]); var blockHash = blockParts[1]; // Retrieve registration date @@ -506,32 +449,17 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se addEvent({type: 'error', message: 'ERROR.WALLET_IDENTITY_EXPIRED', context: 'requirements'}); console.debug("Identity expired for uid={0}.".format(data.uid)); } - resolve(); }) .catch(function(err){ // Special case for currency init (root block not exists): use now if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND && blockNumber === 0) { data.sigDate = Math.trunc(new Date().getTime() / 1000); - resolve(); } else { - reject(err); + throw err; } }); - }) - .catch(function(err) { - resetRequirements(); - // If not a member: continue - if (!!err && - (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER || - err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) { - resolve(); - } - else { - reject(err); - } }); - }); }, loadTxAndSources = function(fromTime) { @@ -546,15 +474,8 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se }); }, - // Must be call after loadCurrency() and loadRequirements() - finishLoadRequirements = function(currency) { - currency = currency || csCurrency.data; - data.requirements.needCertificationCount = (!data.requirements.needMembership && (data.requirements.certificationCount < currency.parameters.sigQty)) ? - (currency.parameters.sigQty - data.requirements.certificationCount) : 0; - data.requirements.willNeedCertificationCount = (!data.requirements.needMembership && - data.requirements.needCertificationCount === 0 && (data.requirements.certificationCount - data.requirements.willExpireCertificationCount) < currency.parameters.sigQty) ? - (currency.parameters.sigQty - data.requirements.certificationCount + data.requirements.willExpireCertificationCount) : 0; - data.requirements.pendingCertificationCount = 0 ; // init to 0, because not loaded here (see wot-service.js) + // Generate events from requirements + addEvents = function() { // Add user events if (data.requirements.revoked) { @@ -567,6 +488,10 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se if (data.requirements.pendingMembership) { addEvent({type:'pending', message: 'ACCOUNT.WAITING_MEMBERSHIP', context: 'requirements'}); } + // If user has send a SELF, ask for membership - fix #625 + else if (!data.requirements.needSelf && data.requirements.needMembership){ + addEvent({type:'warn', message: 'ACCOUNT.NO_WAITING_MEMBERSHIP', context: 'requirements'}); + } if (data.requirements.needCertificationCount > 0) { addEvent({type:'warn', message: 'ACCOUNT.WAITING_CERTIFICATIONS', messageParams: data.requirements, context: 'requirements'}); } @@ -609,7 +534,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se var alertIfUnusedWallet = !csCurrency.data.initPhase && (!csSettings.data.wallet || csSettings.data.wallet.alertIfUnusedWallet) && !data.loaded && (!options || !options.minData); - // Make to load once + // Make sure to load once at a time if (loadPromise) { return loadPromise.then(function() { return isDataLoaded(options) ? data : refreshData(options); @@ -668,9 +593,6 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se return $q.all([ - // Get currency - csCurrency.get(), - // Get requirements loadRequirements(), @@ -680,9 +602,10 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se // Load sigStock loadSigStock() ]) - .then(function(res) { - var currency = res[0]; - finishLoadRequirements(currency); // must be call after csCurrency.get() and loadRequirements() + .then(function() { + + // Load wallet events + addEvents(); // API extension return api.data.raisePromise.load(data) @@ -731,19 +654,17 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se var jobs = []; - // Reset events - cleanEventsByContext('requirements'); - // Get requirements if (options.requirements) { - jobs.push($q.all([ - csCurrency.get(), - loadRequirements() - ]) - .then(function(res) { - var currency = res[0]; - finishLoadRequirements(currency); - })); + // Reset events + cleanEventsByContext('requirements'); + + jobs.push( + loadRequirements() + + // Add wallet events + .then(addEvents) + ); } if (options.sources || (options.tx && options.tx.enable)) { @@ -764,6 +685,30 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se }); }, + setSelf = function(uid, blockUid){ + // Skip if same self + if (data.uid == uid && (!blockUid || data.blockUid == blockUid)) return $q.when(); + + // Data not loaded + if (!data.loaded) { + return !loadPromise ? + // If no pending load: ok + $q.when() : + // If a load is running: force a reload + loadPromise.then(function() { + return setSelf(uid, blockUid); // loop + }); + } + + data.uid = uid; + data.blockUid = blockUid; + + // Refresh requirements + return refreshData({requirements: true, sigStock: true}) + // Store (to remember the new uid) + .then(store); + }, + isBase = function(amount, base) { if (!base) return true; // no base if (amount < Math.pow(10, base)) return false; // too small @@ -1270,9 +1215,9 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se return loadRequirements(); }, 1000); // waiting for node to process membership doc }) - .then(function() { - finishLoadRequirements(); - }); + + // Add wallet events + .then(addEvents); }; }, @@ -1488,9 +1433,9 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se }, 1000); // waiting for node to process membership doc }) - .then(function() { - finishLoadRequirements(); - }) + // Add wallet events + .then(addEvents) + .catch(function(err) { if (err && err.ucode == BMA.errorCodes.REVOCATION_ALREADY_REGISTERED) { // Already registered by node: just add an event @@ -1505,19 +1450,18 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se revokeWithFile = function(revocation){ return $q.all([ - csCurrency.get(), BMA.wot.revoke({revocation: revocation}) ]) // Reload requirements .then(function(res) { - var currency = res[0]; if (isLogin()) { return $timeout(function () { return loadRequirements(); }, 1000) // waiting for node to process membership doc - .then(function () { - finishLoadRequirements(currency); - }) + + // Add wallet events + .then(addEvents) + .catch(function (err) { if (err && err.ucode == BMA.errorCodes.REVOCATION_ALREADY_REGISTERED) { // Already registered by node: just add an event @@ -1750,6 +1694,10 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se isAuth: isAuth, getKeypair: getKeypair, hasSelf: hasSelf, + setSelf: setSelf, + isMember: function() { + return data.isMember; + }, isDataLoaded : isDataLoaded, isNeverUsed: isNeverUsed, isNew: isNew, diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js index 80bb313cc0281a29db3fd272be8c0cab14e673cf..eaf473c83121e9a140ee1b9a6aa7fdfc26358453 100644 --- a/www/js/services/wot-services.js +++ b/www/js/services/wot-services.js @@ -56,11 +56,85 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c return certifications; }, - loadRequirements = function(pubkey, uid) { - if (!pubkey) return $q.when({}); - // Get requirements - return BMA.wot.requirements({pubkey: pubkey}) + _resetRequirements = function(data) { + data.requirements = { + needSelf: true, + needMembership: true, + canMembershipOut: false, + needRenew: false, + pendingMembership: false, + wasMember: false, + certificationCount: 0, + needCertifications: false, + needCertificationCount: 0, + willNeedCertificationCount: 0, + alternatives: undefined + }; + data.blockUid = null; + data.isMember = false; + data.sigDate = null; + }, + + _fillRequirements = function(requirements, currencyParameters) { + // Add useful custom fields + requirements.hasSelf = true; + requirements.needSelf = false; + requirements.wasMember = angular.isDefined(requirements.wasMember) ? requirements.wasMember : false; // Compat with Duniter 0.9 + requirements.needMembership = (requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn <= 0 && !requirements.wasMember); + requirements.needRenew = (!requirements.needMembership && + requirements.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership && + requirements.membershipPendingExpiresIn <= 0) || + (requirements.wasMember && requirements.membershipExpiresIn === 0 && + requirements.membershipPendingExpiresIn === 0); + requirements.canMembershipOut = (requirements.membershipExpiresIn > 0); + requirements.pendingMembership = (requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn > 0); + requirements.isMember = (requirements.membershipExpiresIn > 0); + requirements.blockUid = requirements.meta.timestamp; + // Force certification count to 0, is not a member yet - fix #269 + requirements.certificationCount = (requirements.isMember && requirements.certifications) ? requirements.certifications.length : 0; + requirements.willExpireCertificationCount = requirements.certifications ? requirements.certifications.reduce(function(count, cert){ + return count + (cert.expiresIn <= csSettings.data.timeWarningExpire ? 1 : 0); + }, 0) : 0; + requirements.willExpire = requirements.willExpireCertificationCount > 0; + requirements.pendingRevocation = !requirements.revoked && !!requirements.revocation_sig; + + // Fix pending certifications count - Fix #624 + if (!requirements.isMember && !requirements.wasMember) { + var certifiers = _.union( + _.pluck(requirements.pendingCerts || [], 'from'), + _.pluck(requirements.certifications || [], 'from') + ); + requirements.pendingCertificationCount = _.size(certifiers); + } + else { + requirements.pendingCertificationCount = angular.isDefined(requirements.pendingCerts) ? requirements.pendingCerts.length : 0 ; + } + + // Compute + requirements.needCertificationCount = (!requirements.needSelf && (requirements.certificationCount < currencyParameters.sigQty)) ? + (currencyParameters.sigQty - requirements.certificationCount) : 0; + requirements.willNeedCertificationCount = (!requirements.needMembership && !requirements.needCertificationCount && + (requirements.certificationCount - requirements.willExpireCertificationCount) < currencyParameters.sigQty) ? + (currencyParameters.sigQty - requirements.certificationCount + requirements.willExpireCertificationCount) : 0; + + + return requirements; + }, + + loadRequirements = function(data) { + if (!data || (!data.pubkey && !data.uid)) return $q.when(data); + + return $q.all([ + // Get currency + csCurrency.get(), + // Get requirements + BMA.wot.requirements({pubkey: data.pubkey||data.uid}) + ]) .then(function(res){ + var currency = res[0]; + + res = res[1]; + if (!res.identities || !res.identities.length) return; // Sort to select the best identity @@ -77,60 +151,53 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c // max(membershipPendingExpiresIn) = must recent membership res.identities = _.sortBy(res.identities, function(idty) { var score = 0; - score += (10000000000 * ((uid && idty.uid === uid) ? 1 : 0)); + score += (10000000000 * ((data.uid && idty.uid === data.uid) ? 1 : 0)); + score += (10000000000 * ((data.blockUid && idty.meta && idty.meta.timestamp === data.blockUid) ? 1 : 0)); score += (1000000000 * (idty.membershipExpiresIn > 0 ? 1 : 0)); score += (100000000 * (idty.membershipPendingExpiresIn > 0 ? 1 : 0)); score += (10000000 * (!idty.expired ? 1 : 0)); score += (1000000 * (!idty.outdistanced ? 1 : 0)); + score += (100000 * (idty.wasMember ? 1 : 0)); var certCount = !idty.expired && idty.certifications ? idty.certifications.length : 0; score += (1 * (certCount ? certCount : 0)); score += (1 * (!certCount && idty.membershipPendingExpiresIn > 0 ? idty.membershipPendingExpiresIn/1000 : 0)); return -score; }); - console.debug('Found {0} identities. Will selected the best one'.format(res.identities.length)); + console.debug('[wot] Found {0} identities. Will selected the best one'.format(res.identities.length)); } - var requirements = res.identities[0]; - // Add useful custom fields - requirements.hasSelf = true; - requirements.needMembership = (requirements.membershipExpiresIn <= 0 && - requirements.membershipPendingExpiresIn <= 0 ); - requirements.needRenew = (!requirements.needMembership && - requirements.membershipExpiresIn <= csSettings.data.timeWarningExpire && - requirements.membershipPendingExpiresIn <= 0 ); - requirements.canMembershipOut = (requirements.membershipExpiresIn > 0); - requirements.pendingMembership = (requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn > 0); - requirements.isMember = (requirements.membershipExpiresIn > 0); - // Force certification count to 0, is not a member yet - fix #269 - requirements.certificationCount = (requirements.isMember && requirements.certifications) ? requirements.certifications.length : 0; - requirements.willExpireCertificationCount = requirements.certifications ? requirements.certifications.reduce(function(count, cert){ - if (cert.expiresIn <= csSettings.data.timeWarningExpire) { - cert.willExpire = true; - return count + 1; - } - return count; - }, 0) : 0; - requirements.pendingRevocation = !requirements.revoked && !!requirements.revocation_sig; - return requirements; + // Select the first identity + var requirements = _fillRequirements(res.identities[0], currency.parameters); + + data.requirements = requirements; + data.pubkey = requirements.pubkey; + data.uid = requirements.uid; + data.isMember = requirements.isMember; + data.blockUid = requirements.blockUid; + + // Prepare alternatives identities if any + if (!requirements.isMember && !requirements.wasMember && res.identities.length > 1) { + requirements.alternatives = res.identities.splice(1); + _.forEach(requirements.alternatives, function(requirements) { + _fillRequirements(requirements, currency.parameters); + }); + } + + // TODO : get sigDate from blockUid ?? + + return data; }) .catch(function(err) { + _resetRequirements(data); // If not a member: continue if (!!err && (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER || err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) { - return { - hasSelf: false, - needMembership: true, - canMembershipOut: false, - needRenew: false, - pendingMembership: false, - needCertifications: false, - needCertificationCount: 0, - willNeedCertificationCount: 0 - }; + return data; } throw err; - }); + }) + ; }, loadIdentityByLookup = function(pubkey, uid) { @@ -177,6 +244,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c return res.uids.reduce(function(certsMap, idty) { var idtyFullKey = idty.uid + '-' + (idty.meta ? idty.meta.timestamp : ''); certsMap[idtyFullKey] = idty.others.reduce(function(certs, cert) { + var certFullKey = idtyFullKey + '-' + cert.pubkey; var result = { pubkey: cert.pubkey, uid: cert.uids[0], @@ -187,12 +255,12 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c isMember: cert.isMember, wasMember: cert.wasMember, }; - if (!certPubkeys[cert.pubkey]) { - certPubkeys[cert.pubkey] = result; + if (!certPubkeys[certFullKey]) { + certPubkeys[certFullKey] = result; } else { // if duplicated cert: keep the most recent - if (result.cert_time.block > certPubkeys[cert.pubkey].cert_time.block) { - certPubkeys[cert.pubkey] = result; + if (result.cert_time.block > certPubkeys[certFullKey].cert_time.block) { + certPubkeys[certFullKey] = result; certs.splice(_.findIndex(certs, {pubkey: cert.pubkey}), 1, result); return certs; } @@ -451,18 +519,9 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c ; }, - finishLoadRequirements = function(data) { - data.requirements.needCertificationCount = (!data.requirements.needMembership && (data.requirements.certificationCount < data.sigQty)) ? - (data.sigQty - data.requirements.certificationCount) : 0; - data.requirements.willNeedCertificationCount = (!data.requirements.needMembership && !data.requirements.needCertificationCount && - (data.requirements.certificationCount - data.requirements.willExpireCertificationCount) < data.sigQty) ? - (data.sigQty - data.requirements.certificationCount + data.requirements.willExpireCertificationCount) : 0; - data.requirements.pendingCertificationCount = data.received_cert_pending ? data.received_cert_pending.length : 0; + // Add events on given account + addEvents = function(data) { - // Use /wot/lookup.revoked when requirements not filled - data.requirements.revoked = angular.isDefined(data.requirements.revoked) ? data.requirements.revoked : data.revoked; - - // Add events if (data.requirements.revoked) { delete data.hasBadSelfBlock; addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_REVOKED', messageParams: {revocationTime: data.revocationTime}}); @@ -487,6 +546,10 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c addEvent(data, {type: 'error', message: 'INFO.IDENTITY_WILL_MISSING_CERTIFICATIONS', messageParams: data.requirements}); console.debug("[wot] Identity {0} will need {1} certification(s)".format(data.uid, data.requirements.willNeedCertificationCount)); } + else if (!data.requirements.needSelf && data.requirements.needMembership) { + addEvent(data, {type: 'error', message: 'INFO.IDENTITY_NEED_MEMBERSHIP'}); + console.debug("[wot] Identity {0} has a self but no membership".format(data.uid)); + } }, loadData = function(pubkey, withCache, uid, force) { @@ -510,11 +573,16 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c return $q.when(data); } console.debug("[wot] Loading identity " + pubkey.substring(0, 8) + "..."); - data = {pubkey: pubkey}; + data = { + pubkey: pubkey, + uid: uid + }; } else { console.debug("[wot] Loading identity from uid " + uid); - data = {}; + data = { + uid: uid + }; } var now = new Date().getTime(); @@ -527,8 +595,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c BMA.blockchain.parameters() .then(function(res) { parameters = res; - data.sigQty = parameters.sigQty; - data.sigStock = parameters.sigStock; + }), // Get current time BMA.blockchain.current() @@ -546,11 +613,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c }), // Get requirements - loadRequirements(pubkey, uid) - .then(function (requirements) { - data.requirements = requirements; - data.isMember = requirements.isMember; - }), + loadRequirements(data), // Get identity using lookup loadIdentityByLookup(pubkey, uid) @@ -582,8 +645,14 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c ]); }) .then(function() { + // Add compute some additional requirements (that required all data like certifications) - finishLoadRequirements(data); + data.requirements.pendingCertificationCount = data.received_cert_pending ? data.received_cert_pending.length : data.requirements.pendingCertificationCount; + // Use /wot/lookup.revoked when requirements not filled + data.requirements.revoked = angular.isDefined(data.requirements.revoked) ? data.requirements.revoked : data.revoked; + + // Add account events + addEvents(data); // API extension return api.data.raisePromise.load(data) @@ -1005,6 +1074,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c return { id: id, load: loadData, + loadRequirements: loadRequirements, search: search, newcomers: getNewcomers, pending: getPending, diff --git a/www/templates/wallet/popover_actions.html b/www/templates/wallet/popover_actions.html index 108cf1b0b5bb909228325188b9f72fdf94f86bdc..8a5678635752f6665540e2fd728401b6322f5d0c 100644 --- a/www/templates/wallet/popover_actions.html +++ b/www/templates/wallet/popover_actions.html @@ -11,6 +11,16 @@ {{'COMMON.BTN_SHARE' | translate}} </a> + + <!-- alternatives identities --> + <a class="item item-icon-left ink" + ng-if="walletData.requirements.alternatives" + ng-click="showSelectIdentitiesModal()"> + <i class="icon ion-person"></i> + <b class="icon-secondary ion-loop" style="margin-top: 4px; left: 15px;"></b> + {{'ACCOUNT.BTN_SELECT_ALTERNATIVES_IDENTITIES' | translate}} + </a> + <!-- identity --> <a class="item item-icon-left ink" ng-if="walletData.requirements.needSelf" diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html index dbcc318b4108926b921033fe36ac1a20cff0574a..43522adbb77c245bb3dd1791b12950dfe9ca09e6 100644 --- a/www/templates/wallet/view_wallet.html +++ b/www/templates/wallet/view_wallet.html @@ -124,13 +124,13 @@ <!-- Certifications --> <a id="helptip-wallet-certifications" class="item item-icon-left item-icon-right item-text-wrap ink" - ng-if="formData.isMember||formData.requirements.pendingMembership" + ng-if="formData.isMember||formData.requirements.pendingMembership||!formData.requirements.needSelf" ng-click="showCertifications()"> <i class="icon ion-ribbon-b"></i> <b ng-if="formData.requirements.isSentry" class="ion-star icon-secondary" style="color: yellow; font-size: 16px; left: 25px; top: -7px;"></b> {{:locale:'ACCOUNT.CERTIFICATION_COUNT'|translate}} <cs-badge-certification requirements="formData.requirements" - parameters="{sigQty: formData.parameters.sigQty}"> + parameters="::currency.parameters"> </cs-badge-certification> <i class="gray icon ion-ios-arrow-right"></i> </a> diff --git a/www/templates/wot/items_given_certifications.html b/www/templates/wot/items_given_certifications.html index b009bd55b1a12726173df42631b0160e468c2028..68d37d34a399af15bd2b39ec5418f603905420c1 100644 --- a/www/templates/wot/items_given_certifications.html +++ b/www/templates/wot/items_given_certifications.html @@ -11,7 +11,7 @@ <span translate>WOT.GIVEN_CERTIFICATIONS.SENT</span> <cs-badge-given-certification identity="formData" - parameters="{sigStock: formData.sigStock}"> + parameters="$root.currency.parameters"> </cs-badge-given-certification> </div> diff --git a/www/templates/wot/items_received_certifications.html b/www/templates/wot/items_received_certifications.html index 2bcbc83a615cfac6b5196ec112a45b9f31ba2f86..b09d4864e3efd6f75614a508afc7b28af441fc4b 100644 --- a/www/templates/wot/items_received_certifications.html +++ b/www/templates/wot/items_received_certifications.html @@ -15,7 +15,7 @@ <cs-badge-certification cs-id="helptip-wot-view-certifications-count" requirements="formData.requirements" - parameters="{sigQty: formData.sigQty}"> + parameters="$root.currency.parameters"> </cs-badge-certification> </div> diff --git a/www/templates/wot/modal_select_pubkey_identity.html b/www/templates/wot/modal_select_pubkey_identity.html new file mode 100644 index 0000000000000000000000000000000000000000..9c7cbfb8396d1d9320e14f1b1e61319d3c89a088 --- /dev/null +++ b/www/templates/wot/modal_select_pubkey_identity.html @@ -0,0 +1,48 @@ +<ion-modal-view id="transfer" class="modal-full-height modal-transfer"> + <ion-header-bar class="bar-positive"> + <button class="button button-clear" ng-click="closeModal()" translate>COMMON.BTN_CANCEL</button> + <h1 class="title" translate>ACCOUNT.SELECT_IDENTITY_MODAL.TITLE</h1> + </ion-header-bar> + + <ion-content scroll="true"> + + <div class="padding"> + <p trust-as-html="'ACCOUNT.SELECT_IDENTITY_MODAL.HELP'|translate:{pubkey: pubkey}"></p> + </div> + + <ion-list> + <ion-item + class="item-avatar item-icon-right" + ng-repeat="item in identities" + ng-click="closeModal(item)"> + + <i class="item-image icon ion-person"></i> + + <h2>{{item.uid}}</h2> + + <h4 class="gray"> + <b class="ion-key"></b> + {{::item.pubkey | formatPubkey}} + <span ng-if="::!item.revoked && !item.pendingRevocation && !item.isMember" class="assertive" translate>WOT.NOT_MEMBER_PARENTHESIS</span> + <span ng-if="::item.revoked || item.pendingRevocation" class="assertive bold" translate>WOT.IDENTITY_REVOKED_PARENTHESIS</span> + </h4> + + <ng-if ng-if="::!item.revoked && !item.pendingRevocation && (item.certificationCount || item.pendingCertificationCount)"> + + <!-- certification count --> + <cs-badge-certification requirements="item" + parameters="$root.currency.parameters"></cs-badge-certification> + + <!-- certification label --> + <div class="gray badge badge-secondary" > + <span translate>ACCOUNT.CERTIFICATION_COUNT</span> + </div> + </ng-if> + + <i class="icon ion-ios-arrow-right "></i> + </ion-item> + </ion-list> + + </ion-content> + +</ion-modal-view>