From 4c2455554540b9d0dd979bad329892279fddd8b9 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Fri, 30 Nov 2018 19:21:24 -0330 Subject: Save recent network balances in local storage (#5843) * Use selector for state.metamask.accounts in all cases. * Default to cached balance when selecting metamask accounts * Adds the cached-balances controller * Documentation and small codes fixes for #5843 Co-Authored-By: danjm --- app/scripts/controllers/cached-balances.js | 83 ++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 9 ++++ 2 files changed, 92 insertions(+) create mode 100644 app/scripts/controllers/cached-balances.js (limited to 'app') diff --git a/app/scripts/controllers/cached-balances.js b/app/scripts/controllers/cached-balances.js new file mode 100644 index 000000000..925c45334 --- /dev/null +++ b/app/scripts/controllers/cached-balances.js @@ -0,0 +1,83 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') + +/** + * @typedef {Object} CachedBalancesOptions + * @property {Object} accountTracker An {@code AccountTracker} reference + * @property {Function} getNetwork A function to get the current network + * @property {Object} initState The initial controller state + */ + +/** + * Background controller responsible for maintaining + * a cache of account balances in local storage + */ +class CachedBalancesController { + /** + * Creates a new controller instance + * + * @param {CachedBalancesOptions} [opts] Controller configuration parameters + */ + constructor (opts = {}) { + const { accountTracker, getNetwork } = opts + + this.accountTracker = accountTracker + this.getNetwork = getNetwork + + const initState = extend({ + cachedBalances: {}, + }, opts.initState) + this.store = new ObservableStore(initState) + + this._registerUpdates() + } + + /** + * Updates the cachedBalances property for the current network. Cached balances will be updated to those in the passed accounts + * if balances in the passed accounts are truthy. + * + * @param {Object} obj The the recently updated accounts object for the current network + * @returns {Promise} + */ + async updateCachedBalances ({ accounts }) { + const network = await this.getNetwork() + const balancesToCache = await this._generateBalancesToCache(accounts, network) + this.store.updateState({ + cachedBalances: balancesToCache, + }) + } + + _generateBalancesToCache (newAccounts, currentNetwork) { + const { cachedBalances } = this.store.getState() + const currentNetworkBalancesToCache = { ...cachedBalances[currentNetwork] } + + Object.keys(newAccounts).forEach(accountID => { + const account = newAccounts[accountID] + + if (account.balance) { + currentNetworkBalancesToCache[accountID] = account.balance + } + }) + const balancesToCache = { + ...cachedBalances, + [currentNetwork]: currentNetworkBalancesToCache, + } + + return balancesToCache + } + + /** + * Sets up listeners and subscriptions which should trigger an update of cached balances. These updates will + * happen when the current account changes. Which happens on block updates, as well as on network and account + * selections. + * + * @private + * + */ + _registerUpdates () { + const update = this.updateCachedBalances.bind(this) + this.accountTracker.store.subscribe(update) + } +} + +module.exports = CachedBalancesController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 7d6f06f92..fe806e47e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -29,6 +29,7 @@ const ShapeShiftController = require('./controllers/shapeshift') const AddressBookController = require('./controllers/address-book') const InfuraController = require('./controllers/infura') const BlacklistController = require('./controllers/blacklist') +const CachedBalancesController = require('./controllers/cached-balances') const RecentBlocksController = require('./controllers/recent-blocks') const MessageManager = require('./lib/message-manager') const PersonalMessageManager = require('./lib/personal-message-manager') @@ -142,6 +143,12 @@ module.exports = class MetamaskController extends EventEmitter { } }) + this.cachedBalancesController = new CachedBalancesController({ + accountTracker: this.accountTracker, + getNetwork: this.networkController.getNetworkState.bind(this.networkController), + initState: initState.CachedBalancesController, + }) + // ensure accountTracker updates balances after network change this.networkController.on('networkDidChange', () => { this.accountTracker._updateAccounts() @@ -241,6 +248,7 @@ module.exports = class MetamaskController extends EventEmitter { ShapeShiftController: this.shapeshiftController.store, NetworkController: this.networkController.store, InfuraController: this.infuraController.store, + CachedBalancesController: this.cachedBalancesController.store, }) this.memStore = new ComposableObservableStore(null, { @@ -248,6 +256,7 @@ module.exports = class MetamaskController extends EventEmitter { AccountTracker: this.accountTracker.store, TxController: this.txController.memStore, BalancesController: this.balancesController.store, + CachedBalancesController: this.cachedBalancesController.store, TokenRatesController: this.tokenRatesController.store, MessageManager: this.messageManager.memStore, PersonalMessageManager: this.personalMessageManager.memStore, -- cgit v1.2.3