diff --git a/.gitignore b/.gitignore index 5f2d2f551..2bf38cad4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ npm-debug.log node_modules +yarn.lock app/bower_components test/bower_components diff --git a/CHANGELOG.md b/CHANGELOG.md index 80bef88b1..e7d4a09fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Add ability for internationalization. - Will now throw an error if the `to` field in txParams is not valid. - Will strip null values from the `to` field. - Fix flashing to Log in screen after logging in or restoring from seed phrase. @@ -10,7 +11,7 @@ - Add a welcome screen to new-ui onboarding flow - Make new-ui create password screen responsive - Hide network dropdown before account is initialized -- Add ability for internationalization. +- Fix bug that could prevent MetaMask from saving the latest vault. ## 4.2.0 Tue Mar 06 2018 diff --git a/app/manifest.json b/app/manifest.json index 6c5ed686b..6fcf6cd7c 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -56,6 +56,7 @@ ], "permissions": [ "storage", + "unlimitedStorage", "clipboardWrite", "http://localhost:8545/", "https://*.infura.io/" diff --git a/app/scripts/background.js b/app/scripts/background.js index 601ae0372..ef5513ec7 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,9 +1,11 @@ const urlUtil = require('url') const endOfStream = require('end-of-stream') const pump = require('pump') +const debounce = require('debounce-stream') const log = require('loglevel') const extension = require('extensionizer') const LocalStorageStore = require('obs-store/lib/localStorage') +const LocalStore = require('./lib/local-store') const storeTransform = require('obs-store/lib/transform') const asStream = require('obs-store/lib/asStream') const ExtensionPlatform = require('./platforms/extension') @@ -44,6 +46,8 @@ let openMetamaskTabsIDs = {} // state persistence const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) +const localStore = new LocalStore() +let versionedData // initialization flow initialize().catch(log.error) @@ -64,12 +68,23 @@ async function initialize () { async function loadStateFromPersistence () { // migrations const migrator = new Migrator({ migrations }) + // read from disk - let versionedData = diskStore.getState() || migrator.generateInitialState(firstTimeState) + // first from preferred, async API: + versionedData = (await localStore.get()) || + diskStore.getState() || + migrator.generateInitialState(firstTimeState) + // migrate data versionedData = await migrator.migrateData(versionedData) + if (!versionedData) { + throw new Error('MetaMask - migrator returned undefined') + } + // write to disk + if (localStore.isSupported) localStore.set(versionedData) diskStore.putState(versionedData) + // return just the data return versionedData.data } @@ -102,16 +117,30 @@ function setupController (initState) { // setup state persistence pump( asStream(controller.store), + debounce(1000), storeTransform(versionifyData), - asStream(diskStore) + storeTransform(syncDataWithExtension), + asStream(diskStore), + (error) => { + log.error('pump hit error', error) + } ) function versionifyData (state) { - const versionedData = diskStore.getState() versionedData.data = state return versionedData } + function syncDataWithExtension(state) { + if (localStore.isSupported) { + localStore.set(state) + .catch((err) => { + log.error('error setting state in local store:', err) + }) + } + return state + } + // // connect to other contexts // diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js new file mode 100644 index 000000000..781aea17e --- /dev/null +++ b/app/scripts/lib/local-store.js @@ -0,0 +1,38 @@ +// We should not rely on local storage in an extension! +// We should use this instead! +// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local + +const extension = require('extensionizer') +const { promisify } = require('util').promisify + +module.exports = class ExtensionStore { + constructor() { + this.isSupported = !!(extension.storage.local) + if (!this.isSupported) { + log.error('Storage local API not available.') + } + const local = extension.storage.local + this._get = promisify(local.get).bind(local) + this._set = promisify(local.set).bind(local) + } + + async get() { + if (!this.isSupported) return undefined + const result = await this._get() + // extension.storage.local always returns an obj + // if the object is empty, treat it as undefined + if (isEmpty(result)) { + return undefined + } else { + return result + } + } + + async set(state) { + return this._set(state) + } +} + +function isEmpty(obj) { + return Object.keys(obj).length === 0 +} diff --git a/package.json b/package.json index a80b7e28e..00587ece6 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "clone": "^2.1.1", "copy-to-clipboard": "^3.0.8", "debounce": "^1.0.0", + "debounce-stream": "^2.0.0", "deep-extend": "^0.5.0", "detect-node": "^2.0.3", "disc": "^1.3.2",