diff --git a/extension/package.json b/extension/package.json index 35c9d0bd..b06a3709 100644 --- a/extension/package.json +++ b/extension/package.json @@ -41,13 +41,15 @@ "@redux-devtools/utils": "^1.0.0-6", "@types/jsan": "^3.1.2", "jsan": "^3.1.13", + "localforage": "^1.10.0", "lodash": "^4.17.21", "react": "^16.14.0", "react-dom": "^16.14.0", "react-icons": "^4.2.0", "react-json-tree": "^0.15.0", "react-redux": "^7.2.4", - "redux": "^4.1.1" + "redux": "^4.1.1", + "redux-persist": "^6.0.0" }, "devDependencies": { "@babel/register": "^7.15.3", diff --git a/extension/src/app/stores/panelStore.ts b/extension/src/app/stores/panelStore.ts index 977ed96e..130d9005 100644 --- a/extension/src/app/stores/panelStore.ts +++ b/extension/src/app/stores/panelStore.ts @@ -1,14 +1,26 @@ -import { createStore, applyMiddleware, PreloadedState } from 'redux'; +import { createStore, applyMiddleware, PreloadedState, Reducer } from 'redux'; +import localForage from 'localforage'; +import { persistReducer, persistStore } from 'redux-persist'; import exportState from '@redux-devtools/app/lib/middlewares/exportState'; import panelDispatcher from '../middlewares/panelSync'; -import rootReducer from '../reducers/panel'; -import { StoreState } from '@redux-devtools/app/lib/reducers'; +import rootReducer, { StoreStateWithoutSocket } from '../reducers/panel'; +import { StoreAction } from '@redux-devtools/app/lib/actions'; + +const persistConfig = { + key: 'redux-devtools', + blacklist: ['instances', 'socket'], + storage: localForage, +}; + +const persistedReducer: Reducer = + persistReducer(persistConfig, rootReducer) as any; export default function configureStore( position: string, - bgConnection: chrome.runtime.Port, - preloadedState: PreloadedState + bgConnection: chrome.runtime.Port ) { const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection)); - return createStore(rootReducer, preloadedState, enhancer); + const store = createStore(persistedReducer, enhancer); + const persistor = persistStore(store); + return { store, persistor }; } diff --git a/extension/src/app/stores/windowStore.ts b/extension/src/app/stores/windowStore.ts index 37dc535e..5e12ed97 100644 --- a/extension/src/app/stores/windowStore.ts +++ b/extension/src/app/stores/windowStore.ts @@ -3,15 +3,15 @@ import { compose, applyMiddleware, Store, - PreloadedState, StoreEnhancer, + Reducer, } from 'redux'; +import localForage from 'localforage'; +import { persistReducer, persistStore } from 'redux-persist'; import exportState from '@redux-devtools/app/lib/middlewares/exportState'; import api from '@redux-devtools/app/lib/middlewares/api'; import { CONNECT_REQUEST } from '@redux-devtools/app/lib/constants/socketActionTypes'; -import { StoreState } from '@redux-devtools/app/lib/reducers'; import { - StoreAction, StoreActionWithoutUpdateState, UpdateStateAction, } from '@redux-devtools/app/lib/actions'; @@ -22,6 +22,7 @@ import rootReducer from '../reducers/window'; import { BackgroundState } from '../reducers/background'; import { BackgroundAction } from './backgroundStore'; import { EmptyUpdateStateAction, NAAction } from '../middlewares/api'; +import { StoreState } from '@redux-devtools/app/lib/reducers'; export interface ExpandedUpdateStateAction extends UpdateStateAction { readonly instances: InstancesState; @@ -33,10 +34,20 @@ export type WindowStoreAction = | NAAction | EmptyUpdateStateAction; +const persistConfig = { + key: 'redux-devtools', + blacklist: ['instances', 'socket'], + storage: localForage, +}; + +const persistedReducer: Reducer = persistReducer( + persistConfig, + rootReducer +) as any; + export default function configureStore( baseStore: Store, - position: string, - preloadedState: PreloadedState + position: string ) { let enhancer: StoreEnhancer; const middlewares = [exportState, api, syncStores(baseStore)]; @@ -54,7 +65,8 @@ export default function configureStore( : (noop: unknown) => noop ); } - const store = createStore(rootReducer, preloadedState, enhancer); + const store = createStore(persistedReducer, enhancer); + const persistor = persistStore(store); if ( store.getState().connection.options.hostname && @@ -65,5 +77,5 @@ export default function configureStore( }); } - return store; + return { store, persistor }; } diff --git a/extension/src/browser/extension/background/getPreloadedState.ts b/extension/src/browser/extension/background/getPreloadedState.ts deleted file mode 100644 index e4cfa83a..00000000 --- a/extension/src/browser/extension/background/getPreloadedState.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { PreloadedState } from 'redux'; -import { StoreState } from '@redux-devtools/app/lib/reducers'; - -const getIfExists = (sel: any, template: any) => - typeof sel === 'undefined' || - typeof template === 'undefined' || - typeof template[sel] === 'undefined' - ? 0 - : sel; - -export default function getPreloadedState( - position: string, - cb: (state: PreloadedState) => void -) { - chrome.storage.local.get( - [ - 'monitor' + position, - 'slider' + position, - 'dispatcher' + position, - 'test-templates', - 'test-templates-sel', - ], - (options) => { - cb({ - monitor: { - selected: options['monitor' + position], - sliderIsOpen: options['slider' + position] || false, - dispatcherIsOpen: options['dispatcher' + position] || false, - }, - test: { - selected: getIfExists( - options['test-templates-sel'], - options['test-templates'] - ), - templates: options['test-templates'], - }, - } as any); - } - ); -} diff --git a/extension/src/browser/extension/devpanel/index.tsx b/extension/src/browser/extension/devpanel/index.tsx index 7847a5e9..658612b6 100644 --- a/extension/src/browser/extension/devpanel/index.tsx +++ b/extension/src/browser/extension/devpanel/index.tsx @@ -1,17 +1,17 @@ import React, { CSSProperties } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { Provider } from 'react-redux'; +import { Persistor } from 'redux-persist'; import { REMOVE_INSTANCE } from '@redux-devtools/app/lib/constants/actionTypes'; import App from '../../../app/containers/App'; import configureStore from '../../../app/stores/panelStore'; -import getPreloadedState from '../background/getPreloadedState'; import '../../views/devpanel.pug'; -import { Action, PreloadedState, Store } from 'redux'; -import { StoreState } from '@redux-devtools/app/lib/reducers'; +import { Action, Store } from 'redux'; import { StoreAction } from '@redux-devtools/app/lib/actions'; import { PanelMessage } from '../../../app/middlewares/api'; import { StoreStateWithoutSocket } from '../../../app/reducers/panel'; +import { PersistGate } from 'redux-persist/integration/react'; const position = location.hash; const messageStyle: CSSProperties = { @@ -22,24 +22,22 @@ const messageStyle: CSSProperties = { let rendered: boolean | undefined; let store: Store | undefined; +let persistor: Persistor | undefined; let bgConnection: chrome.runtime.Port; let naTimeout: NodeJS.Timeout; -let preloadedState: PreloadedState; const isChrome = navigator.userAgent.indexOf('Firefox') === -1; -getPreloadedState(position, (state) => { - preloadedState = state; -}); - function renderDevTools() { const node = document.getElementById('root'); unmountComponentAtNode(node!); clearTimeout(naTimeout); - store = configureStore(position, bgConnection, preloadedState); + ({ store, persistor } = configureStore(position, bgConnection)); render( - + + + , node ); diff --git a/extension/src/browser/extension/window/index.tsx b/extension/src/browser/extension/window/index.tsx index f108fbb5..a49aa01a 100644 --- a/extension/src/browser/extension/window/index.tsx +++ b/extension/src/browser/extension/window/index.tsx @@ -1,25 +1,19 @@ import React from 'react'; import { render } from 'react-dom'; -import { PreloadedState } from 'redux'; import { Provider } from 'react-redux'; +import { PersistGate } from 'redux-persist/integration/react'; import { UPDATE_STATE } from '@redux-devtools/app/lib/constants/actionTypes'; -import { StoreState } from '@redux-devtools/app/lib/reducers'; import App from '../../../app/containers/App'; import configureStore from '../../../app/stores/windowStore'; -import getPreloadedState from '../background/getPreloadedState'; import { MonitorMessage } from '../../../app/middlewares/api'; import '../../views/window.pug'; const position = location.hash; -let preloadedState: PreloadedState; -getPreloadedState(position, (state) => { - preloadedState = state; -}); chrome.runtime.getBackgroundPage((window) => { const { store } = window!; - const localStore = configureStore(store, position, preloadedState); + const { store: localStore, persistor } = configureStore(store, position); let name = 'monitor'; if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) { name += chrome.devtools.inspectedWindow.tabId; @@ -33,7 +27,9 @@ chrome.runtime.getBackgroundPage((window) => { render( - + + + , document.getElementById('root') ); diff --git a/extension/test/app/containers/App.spec.js b/extension/test/app/containers/App.spec.js index 3e919416..d7baf977 100644 --- a/extension/test/app/containers/App.spec.js +++ b/extension/test/app/containers/App.spec.js @@ -4,7 +4,7 @@ import { Provider } from 'react-redux'; import configureStore from '../../../src/app/stores/windowStore'; import App from '../../../src/app/containers/App'; -const store = configureStore(store); +const { store } = configureStore(store); const component = mount( diff --git a/extension/test/chrome/extension.spec.js b/extension/test/chrome/extension.spec.js index 5671fbc8..f807bdf0 100644 --- a/extension/test/chrome/extension.spec.js +++ b/extension/test/chrome/extension.spec.js @@ -43,6 +43,7 @@ describe('Chrome extension', function () { }); it("should contain inspector monitor's component", async () => { + await delay(500); const val = await driver .findElement(webdriver.By.xpath('//div[contains(@class, "inspector-")]')) .getText(); diff --git a/packages/redux-devtools-app/src/store/configureStore.ts b/packages/redux-devtools-app/src/store/configureStore.ts index ae54266b..9697ebed 100644 --- a/packages/redux-devtools-app/src/store/configureStore.ts +++ b/packages/redux-devtools-app/src/store/configureStore.ts @@ -1,4 +1,4 @@ -import { createStore, compose, applyMiddleware } from 'redux'; +import { createStore, compose, applyMiddleware, Reducer } from 'redux'; import localForage from 'localforage'; import { persistReducer, persistStore } from 'redux-persist'; import api from '../middlewares/api'; @@ -12,7 +12,10 @@ const persistConfig = { storage: localForage, }; -const persistedReducer = persistReducer(persistConfig, rootReducer); +const persistedReducer: Reducer = persistReducer( + persistConfig, + rootReducer +) as any; export default function configureStore() { let composeEnhancers = compose; @@ -40,8 +43,8 @@ export default function configureStore() { } } - const store = createStore( - persistedReducer as any, + const store = createStore( + persistedReducer, composeEnhancers(applyMiddleware(exportState, api)) ); const persistor = persistStore(store); diff --git a/yarn.lock b/yarn.lock index 17cf81bd..a30cfd4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25958,6 +25958,7 @@ fsevents@^1.2.7: eslint-plugin-react-hooks: ^4.2.0 gitbook-cli: ^2.3.2 jsan: ^3.1.13 + localforage: ^1.10.0 lodash: ^4.17.21 path-browserify: ^1.0.1 react: ^16.14.0 @@ -25968,6 +25969,7 @@ fsevents@^1.2.7: react-transform-catch-errors: ^1.0.2 react-transform-hmr: ^1.0.4 redux: ^4.1.1 + redux-persist: ^6.0.0 selenium-webdriver: ^3.6.0 sinon-chrome: ^3.0.1 languageName: unknown