1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

obs-store - use published module

This commit is contained in:
kumavis 2017-01-24 19:47:00 -08:00
parent a06ee45404
commit 76ce348a04
16 changed files with 112 additions and 326 deletions

View File

@ -2,10 +2,11 @@ const urlUtil = require('url')
const Dnode = require('dnode')
const eos = require('end-of-stream')
const asyncQ = require('async-q')
const pipe = require('pump')
const LocalStorageStore = require('obs-store/lib/localStorage')
const storeTransform = require('obs-store/lib/transform')
const Migrator = require('./lib/migrator/')
const migrations = require('./lib/migrations/')
const LocalStorageStore = require('./lib/observable/local-storage')
const synchronizeStore = require('./lib/observable/util/sync')
const migrations = require('./migrations/')
const PortStream = require('./lib/port-stream.js')
const notification = require('./lib/notifications.js')
const messageManager = require('./lib/message-manager')
@ -40,12 +41,12 @@ function loadStateFromPersistence() {
let initialState = migrator.generateInitialState(firstTimeState)
return asyncQ.waterfall([
// read from disk
() => Promise.resolve(diskStore.get() || initialState),
() => Promise.resolve(diskStore.getState() || initialState),
// migrate data
(versionedData) => migrator.migrateData(versionedData),
// write to disk
(versionedData) => {
diskStore.put(versionedData)
diskStore.putState(versionedData)
return Promise.resolve(versionedData)
},
// resolve to just data
@ -70,11 +71,17 @@ function setupController (initState) {
global.metamaskController = controller
// setup state persistence
synchronizeStore(controller.store, diskStore, (state) => {
let versionedData = diskStore.get()
pipe(
controller.store,
storeTransform(versionifyData),
diskStore
)
function versionifyData(state) {
let versionedData = diskStore.getState()
versionedData.data = state
return versionedData
})
}
//
// connect to other contexts

View File

@ -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) {

View File

@ -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
var multiStream = self.multiStream = ObjectMultiplex()
pipe(
connectionStream,
multiStream,
connectionStream,
(err) => logStreamDisconnectWarning('MetaMask', err)
)
// 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
// 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 () {}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -1,6 +1,9 @@
const EventEmitter = require('events')
const extend = require('xtend')
const promiseToCallback = require('promise-to-callback')
const pipe = require('pump')
const ObservableStore = require('obs-store')
const storeTransform = require('obs-store/lib/transform')
const EthStore = require('./lib/eth-store')
const MetaMaskProvider = require('web3-provider-engine/zero.js')
const KeyringController = require('./keyring-controller')
@ -13,9 +16,6 @@ const extension = require('./lib/extension')
const autoFaucet = require('./lib/auto-faucet')
const nodeify = require('./lib/nodeify')
const IdStoreMigrator = require('./lib/idStore-migrator')
const ObservableStore = require('./lib/observable/')
const HostStore = require('./lib/observable/host')
const synchronizeStore = require('./lib/observable/util/sync')
const accountImporter = require('./account-import-strategies')
const version = require('../manifest.json').version
@ -258,18 +258,21 @@ module.exports = class MetamaskController extends EventEmitter {
initPublicConfigStore () {
// get init state
var initPublicState = this.store.get()
var publicConfigStore = new HostStore(initPublicState, { readOnly: true })
const publicConfigStore = new ObservableStore()
// sync publicConfigStore with transform
synchronizeStore(this.store, publicConfigStore, selectPublicState)
pipe(
this.store,
storeTransform(selectPublicState),
publicConfigStore
)
function selectPublicState(state) {
let result = { selectedAccount: undefined }
const result = { selectedAccount: undefined }
try {
result.selectedAccount = state.config.selectedAccount
} catch (err) {
console.warn('Error in "selectPublicState": ' + err.message)
} catch (_) {
// thats fine, im sure it will be there next time...
}
return result
}
@ -314,9 +317,11 @@ module.exports = class MetamaskController extends EventEmitter {
this.opts.showUnconfirmedMessage(msgParams, msgId)
}
setupPublicConfig (stream) {
var storeStream = this.publicConfigStore.createStream()
stream.pipe(storeStream).pipe(stream)
setupPublicConfig (outStream) {
pipe(
this.publicConfigStore,
outStream
)
}
// Log blocks

View File

@ -7,7 +7,7 @@ which we dont have access to at the time of this writing.
*/
const ObservableStore = require('../../app/scripts/lib/observable/')
const ObservableStore = require('obs-store')
const ConfigManager = require('../../app/scripts/lib/config-manager')
const IdentityStoreMigrator = require('../../app/scripts/lib/idStore-migrator')
const KeyringController = require('../../app/scripts/lib/keyring-controller')

View File

@ -18,6 +18,8 @@ if (!global.localStorage) global.localStorage
const extend = require('xtend')
const render = require('react-dom').render
const h = require('react-hyperscript')
const pipe = require('mississippi').pipe
const LocalStorageStore = require('obs-store/lib/localStorage')
const Root = require('./ui/app/root')
const configureStore = require('./ui/app/store')
const actions = require('./ui/app/actions')
@ -25,8 +27,6 @@ const states = require('./development/states')
const Selector = require('./development/selector')
const MetamaskController = require('./app/scripts/metamask-controller')
const firstTimeState = require('./app/scripts/first-time-state')
const LocalStorageStore = require('./app/scripts/lib/observable/local-storage')
const synchronizeStore = require('./app/scripts/lib/observable/util/sync')
const extension = require('./development/mockExtension')
const noop = function () {}
@ -61,8 +61,8 @@ const injectCss = require('inject-css')
let dataStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
// initial state for first time users
if (!dataStore.get()) {
dataStore.put(firstTimeState)
if (!dataStore.getState()) {
dataStore.putState(firstTimeState)
}
const controller = new MetamaskController({
@ -71,11 +71,14 @@ const controller = new MetamaskController({
unlockAccountMessage: noop,
showUnapprovedTx: noop,
// initial state
initState: dataStore.get(),
initState: dataStore.getState(),
})
// setup state persistence
synchronizeStore(controller.store, dataStore)
pipe(
controller.store,
dataStore
)
//
// User Interface

View File

@ -70,6 +70,7 @@
"mississippi": "^1.2.0",
"mkdirp": "^0.5.1",
"multiplex": "^6.7.0",
"obs-store": "^2.2.3",
"once": "^1.3.3",
"ping-pong-stream": "^1.0.0",
"pojo-migrator": "^2.1.0",
@ -77,6 +78,7 @@
"post-message-stream": "^1.0.0",
"promise-filter": "^1.1.0",
"promise-to-callback": "^1.0.0",
"pump": "^1.0.2",
"pumpify": "^1.3.4",
"qrcode-npm": "0.0.3",
"react": "^15.0.2",

View File

@ -1,4 +1,4 @@
const ObservableStore = require('../../../app/scripts/lib/observable/')
const ObservableStore = require('obs-store')
const ConfigManager = require('../../../app/scripts/lib/config-manager')
const IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator')
const SimpleKeyring = require('../../../app/scripts/keyrings/simple')

View File

@ -1,11 +1,10 @@
const ObservableStore = require('obs-store')
const clone = require('clone')
const ConfigManager = require('../../app/scripts/lib/config-manager')
const LocalStorageStore = require('../../app/scripts/lib/observable/local-storage')
const firstTimeState = require('../../app/scripts/first-time-state')
const STORAGE_KEY = 'metamask-config'
module.exports = function() {
let dataStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
// initial state for first time users
if (!dataStore.get()) dataStore.put(firstTimeState)
return new ConfigManager({ store: dataStore })
let store = new ObservableStore(clone(firstTimeState))
return new ConfigManager({ store })
}

View File

@ -1,27 +1,23 @@
// polyfill fetch
global.fetch = global.fetch || require('isomorphic-fetch')
// pollyfill localStorage support into JSDom
global.localStorage = global.localStorage || polyfillLocalStorage()
const assert = require('assert')
const extend = require('xtend')
const rp = require('request-promise')
const nock = require('nock')
const configManagerGen = require('../lib/mock-config-manager')
const STORAGE_KEY = 'metamask-persistance-key'
describe('config-manager', function() {
var configManager
beforeEach(function() {
global.localStorage.clear()
configManager = configManagerGen()
})
describe('currency conversions', function() {
describe('#getCurrentFiat', function() {
it('should return false if no previous key exists', function() {
it('should return undefined if no previous key exists', function() {
var result = configManager.getCurrentFiat()
assert.ok(!result)
})
@ -29,14 +25,14 @@ describe('config-manager', function() {
describe('#setCurrentFiat', function() {
it('should make getCurrentFiat return true once set', function() {
assert.equal(configManager.getCurrentFiat(), false)
assert.equal(configManager.getCurrentFiat(), undefined)
configManager.setCurrentFiat('USD')
var result = configManager.getCurrentFiat()
assert.equal(result, 'USD')
})
it('should work with other currencies as well', function() {
assert.equal(configManager.getCurrentFiat(), false)
assert.equal(configManager.getCurrentFiat(), undefined)
configManager.setCurrentFiat('JPY')
var result = configManager.getCurrentFiat()
assert.equal(result, 'JPY')
@ -44,7 +40,7 @@ describe('config-manager', function() {
})
describe('#getConversionRate', function() {
it('should return false if non-existent', function() {
it('should return undefined if non-existent', function() {
var result = configManager.getConversionRate()
assert.ok(!result)
})
@ -57,7 +53,7 @@ describe('config-manager', function() {
.get('/api/ticker/eth-USD')
.reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
assert.equal(configManager.getConversionRate(), false)
assert.equal(configManager.getConversionRate(), 0)
var promise = new Promise(
function (resolve, reject) {
configManager.setCurrentFiat('USD')
@ -78,7 +74,7 @@ describe('config-manager', function() {
it('should work for JPY as well.', function() {
this.timeout(15000)
assert.equal(configManager.getConversionRate(), false)
assert.equal(configManager.getConversionRate(), 0)
var jpyMock = nock('https://www.cryptonator.com')
.get('/api/ticker/eth-JPY')
@ -106,7 +102,7 @@ describe('config-manager', function() {
describe('confirmation', function() {
describe('#getConfirmedDisclaimer', function() {
it('should return false if no previous key exists', function() {
it('should return undefined if no previous key exists', function() {
var result = configManager.getConfirmedDisclaimer()
assert.ok(!result)
})
@ -114,16 +110,16 @@ describe('config-manager', function() {
describe('#setConfirmedDisclaimer', function() {
it('should make getConfirmedDisclaimer return true once set', function() {
assert.equal(configManager.getConfirmedDisclaimer(), false)
assert.equal(configManager.getConfirmedDisclaimer(), undefined)
configManager.setConfirmedDisclaimer(true)
var result = configManager.getConfirmedDisclaimer()
assert.equal(result, true)
})
it('should be able to set false', function() {
configManager.setConfirmedDisclaimer(false)
it('should be able to set undefined', function() {
configManager.setConfirmedDisclaimer(undefined)
var result = configManager.getConfirmedDisclaimer()
assert.equal(result, false)
assert.equal(result, undefined)
})
it('should persist to local storage', function() {
@ -240,7 +236,3 @@ describe('config-manager', function() {
})
})
})
function polyfillLocalStorage(){
return Object.create({ clear: function(){ global.localStorage = polyfillLocalStorage() } })
}

View File

@ -1,9 +1,9 @@
const async = require('async')
const assert = require('assert')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const ConfigManager = require('../../app/scripts/lib/config-manager')
const ObservableStore = require('../../app/scripts/lib/observable/')
const delegateCallCode = require('../lib/example-code.json').delegateCallCode
// The old way: