mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Merge pull request #3975 from MetaMask/i3947-composableStore
Add ComposableObservableStore for subscription management
This commit is contained in:
commit
3afe76bcba
49
app/scripts/lib/ComposableObservableStore.js
Normal file
49
app/scripts/lib/ComposableObservableStore.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
const ObservableStore = require('obs-store')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ObservableStore that can composes a flat
|
||||||
|
* structure of child stores based on configuration
|
||||||
|
*/
|
||||||
|
class ComposableObservableStore extends ObservableStore {
|
||||||
|
/**
|
||||||
|
* Create a new store
|
||||||
|
*
|
||||||
|
* @param {Object} [initState] - The initial store state
|
||||||
|
* @param {Object} [config] - Map of internal state keys to child stores
|
||||||
|
*/
|
||||||
|
constructor (initState, config) {
|
||||||
|
super(initState)
|
||||||
|
this.updateStructure(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes a new internal store subscription structure
|
||||||
|
*
|
||||||
|
* @param {Object} [config] - Map of internal state keys to child stores
|
||||||
|
*/
|
||||||
|
updateStructure (config) {
|
||||||
|
this.config = config
|
||||||
|
this.removeAllListeners()
|
||||||
|
for (const key in config) {
|
||||||
|
config[key].subscribe((state) => {
|
||||||
|
this.updateState({ [key]: state })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges all child store state into a single object rather than
|
||||||
|
* returning an object keyed by child store class name
|
||||||
|
*
|
||||||
|
* @returns {Object} - Object containing merged child store state
|
||||||
|
*/
|
||||||
|
getFlatState () {
|
||||||
|
let flatState = {}
|
||||||
|
for (const key in this.config) {
|
||||||
|
flatState = { ...flatState, ...this.config[key].getState() }
|
||||||
|
}
|
||||||
|
return flatState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ComposableObservableStore
|
@ -5,10 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const EventEmitter = require('events')
|
const EventEmitter = require('events')
|
||||||
const extend = require('xtend')
|
|
||||||
const pump = require('pump')
|
const pump = require('pump')
|
||||||
const Dnode = require('dnode')
|
const Dnode = require('dnode')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
|
const ComposableObservableStore = require('./lib/ComposableObservableStore')
|
||||||
const asStream = require('obs-store/lib/asStream')
|
const asStream = require('obs-store/lib/asStream')
|
||||||
const AccountTracker = require('./lib/account-tracker')
|
const AccountTracker = require('./lib/account-tracker')
|
||||||
const RpcEngine = require('json-rpc-engine')
|
const RpcEngine = require('json-rpc-engine')
|
||||||
@ -65,7 +65,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.platform = opts.platform
|
this.platform = opts.platform
|
||||||
|
|
||||||
// observable state store
|
// observable state store
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ComposableObservableStore(initState)
|
||||||
|
|
||||||
// lock to ensure only one vault created at once
|
// lock to ensure only one vault created at once
|
||||||
this.createVaultMutex = new Mutex()
|
this.createVaultMutex = new Mutex()
|
||||||
@ -184,53 +184,36 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.typedMessageManager = new TypedMessageManager()
|
this.typedMessageManager = new TypedMessageManager()
|
||||||
this.publicConfigStore = this.initPublicConfigStore()
|
this.publicConfigStore = this.initPublicConfigStore()
|
||||||
|
|
||||||
// manual disk state subscriptions
|
this.store.updateStructure({
|
||||||
this.txController.store.subscribe((state) => {
|
TransactionController: this.txController.store,
|
||||||
this.store.updateState({ TransactionController: state })
|
KeyringController: this.keyringController.store,
|
||||||
})
|
PreferencesController: this.preferencesController.store,
|
||||||
this.keyringController.store.subscribe((state) => {
|
AddressBookController: this.addressBookController.store,
|
||||||
this.store.updateState({ KeyringController: state })
|
CurrencyController: this.currencyController.store,
|
||||||
})
|
NoticeController: this.noticeController.store,
|
||||||
this.preferencesController.store.subscribe((state) => {
|
ShapeShiftController: this.shapeshiftController.store,
|
||||||
this.store.updateState({ PreferencesController: state })
|
NetworkController: this.networkController.store,
|
||||||
})
|
InfuraController: this.infuraController.store,
|
||||||
this.addressBookController.store.subscribe((state) => {
|
|
||||||
this.store.updateState({ AddressBookController: state })
|
|
||||||
})
|
|
||||||
this.currencyController.store.subscribe((state) => {
|
|
||||||
this.store.updateState({ CurrencyController: state })
|
|
||||||
})
|
|
||||||
this.noticeController.store.subscribe((state) => {
|
|
||||||
this.store.updateState({ NoticeController: state })
|
|
||||||
})
|
|
||||||
this.shapeshiftController.store.subscribe((state) => {
|
|
||||||
this.store.updateState({ ShapeShiftController: state })
|
|
||||||
})
|
|
||||||
this.networkController.store.subscribe((state) => {
|
|
||||||
this.store.updateState({ NetworkController: state })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.infuraController.store.subscribe((state) => {
|
this.memStore = new ComposableObservableStore(null, {
|
||||||
this.store.updateState({ InfuraController: state })
|
NetworkController: this.networkController.store,
|
||||||
|
AccountTracker: this.accountTracker.store,
|
||||||
|
TxController: this.txController.memStore,
|
||||||
|
BalancesController: this.balancesController.store,
|
||||||
|
MessageManager: this.messageManager.memStore,
|
||||||
|
PersonalMessageManager: this.personalMessageManager.memStore,
|
||||||
|
TypesMessageManager: this.typedMessageManager.memStore,
|
||||||
|
KeyringController: this.keyringController.memStore,
|
||||||
|
PreferencesController: this.preferencesController.store,
|
||||||
|
RecentBlocksController: this.recentBlocksController.store,
|
||||||
|
AddressBookController: this.addressBookController.store,
|
||||||
|
CurrencyController: this.currencyController.store,
|
||||||
|
NoticeController: this.noticeController.memStore,
|
||||||
|
ShapeshiftController: this.shapeshiftController.store,
|
||||||
|
InfuraController: this.infuraController.store,
|
||||||
})
|
})
|
||||||
|
this.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
// manual mem state subscriptions
|
|
||||||
const sendUpdate = this.sendUpdate.bind(this)
|
|
||||||
this.networkController.store.subscribe(sendUpdate)
|
|
||||||
this.accountTracker.store.subscribe(sendUpdate)
|
|
||||||
this.txController.memStore.subscribe(sendUpdate)
|
|
||||||
this.balancesController.store.subscribe(sendUpdate)
|
|
||||||
this.messageManager.memStore.subscribe(sendUpdate)
|
|
||||||
this.personalMessageManager.memStore.subscribe(sendUpdate)
|
|
||||||
this.typedMessageManager.memStore.subscribe(sendUpdate)
|
|
||||||
this.keyringController.memStore.subscribe(sendUpdate)
|
|
||||||
this.preferencesController.store.subscribe(sendUpdate)
|
|
||||||
this.recentBlocksController.store.subscribe(sendUpdate)
|
|
||||||
this.addressBookController.store.subscribe(sendUpdate)
|
|
||||||
this.currencyController.store.subscribe(sendUpdate)
|
|
||||||
this.noticeController.memStore.subscribe(sendUpdate)
|
|
||||||
this.shapeshiftController.store.subscribe(sendUpdate)
|
|
||||||
this.infuraController.store.subscribe(sendUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -308,33 +291,16 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
const vault = this.keyringController.store.getState().vault
|
const vault = this.keyringController.store.getState().vault
|
||||||
const isInitialized = (!!wallet || !!vault)
|
const isInitialized = (!!wallet || !!vault)
|
||||||
|
|
||||||
return extend(
|
return {
|
||||||
{
|
...{ isInitialized },
|
||||||
isInitialized,
|
...this.memStore.getFlatState(),
|
||||||
},
|
...this.configManager.getConfig(),
|
||||||
this.networkController.store.getState(),
|
...{
|
||||||
this.accountTracker.store.getState(),
|
|
||||||
this.txController.memStore.getState(),
|
|
||||||
this.messageManager.memStore.getState(),
|
|
||||||
this.personalMessageManager.memStore.getState(),
|
|
||||||
this.typedMessageManager.memStore.getState(),
|
|
||||||
this.keyringController.memStore.getState(),
|
|
||||||
this.balancesController.store.getState(),
|
|
||||||
this.preferencesController.store.getState(),
|
|
||||||
this.addressBookController.store.getState(),
|
|
||||||
this.currencyController.store.getState(),
|
|
||||||
this.noticeController.memStore.getState(),
|
|
||||||
this.infuraController.store.getState(),
|
|
||||||
this.recentBlocksController.store.getState(),
|
|
||||||
// config manager
|
|
||||||
this.configManager.getConfig(),
|
|
||||||
this.shapeshiftController.store.getState(),
|
|
||||||
{
|
|
||||||
lostAccounts: this.configManager.getLostAccounts(),
|
lostAccounts: this.configManager.getLostAccounts(),
|
||||||
seedWords: this.configManager.getSeedWords(),
|
seedWords: this.configManager.getSeedWords(),
|
||||||
forgottenPassword: this.configManager.getPasswordForgotten(),
|
forgottenPassword: this.configManager.getPasswordForgotten(),
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
705
package-lock.json
generated
705
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@
|
|||||||
"mascara": "gulp dev:mascara & node ./mascara/example/server",
|
"mascara": "gulp dev:mascara & node ./mascara/example/server",
|
||||||
"dist": "gulp dist",
|
"dist": "gulp dist",
|
||||||
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
||||||
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
|
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\"",
|
||||||
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
||||||
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
||||||
"test:integration:build": "gulp build:scss",
|
"test:integration:build": "gulp build:scss",
|
||||||
|
5
test/setup.js
Normal file
5
test/setup.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
require('babel-register')({
|
||||||
|
ignore: name => name.includes('node_modules') && !name.includes('obs-store'),
|
||||||
|
})
|
||||||
|
|
||||||
|
require('./helper')
|
35
test/unit/ComposableObservableStore.js
Normal file
35
test/unit/ComposableObservableStore.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const assert = require('assert')
|
||||||
|
const ComposableObservableStore = require('../../app/scripts/lib/ComposableObservableStore')
|
||||||
|
const ObservableStore = require('obs-store')
|
||||||
|
|
||||||
|
describe('ComposableObservableStore', () => {
|
||||||
|
it('should register initial state', () => {
|
||||||
|
const store = new ComposableObservableStore('state')
|
||||||
|
assert.strictEqual(store.getState(), 'state')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should register initial structure', () => {
|
||||||
|
const testStore = new ObservableStore()
|
||||||
|
const store = new ComposableObservableStore(null, { TestStore: testStore })
|
||||||
|
testStore.putState('state')
|
||||||
|
assert.deepEqual(store.getState(), { TestStore: 'state' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update structure', () => {
|
||||||
|
const testStore = new ObservableStore()
|
||||||
|
const store = new ComposableObservableStore()
|
||||||
|
store.updateStructure({ TestStore: testStore })
|
||||||
|
testStore.putState('state')
|
||||||
|
assert.deepEqual(store.getState(), { TestStore: 'state' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return flattened state', () => {
|
||||||
|
const fooStore = new ObservableStore({ foo: 'foo' })
|
||||||
|
const barStore = new ObservableStore({ bar: 'bar' })
|
||||||
|
const store = new ComposableObservableStore(null, {
|
||||||
|
FooStore: fooStore,
|
||||||
|
BarStore: barStore,
|
||||||
|
})
|
||||||
|
assert.deepEqual(store.getFlatState(), { foo: 'foo', bar: 'bar' })
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user