'use strict'; angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, $ionicScrollDelegate, $ionicPopup, $ionicSideMenuDelegate, $httpBackend, latestReleaseService, feeService, bwcService, pushNotificationsService, lodash, go, profileService, configService, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, addonManager, bwcError, txFormatService, uxLanguage, glideraService, coinbaseService, platformInfo, addressbookService, openURLService, ongoingProcess) { var self = this; var SOFT_CONFIRMATION_LIMIT = 12; var errors = bwcService.getErrors(); var historyUpdateInProgress = {}; var isChromeApp = platformInfo.isChromeApp; var isCordova = platformInfo.isCordova; var isNW = platformInfo.isNW; var ret = {}; ret.isCordova = isCordova; ret.isChromeApp = isChromeApp; ret.isSafari = platformInfo.isSafari; ret.isWindowsPhoneApp = platformInfo.isWP; ret.historyShowLimit = 10; ret.historyShowMoreLimit = 10; ret.isSearching = false; ret.prevState = 'walletHome'; ret.physicalScreenWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width); ret.appConfig = window.appConfig; // Only for testing //storageService.checkQuota(); ret.menu = [{ 'title': gettext('Receive'), 'icon': { false: 'icon-receive', true: 'icon-receive-active' }, 'link': 'receive' }, { 'title': gettext('Activity'), 'icon': { false: 'icon-activity', true: 'icon-activity-active' }, 'link': 'walletHome' }, { 'title': gettext('Send'), 'icon': { false: 'icon-send', true: 'icon-send-active' }, 'link': 'send' }]; ret.addonViews = addonManager.addonViews(); ret.txTemplateUrl = addonManager.txTemplateUrl() || 'views/includes/transaction.html'; ret.tab = 'walletHome'; var vanillaScope = ret; if (isNW) { latestReleaseService.checkLatestRelease(function(err, newRelease) { if (err) { $log.warn(err); return; } if (newRelease) $scope.newRelease = gettext('There is a new version of Copay. Please update'); }); } function strip(number) { return (parseFloat(number.toPrecision(12))); }; self.goHome = function() { go.walletHome(); }; self.allowRefresher = function() { if ($ionicSideMenuDelegate.getOpenRatio() != 0) self.allowPullToRefresh = false; } self.hideBalance = function() { storageService.getHideBalanceFlag(self.walletId, function(err, shouldHideBalance) { if (err) self.shouldHideBalance = false; else self.shouldHideBalance = (shouldHideBalance == 'true') ? true : false; }); } self.onHold = function() { self.shouldHideBalance = !self.shouldHideBalance; storageService.setHideBalanceFlag(self.walletId, self.shouldHideBalance.toString(), function() {}); } self.setWalletPreferencesTitle = function() { return gettext("Wallet Preferences"); } self.cleanInstance = function() { $log.debug('Cleaning Index Instance'); lodash.each(self, function(v, k) { if (lodash.isFunction(v)) return; // This are to prevent flicker in mobile: if (k == 'hasProfile') return; if (k == 'tab') return; if (k == 'noFocusedWallet') return; if (k == 'backgroundColor') return; if (k == 'physicalScreenWidth') return; if (k == 'loadingWallet') { self.loadingWallet = true; return; } if (!lodash.isUndefined(vanillaScope[k])) { self[k] = vanillaScope[k]; return; } delete self[k]; }); }; self.setFocusedWallet = function() { var fc = profileService.focusedClient; if (!fc) return; self.cleanInstance(); self.loadingWallet = true; self.setSpendUnconfirmed(); $timeout(function() { $rootScope.$apply(); self.hasProfile = true; self.isSingleAddress = false; self.noFocusedWallet = false; self.updating = false; // Credentials Shortcuts self.m = fc.credentials.m; self.n = fc.credentials.n; self.network = fc.credentials.network; self.copayerId = fc.credentials.copayerId; self.copayerName = fc.credentials.copayerName; self.requiresMultipleSignatures = fc.credentials.m > 1; self.isShared = fc.credentials.n > 1; self.walletName = fc.credentials.walletName; self.walletId = fc.credentials.walletId; self.isComplete = fc.isComplete(); self.canSign = fc.canSign(); self.isPrivKeyExternal = fc.isPrivKeyExternal(); self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted(); self.externalSource = fc.getPrivKeyExternalSourceName(); self.account = fc.credentials.account; self.incorrectDerivation = fc.keyDerivationOk === false; if (self.externalSource == 'trezor') self.account++; self.txps = []; self.copayers = []; self.updateColor(); self.updateAlias(); self.setAddressbook(); self.initGlidera(); self.initCoinbase(); self.hideBalance(); self.setCustomBWSFlag(); if (!self.isComplete) { $log.debug('Wallet not complete BEFORE update... redirecting'); go.path('copayers'); } else { if (go.is('copayers')) { $log.debug('Wallet Complete BEFORE update... redirect to home'); go.walletHome(); } } profileService.needsBackup(fc, function(needsBackup) { self.needsBackup = needsBackup; self.openWallet(function() { if (!self.isComplete) { $log.debug('Wallet not complete after update... redirecting'); go.path('copayers'); } else { if (go.is('copayers')) { $log.debug('Wallet Complete after update... redirect to home'); go.walletHome(); } } }); }); }); }; self.setCustomBWSFlag = function() { var defaults = configService.getDefaults(); var config = configService.getSync(); self.usingCustomBWS = config.bwsFor && config.bwsFor[self.walletId] && (config.bwsFor[self.walletId] != defaults.bws.url); }; self.setTab = function(tab, reset, tries, switchState) { tries = tries || 0; // check if the whole menu item passed if (typeof tab == 'object') { if (tab.open) { if (tab.link) { self.tab = tab.link; } tab.open(); return; } else { return self.setTab(tab.link, reset, tries, switchState); } } if (self.tab === tab && !reset) return; if (!document.getElementById('menu-' + tab) && ++tries < 5) { return $timeout(function() { self.setTab(tab, reset, tries, switchState); }, 300); } if (!self.tab || !go.is('walletHome')) self.tab = 'walletHome'; var changeTab = function() { if (document.getElementById(self.tab)) { document.getElementById(self.tab).className = 'tab-out tab-view ' + self.tab; var old = document.getElementById('menu-' + self.tab); if (old) { old.className = ''; } } if (document.getElementById(tab)) { document.getElementById(tab).className = 'tab-in tab-view ' + tab; var newe = document.getElementById('menu-' + tab); if (newe) { newe.className = 'active'; } } self.tab = tab; $rootScope.$emit('Local/TabChanged', tab); }; if (switchState && !go.is('walletHome')) { go.path('walletHome', function() { changeTab(); }); return; } changeTab(); }; self.setSpendUnconfirmed = function(spendUnconfirmed) { self.spendUnconfirmed = spendUnconfirmed || configService.getSync().wallet.spendUnconfirmed; }; self.updateBalance = function() { var fc = profileService.focusedClient; $timeout(function() { ongoingProcess.set('updatingBalance', true); $log.debug('Updating Balance'); fc.getBalance(function(err, balance) { ongoingProcess.set('updatingBalance', false); if (err) { self.handleError(err); return; } $log.debug('Wallet Balance:', balance); self.setBalance(balance); }); }); }; self.updatePendingTxps = function() { var fc = profileService.focusedClient; $timeout(function() { self.updating = true; $log.debug('Updating PendingTxps'); fc.getTxProposals({}, function(err, txps) { self.updating = false; if (err) { self.handleError(err); } else { $log.debug('Wallet PendingTxps:', txps); self.setPendingTxps(txps); } $rootScope.$apply(); }); }); }; // This handles errors from BWS/index which normally // trigger from async events (like updates). // Debounce function avoids multiple popups var _handleError = function(err) { $log.warn('Client ERROR: ', err); if (err instanceof errors.NOT_AUTHORIZED) { self.notAuthorized = true; go.walletHome(); } else if (err instanceof errors.NOT_FOUND) { self.showErrorPopup(gettext('Could not access Wallet Service: Not found')); } else { var msg = "" $scope.$emit('Local/ClientError', (err.error ? err.error : err)); var msg = bwcError.msg(err, gettext('Error at Wallet Service')); self.showErrorPopup(msg); } }; self.handleError = lodash.debounce(_handleError, 1000); self.openWallet = function(cb) { var fc = profileService.focusedClient; $timeout(function() { $rootScope.$apply(); self.updating = true; self.updateError = false; fc.openWallet(function(err, walletStatus) { self.updating = false; if (err) { self.updateError = true; self.handleError(err); return; } $log.debug('Wallet Opened'); self.updateAll(lodash.isObject(walletStatus) ? { walletStatus: walletStatus, cb: cb, } : { cb: cb }); $rootScope.$apply(); }); }); }; self.setPendingTxps = function(txps) { self.pendingTxProposalsCountForUs = 0; var now = Math.floor(Date.now() / 1000); /* Uncomment to test multiple outputs */ /* var txp = { message: 'test multi-output', fee: 1000, createdOn: new Date() / 1000, outputs: [] }; function addOutput(n) { txp.outputs.push({ amount: 600, toAddress: '2N8bhEwbKtMvR2jqMRcTCQqzHP6zXGToXcK', message: 'output #' + (Number(n) + 1) }); }; lodash.times(150, addOutput); txps.push(txp); */ lodash.each(txps, function(tx) { tx = txFormatService.processTx(tx); // no future transactions... if (tx.createdOn > now) tx.createdOn = now; var action = lodash.find(tx.actions, { copayerId: self.copayerId }); if (!action && tx.status == 'pending') { tx.pendingForUs = true; } if (action && action.type == 'accept') { tx.statusForUs = 'accepted'; } else if (action && action.type == 'reject') { tx.statusForUs = 'rejected'; } else { tx.statusForUs = 'pending'; } if (!tx.deleteLockTime) tx.canBeRemoved = true; if (tx.creatorId != self.copayerId) { self.pendingTxProposalsCountForUs = self.pendingTxProposalsCountForUs + 1; } addonManager.formatPendingTxp(tx); }); self.txps = txps; }; var SAFE_CONFIRMATIONS = 6; self.processNewTxs = function(txs) { var config = configService.getSync().wallet.settings; var now = Math.floor(Date.now() / 1000); var txHistoryUnique = {}; var ret = []; self.hasUnsafeConfirmed = false; lodash.each(txs, function(tx) { tx = txFormatService.processTx(tx); // no future transactions... if (tx.time > now) tx.time = now; if (tx.confirmations >= SAFE_CONFIRMATIONS) { tx.safeConfirmed = SAFE_CONFIRMATIONS + '+'; } else { tx.safeConfirmed = false; self.hasUnsafeConfirmed = true; } if (tx.note) { delete tx.note.encryptedEditedByName; delete tx.note.encryptedBody; } if (!txHistoryUnique[tx.txid]) { ret.push(tx); txHistoryUnique[tx.txid] = true; } else { $log.debug('Ignoring duplicate TX in history: ' + tx.txid) } }); return ret; }; self.updateAlias = function() { var config = configService.getSync(); config.aliasFor = config.aliasFor || {}; self.alias = config.aliasFor[self.walletId]; var fc = profileService.focusedClient; fc.alias = self.alias; }; self.updateColor = function() { var config = configService.getSync(); config.colorFor = config.colorFor || {}; self.backgroundColor = config.colorFor[self.walletId] || '#4A90E2'; var fc = profileService.focusedClient; fc.backgroundColor = self.backgroundColor; if (isCordova && StatusBar.isVisible) { StatusBar.backgroundColorByHexString(fc.backgroundColor); } }; self.setBalance = function(balance) { if (!balance) return; var config = configService.getSync().wallet.settings; var COIN = 1e8; // Address with Balance self.balanceByAddress = balance.byAddress; // Spend unconfirmed funds if (self.spendUnconfirmed) { self.totalBalanceSat = balance.totalAmount; self.lockedBalanceSat = balance.lockedAmount; self.availableBalanceSat = balance.availableAmount; self.totalBytesToSendMax = balance.totalBytesToSendMax; self.pendingAmount = null; } else { self.totalBalanceSat = balance.totalConfirmedAmount; self.lockedBalanceSat = balance.lockedConfirmedAmount; self.availableBalanceSat = balance.availableConfirmedAmount; self.totalBytesToSendMax = balance.totalBytesToSendConfirmedMax; self.pendingAmount = balance.totalAmount - balance.totalConfirmedAmount; } // Selected unit self.unitToSatoshi = config.unitToSatoshi; self.satToUnit = 1 / self.unitToSatoshi; self.unitName = config.unitName; //STR self.totalBalanceStr = profileService.formatAmount(self.totalBalanceSat) + ' ' + self.unitName; self.lockedBalanceStr = profileService.formatAmount(self.lockedBalanceSat) + ' ' + self.unitName; self.availableBalanceStr = profileService.formatAmount(self.availableBalanceSat) + ' ' + self.unitName; if (self.pendingAmount) { self.pendingAmountStr = profileService.formatAmount(self.pendingAmount) + ' ' + self.unitName; } else { self.pendingAmountStr = null; } self.alternativeName = config.alternativeName; self.alternativeIsoCode = config.alternativeIsoCode; // Check address addressService.isUsed(self.walletId, balance.byAddress, function(err, used) { if (used) { $log.debug('Address used. Creating new'); $rootScope.$emit('Local/AddressIsUsed'); } }); rateService.whenAvailable(function() { var totalBalanceAlternative = rateService.toFiat(self.totalBalanceSat, self.alternativeIsoCode); var lockedBalanceAlternative = rateService.toFiat(self.lockedBalanceSat, self.alternativeIsoCode); var alternativeConversionRate = rateService.toFiat(100000000, self.alternativeIsoCode); self.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative); self.lockedBalanceAlternative = $filter('formatFiatAmount')(lockedBalanceAlternative); self.alternativeConversionRate = $filter('formatFiatAmount')(alternativeConversionRate); self.alternativeBalanceAvailable = true; self.isRateAvailable = true; $rootScope.$apply(); }); if (!rateService.isAvailable()) { $rootScope.$apply(); } }; self.showMore = function() { $timeout(function() { if (self.isSearching) { self.txHistorySearchResults = self.result.slice(0, self.nextTxHistory); $log.debug('Total txs: ', self.txHistorySearchResults.length + '/' + self.result.length); if (self.txHistorySearchResults.length >= self.result.length) self.historyShowMore = false; } else { self.txHistory = self.completeHistory.slice(0, self.nextTxHistory); $log.debug('Total txs: ', self.txHistory.length + '/' + self.completeHistory.length); if (self.txHistory.length >= self.completeHistory.length) self.historyShowMore = false; } self.nextTxHistory += self.historyShowMoreLimit; $scope.$broadcast('scroll.infiniteScrollComplete'); }, 100); }; self.toggleLeftMenu = function() { profileService.isDisclaimerAccepted(function(val) { if (val) go.toggleLeftMenu(); else $log.debug('Disclaimer not accepted, cannot open menu'); }); }; self.initGlidera = function(accessToken) { self.glideraEnabled = configService.getSync().glidera.enabled; self.glideraTestnet = configService.getSync().glidera.testnet; var network = self.glideraTestnet ? 'testnet' : 'livenet'; self.glideraToken = null; self.glideraError = null; self.glideraPermissions = null; self.glideraEmail = null; self.glideraPersonalInfo = null; self.glideraTxs = null; self.glideraStatus = null; if (!self.glideraEnabled) return; glideraService.setCredentials(network); var getToken = function(cb) { if (accessToken) { cb(null, accessToken); } else { storageService.getGlideraToken(network, cb); } }; getToken(function(err, accessToken) { if (err || !accessToken) return; else { glideraService.getAccessTokenPermissions(accessToken, function(err, p) { if (err) { self.glideraError = err; } else { self.glideraToken = accessToken; self.glideraPermissions = p; self.updateGlidera({ fullUpdate: true }); } }); } }); }; self.updateGlidera = function(opts) { if (!self.glideraToken || !self.glideraPermissions) return; var accessToken = self.glideraToken; var permissions = self.glideraPermissions; opts = opts || {}; glideraService.getStatus(accessToken, function(err, data) { self.glideraStatus = data; }); glideraService.getLimits(accessToken, function(err, limits) { self.glideraLimits = limits; }); if (permissions.transaction_history) { glideraService.getTransactions(accessToken, function(err, data) { self.glideraTxs = data; }); } if (permissions.view_email_address && opts.fullUpdate) { glideraService.getEmail(accessToken, function(err, data) { self.glideraEmail = data.email; }); } if (permissions.personal_info && opts.fullUpdate) { glideraService.getPersonalInfo(accessToken, function(err, data) { self.glideraPersonalInfo = data; }); } }; self.initCoinbase = function(accessToken) { self.coinbaseEnabled = configService.getSync().coinbase.enabled; self.coinbaseTestnet = configService.getSync().coinbase.testnet; var network = self.coinbaseTestnet ? 'testnet' : 'livenet'; self.coinbaseToken = null; self.coinbaseError = null; self.coinbasePermissions = null; self.coinbaseEmail = null; self.coinbasePersonalInfo = null; self.coinbaseTxs = null; self.coinbaseStatus = null; if (!self.coinbaseEnabled) return; coinbaseService.setCredentials(network); var getToken = function(cb) { if (accessToken) { cb(null, accessToken); } else { storageService.getCoinbaseToken(network, cb); } }; getToken(function(err, accessToken) { if (err || !accessToken) return; else { coinbaseService.getAccounts(accessToken, function(err, a) { if (err) { self.coinbaseError = err; if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); } } else { self.coinbaseToken = accessToken; lodash.each(a.data, function(account) { if (account.primary && account.type == 'wallet') { self.coinbaseAccount = account; self.updateCoinbase(); } }); } }); } }); }; self.updateCoinbase = lodash.debounce(function(opts) { if (!self.coinbaseToken || !self.coinbaseAccount) return; var accessToken = self.coinbaseToken; var accountId = self.coinbaseAccount.id; opts = opts || {}; if (opts.updateAccount) { coinbaseService.getAccount(accessToken, accountId, function(err, a) { if (err) { self.coinbaseError = err; if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); } return; } self.coinbaseAccount = a.data; }); } coinbaseService.getCurrentUser(accessToken, function(err, u) { if (err) { self.coinbaseError = err; if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); } return; } self.coinbaseUser = u.data; }); coinbaseService.getPendingTransactions(function(err, txs) { self.coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs; lodash.forEach(txs, function(dataFromStorage, txId) { if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') || (dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') || dataFromStorage.status == 'error' || (dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return; coinbaseService.getTransaction(accessToken, accountId, txId, function(err, tx) { if (err) { if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); return; } coinbaseService.savePendingTransaction(dataFromStorage, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); }); return; } _updateCoinbasePendingTransactions(dataFromStorage, tx.data); self.coinbasePendingTransactions[txId] = dataFromStorage; if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) { coinbaseService.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) { if (err) { if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); return; } coinbaseService.savePendingTransaction(dataFromStorage, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); }); return; } var newSellPrice = s.data.amount; var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100); if (variance < dataFromStorage.price_sensitivity.value) { self.sellPending(tx.data); } else { var error = { errors: [{ message: 'Price falls over the selected percentage' }] }; coinbaseService.savePendingTransaction(dataFromStorage, { status: 'error', error: error }, function(err) { if (err) $log.debug(err); }); } }); } else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) { self.sendToCopay(dataFromStorage); } else { coinbaseService.savePendingTransaction(dataFromStorage, {}, function(err) { if (err) $log.debug(err); }); } }); }); }); }, 1000); var _updateCoinbasePendingTransactions = function(obj /*, …*/ ) { for (var i = 1; i < arguments.length; i++) { for (var prop in arguments[i]) { var val = arguments[i][prop]; if (typeof val == "object") _updateCoinbasePendingTransactions(obj[prop], val); else obj[prop] = val ? val : obj[prop]; } } return obj; }; self.refreshCoinbaseToken = function() { var network = self.coinbaseTestnet ? 'testnet' : 'livenet'; storageService.getCoinbaseRefreshToken(network, function(err, refreshToken) { if (!refreshToken) return; coinbaseService.refreshToken(refreshToken, function(err, data) { if (err) { self.coinbaseError = err; } else if (data && data.access_token && data.refresh_token) { storageService.setCoinbaseToken(network, data.access_token, function() { storageService.setCoinbaseRefreshToken(network, data.refresh_token, function() { $timeout(function() { self.initCoinbase(data.access_token); }, 100); }); }); } }); }); }; self.sendToCopay = function(tx) { if (!tx) return; var data = { to: tx.toAddr, amount: tx.amount.amount, currency: tx.amount.currency, description: 'To Copay Wallet' }; coinbaseService.sendTo(self.coinbaseToken, self.coinbaseAccount.id, data, function(err, res) { if (err) { if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); return; } coinbaseService.savePendingTransaction(tx, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); }); } else { if (!res.data.id) { coinbaseService.savePendingTransaction(tx, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); }); return; } coinbaseService.getTransaction(self.coinbaseToken, self.coinbaseAccount.id, res.data.id, function(err, sendTx) { coinbaseService.savePendingTransaction(tx, { remove: true }, function(err) { coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) { $timeout(function() { self.updateCoinbase({ updateAccount: true }); }, 1000); }); }); }); } }); }; self.sellPending = function(tx) { if (!tx) return; var data = tx.amount; data['commit'] = true; coinbaseService.sellRequest(self.coinbaseToken, self.coinbaseAccount.id, data, function(err, res) { if (err) { if (err.errors[0] && err.errors[0].id == 'expired_token') { self.refreshCoinbaseToken(); return; } coinbaseService.savePendingTransaction(tx, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); }); } else { if (!res.data.transaction) { coinbaseService.savePendingTransaction(tx, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); }); return; } coinbaseService.savePendingTransaction(tx, { remove: true }, function(err) { coinbaseService.getTransaction(self.coinbaseToken, self.coinbaseAccount.id, res.data.transaction.id, function(err, updatedTx) { coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) { if (err) $log.debug(err); $timeout(function() { self.updateCoinbase({ updateAccount: true }); }, 1000); }); }); }); } }); }; self.isInFocus = function(walletId) { var fc = profileService.focusedClient; return fc && fc.credentials.walletId == walletId; }; self.setAddressbook = function(ab) { if (ab) { self.addressbook = ab; return; } addressbookService.list(function(err, ab) { if (err) { $log.error('Error getting the addressbook'); return; } self.addressbook = ab; }); }; $rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams) { self.prevState = from.name || 'walletHome'; self.tab = 'walletHome'; }); $rootScope.$on('Local/ValidatingWalletEnded', function(ev, walletId, isOK) { if (self.isInFocus(walletId)) { // NOTE: If the user changed the wallet, the flag is already turn off. self.incorrectDerivation = isOK === false; } }); $rootScope.$on('Local/ClearHistory', function(event) { $log.debug('The wallet transaction history has been deleted'); self.txHistory = self.completeHistory = self.txHistorySearchResults = []; self.debounceUpdateHistory(); }); $rootScope.$on('Local/AddressbookUpdated', function(event, ab) { self.setAddressbook(ab); }); // UX event handlers $rootScope.$on('Local/ColorUpdated', function(event) { self.updateColor(); $timeout(function() { $rootScope.$apply(); }); }); $rootScope.$on('Local/AliasUpdated', function(event) { self.updateAlias(); $timeout(function() { $rootScope.$apply(); }); }); $rootScope.$on('Local/SpendUnconfirmedUpdated', function(event, spendUnconfirmed) { self.setSpendUnconfirmed(spendUnconfirmed); self.updateAll(); }); $rootScope.$on('Local/GlideraUpdated', function(event, accessToken) { self.initGlidera(accessToken); }); $rootScope.$on('Local/CoinbaseUpdated', function(event, accessToken) { self.initCoinbase(accessToken); }); $rootScope.$on('Local/GlideraTx', function(event, accessToken, permissions) { self.updateGlidera(); }); $rootScope.$on('Local/CoinbaseTx', function(event) { self.updateCoinbase({ updateAccount: true }); }); $rootScope.$on('Local/GlideraError', function(event) { self.debouncedUpdate(); }); $rootScope.$on('Local/UnitSettingUpdated', function(event) { self.updateAll({ triggerTxUpdate: true, }); }); $rootScope.$on('Local/WalletCompleted', function(event, walletId) { if (self.isInFocus(walletId)) { // reset main wallet variables self.setFocusedWallet(); go.walletHome(); } }); self.debouncedUpdate = function() { var now = Date.now(); var oneHr = 1000 * 60 * 60; if (!self.lastUpdate || (now - self.lastUpdate) > oneHr) { self.updateAll({ quiet: true, triggerTxUpdate: true }); } }; $rootScope.$on('Local/Resume', function(event) { $log.debug('### Resume event'); profileService.isDisclaimerAccepted(function(v) { if (!v) { $log.debug('Disclaimer not accepted, resume to home'); go.path('disclaimer'); } }); self.debouncedUpdate(); }); $rootScope.$on('Local/BackupDone', function(event, walletId) { self.needsBackup = false; $log.debug('Backup done'); storageService.setBackupFlag(walletId || self.walletId, function(err) { $log.debug('Backup stored'); }); }); $rootScope.$on('Local/DeviceError', function(event, err) { self.showErrorPopup(err, function() { if (isCordova && navigator && navigator.app) { navigator.app.exitApp(); } }); }); $rootScope.$on('Local/WalletImported', function(event, walletId) { console.log('[index.js:1063] walletImported'); //TODO return; self.needsBackup = false; storageService.setBackupFlag(walletId, function() { $log.debug('Backup done stored'); addressService.expireAddress(walletId, function(err) { $timeout(function() { self.txHistory = self.completeHistory = self.txHistorySearchResults = []; storageService.removeTxHistory(walletId, function() { self.startScan(walletId); }); }, 500); }); }); }); $rootScope.$on('NewIncomingTx', function() { self.newTx = true; self.updateAll({ walletStatus: null, untilItChanges: true, triggerTxUpdate: true, }); }); $rootScope.$on('NewBlock', function() { if (self.glideraEnabled) { $timeout(function() { self.updateGlidera(); }); } if (self.coinbaseEnabled) { $timeout(function() { self.updateCoinbase(); }); } if (self.pendingAmount) { self.updateAll({ walletStatus: null, untilItChanges: null, triggerTxUpdate: true, }); } else if (self.hasUnsafeConfirmed) { $log.debug('Wallet has transactions with few confirmations. Updating.') if (self.network == 'testnet') { self.throttledUpdateHistory(); } else { self.debounceUpdateHistory(); } } }); $rootScope.$on('BalanceUpdated', function(e, n) { self.setBalance(n.data); }); //untilItChange TRUE lodash.each(['NewOutgoingTx', 'NewOutgoingTxByThirdParty'], function(eventName) { $rootScope.$on(eventName, function(event) { self.newTx = true; self.updateAll({ walletStatus: null, untilItChanges: true, triggerTxUpdate: true, }); }); }); //untilItChange FALSE lodash.each(['NewTxProposal', 'TxProposalFinallyRejected', 'TxProposalRemoved', 'NewOutgoingTxByThirdParty', 'Local/GlideraTx' ], function(eventName) { $rootScope.$on(eventName, function(event) { self.updateAll({ walletStatus: null, untilItChanges: null, triggerTxUpdate: true, }); }); }); //untilItChange Maybe $rootScope.$on('Local/TxProposalAction', function(event, untilItChanges) { self.newTx = untilItChanges; self.updateAll({ walletStatus: null, untilItChanges: untilItChanges, triggerTxUpdate: true, }); }); $rootScope.$on('ScanFinished', function() { $log.debug('Scan Finished. Updating history'); storageService.removeTxHistory(self.walletId, function() { self.updateAll({ walletStatus: null, triggerTxUpdate: true, }); }); }); lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy'], function(eventName) { $rootScope.$on(eventName, function() { var f = function() { if (self.updating) { return $timeout(f, 200); }; self.updatePendingTxps(); }; f(); }); }); $rootScope.$on('Local/NoWallets', function(event) { $timeout(function() { self.hasProfile = true; self.noFocusedWallet = true; self.isComplete = null; self.walletName = null; uxLanguage.update(); }); }); $rootScope.$on('Local/NewFocusedWallet', function() { console.log('[index.js.1200:NewFocusedWallet:] TODO'); //TODO return; uxLanguage.update(); self.setFocusedWallet(); self.updateHistory(); storageService.getCleanAndScanAddresses(function(err, walletId) { if (walletId && profileService.walletClients[walletId]) { $log.debug('Clear last address cache and Scan ', walletId); addressService.expireAddress(walletId, function(err) { self.startScan(walletId); }); storageService.removeCleanAndScanAddresses(function() { $rootScope.$emit('Local/NewFocusedWalletReady'); }); } else { $rootScope.$emit('Local/NewFocusedWalletReady'); } }); }); $rootScope.$on('Local/SetTab', function(event, tab, reset) { self.setTab(tab, reset); }); $rootScope.$on('disclaimerAccepted', function(event) { $scope.isDisclaimerAccepted = true; }); $rootScope.$on('Local/WindowResize', function() { self.physicalScreenWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width); }); $rootScope.$on('Local/NeedsConfirmation', function(event, txp, cb) { function openConfirmationPopup(txp, cb) { var config = configService.getSync(); $scope.color = config.colorFor[txp.walletId] || '#4A90E2'; $scope.tx = txFormatService.processTx(txp); self.confirmationPopup = $ionicPopup.show({ templateUrl: 'views/includes/confirm-tx.html', scope: $scope, }); $scope.processFee = function(amount, fee) { var walletSettings = configService.getSync().wallet.settings; var feeAlternativeIsoCode = walletSettings.alternativeIsoCode; $scope.feeLevel = feeService.feeOpts[feeService.getCurrentFeeLevel()]; $scope.feeAlternativeStr = parseFloat((rateService.toFiat(fee, feeAlternativeIsoCode)).toFixed(2), 10) + ' ' + feeAlternativeIsoCode; $scope.feeRateStr = (fee / (amount + fee) * 100).toFixed(2) + '%'; }; $scope.cancel = function() { return cb(); }; $scope.accept = function() { return cb(true); }; } openConfirmationPopup(txp, function(accept) { self.confirmationPopup.close(); return cb(accept); }); }); $rootScope.$on('Local/NeedsPassword', function(event, isSetup, cb) { function openPasswordPopup(isSetup, cb) { $scope.data = {}; $scope.data.password = null; $scope.isSetup = isSetup; $scope.isVerification = false; $scope.loading = false; var pass = null; self.passwordPopup = $ionicPopup.show({ templateUrl: 'views/includes/password.html', scope: $scope, }); $scope.cancel = function() { return cb('No spending password given'); }; $scope.keyPress = function(event) { if (!$scope.data.password || $scope.loading) return; if (event.keyCode == 13) $scope.set(); } $scope.set = function() { $scope.loading = true; $scope.error = null; $timeout(function() { if (isSetup && !$scope.isVerification) { $scope.loading = false; $scope.isVerification = true; pass = $scope.data.password; $scope.data.password = null; return; } if (isSetup && pass != $scope.data.password) { $scope.loading = false; $scope.error = gettext('Spending Passwords do not match'); $scope.isVerification = false; $scope.data.password = null; pass = null; return; } return cb(null, $scope.data.password); }, 100); }; }; openPasswordPopup(isSetup, function(err, pass) { self.passwordPopup.close(); return cb(err, pass); }); }); $rootScope.$on('Local/EmailUpdated', function(event, email) { self.preferences.email = email; }); lodash.each(['NewCopayer', 'CopayerUpdated'], function(eventName) { $rootScope.$on(eventName, function() { console.log('[index.js:1324] NewCopayer, CopayerUpdated'); //TODO return; // Re try to open wallet (will triggers) self.setFocusedWallet(); }); }); $rootScope.$on('Local/NewEncryptionSetting', function() { var fc = profileService.focusedClient; self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted(); $timeout(function() { $rootScope.$apply(); }); }); /* Start setup */ lodash.assign(self, vanillaScope); openURLService.init(); });