From 8012ede12698477692b80769781096b559159a32 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Jan 2017 19:04:19 -0800 Subject: background - introduce ObservableStore --- app/scripts/lib/config-manager.js | 10 ++- app/scripts/lib/inpage-provider.js | 18 ++++-- app/scripts/lib/observable/host.js | 50 ++++++++++++++ app/scripts/lib/observable/index.js | 33 ++++++++++ app/scripts/lib/observable/remote.js | 51 +++++++++++++++ app/scripts/lib/observable/util/transform.js | 13 ++++ app/scripts/lib/remote-store.js | 97 ---------------------------- 7 files changed, 163 insertions(+), 109 deletions(-) create mode 100644 app/scripts/lib/observable/host.js create mode 100644 app/scripts/lib/observable/index.js create mode 100644 app/scripts/lib/observable/remote.js create mode 100644 app/scripts/lib/observable/util/transform.js delete mode 100644 app/scripts/lib/remote-store.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 3a1f12ac0..01e6ccc3c 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -19,6 +19,7 @@ module.exports = ConfigManager function ConfigManager (opts) { // ConfigManager is observable and will emit updates this._subs = [] + this.store = opts.store /* The migrator exported on the config-manager * has two methods the user should be concerned with: @@ -36,12 +37,9 @@ function ConfigManager (opts) { // config data format, and returns the new one. migrations: migrations, - // How to load initial config. - // Includes step on migrating pre-pojo-migrator data. - loadData: opts.loadData, - - // How to persist migrated config. - setData: opts.setData, + // Data persistence methods + loadData: () => this.store.get(), + setData: (value) => this.store.put(value), }) } diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 11bd5cc3a..64301be78 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -1,7 +1,7 @@ const Streams = require('mississippi') const StreamProvider = require('web3-stream-provider') const ObjectMultiplex = require('./obj-multiplex') -const RemoteStore = require('./remote-store.js').RemoteStore +const RemoteStore = require('./observable/remote') const createRandomId = require('./random-id') module.exports = MetamaskInpageProvider @@ -72,13 +72,13 @@ MetamaskInpageProvider.prototype.send = function (payload) { case 'eth_accounts': // read from localStorage - selectedAccount = self.publicConfigStore.get('selectedAccount') + selectedAccount = self.publicConfigStore.get().selectedAccount result = selectedAccount ? [selectedAccount] : [] break case 'eth_coinbase': // read from localStorage - selectedAccount = self.publicConfigStore.get('selectedAccount') + selectedAccount = self.publicConfigStore.get().selectedAccount result = selectedAccount || '0x0000000000000000000000000000000000000000' break @@ -117,9 +117,15 @@ MetamaskInpageProvider.prototype.isMetaMask = true function remoteStoreWithLocalStorageCache (storageKey) { // read local cache - var initState = JSON.parse(localStorage[storageKey] || '{}') - var store = new RemoteStore(initState) - // cache the latest state locally + let initState + try { + initState = JSON.parse(localStorage[storageKey] || '{}') + } catch (err) { + initState = {} + } + // intialize store + const store = new RemoteStore(initState) + // write local cache store.subscribe(function (state) { localStorage[storageKey] = JSON.stringify(state) }) diff --git a/app/scripts/lib/observable/host.js b/app/scripts/lib/observable/host.js new file mode 100644 index 000000000..69f674be8 --- /dev/null +++ b/app/scripts/lib/observable/host.js @@ -0,0 +1,50 @@ +const Dnode = require('dnode') +const ObservableStore = require('./index') +const endOfStream = require('end-of-stream') + +// +// HostStore +// +// plays host to many RemoteStores and sends its state over a stream +// + +class HostStore extends ObservableStore { + + constructor (initState, opts) { + super(initState) + this.opts = opts || {} + } + + createStream () { + const self = this + // setup remotely exposed api + let remoteApi = {} + if (!self.opts.readOnly) { + remoteApi.put = (newState) => self.put(newState) + } + // listen for connection to remote + const dnode = Dnode(remoteApi) + dnode.on('remote', (remote) => { + // setup update subscription lifecycle + const updateHandler = (state) => remote.put(state) + self._onConnect(updateHandler) + endOfStream(dnode, () => self._onDisconnect(updateHandler)) + }) + return dnode + } + + _onConnect (updateHandler) { + // subscribe to updates + this.subscribe(updateHandler) + // send state immediately + updateHandler(this.get()) + } + + _onDisconnect (updateHandler) { + // unsubscribe to updates + this.unsubscribe(updateHandler) + } + +} + +module.exports = HostStore diff --git a/app/scripts/lib/observable/index.js b/app/scripts/lib/observable/index.js new file mode 100644 index 000000000..d193e5554 --- /dev/null +++ b/app/scripts/lib/observable/index.js @@ -0,0 +1,33 @@ +const EventEmitter = require('events').EventEmitter + +class ObservableStore extends EventEmitter { + + constructor (initialState) { + super() + this._state = initialState + } + + get () { + return this._state + } + + put (newState) { + this._put(newState) + } + + subscribe (handler) { + this.on('update', handler) + } + + unsubscribe (handler) { + this.removeListener('update', handler) + } + + _put (newState) { + this._state = newState + this.emit('update', newState) + } + +} + +module.exports = ObservableStore diff --git a/app/scripts/lib/observable/remote.js b/app/scripts/lib/observable/remote.js new file mode 100644 index 000000000..b5a3254a2 --- /dev/null +++ b/app/scripts/lib/observable/remote.js @@ -0,0 +1,51 @@ +const Dnode = require('dnode') +const ObservableStore = require('./index') +const endOfStream = require('end-of-stream') + +// +// RemoteStore +// +// connects to a HostStore and receives its latest state +// + +class RemoteStore extends ObservableStore { + + constructor (initState, opts) { + super(initState) + this.opts = opts || {} + this._remote = null + } + + put (newState) { + if (!this._remote) throw new Error('RemoteStore - "put" called before connection to HostStore') + this._put(newState) + this._remote.put(newState) + } + + createStream () { + const self = this + const dnode = Dnode({ + put: (newState) => self._put(newState), + }) + // listen for connection to remote + dnode.once('remote', (remote) => { + // setup connection lifecycle + self._onConnect(remote) + endOfStream(dnode, () => self._onDisconnect()) + }) + return dnode + } + + _onConnect (remote) { + this._remote = remote + this.emit('connected') + } + + _onDisconnect () { + this._remote = null + this.emit('disconnected') + } + +} + +module.exports = RemoteStore \ No newline at end of file diff --git a/app/scripts/lib/observable/util/transform.js b/app/scripts/lib/observable/util/transform.js new file mode 100644 index 000000000..87946f402 --- /dev/null +++ b/app/scripts/lib/observable/util/transform.js @@ -0,0 +1,13 @@ + +module.exports = transformStore + + +function transformStore(inStore, outStore, stateTransform) { + const initState = stateTransform(inStore.get()) + outStore.put(initState) + inStore.subscribe((inState) => { + const outState = stateTransform(inState) + outStore.put(outState) + }) + return outStore +} \ No newline at end of file diff --git a/app/scripts/lib/remote-store.js b/app/scripts/lib/remote-store.js deleted file mode 100644 index fbfab7bad..000000000 --- a/app/scripts/lib/remote-store.js +++ /dev/null @@ -1,97 +0,0 @@ -const Dnode = require('dnode') -const inherits = require('util').inherits - -module.exports = { - HostStore: HostStore, - RemoteStore: RemoteStore, -} - -function BaseStore (initState) { - this._state = initState || {} - this._subs = [] -} - -BaseStore.prototype.set = function (key, value) { - throw Error('Not implemented.') -} - -BaseStore.prototype.get = function (key) { - return this._state[key] -} - -BaseStore.prototype.subscribe = function (fn) { - this._subs.push(fn) - var unsubscribe = this.unsubscribe.bind(this, fn) - return unsubscribe -} - -BaseStore.prototype.unsubscribe = function (fn) { - var index = this._subs.indexOf(fn) - if (index !== -1) this._subs.splice(index, 1) -} - -BaseStore.prototype._emitUpdates = function (state) { - this._subs.forEach(function (handler) { - handler(state) - }) -} - -// -// host -// - -inherits(HostStore, BaseStore) -function HostStore (initState, opts) { - BaseStore.call(this, initState) -} - -HostStore.prototype.set = function (key, value) { - this._state[key] = value - process.nextTick(this._emitUpdates.bind(this, this._state)) -} - -HostStore.prototype.createStream = function () { - var dnode = Dnode({ - // update: this._didUpdate.bind(this), - }) - dnode.on('remote', this._didConnect.bind(this)) - return dnode -} - -HostStore.prototype._didConnect = function (remote) { - this.subscribe(function (state) { - remote.update(state) - }) - remote.update(this._state) -} - -// -// remote -// - -inherits(RemoteStore, BaseStore) -function RemoteStore (initState, opts) { - BaseStore.call(this, initState) - this._remote = null -} - -RemoteStore.prototype.set = function (key, value) { - this._remote.set(key, value) -} - -RemoteStore.prototype.createStream = function () { - var dnode = Dnode({ - update: this._didUpdate.bind(this), - }) - dnode.once('remote', this._didConnect.bind(this)) - return dnode -} - -RemoteStore.prototype._didConnect = function (remote) { - this._remote = remote -} - -RemoteStore.prototype._didUpdate = function (state) { - this._state = state - this._emitUpdates(state) -} -- cgit v1.2.3 From 3bc996878b467e1fa5fd63656bd465377daa137d Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Jan 2017 22:47:56 -0800 Subject: background - move pojo migrator to outside of metamask controller --- app/scripts/lib/config-manager.js | 49 ++++++++-------------------- app/scripts/lib/migrations.js | 13 ++++++++ app/scripts/lib/observable/host.js | 4 +-- app/scripts/lib/observable/index.js | 10 +++++- app/scripts/lib/observable/local-storage.js | 37 +++++++++++++++++++++ app/scripts/lib/observable/remote.js | 2 +- app/scripts/lib/observable/util/sync.js | 24 ++++++++++++++ app/scripts/lib/observable/util/transform.js | 13 -------- 8 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 app/scripts/lib/observable/local-storage.js create mode 100644 app/scripts/lib/observable/util/sync.js delete mode 100644 app/scripts/lib/observable/util/transform.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 01e6ccc3c..6d7305377 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -1,6 +1,4 @@ -const Migrator = require('pojo-migrator') const MetamaskConfig = require('../config.js') -const migrations = require('./migrations') const ethUtil = require('ethereumjs-util') const normalize = require('./sig-util').normalize @@ -20,38 +18,17 @@ function ConfigManager (opts) { // ConfigManager is observable and will emit updates this._subs = [] this.store = opts.store - - /* The migrator exported on the config-manager - * has two methods the user should be concerned with: - * - * getData(), which returns the app-consumable data object - * saveData(), which persists the app-consumable data object. - */ - this.migrator = new Migrator({ - - // Migrations must start at version 1 or later. - // They are objects with a `version` number - // and a `migrate` function. - // - // The `migrate` function receives the previous - // config data format, and returns the new one. - migrations: migrations, - - // Data persistence methods - loadData: () => this.store.get(), - setData: (value) => this.store.put(value), - }) } ConfigManager.prototype.setConfig = function (config) { - var data = this.migrator.getData() + var data = this.store.get() data.config = config this.setData(data) this._emitUpdates(config) } ConfigManager.prototype.getConfig = function () { - var data = this.migrator.getData() + var data = this.store.get() if ('config' in data) { return data.config } else { @@ -94,15 +71,15 @@ ConfigManager.prototype.getProvider = function () { } ConfigManager.prototype.setData = function (data) { - this.migrator.saveData(data) + this.store.put(data) } ConfigManager.prototype.getData = function () { - return this.migrator.getData() + return this.store.get() } ConfigManager.prototype.setWallet = function (wallet) { - var data = this.migrator.getData() + var data = this.store.get() data.wallet = wallet this.setData(data) } @@ -119,11 +96,11 @@ ConfigManager.prototype.getVault = function () { } ConfigManager.prototype.getKeychains = function () { - return this.migrator.getData().keychains || [] + return this.store.get().keychains || [] } ConfigManager.prototype.setKeychains = function (keychains) { - var data = this.migrator.getData() + var data = this.store.get() data.keychains = keychains this.setData(data) } @@ -140,19 +117,19 @@ ConfigManager.prototype.setSelectedAccount = function (address) { } ConfigManager.prototype.getWallet = function () { - return this.migrator.getData().wallet + return this.store.get().wallet } // Takes a boolean ConfigManager.prototype.setShowSeedWords = function (should) { - var data = this.migrator.getData() + var data = this.store.get() data.showSeedWords = should this.setData(data) } ConfigManager.prototype.getShouldShowSeedWords = function () { - var data = this.migrator.getData() + var data = this.store.get() return data.showSeedWords } @@ -187,7 +164,7 @@ ConfigManager.prototype.getCurrentRpcAddress = function () { } ConfigManager.prototype.setData = function (data) { - this.migrator.saveData(data) + this.store.put(data) } // @@ -195,7 +172,7 @@ ConfigManager.prototype.setData = function (data) { // ConfigManager.prototype.getTxList = function () { - var data = this.migrator.getData() + var data = this.store.get() if (data.transactions !== undefined) { return data.transactions } else { @@ -204,7 +181,7 @@ ConfigManager.prototype.getTxList = function () { } ConfigManager.prototype.setTxList = function (txList) { - var data = this.migrator.getData() + var data = this.store.get() data.transactions = txList this.setData(data) } diff --git a/app/scripts/lib/migrations.js b/app/scripts/lib/migrations.js index f026cbe53..12f60def1 100644 --- a/app/scripts/lib/migrations.js +++ b/app/scripts/lib/migrations.js @@ -1,3 +1,16 @@ +/* The migrator has two methods the user should be concerned with: + * + * getData(), which returns the app-consumable data object + * saveData(), which persists the app-consumable data object. + */ + +// Migrations must start at version 1 or later. +// They are objects with a `version` number +// and a `migrate` function. +// +// The `migrate` function receives the previous +// config data format, and returns the new one. + module.exports = [ require('../migrations/002'), require('../migrations/003'), diff --git a/app/scripts/lib/observable/host.js b/app/scripts/lib/observable/host.js index 69f674be8..d1b110503 100644 --- a/app/scripts/lib/observable/host.js +++ b/app/scripts/lib/observable/host.js @@ -12,14 +12,14 @@ class HostStore extends ObservableStore { constructor (initState, opts) { super(initState) - this.opts = opts || {} + this._opts = opts || {} } createStream () { const self = this // setup remotely exposed api let remoteApi = {} - if (!self.opts.readOnly) { + if (!self._opts.readOnly) { remoteApi.put = (newState) => self.put(newState) } // listen for connection to remote diff --git a/app/scripts/lib/observable/index.js b/app/scripts/lib/observable/index.js index d193e5554..1ff112e95 100644 --- a/app/scripts/lib/observable/index.js +++ b/app/scripts/lib/observable/index.js @@ -7,22 +7,30 @@ class ObservableStore extends EventEmitter { this._state = initialState } + // wrapper around internal get get () { return this._state } - + + // wrapper around internal put put (newState) { this._put(newState) } + // subscribe to changes subscribe (handler) { this.on('update', handler) } + // unsubscribe to changes unsubscribe (handler) { this.removeListener('update', handler) } + // + // private + // + _put (newState) { this._state = newState this.emit('update', newState) diff --git a/app/scripts/lib/observable/local-storage.js b/app/scripts/lib/observable/local-storage.js new file mode 100644 index 000000000..6ed3860f6 --- /dev/null +++ b/app/scripts/lib/observable/local-storage.js @@ -0,0 +1,37 @@ +const ObservableStore = require('./index') + +// +// LocalStorageStore +// +// uses localStorage instead of a cache +// + +class LocalStorageStore extends ObservableStore { + + constructor (opts) { + super() + delete this._state + + this._opts = opts || {} + if (!this._opts.storageKey) { + throw new Error('LocalStorageStore - no "storageKey" specified') + } + this._storageKey = this._opts.storageKey + } + + get() { + try { + return JSON.parse(global.localStorage[this._storageKey]) + } catch (err) { + return undefined + } + } + + _put(newState) { + global.localStorage[this._storageKey] = JSON.stringify(newState) + this.emit('update', newState) + } + +} + +module.exports = LocalStorageStore diff --git a/app/scripts/lib/observable/remote.js b/app/scripts/lib/observable/remote.js index b5a3254a2..603f6f0b8 100644 --- a/app/scripts/lib/observable/remote.js +++ b/app/scripts/lib/observable/remote.js @@ -12,7 +12,7 @@ class RemoteStore extends ObservableStore { constructor (initState, opts) { super(initState) - this.opts = opts || {} + this._opts = opts || {} this._remote = null } diff --git a/app/scripts/lib/observable/util/sync.js b/app/scripts/lib/observable/util/sync.js new file mode 100644 index 000000000..c61feb02e --- /dev/null +++ b/app/scripts/lib/observable/util/sync.js @@ -0,0 +1,24 @@ + +// +// synchronizeStore(inStore, outStore, stateTransform) +// +// keeps outStore synchronized with inStore, via an optional stateTransform +// + +module.exports = synchronizeStore + + +function synchronizeStore(inStore, outStore, stateTransform) { + stateTransform = stateTransform || transformNoop + const initState = stateTransform(inStore.get()) + outStore.put(initState) + inStore.subscribe((inState) => { + const outState = stateTransform(inState) + outStore.put(outState) + }) + return outStore +} + +function transformNoop(state) { + return state +} \ No newline at end of file diff --git a/app/scripts/lib/observable/util/transform.js b/app/scripts/lib/observable/util/transform.js deleted file mode 100644 index 87946f402..000000000 --- a/app/scripts/lib/observable/util/transform.js +++ /dev/null @@ -1,13 +0,0 @@ - -module.exports = transformStore - - -function transformStore(inStore, outStore, stateTransform) { - const initState = stateTransform(inStore.get()) - outStore.put(initState) - inStore.subscribe((inState) => { - const outState = stateTransform(inState) - outStore.put(outState) - }) - return outStore -} \ No newline at end of file -- cgit v1.2.3 From b33c51c0a6c7c8a7b0c0a9a6ca101f874f2db3d1 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 12 Jan 2017 01:17:05 -0800 Subject: migrations - introduce promise-based migrator --- app/scripts/lib/migrator/index.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/scripts/lib/migrator/index.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js new file mode 100644 index 000000000..02d8c2335 --- /dev/null +++ b/app/scripts/lib/migrator/index.js @@ -0,0 +1,31 @@ +const asyncQ = require('async-q') + +class Migrator { + + constructor (opts = {}) { + let migrations = opts.migrations || [] + this.migrations = migrations.sort((a, b) => a.version - b.version) + let lastMigration = this.migrations.slice(-1)[0] + // use specified defaultVersion or highest migration version + this.defaultVersion = opts.defaultVersion || lastMigration && lastMigration.version || 0 + } + + // run all pending migrations on meta in place + migrateData (meta = { version: this.defaultVersion }) { + let remaining = this.migrations.filter(migrationIsPending) + + return ( + asyncQ.eachSeries(remaining, (migration) => migration.migrate(meta)) + .then(() => meta) + ) + + // migration is "pending" if hit has a higher + // version number than currentVersion + function migrationIsPending(migration) { + return migration.version > meta.version + } + } + +} + +module.exports = Migrator -- cgit v1.2.3 From 80514d73b5bc6887cea877194091c941cfb9a8e6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 12 Jan 2017 02:24:33 -0800 Subject: migrations - wip - 005 multivault migration --- app/scripts/lib/migrations.js | 18 ------------------ app/scripts/lib/migrator/index.js | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 23 deletions(-) delete mode 100644 app/scripts/lib/migrations.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/migrations.js b/app/scripts/lib/migrations.js deleted file mode 100644 index 12f60def1..000000000 --- a/app/scripts/lib/migrations.js +++ /dev/null @@ -1,18 +0,0 @@ -/* The migrator has two methods the user should be concerned with: - * - * getData(), which returns the app-consumable data object - * saveData(), which persists the app-consumable data object. - */ - -// Migrations must start at version 1 or later. -// They are objects with a `version` number -// and a `migrate` function. -// -// The `migrate` function receives the previous -// config data format, and returns the new one. - -module.exports = [ - require('../migrations/002'), - require('../migrations/003'), - require('../migrations/004'), -] diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js index 02d8c2335..ab5a757b3 100644 --- a/app/scripts/lib/migrator/index.js +++ b/app/scripts/lib/migrator/index.js @@ -7,22 +7,31 @@ class Migrator { this.migrations = migrations.sort((a, b) => a.version - b.version) let lastMigration = this.migrations.slice(-1)[0] // use specified defaultVersion or highest migration version - this.defaultVersion = opts.defaultVersion || lastMigration && lastMigration.version || 0 + this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0 } // run all pending migrations on meta in place - migrateData (meta = { version: this.defaultVersion }) { + migrateData (versionedData = this.generateInitialState()) { let remaining = this.migrations.filter(migrationIsPending) return ( - asyncQ.eachSeries(remaining, (migration) => migration.migrate(meta)) - .then(() => meta) + asyncQ.eachSeries(remaining, (migration) => migration.migrate(versionedData)) + .then(() => versionedData) ) // migration is "pending" if hit has a higher // version number than currentVersion function migrationIsPending(migration) { - return migration.version > meta.version + return migration.version > versionedData.meta.version + } + } + + generateInitialState (initState) { + return { + meta: { + version: this.defaultVersion, + }, + data: initState, } } -- cgit v1.2.3 From 76ce348a04b83693eda0e8a40f9888c1f5fe7ef5 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 24 Jan 2017 19:47:00 -0800 Subject: obs-store - use published module --- app/scripts/lib/config-manager.js | 44 ++++++++-------- app/scripts/lib/inpage-provider.js | 79 ++++++++++++----------------- app/scripts/lib/observable/host.js | 50 ------------------ app/scripts/lib/observable/index.js | 41 --------------- app/scripts/lib/observable/local-storage.js | 37 -------------- app/scripts/lib/observable/remote.js | 51 ------------------- app/scripts/lib/observable/util/sync.js | 24 --------- 7 files changed, 52 insertions(+), 274 deletions(-) delete mode 100644 app/scripts/lib/observable/host.js delete mode 100644 app/scripts/lib/observable/index.js delete mode 100644 app/scripts/lib/observable/local-storage.js delete mode 100644 app/scripts/lib/observable/remote.js delete mode 100644 app/scripts/lib/observable/util/sync.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index daba8bc7b..6d088906c 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -21,14 +21,14 @@ function ConfigManager (opts) { } ConfigManager.prototype.setConfig = function (config) { - var data = this.store.get() + var data = this.getData() data.config = config this.setData(data) this._emitUpdates(config) } ConfigManager.prototype.getConfig = function () { - var data = this.store.get() + var data = this.getData() if ('config' in data) { return data.config } else { @@ -71,15 +71,15 @@ ConfigManager.prototype.getProvider = function () { } ConfigManager.prototype.setData = function (data) { - this.store.put(data) + this.store.putState(data) } ConfigManager.prototype.getData = function () { - return this.store.get() + return this.store.getState() } ConfigManager.prototype.setWallet = function (wallet) { - var data = this.store.get() + var data = this.getData() data.wallet = wallet this.setData(data) } @@ -96,11 +96,11 @@ ConfigManager.prototype.getVault = function () { } ConfigManager.prototype.getKeychains = function () { - return this.store.get().keychains || [] + return this.getData().keychains || [] } ConfigManager.prototype.setKeychains = function (keychains) { - var data = this.store.get() + var data = this.getData() data.keychains = keychains this.setData(data) } @@ -117,19 +117,19 @@ ConfigManager.prototype.setSelectedAccount = function (address) { } ConfigManager.prototype.getWallet = function () { - return this.store.get().wallet + return this.getData().wallet } // Takes a boolean ConfigManager.prototype.setShowSeedWords = function (should) { - var data = this.store.get() + var data = this.getData() data.showSeedWords = should this.setData(data) } ConfigManager.prototype.getShouldShowSeedWords = function () { - var data = this.store.get() + var data = this.getData() return data.showSeedWords } @@ -141,7 +141,7 @@ ConfigManager.prototype.setSeedWords = function (words) { ConfigManager.prototype.getSeedWords = function () { var data = this.getData() - return ('seedWords' in data) && data.seedWords + return data.seedWords } ConfigManager.prototype.getCurrentRpcAddress = function () { @@ -163,16 +163,12 @@ ConfigManager.prototype.getCurrentRpcAddress = function () { } } -ConfigManager.prototype.setData = function (data) { - this.store.put(data) -} - // // Tx // ConfigManager.prototype.getTxList = function () { - var data = this.store.get() + var data = this.getData() if (data.transactions !== undefined) { return data.transactions } else { @@ -181,7 +177,7 @@ ConfigManager.prototype.getTxList = function () { } ConfigManager.prototype.setTxList = function (txList) { - var data = this.store.get() + var data = this.getData() data.transactions = txList this.setData(data) } @@ -214,7 +210,7 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) { ConfigManager.prototype.getSalt = function () { var data = this.getData() - return ('salt' in data) && data.salt + return data.salt } ConfigManager.prototype.setSalt = function (salt) { @@ -248,7 +244,7 @@ ConfigManager.prototype.setConfirmedDisclaimer = function (confirmed) { ConfigManager.prototype.getConfirmedDisclaimer = function () { var data = this.getData() - return ('isDisclaimerConfirmed' in data) && data.isDisclaimerConfirmed + return data.isDisclaimerConfirmed } ConfigManager.prototype.setTOSHash = function (hash) { @@ -259,7 +255,7 @@ ConfigManager.prototype.setTOSHash = function (hash) { ConfigManager.prototype.getTOSHash = function () { var data = this.getData() - return ('TOSHash' in data) && data.TOSHash + return data.TOSHash } ConfigManager.prototype.setCurrentFiat = function (currency) { @@ -270,7 +266,7 @@ ConfigManager.prototype.setCurrentFiat = function (currency) { ConfigManager.prototype.getCurrentFiat = function () { var data = this.getData() - return ('fiatCurrency' in data) && data.fiatCurrency + return data.fiatCurrency } ConfigManager.prototype.updateConversionRate = function () { @@ -301,12 +297,12 @@ ConfigManager.prototype.setConversionDate = function (datestring) { ConfigManager.prototype.getConversionRate = function () { var data = this.getData() - return (('conversionRate' in data) && data.conversionRate) || 0 + return (data.conversionRate) || 0 } ConfigManager.prototype.getConversionDate = function () { var data = this.getData() - return (('conversionDate' in data) && data.conversionDate) || 'N/A' + return (data.conversionDate) || 'N/A' } ConfigManager.prototype.getShapeShiftTxList = function () { @@ -345,7 +341,7 @@ ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositTy ConfigManager.prototype.getGasMultiplier = function () { var data = this.getData() - return ('gasMultiplier' in data) && data.gasMultiplier + return data.gasMultiplier } ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) { diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 64301be78..066916b4d 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -1,7 +1,7 @@ -const Streams = require('mississippi') +const pipe = require('pump') const StreamProvider = require('web3-stream-provider') +const LocalStorageStore = require('obs-store') const ObjectMultiplex = require('./obj-multiplex') -const RemoteStore = require('./observable/remote') const createRandomId = require('./random-id') module.exports = MetamaskInpageProvider @@ -10,33 +10,30 @@ function MetamaskInpageProvider (connectionStream) { const self = this // setup connectionStream multiplexing - var multiStream = ObjectMultiplex() - Streams.pipe(connectionStream, multiStream, connectionStream, function (err) { - let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask' - if (err) warningMsg += '\n' + err.stack - console.warn(warningMsg) - }) - self.multiStream = multiStream - - // subscribe to metamask public config - var publicConfigStore = remoteStoreWithLocalStorageCache('MetaMask-Config') - var storeStream = publicConfigStore.createStream() - Streams.pipe(storeStream, multiStream.createStream('publicConfig'), storeStream, function (err) { - let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask publicConfig' - if (err) warningMsg += '\n' + err.stack - console.warn(warningMsg) - }) - self.publicConfigStore = publicConfigStore + var multiStream = self.multiStream = ObjectMultiplex() + pipe( + connectionStream, + multiStream, + connectionStream, + (err) => logStreamDisconnectWarning('MetaMask', err) + ) + + // subscribe to metamask public config (one-way) + self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) + pipe( + multiStream.createStream('publicConfig'), + self.publicConfigStore, + (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) + ) // connect to async provider - var asyncProvider = new StreamProvider() - Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) { - let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask provider' - if (err) warningMsg += '\n' + err.stack - console.warn(warningMsg) - }) - asyncProvider.on('error', console.error.bind(console)) - self.asyncProvider = asyncProvider + const asyncProvider = self.asyncProvider = new StreamProvider() + pipe( + asyncProvider, + multiStream.createStream('provider'), + asyncProvider, + (err) => logStreamDisconnectWarning('MetaMask RpcProvider', err) + ) self.idMap = {} // handle sendAsync requests via asyncProvider @@ -72,13 +69,13 @@ MetamaskInpageProvider.prototype.send = function (payload) { case 'eth_accounts': // read from localStorage - selectedAccount = self.publicConfigStore.get().selectedAccount + selectedAccount = self.publicConfigStore.getState().selectedAccount result = selectedAccount ? [selectedAccount] : [] break case 'eth_coinbase': // read from localStorage - selectedAccount = self.publicConfigStore.get().selectedAccount + selectedAccount = self.publicConfigStore.getState().selectedAccount result = selectedAccount || '0x0000000000000000000000000000000000000000' break @@ -115,24 +112,6 @@ MetamaskInpageProvider.prototype.isMetaMask = true // util -function remoteStoreWithLocalStorageCache (storageKey) { - // read local cache - let initState - try { - initState = JSON.parse(localStorage[storageKey] || '{}') - } catch (err) { - initState = {} - } - // intialize store - const store = new RemoteStore(initState) - // write local cache - store.subscribe(function (state) { - localStorage[storageKey] = JSON.stringify(state) - }) - - return store -} - function eachJsonMessage (payload, transformFn) { if (Array.isArray(payload)) { return payload.map(transformFn) @@ -141,4 +120,10 @@ function eachJsonMessage (payload, transformFn) { } } +function logStreamDisconnectWarning(remoteLabel, err){ + let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}` + if (err) warningMsg += '\n' + err.stack + console.warn(warningMsg) +} + function noop () {} diff --git a/app/scripts/lib/observable/host.js b/app/scripts/lib/observable/host.js deleted file mode 100644 index d1b110503..000000000 --- a/app/scripts/lib/observable/host.js +++ /dev/null @@ -1,50 +0,0 @@ -const Dnode = require('dnode') -const ObservableStore = require('./index') -const endOfStream = require('end-of-stream') - -// -// HostStore -// -// plays host to many RemoteStores and sends its state over a stream -// - -class HostStore extends ObservableStore { - - constructor (initState, opts) { - super(initState) - this._opts = opts || {} - } - - createStream () { - const self = this - // setup remotely exposed api - let remoteApi = {} - if (!self._opts.readOnly) { - remoteApi.put = (newState) => self.put(newState) - } - // listen for connection to remote - const dnode = Dnode(remoteApi) - dnode.on('remote', (remote) => { - // setup update subscription lifecycle - const updateHandler = (state) => remote.put(state) - self._onConnect(updateHandler) - endOfStream(dnode, () => self._onDisconnect(updateHandler)) - }) - return dnode - } - - _onConnect (updateHandler) { - // subscribe to updates - this.subscribe(updateHandler) - // send state immediately - updateHandler(this.get()) - } - - _onDisconnect (updateHandler) { - // unsubscribe to updates - this.unsubscribe(updateHandler) - } - -} - -module.exports = HostStore diff --git a/app/scripts/lib/observable/index.js b/app/scripts/lib/observable/index.js deleted file mode 100644 index 1ff112e95..000000000 --- a/app/scripts/lib/observable/index.js +++ /dev/null @@ -1,41 +0,0 @@ -const EventEmitter = require('events').EventEmitter - -class ObservableStore extends EventEmitter { - - constructor (initialState) { - super() - this._state = initialState - } - - // wrapper around internal get - get () { - return this._state - } - - // wrapper around internal put - put (newState) { - this._put(newState) - } - - // subscribe to changes - subscribe (handler) { - this.on('update', handler) - } - - // unsubscribe to changes - unsubscribe (handler) { - this.removeListener('update', handler) - } - - // - // private - // - - _put (newState) { - this._state = newState - this.emit('update', newState) - } - -} - -module.exports = ObservableStore diff --git a/app/scripts/lib/observable/local-storage.js b/app/scripts/lib/observable/local-storage.js deleted file mode 100644 index 6ed3860f6..000000000 --- a/app/scripts/lib/observable/local-storage.js +++ /dev/null @@ -1,37 +0,0 @@ -const ObservableStore = require('./index') - -// -// LocalStorageStore -// -// uses localStorage instead of a cache -// - -class LocalStorageStore extends ObservableStore { - - constructor (opts) { - super() - delete this._state - - this._opts = opts || {} - if (!this._opts.storageKey) { - throw new Error('LocalStorageStore - no "storageKey" specified') - } - this._storageKey = this._opts.storageKey - } - - get() { - try { - return JSON.parse(global.localStorage[this._storageKey]) - } catch (err) { - return undefined - } - } - - _put(newState) { - global.localStorage[this._storageKey] = JSON.stringify(newState) - this.emit('update', newState) - } - -} - -module.exports = LocalStorageStore diff --git a/app/scripts/lib/observable/remote.js b/app/scripts/lib/observable/remote.js deleted file mode 100644 index 603f6f0b8..000000000 --- a/app/scripts/lib/observable/remote.js +++ /dev/null @@ -1,51 +0,0 @@ -const Dnode = require('dnode') -const ObservableStore = require('./index') -const endOfStream = require('end-of-stream') - -// -// RemoteStore -// -// connects to a HostStore and receives its latest state -// - -class RemoteStore extends ObservableStore { - - constructor (initState, opts) { - super(initState) - this._opts = opts || {} - this._remote = null - } - - put (newState) { - if (!this._remote) throw new Error('RemoteStore - "put" called before connection to HostStore') - this._put(newState) - this._remote.put(newState) - } - - createStream () { - const self = this - const dnode = Dnode({ - put: (newState) => self._put(newState), - }) - // listen for connection to remote - dnode.once('remote', (remote) => { - // setup connection lifecycle - self._onConnect(remote) - endOfStream(dnode, () => self._onDisconnect()) - }) - return dnode - } - - _onConnect (remote) { - this._remote = remote - this.emit('connected') - } - - _onDisconnect () { - this._remote = null - this.emit('disconnected') - } - -} - -module.exports = RemoteStore \ No newline at end of file diff --git a/app/scripts/lib/observable/util/sync.js b/app/scripts/lib/observable/util/sync.js deleted file mode 100644 index c61feb02e..000000000 --- a/app/scripts/lib/observable/util/sync.js +++ /dev/null @@ -1,24 +0,0 @@ - -// -// synchronizeStore(inStore, outStore, stateTransform) -// -// keeps outStore synchronized with inStore, via an optional stateTransform -// - -module.exports = synchronizeStore - - -function synchronizeStore(inStore, outStore, stateTransform) { - stateTransform = stateTransform || transformNoop - const initState = stateTransform(inStore.get()) - outStore.put(initState) - inStore.subscribe((inState) => { - const outState = stateTransform(inState) - outStore.put(outState) - }) - return outStore -} - -function transformNoop(state) { - return state -} \ No newline at end of file -- cgit v1.2.3