Forked from
clients / Cesium-grp / Cesium
989 commits behind the upstream repository.
-
Benoit Lavenier authoredBenoit Lavenier authored
wallets-controllers.js 21.00 KiB
angular.module('cesium.wallets.controllers', ['cesium.services', 'cesium.currency.controllers'])
.config(function($stateProvider) {
'ngInject';
$stateProvider
.state('app.view_wallets', {
url: "/wallets",
views: {
'menuContent': {
templateUrl: "templates/wallet/list/view_wallets.html",
controller: 'WalletListCtrl'
}
},
data: {
login: true
}
})
.state('app.view_wallet_by_id', {
url: "/wallets/:id?refresh",
views: {
'menuContent': {
templateUrl: "templates/wallet/view_wallet.html",
controller: 'WalletCtrl'
}
},
data: {
login: true,
silentLocationChange: true
}
})
.state('app.view_wallet_tx_by_id', {
url: "/history/wallets/:id?refresh",
views: {
'menuContent': {
templateUrl: "templates/wallet/view_wallet_tx.html",
controller: 'WalletTxCtrl'
}
},
data: {
login: true,
silentLocationChange: true
}
})
.state('app.view_wallet_tx_errors_by_id', {
url: "/history/wallets/:id/errors",
views: {
'menuContent': {
templateUrl: "templates/wallet/view_wallet_tx_error.html",
controller: 'WalletTxErrorCtrl'
}
},
data: {
login: true
}
})
;
})
.controller('WalletListCtrl', WalletListController)
.controller('WalletSelectModalCtrl', WalletSelectModalController)
.controller('WalletListImportModalCtrl', WalletListImportModalController)
.controller('PopoverWalletSelectModalCtrl', PopoverWalletSelectModalController)
;
function WalletListController($scope, $controller, $state, $timeout, $q, $translate, $ionicPopover, $ionicPopup,
ModalUtils,
UIUtils, Modals, csCurrency, csSettings, csWallet){
'ngInject';
$scope.settings = csSettings.data;
$scope.listeners = [];
// Initialize the super class and extend it.
angular.extend(this, $controller('WalletSelectModalCtrl', {$scope: $scope, parameters: {}}));
// Override defaults
$scope.formData.name = undefined;
$scope.motion = UIUtils.motion.default;
$scope.enter = function(e, state) {
// First enter
if ($scope.loading) {
$scope.setParameters({
showDefault: true,
showBalance: true
});
return $scope.load()
.then(function() {
UIUtils.loading.hide();
if (!$scope.wallets) return; // user cancel, or error
$scope.addListeners();
$scope.showFab('fab-add-wallet');
});
}
// If already enter
else {
// Re-add listeners
$scope.addListeners();
}
};
$scope.$on('$ionicView.enter', $scope.enter);
$scope.leave = function() {
$scope.removeListeners();
};
$scope.$on('$ionicView.leave', $scope.leave);
$scope.cancel = function() {
$scope.showHome();
};
$scope.select = function(event, wallet) {
if (event.isDefaultPrevented()) return;
$state.go('app.view_wallet_by_id', {id: wallet.id});
};
$scope.editWallet = function(event, wallet) {
event.preventDefault();
return $scope.showEditPopup(wallet)
.then(function(newName) {
if (!newName) return;
// Save changes
return csWallet.auth({minData: true})
.then(function() {
wallet.data.localName = newName;
csWallet.storeData();
UIUtils.loading.hide();
$scope.updateView();
})
.catch(function(err) {
if (err === 'CANCELLED') {
return UIUtils.loading.hide();
}
UIUtils.onError('ERROR.SAVE_WALLET_LIST_FAILED')(err);
});
});
};
$scope.downloadAsFile = function() {
if (!$scope.wallets) return; // user cancel
return csWallet.children.downloadFile();
};
$scope.addNewWallet = function(wallet) {
if (!wallet) return $q.reject("Missing 'wallet' argument");
// Make sure auth on the main wallet
if (!csWallet.isAuth()) {
return csWallet.auth({minData: true})
.then(function() {
return $scope.addNewWallet(wallet); // loop
})
.catch(function(err) {
if (err === 'CANCELLED') {
return UIUtils.loading.hide();
}
UIUtils.onError('ERROR.ADD_SECONDARY_WALLET_FAILED')(err);
});
}
// Call API extension on child wallet
return csWallet.api.data.raisePromise.load(wallet.data)
// continue, when plugins extension failed (just log in console)
.catch(console.error)
.then(function() {
$scope.addListenersOnWallet(wallet);
csWallet.children.add(wallet);
});
};
/* -- modals -- */
$scope.showNewWalletModal = function() {
var wallet = csWallet.children.instance();
return wallet.login({
showNewAccountLink: false,
title: 'ACCOUNT.WALLET_LIST.BTN_NEW',
okText: 'COMMON.BTN_ADD',
// Load data options :
minData: true,
sources: true,
api: false,
success: UIUtils.loading.show,
method: 'PUBKEY' // Default method - fix #767
})
.then(function(walletData) {
if (!walletData) { // User cancelled
UIUtils.loading.hide(100);
return;
}
// Avoid to add main wallet again
if (csWallet.isUserPubkey(walletData.pubkey)) {
UIUtils.loading.hide();
UIUtils.alert.error('ERROR.COULD_NOT_ADD_MAIN_WALLET');
return;
}
// Avoid to add exists wallet again
if (csWallet.children.hasPubkey(walletData.pubkey)) {
UIUtils.loading.hide();
UIUtils.alert.error('ERROR.COULD_NOT_ADD_EXISTING_WALLET');
return;
}
console.debug("[wallet] Adding secondary wallet {"+walletData.pubkey.substring(0,8)+"}");
// Add the child wallet
return $scope.addNewWallet(wallet)
.then(function() {
UIUtils.loading.hide();
$scope.updateView();
});
})
.catch(function(err) {
if (err === 'CANCELLED') {
// Silent
UIUtils.loading.hide();
}
});
};
$scope.selectAndRemoveWallet = function() {
$scope.hideActionsPopover();
return Modals.showSelectWallet({
wallets: $scope.wallets,
showDefault: false
})
.then(function(wallet) {
if (!wallet || !wallet.id) return;
// Make sure to auth on the main wallet
return csWallet.auth({minData: true})
.then(function() {
csWallet.children.remove(wallet.id);
UIUtils.loading.hide();
$scope.updateView();
})
.catch(function(err) {
if (err === 'CANCELLED') {
return UIUtils.loading.hide();
}
UIUtils.onError('ERROR.ADD_SECONDARY_WALLET_FAILED')(err);
});
});
};
$scope.showImportFileModal = function() {
$scope.hideActionsPopover();
var loginAndAddWallet = function(authData) {
console.debug("[wallet] Adding secondary wallet {"+authData.pubkey.substring(0,8)+"}");
var wallet = csWallet.children.instance();
return wallet.login({
authData: authData,
// Load data options :
minData: true,
sources: true,
api: false,
success: UIUtils.loading.show
})
.then(function(walletData) {
walletData.localName = authData.localName;
return $scope.addNewWallet(wallet);
});
};
return ModalUtils.show(
'templates/wallet/list/modal_import_file.html',
'WalletListImportModalCtrl'
)
.then(function(items){
if (!items || !items.length) return; // User cancel
UIUtils.loading.show();
// Make sure to auth on the main wallet
return csWallet.auth({minData: true})
.then(function() {
// Add wallet one after one
return items.reduce(function(promise, authData){
return promise.then(function() {
return loginAndAddWallet(authData);
});
}, $q.when());
})
.then(function() {
UIUtils.loading.hide();
$scope.updateView();
})
.catch(function(err) {
if (err === 'CANCELLED') {
return UIUtils.loading.hide();
}
UIUtils.onError('ERROR.ADD_SECONDARY_WALLET_FAILED')(err);
});
});
};
/* -- popups -- */
$scope.setEditForm = function(editForm) {
$scope.editForm = editForm;
};
$scope.showEditPopup = function(wallet) {
return $q(function(resolve, reject) {
$translate(['ACCOUNT.WALLET_LIST.EDIT_POPOVER.TITLE', 'ACCOUNT.WALLET_LIST.EDIT_POPOVER.HELP', 'COMMON.BTN_OK', 'COMMON.BTN_CANCEL'])
.then(function (translations) {
$scope.formData.name = wallet.data.localName || wallet.data.name || wallet.data.uid || wallet.data.pubkey.substring(0, 8);
// Choose UID popup
$ionicPopup.show({
templateUrl: 'templates/wallet/list/popup_edit_name.html',
title: translations['ACCOUNT.WALLET_LIST.EDIT_POPOVER.TITLE'],
subTitle: translations['ACCOUNT.WALLET_LIST.EDIT_POPOVER.HELP'],
scope: $scope,
buttons: [
{ text: translations['COMMON.BTN_CANCEL'] },
{
text: translations['COMMON.BTN_OK'],
type: 'button-positive',
onTap: function(e) {
$scope.editForm.$submitted=true;
if(!$scope.editForm.$valid || !$scope.formData.name) {
//don't allow the user to close unless he enters a name
e.preventDefault();
} else {
return $scope.formData.name;
}
}
}
]
})
.then(function(name) {
if (!name) { // user cancel
delete $scope.formData.name;
UIUtils.loading.hide();
return;
}
resolve(name);
});
});
});
};
/* -- popovers -- */
$scope.showActionsPopover = function(event) {
if (!$scope.actionsPopover) {
$ionicPopover.fromTemplateUrl('templates/wallet/list/popover_actions.html', {
scope: $scope
}).then(function(popover) {
$scope.actionsPopover = popover;
//Cleanup the popover when we're done with it!
$scope.$on('$destroy', function() {
$scope.actionsPopover.remove();
});
$scope.actionsPopover.show(event);
});
}
else {
$scope.actionsPopover.show(event);
}
};
$scope.hideActionsPopover = function() {
if ($scope.actionsPopover) {
$scope.actionsPopover.hide();
}
};
/* -- listeners -- */
// Clean controller data when logout
$scope.onWalletLogout = function() {
$scope.resetData();
$scope.removeListeners();
};
$scope.addListeners = function() {
// First remove lod listeners, if any
$scope.removeListeners();
$scope.listeners = [
csWallet.api.data.on.logout($scope, $scope.onWalletLogout)
];
// Auto-update on new block
if (csSettings.data.walletHistoryAutoRefresh) {
$scope.listeners.push(
csCurrency.api.data.on.newBlock($scope, function (block) {
if ($scope.loading) return;
console.debug("[wallet-list] Received new block. Will reload list.");
$timeout(function () {
$scope.doUpdate(true);
}, 300/*waiting for node cache propagation*/);
}));
}
// Listen auth events on each wallet
_.forEach($scope.wallets||[], $scope.addListenersOnWallet);
};
$scope.addListenersOnWallet = function(wallet) {
if (!wallet) return;
$scope.listeners.push(wallet.api.data.on.unauth($scope, $scope.updateView));
$scope.listeners.push(wallet.api.data.on.auth($scope, function (data, deferred) {
$timeout($scope.updateView);
return deferred ? deferred.resolve() : $q.when();
}));
};
$scope.removeListeners = function() {
_.forEach($scope.listeners, function(remove){
remove();
});
$scope.listeners = [];
};
var inheritedUpdateView = $scope.updateView;
$scope.updateView = function() {
inheritedUpdateView();
$scope.$broadcast('$$rebind::' + 'rebind'); // force rebind
};
// Detect changes in settings useRelative
$scope.$watch('settings.useRelative', function(newVal, oldVal) {
if (!$scope.formData || $scope.loading || (newVal === oldVal)) return;
$scope.formData.useRelative = $scope.settings.useRelative;
$scope.updateView();
}, true);
}
function WalletSelectModalController($scope, $q, $timeout, UIUtils, filterTranslations, csSettings, csCurrency, csWallet, parameters){
'ngInject';
var loadWalletWaitTime = 100;
$scope.loading = true;
$scope.wallets = null;
$scope.formData = {
useRelative: csSettings.data.useRelative,
showDefault: true,
showBalance: false,
balance: undefined
};
$scope.motion = null; // no animation
$scope.setParameters = function(parameters) {
parameters = parameters || {};
$scope.formData.useRelative = angular.isDefined(parameters.useRelative) ? parameters.useRelative : $scope.formData.useRelative;
$scope.formData.showDefault = angular.isDefined(parameters.showDefault) ? parameters.showDefault : $scope.formData.showDefault;
$scope.formData.showBalance = angular.isDefined(parameters.showBalance) ? parameters.showBalance : $scope.formData.showBalance;
};
$scope.load = function(options) {
options = options || {};
$scope.loading = !options.silent;
// Load currency, and filter translations (need by 'formatAmount' filter)
var jobs = [
csCurrency.name()
.then(function(name) {
$scope.currency = name;
return filterTranslations.ready();
})
];
// Get children wallets
if (!$scope.wallets) {
jobs.push(
csWallet.children.all()
.then(function(children) {
$scope.wallets = $scope.formData.showDefault ? [csWallet].concat(children) : children;
})
);
}
// Prepare load options
var walletLoadOptions = {
silent: true,
minData: true,
sources: $scope.formData.showBalance,
tx: {
enable: false
},
api: true
};
var hasLoadError = false;
var loadCounter = 0;
var now = Date.now();
var balance = 0;
return $q.all(jobs)
// Load wallet data (apply a timeout between each wallet)
.then(function() {
console.debug("[wallets] Loading {0} wallets...".format($scope.wallets.length));
return $scope.wallets.reduce(function(res, wallet) {
var skip= !options.refresh && wallet.isDataLoaded(walletLoadOptions);
if (skip) {
console.debug("[wallets] Wallet #{0} already loaded. Skipping".format(wallet.id), walletLoadOptions);
balance += wallet.data.balance;
return res;
}
loadCounter++;
return res.then(function() {
// Loading next wallet, after waiting some time
return $timeout(function() {
return (!options.refresh ?
wallet.loadData(walletLoadOptions) :
wallet.refreshData(angular.merge({
requirements: wallet.requirements && (wallet.requirements.isMember || wallet.requirements.wasMember || wallet.requirements.pendingMembership)
}, walletLoadOptions))
).then(function(walletData) {
balance += walletData.balance;
})
.catch(function(err) {
console.error("[wallets] Error while loading data of wallet #{0}".format(wallet.id), err);
hasLoadError = true;
});
}, loadWalletWaitTime);
});
}, $q.when());
})
.then(function() {
if (hasLoadError) {
return UIUtils.alert.error('ERROR.LOAD_WALLET_LIST_FAILED')
.then(function() {
$scope.resetData();
$scope.cancel();
});
}
if (loadCounter) {
console.debug("[wallets] Loaded data of {0} wallet(s) in {1}ms".format(loadCounter, (Date.now() - now)));
}
$scope.formData.balance = balance;
$scope.loading = false;
UIUtils.loading.hide();
$scope.updateView();
})
.catch(function(err) {
$scope.resetData();
if (err && err === 'CANCELLED') {
$scope.cancel();
throw err;
}
return UIUtils.onError('ERROR.LOAD_WALLET_LIST_FAILED')(err);
});
};
$scope.$on('modal.shown', $scope.load);
$scope.cancel = function() {
$scope.closeModal();
};
$scope.select = function($event, wallet) {
$scope.closeModal(wallet);
};
// Clean controller data
$scope.resetData = function() {
$scope.wallets = null;
$scope.loading = true;
$scope.formData.balance = undefined;
};
$scope.updateView = function() {
if (!$scope.wallets || !$scope.wallets.length) return;
if ($scope.motion) {
$scope.motion.show({selector: '.list .item.item-wallet', ink: true});
}
else {
UIUtils.ink({selector: '.list .item.item-wallet'});
}
};
$scope.doUpdate = function(silent) {
if ($scope.loading || !$scope.wallets || !$scope.wallets.length) return $q.when();
return $scope.load({silent: silent, refresh: true})
.then(function() {
$scope.loading = false;
if (silent) {
$scope.$broadcast('$$rebind::' + 'rebind'); // force rebind
}
$scope.updateView();
});
};
// Default actions
$scope.setParameters(parameters);
}
function PopoverWalletSelectModalController($scope, $controller, UIUtils) {
'ngInject';
// Initialize the super class and extend it.
angular.extend(this, $controller('WalletSelectModalCtrl', {$scope: $scope, parameters: {
showDefault: true,
showBalance: false
}}));
// Disable list motion
$scope.motion = null;
$scope.$on('popover.shown', function() {
if ($scope.loading) {
$scope.load();
}
});
$scope.updateView = function() {
if (!$scope.wallets || !$scope.wallets.length) return;
UIUtils.ink({selector: '.popover-wallets .list .item'});
};
$scope.select = function($event, wallet) {
if ($event.preventDefault() || !wallet) return; // no selection
$scope.closePopover(wallet);
};
}
function WalletListImportModalController($scope, $timeout, BMA, csWallet) {
'ngInject';
$scope.hasContent = false;
$scope.content = null;
$scope.fileData = '';
$scope.isValidFile = false;
$scope.validatingFile = false;
$scope.importFromFile = function(file) {
$scope.validatingFile = true;
$scope.hasContent = angular.isDefined(file) && file !== '';
$scope.fileData = file.fileData ? file.fileData : '';
var isValidFile = $scope.fileData !== '' && ($scope.fileData.type == 'text/csv' || $scope.fileData.type == 'text/plain');
// Bad file type: invalid file
if (!isValidFile) {
console.error("[wallet] Import failed. Invalid file type: " + $scope.fileData.type);
$scope.isValidFile = false;
$scope.validatingFile = false;
return;
}
// Parse file
console.debug("[wallet] Parsing file to import...");
var rows = file.fileContent.split('\n');
$scope.content = rows.reduce(function(res, row) {
// Skip empty row
if (!row || !row.trim().length) return res;
// Split
var cols = row.split('\t', 3) || undefined;
// Invalid column count: mark file as invalid
if (cols && cols.length != 3) {
console.debug("[wallet] Import: skip invalid row: " + row);
isValidFile = false;
return res;
}
var item = {
pubkey: cols[0],
uid: cols[1],
localName: cols[2]
};
// Check pubkey validity
if (!BMA.regexp.PUBKEY.test(item.pubkey)) {
console.debug("[wallet] Invalid pubkey, found in this row: ", row);
isValidFile = false;
return res;
}
// Ignore if same as current wallet
if (csWallet.isUserPubkey(item.pubkey)) {
console.debug("[wallet] Pubkey equals to main wallet. Skip this row: ", row);
return res;
}
// Ignore if already in children wallet
if (csWallet.children.hasPubkey(item.pubkey)) {
console.debug("[wallet] Pubkey already in wallet list. Skip this row", row);
return res;
}
// OK: add it to result
return res.concat(item);
}, []);
$scope.isValidFile = isValidFile;
$timeout(function() {
$scope.validatingFile = false;
}, 250); // need to have a loading effect
};
$scope.removeFile = function() {
$scope.hasContent = false;
$scope.content = null;
$scope.fileData = '';
$scope.isValidFile = false;
$scope.validatingFile = false;
};
}