From e9e6c0b7a3b8d4b4ff7380b4c3142043c67617cb Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 2 Feb 2017 11:54:56 -0800 Subject: [PATCH] Persist app state to localStorage --- ui/src/index.js | 9 ++++- ui/src/store/configureStore.js | 2 ++ ui/src/store/persistStateEnhancer.js | 51 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ui/src/store/persistStateEnhancer.js diff --git a/ui/src/index.js b/ui/src/index.js index 2a184fdc6..7c2d2b177 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -23,10 +23,17 @@ import 'src/style/chronograf.scss'; const defaultTimeRange = {upper: null, lower: 'now() - 15m'}; const lsTimeRange = window.localStorage.getItem('timeRange'); +const persistedPanels = window.localStorage.getItem('panels'); +const persistedQueryConfigs = window.localStorage.getItem('queryConfigs'); const parsedTimeRange = JSON.parse(lsTimeRange) || {}; const timeRange = Object.assign(defaultTimeRange, parsedTimeRange); -const store = configureStore({timeRange}); +const store = configureStore({ + timeRange, + panels: JSON.parse(persistedPanels), + queryConfigs: JSON.parse(persistedQueryConfigs), +}); + const rootNode = document.getElementById('react-root'); let browserHistory; diff --git a/ui/src/store/configureStore.js b/ui/src/store/configureStore.js index 37cddf4da..1b7b3ec2b 100644 --- a/ui/src/store/configureStore.js +++ b/ui/src/store/configureStore.js @@ -5,6 +5,7 @@ import makeQueryExecuter from 'src/shared/middleware/queryExecuter'; import * as chronografReducers from 'src/data_explorer/reducers'; import * as sharedReducers from 'src/shared/reducers'; import rulesReducer from 'src/kapacitor/reducers/rules'; +import persistStateEnhancer from './persistStateEnhancer'; const rootReducer = combineReducers({ ...sharedReducers, @@ -16,6 +17,7 @@ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; export default function configureStore(initialState) { const createPersistentStore = composeEnhancers( + persistStateEnhancer(), applyMiddleware(thunkMiddleware, makeQueryExecuter()), )(createStore); diff --git a/ui/src/store/persistStateEnhancer.js b/ui/src/store/persistStateEnhancer.js new file mode 100644 index 000000000..5a34e7e34 --- /dev/null +++ b/ui/src/store/persistStateEnhancer.js @@ -0,0 +1,51 @@ +/** + * Redux store enhancer (https://github.com/reactjs/redux/blob/master/docs/Glossary.md) + * responsible for sending updates on data explorer state to a server to persist. + * It subscribes a listener function to the store -- meaning every time the store emits an update + * (after some state has changed), we'll have a chance to react. + * + * After the store emits an update, we'll queue a function to request a save in x number of + * seconds. The previous timer is cleared out as well, meaning we won't end up firing a ton of + * saves in a short period of time. Only after the store has been idle for x seconds will the save occur. + */ + +const autoSaveTimer = (() => { + let timer; + + return { + set(cb) { + const timeUntilSave = 300; + timer = setTimeout(cb, timeUntilSave); + }, + + clear() { + clearInterval(timer); + }, + }; +})(); + +export default function persistState() { + return (next) => (reducer, initialState, enhancer) => { + const store = next(reducer, initialState, enhancer); + + store.subscribe(() => { + const state = {...store.getState()}; + const {panels, queryConfigs, timeRange} = state; + + window.localStorage.setItem('timeRange', JSON.stringify(timeRange)); + window.localStorage.setItem('panels', JSON.stringify(panels)); + window.localStorage.setItem('queryConfigs', JSON.stringify(queryConfigs)); + + autoSaveTimer.clear(); + autoSaveTimer.set(() => { + try { + // save to localStorage + } catch (err) { + // console.error('Unable to save data explorer session: ', JSON.parse(response).error); // eslint-disable-line no-console + } + }); + }); + + return store; + }; +}