feat(extension): add persistence of settings (#826)

This commit is contained in:
Nathan Bierema 2021-08-31 01:42:01 +00:00 committed by GitHub
parent ad23a3ed8b
commit a3a46cabf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 64 additions and 78 deletions

View File

@ -41,13 +41,15 @@
"@redux-devtools/utils": "^1.0.0-6", "@redux-devtools/utils": "^1.0.0-6",
"@types/jsan": "^3.1.2", "@types/jsan": "^3.1.2",
"jsan": "^3.1.13", "jsan": "^3.1.13",
"localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^16.14.0", "react": "^16.14.0",
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-icons": "^4.2.0", "react-icons": "^4.2.0",
"react-json-tree": "^0.15.0", "react-json-tree": "^0.15.0",
"react-redux": "^7.2.4", "react-redux": "^7.2.4",
"redux": "^4.1.1" "redux": "^4.1.1",
"redux-persist": "^6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/register": "^7.15.3", "@babel/register": "^7.15.3",

View File

@ -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 exportState from '@redux-devtools/app/lib/middlewares/exportState';
import panelDispatcher from '../middlewares/panelSync'; import panelDispatcher from '../middlewares/panelSync';
import rootReducer from '../reducers/panel'; import rootReducer, { StoreStateWithoutSocket } from '../reducers/panel';
import { StoreState } from '@redux-devtools/app/lib/reducers'; import { StoreAction } from '@redux-devtools/app/lib/actions';
const persistConfig = {
key: 'redux-devtools',
blacklist: ['instances', 'socket'],
storage: localForage,
};
const persistedReducer: Reducer<StoreStateWithoutSocket, StoreAction> =
persistReducer(persistConfig, rootReducer) as any;
export default function configureStore( export default function configureStore(
position: string, position: string,
bgConnection: chrome.runtime.Port, bgConnection: chrome.runtime.Port
preloadedState: PreloadedState<StoreState>
) { ) {
const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection)); const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection));
return createStore(rootReducer, preloadedState, enhancer); const store = createStore(persistedReducer, enhancer);
const persistor = persistStore(store);
return { store, persistor };
} }

View File

@ -3,15 +3,15 @@ import {
compose, compose,
applyMiddleware, applyMiddleware,
Store, Store,
PreloadedState,
StoreEnhancer, StoreEnhancer,
Reducer,
} from 'redux'; } from 'redux';
import localForage from 'localforage';
import { persistReducer, persistStore } from 'redux-persist';
import exportState from '@redux-devtools/app/lib/middlewares/exportState'; import exportState from '@redux-devtools/app/lib/middlewares/exportState';
import api from '@redux-devtools/app/lib/middlewares/api'; import api from '@redux-devtools/app/lib/middlewares/api';
import { CONNECT_REQUEST } from '@redux-devtools/app/lib/constants/socketActionTypes'; import { CONNECT_REQUEST } from '@redux-devtools/app/lib/constants/socketActionTypes';
import { StoreState } from '@redux-devtools/app/lib/reducers';
import { import {
StoreAction,
StoreActionWithoutUpdateState, StoreActionWithoutUpdateState,
UpdateStateAction, UpdateStateAction,
} from '@redux-devtools/app/lib/actions'; } from '@redux-devtools/app/lib/actions';
@ -22,6 +22,7 @@ import rootReducer from '../reducers/window';
import { BackgroundState } from '../reducers/background'; import { BackgroundState } from '../reducers/background';
import { BackgroundAction } from './backgroundStore'; import { BackgroundAction } from './backgroundStore';
import { EmptyUpdateStateAction, NAAction } from '../middlewares/api'; import { EmptyUpdateStateAction, NAAction } from '../middlewares/api';
import { StoreState } from '@redux-devtools/app/lib/reducers';
export interface ExpandedUpdateStateAction extends UpdateStateAction { export interface ExpandedUpdateStateAction extends UpdateStateAction {
readonly instances: InstancesState; readonly instances: InstancesState;
@ -33,10 +34,20 @@ export type WindowStoreAction =
| NAAction | NAAction
| EmptyUpdateStateAction; | EmptyUpdateStateAction;
const persistConfig = {
key: 'redux-devtools',
blacklist: ['instances', 'socket'],
storage: localForage,
};
const persistedReducer: Reducer<StoreState, WindowStoreAction> = persistReducer(
persistConfig,
rootReducer
) as any;
export default function configureStore( export default function configureStore(
baseStore: Store<BackgroundState, BackgroundAction>, baseStore: Store<BackgroundState, BackgroundAction>,
position: string, position: string
preloadedState: PreloadedState<StoreState>
) { ) {
let enhancer: StoreEnhancer; let enhancer: StoreEnhancer;
const middlewares = [exportState, api, syncStores(baseStore)]; const middlewares = [exportState, api, syncStores(baseStore)];
@ -54,7 +65,8 @@ export default function configureStore(
: (noop: unknown) => noop : (noop: unknown) => noop
); );
} }
const store = createStore(rootReducer, preloadedState, enhancer); const store = createStore(persistedReducer, enhancer);
const persistor = persistStore(store);
if ( if (
store.getState().connection.options.hostname && store.getState().connection.options.hostname &&
@ -65,5 +77,5 @@ export default function configureStore(
}); });
} }
return store; return { store, persistor };
} }

View File

@ -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<StoreState>) => 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);
}
);
}

View File

@ -1,17 +1,17 @@
import React, { CSSProperties } from 'react'; import React, { CSSProperties } from 'react';
import { render, unmountComponentAtNode } from 'react-dom'; import { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { Persistor } from 'redux-persist';
import { REMOVE_INSTANCE } from '@redux-devtools/app/lib/constants/actionTypes'; import { REMOVE_INSTANCE } from '@redux-devtools/app/lib/constants/actionTypes';
import App from '../../../app/containers/App'; import App from '../../../app/containers/App';
import configureStore from '../../../app/stores/panelStore'; import configureStore from '../../../app/stores/panelStore';
import getPreloadedState from '../background/getPreloadedState';
import '../../views/devpanel.pug'; import '../../views/devpanel.pug';
import { Action, PreloadedState, Store } from 'redux'; import { Action, Store } from 'redux';
import { StoreState } from '@redux-devtools/app/lib/reducers';
import { StoreAction } from '@redux-devtools/app/lib/actions'; import { StoreAction } from '@redux-devtools/app/lib/actions';
import { PanelMessage } from '../../../app/middlewares/api'; import { PanelMessage } from '../../../app/middlewares/api';
import { StoreStateWithoutSocket } from '../../../app/reducers/panel'; import { StoreStateWithoutSocket } from '../../../app/reducers/panel';
import { PersistGate } from 'redux-persist/integration/react';
const position = location.hash; const position = location.hash;
const messageStyle: CSSProperties = { const messageStyle: CSSProperties = {
@ -22,24 +22,22 @@ const messageStyle: CSSProperties = {
let rendered: boolean | undefined; let rendered: boolean | undefined;
let store: Store<StoreStateWithoutSocket, StoreAction> | undefined; let store: Store<StoreStateWithoutSocket, StoreAction> | undefined;
let persistor: Persistor | undefined;
let bgConnection: chrome.runtime.Port; let bgConnection: chrome.runtime.Port;
let naTimeout: NodeJS.Timeout; let naTimeout: NodeJS.Timeout;
let preloadedState: PreloadedState<StoreState>;
const isChrome = navigator.userAgent.indexOf('Firefox') === -1; const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
getPreloadedState(position, (state) => {
preloadedState = state;
});
function renderDevTools() { function renderDevTools() {
const node = document.getElementById('root'); const node = document.getElementById('root');
unmountComponentAtNode(node!); unmountComponentAtNode(node!);
clearTimeout(naTimeout); clearTimeout(naTimeout);
store = configureStore(position, bgConnection, preloadedState); ({ store, persistor } = configureStore(position, bgConnection));
render( render(
<Provider store={store}> <Provider store={store}>
<App position={position} /> <PersistGate loading={null} persistor={persistor}>
<App position={position} />
</PersistGate>
</Provider>, </Provider>,
node node
); );

View File

@ -1,25 +1,19 @@
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import { PreloadedState } from 'redux';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { UPDATE_STATE } from '@redux-devtools/app/lib/constants/actionTypes'; 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 App from '../../../app/containers/App';
import configureStore from '../../../app/stores/windowStore'; import configureStore from '../../../app/stores/windowStore';
import getPreloadedState from '../background/getPreloadedState';
import { MonitorMessage } from '../../../app/middlewares/api'; import { MonitorMessage } from '../../../app/middlewares/api';
import '../../views/window.pug'; import '../../views/window.pug';
const position = location.hash; const position = location.hash;
let preloadedState: PreloadedState<StoreState>;
getPreloadedState(position, (state) => {
preloadedState = state;
});
chrome.runtime.getBackgroundPage((window) => { chrome.runtime.getBackgroundPage((window) => {
const { store } = window!; const { store } = window!;
const localStore = configureStore(store, position, preloadedState); const { store: localStore, persistor } = configureStore(store, position);
let name = 'monitor'; let name = 'monitor';
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) { if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
name += chrome.devtools.inspectedWindow.tabId; name += chrome.devtools.inspectedWindow.tabId;
@ -33,7 +27,9 @@ chrome.runtime.getBackgroundPage((window) => {
render( render(
<Provider store={localStore}> <Provider store={localStore}>
<App position={position} /> <PersistGate loading={null} persistor={persistor}>
<App position={position} />
</PersistGate>
</Provider>, </Provider>,
document.getElementById('root') document.getElementById('root')
); );

View File

@ -4,7 +4,7 @@ import { Provider } from 'react-redux';
import configureStore from '../../../src/app/stores/windowStore'; import configureStore from '../../../src/app/stores/windowStore';
import App from '../../../src/app/containers/App'; import App from '../../../src/app/containers/App';
const store = configureStore(store); const { store } = configureStore(store);
const component = mount( const component = mount(
<Provider store={store}> <Provider store={store}>
<App position="devtools-left" /> <App position="devtools-left" />

View File

@ -43,6 +43,7 @@ describe('Chrome extension', function () {
}); });
it("should contain inspector monitor's component", async () => { it("should contain inspector monitor's component", async () => {
await delay(500);
const val = await driver const val = await driver
.findElement(webdriver.By.xpath('//div[contains(@class, "inspector-")]')) .findElement(webdriver.By.xpath('//div[contains(@class, "inspector-")]'))
.getText(); .getText();

View File

@ -1,4 +1,4 @@
import { createStore, compose, applyMiddleware } from 'redux'; import { createStore, compose, applyMiddleware, Reducer } from 'redux';
import localForage from 'localforage'; import localForage from 'localforage';
import { persistReducer, persistStore } from 'redux-persist'; import { persistReducer, persistStore } from 'redux-persist';
import api from '../middlewares/api'; import api from '../middlewares/api';
@ -12,7 +12,10 @@ const persistConfig = {
storage: localForage, storage: localForage,
}; };
const persistedReducer = persistReducer(persistConfig, rootReducer); const persistedReducer: Reducer<StoreState, StoreAction> = persistReducer(
persistConfig,
rootReducer
) as any;
export default function configureStore() { export default function configureStore() {
let composeEnhancers = compose; let composeEnhancers = compose;
@ -40,8 +43,8 @@ export default function configureStore() {
} }
} }
const store = createStore<StoreState, StoreAction, unknown, unknown>( const store = createStore(
persistedReducer as any, persistedReducer,
composeEnhancers(applyMiddleware(exportState, api)) composeEnhancers(applyMiddleware(exportState, api))
); );
const persistor = persistStore(store); const persistor = persistStore(store);

View File

@ -25958,6 +25958,7 @@ fsevents@^1.2.7:
eslint-plugin-react-hooks: ^4.2.0 eslint-plugin-react-hooks: ^4.2.0
gitbook-cli: ^2.3.2 gitbook-cli: ^2.3.2
jsan: ^3.1.13 jsan: ^3.1.13
localforage: ^1.10.0
lodash: ^4.17.21 lodash: ^4.17.21
path-browserify: ^1.0.1 path-browserify: ^1.0.1
react: ^16.14.0 react: ^16.14.0
@ -25968,6 +25969,7 @@ fsevents@^1.2.7:
react-transform-catch-errors: ^1.0.2 react-transform-catch-errors: ^1.0.2
react-transform-hmr: ^1.0.4 react-transform-hmr: ^1.0.4
redux: ^4.1.1 redux: ^4.1.1
redux-persist: ^6.0.0
selenium-webdriver: ^3.6.0 selenium-webdriver: ^3.6.0
sinon-chrome: ^3.0.1 sinon-chrome: ^3.0.1
languageName: unknown languageName: unknown