From d9fe7eb32c6c74db0db3c919a8fb9db9572f4c3e Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Tue, 6 Sep 2016 15:32:06 +0200 Subject: [PATCH] Add localstorage support for saving view state This PR adds support to remember the view state even after the browser tab has been closed. This helps someone who is always looking at the table view of a certain topology to which currently they have to perform a minimum of 2 clicks. * app view state is backed up in localStorage * when visiting Scope with no URL view state, the localStorage state is used (i.e. the URL state has priority) --- client/app/scripts/utils/router-utils.js | 17 ++++++++++++++++- client/app/scripts/utils/storage-utils.js | 23 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 client/app/scripts/utils/storage-utils.js diff --git a/client/app/scripts/utils/router-utils.js b/client/app/scripts/utils/router-utils.js index c69a1f627..b95735276 100644 --- a/client/app/scripts/utils/router-utils.js +++ b/client/app/scripts/utils/router-utils.js @@ -1,6 +1,7 @@ import page from 'page'; import { route } from '../actions/app-actions'; +import { storageGet, storageSet } from './storage-utils'; // // page.js won't match the routes below if ":state" has a slash in it, so replace those before we @@ -10,6 +11,7 @@ const SLASH = '/'; const SLASH_REPLACEMENT = ''; const PERCENT = '%'; const PERCENT_REPLACEMENT = ''; +const STORAGE_STATE_KEY = 'scopeViewState'; function encodeURL(url) { return url @@ -70,6 +72,9 @@ export function updateRoute(getState) { .replace('#!/', '') || '{}'; const prevState = JSON.parse(decodeURL(urlStateString)); + // back up state in storage as well + storageSet(STORAGE_STATE_KEY, stateUrl); + if (shouldReplaceState(prevState, state)) { // Replace the top of the history rather than pushing on a new item. page.replace(`/state/${stateUrl}`, state, dispatch); @@ -84,7 +89,17 @@ export function getRouter(dispatch, initialState) { page.base(window.location.pathname.replace(/\/$/, '')); page('/', () => { - dispatch(route(initialState)); + // recover from storage state on empty URL + const storageState = storageGet(STORAGE_STATE_KEY); + if (storageState) { + // push storage state to URL + window.location.hash = `!/state/${storageState}`; + const parsedState = JSON.parse(decodeURL(storageState)); + const mergedState = Object.assign(initialState, parsedState); + dispatch(route(mergedState)); + } else { + dispatch(route(initialState)); + } }); page('/state/:state', (ctx) => { diff --git a/client/app/scripts/utils/storage-utils.js b/client/app/scripts/utils/storage-utils.js new file mode 100644 index 000000000..0ff92ff99 --- /dev/null +++ b/client/app/scripts/utils/storage-utils.js @@ -0,0 +1,23 @@ +import debug from 'debug'; + +const log = debug('scope:storage-utils'); + +// localStorage detection +const storage = typeof(Storage) !== 'undefined' ? window.localStorage : null; + +export function storageGet(key, defaultValue) { + if (storage && storage[key] !== undefined) { + return storage.getItem(key); + } + return defaultValue; +} + +export function storageSet(key, value) { + if (storage) { + try { + storage.setItem(key, value); + } catch (e) { + log('Error storing value in storage. Maybe full? Could not store key.', key); + } + } +}