-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
Showing
15 changed files
with
290 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<void>} | ||
*/ | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
const assert = require('assert') | ||
const sinon = require('sinon') | ||
const CachedBalancesController = require('../../../../app/scripts/controllers/cached-balances') | ||
|
||
describe('CachedBalancesController', () => { | ||
describe('updateCachedBalances', () => { | ||
it('should update the cached balances', async () => { | ||
const controller = new CachedBalancesController({ | ||
getNetwork: () => Promise.resolve(17), | ||
accountTracker: { | ||
store: { | ||
subscribe: () => {}, | ||
}, | ||
}, | ||
initState: { | ||
cachedBalances: 'mockCachedBalances', | ||
}, | ||
}) | ||
|
||
controller._generateBalancesToCache = sinon.stub().callsFake(() => Promise.resolve('mockNewCachedBalances')) | ||
|
||
await controller.updateCachedBalances({ accounts: 'mockAccounts' }) | ||
|
||
assert.equal(controller._generateBalancesToCache.callCount, 1) | ||
assert.deepEqual(controller._generateBalancesToCache.args[0], ['mockAccounts', 17]) | ||
assert.equal(controller.store.getState().cachedBalances, 'mockNewCachedBalances') | ||
}) | ||
}) | ||
|
||
describe('_generateBalancesToCache', () => { | ||
it('should generate updated account balances where the current network was updated', () => { | ||
const controller = new CachedBalancesController({ | ||
accountTracker: { | ||
store: { | ||
subscribe: () => {}, | ||
}, | ||
}, | ||
initState: { | ||
cachedBalances: { | ||
17: { | ||
a: '0x1', | ||
b: '0x2', | ||
c: '0x3', | ||
}, | ||
16: { | ||
a: '0xa', | ||
b: '0xb', | ||
c: '0xc', | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
const result = controller._generateBalancesToCache({ | ||
a: { balance: '0x4' }, | ||
b: { balance: null }, | ||
c: { balance: '0x5' }, | ||
}, 17) | ||
|
||
assert.deepEqual(result, { | ||
17: { | ||
a: '0x4', | ||
b: '0x2', | ||
c: '0x5', | ||
}, | ||
16: { | ||
a: '0xa', | ||
b: '0xb', | ||
c: '0xc', | ||
}, | ||
}) | ||
}) | ||
|
||
it('should generate updated account balances where the a new network was selected', () => { | ||
const controller = new CachedBalancesController({ | ||
accountTracker: { | ||
store: { | ||
subscribe: () => {}, | ||
}, | ||
}, | ||
initState: { | ||
cachedBalances: { | ||
17: { | ||
a: '0x1', | ||
b: '0x2', | ||
c: '0x3', | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
const result = controller._generateBalancesToCache({ | ||
a: { balance: '0x4' }, | ||
b: { balance: null }, | ||
c: { balance: '0x5' }, | ||
}, 16) | ||
|
||
assert.deepEqual(result, { | ||
17: { | ||
a: '0x1', | ||
b: '0x2', | ||
c: '0x3', | ||
}, | ||
16: { | ||
a: '0x4', | ||
c: '0x5', | ||
}, | ||
}) | ||
}) | ||
}) | ||
|
||
describe('_registerUpdates', () => { | ||
it('should subscribe to the account tracker with the updateCachedBalances method', async () => { | ||
const subscribeSpy = sinon.spy() | ||
const controller = new CachedBalancesController({ | ||
getNetwork: () => Promise.resolve(17), | ||
accountTracker: { | ||
store: { | ||
subscribe: subscribeSpy, | ||
}, | ||
}, | ||
}) | ||
subscribeSpy.resetHistory() | ||
|
||
const updateCachedBalancesSpy = sinon.spy() | ||
controller.updateCachedBalances = updateCachedBalancesSpy | ||
controller._registerUpdates({ accounts: 'mockAccounts' }) | ||
|
||
assert.equal(subscribeSpy.callCount, 1) | ||
|
||
subscribeSpy.args[0][0]() | ||
|
||
assert.equal(updateCachedBalancesSpy.callCount, 1) | ||
}) | ||
}) | ||
|
||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.