Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions client/app/scripts/actions/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fromJS } from 'immutable';

import ActionTypes from '../constants/action-types';
import { saveGraph } from '../utils/file-utils';
import { updateRoute } from '../utils/router-utils';
import { clearStoredViewState, updateRoute } from '../utils/router-utils';
import {
doControlRequest,
getAllNodes,
Expand All @@ -15,7 +15,6 @@ import {
teardownWebsockets,
getNodes,
} from '../utils/web-api-utils';
import { storageSet } from '../utils/storage-utils';
import { loadTheme } from '../utils/contrast-utils';
import { isPausedSelector } from '../selectors/time-travel';
import {
Expand Down Expand Up @@ -794,7 +793,7 @@ export function route(urlState) {
export function resetLocalViewState() {
return (dispatch) => {
dispatch({type: ActionTypes.RESET_LOCAL_VIEW_STATE});
storageSet('scopeViewState', '');
clearStoredViewState();
// eslint-disable-next-line prefer-destructuring
window.location.href = window.location.href.split('#')[0];
};
Expand Down Expand Up @@ -832,3 +831,10 @@ export function setMonitorState(monitor) {
monitor
};
}

export function setStoreViewState(storeViewState) {
return {
type: ActionTypes.SET_STORE_VIEW_STATE,
storeViewState
};
}
9 changes: 8 additions & 1 deletion client/app/scripts/components/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
setMonitorState,
setTableView,
setResourceView,
setStoreViewState,
shutdown,
setViewportDimensions,
getTopologiesWithInitialPoll,
Expand Down Expand Up @@ -64,6 +65,7 @@ class App extends React.Component {
super(props, context);

this.props.dispatch(setMonitorState(this.props.monitor));
this.props.dispatch(setStoreViewState(!this.props.disableStoreViewState));

this.setViewportDimensions = this.setViewportDimensions.bind(this);
this.handleResize = debounce(this.setViewportDimensions, VIEWPORT_RESIZE_DEBOUNCE_INTERVAL);
Expand All @@ -79,7 +81,7 @@ class App extends React.Component {
window.addEventListener('keypress', this.onKeyPress);
window.addEventListener('keyup', this.onKeyUp);

this.router = getRouter(this.props.dispatch, this.props.urlState);
this.router = this.props.dispatch(getRouter(this.props.urlState));
this.router.start({ hashbang: true });

if (!this.props.routeSet || process.env.WEAVE_CLOUD) {
Expand All @@ -102,6 +104,9 @@ class App extends React.Component {
if (nextProps.monitor !== this.props.monitor) {
this.props.dispatch(setMonitorState(nextProps.monitor));
}
if (nextProps.disableStoreViewState !== this.props.disableStoreViewState) {
this.props.dispatch(setStoreViewState(!nextProps.disableStoreViewState));
}
}

onKeyUp(ev) {
Expand Down Expand Up @@ -267,12 +272,14 @@ App.propTypes = {
renderTimeTravel: PropTypes.func,
renderNodeDetailsExtras: PropTypes.func,
monitor: PropTypes.bool,
disableStoreViewState: PropTypes.bool,
};

App.defaultProps = {
renderTimeTravel: () => <TimeTravelWrapper />,
renderNodeDetailsExtras: () => null,
monitor: false,
disableStoreViewState: false,
};

export default connect(mapStateToProps)(App);
1 change: 1 addition & 0 deletions client/app/scripts/constants/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const ACTION_TYPES = [
'SELECT_NETWORK',
'SET_EXPORTING_GRAPH',
'SET_RECEIVED_NODES_DELTA',
'SET_STORE_VIEW_STATE',
'SET_VIEW_MODE',
'SET_VIEWPORT_DIMENSIONS',
'SHOW_HELP',
Expand Down
5 changes: 5 additions & 0 deletions client/app/scripts/reducers/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const initialState = makeMap({
plugins: makeList(),
pinnedSearches: makeList(), // list of node filters
routeSet: false,
storeViewState: true,
searchFocused: false,
searchQuery: '',
selectedNetwork: null,
Expand Down Expand Up @@ -752,6 +753,10 @@ export function rootReducer(state = initialState, action) {
return state.set('monitor', action.monitor);
}

case ActionTypes.SET_STORE_VIEW_STATE: {
return state.set('storeViewState', action.storeViewState);
}

default: {
return state;
}
Expand Down
75 changes: 45 additions & 30 deletions client/app/scripts/utils/router-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ export function parseHashState(hash = window.location.hash) {
return JSON.parse(decodeURL(urlStateString));
}

export function clearStoredViewState() {
storageSet(STORAGE_STATE_KEY, '');
}

function isStoreViewStateEnabled(state) {
return state.get('storeViewState');
}

function shouldReplaceState(prevState, nextState) {
// Opening a new terminal while an existing one is open.
const terminalToTerminal = (prevState.controlPipe && nextState.controlPipe);
Expand Down Expand Up @@ -116,7 +124,9 @@ export function updateRoute(getState) {
if (stateUrl === prevStateUrl) return;

// back up state in storage as well
storageSet(STORAGE_STATE_KEY, stateUrl);
if (isStoreViewStateEnabled(getState())) {
storageSet(STORAGE_STATE_KEY, stateUrl);
}

if (shouldReplaceState(prevState, state)) {
// Replace the top of the history rather than pushing on a new item.
Expand All @@ -141,38 +151,43 @@ function detectOldOptions(topologyOptions) {
}


export function getRouter(dispatch, initialState) {
// strip any trailing '/'s.
page.base(window.location.pathname.replace(/\/$/, ''));

page('/', () => {
// recover from storage state on empty URL
const storageState = storageGet(STORAGE_STATE_KEY);
if (storageState) {
const parsedState = JSON.parse(decodeURL(storageState));
const dirtyOptions = detectOldOptions(parsedState.topologyOptions);
if (dirtyOptions) {
dispatch(route(initialState));
export function getRouter(initialState) {
return (dispatch, getState) => {
// strip any trailing '/'s.
page.base(window.location.pathname.replace(/\/$/, ''));

page('/', () => {
// recover from storage state on empty URL
const storageState = storageGet(STORAGE_STATE_KEY);
if (storageState && isStoreViewStateEnabled(getState())) {
const parsedState = JSON.parse(decodeURL(storageState));
const dirtyOptions = detectOldOptions(parsedState.topologyOptions);
if (dirtyOptions) {
dispatch(route(initialState));
} else {
const mergedState = Object.assign(initialState, parsedState);
// push storage state to URL
window.location.hash = `!/state/${stableStringify(mergedState)}`;
dispatch(route(mergedState));
}
} else {
const mergedState = Object.assign(initialState, parsedState);
// push storage state to URL
window.location.hash = `!/state/${stableStringify(mergedState)}`;
dispatch(route(mergedState));
dispatch(route(initialState));
}
} else {
dispatch(route(initialState));
}
});
});

page('/state/:state', (ctx) => {
const state = JSON.parse(decodeURL(ctx.params.state));
const dirtyOptions = detectOldOptions(state.topologyOptions);
const nextState = dirtyOptions ? initialState : state;
page('/state/:state', (ctx) => {
const state = JSON.parse(decodeURL(ctx.params.state));
const dirtyOptions = detectOldOptions(state.topologyOptions);
const nextState = dirtyOptions ? initialState : state;

// back up state in storage and redirect
storageSet(STORAGE_STATE_KEY, encodeURL(stableStringify(state)));
dispatch(route(nextState));
});
// back up state in storage and redirect
if (isStoreViewStateEnabled(getState())) {
storageSet(STORAGE_STATE_KEY, encodeURL(stableStringify(state)));
}

dispatch(route(nextState));
});

return page;
return page;
};
}
48 changes: 36 additions & 12 deletions client/app/scripts/utils/storage-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,54 @@ import debug from 'debug';

const log = debug('scope:storage-utils');

// localStorage detection
const storage = (typeof Storage) !== 'undefined' ? window.localStorage : null;
export const localSessionStorage = {
getItem(k) {
return window.sessionStorage.getItem(k) || window.localStorage.getItem(k);
},
setItem(k, v) {
window.sessionStorage.setItem(k, v);
window.localStorage.setItem(k, v);
},
clear() {
window.sessionStorage.clear();
window.localStorage.clear();
}
};

export function storageGet(key, defaultValue) {
if (storage && storage.getItem(key) !== undefined) {
return storage.getItem(key);
export function storageGet(key, defaultValue, storage = localSessionStorage) {
if (!storage) {
return defaultValue;
}
return defaultValue;

const value = storage.getItem(key);
if (value == null) {
return defaultValue;
}

return value;
}

export function storageSet(key, value) {
export function storageSet(key, value, storage = localSessionStorage) {
if (storage) {
try {
storage.setItem(key, value);
return true;
} catch (e) {
log('Error storing value in storage. Maybe full? Could not store key.', key);
log(
'Error storing value in storage. Maybe full? Could not store key.',
key
);
}
}
return false;
}

export function storageGetObject(key, defaultValue) {
const value = storageGet(key);
export function storageGetObject(
key,
defaultValue,
storage = localSessionStorage
) {
const value = storageGet(key, undefined, storage);
if (value) {
try {
return JSON.parse(value);
Expand All @@ -36,9 +60,9 @@ export function storageGetObject(key, defaultValue) {
return defaultValue;
}

export function storageSetObject(key, obj) {
export function storageSetObject(key, obj, storage = localSessionStorage) {
try {
return storageSet(key, JSON.stringify(obj));
return storageSet(key, JSON.stringify(obj), storage);
} catch (e) {
log('Error encoding object for key', key);
}
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
},
"setupFiles": [
"<rootDir>/test/support/raf.js",
"<rootDir>/test/support/localStorage.js"
"<rootDir>/test/support/storage.js"
],
"roots": [
"<rootDir>/app/scripts"
Expand Down
17 changes: 0 additions & 17 deletions client/test/support/localStorage.js

This file was deleted.

21 changes: 21 additions & 0 deletions client/test/support/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const makeStorageMock = function () {
let store = {};
return {
store,
getItem(key) {
return store[key];
},
setItem(key, value) {
store[key] = value;
},
clear() {
store = {};
}
};
};

const localStorageMock = makeStorageMock();
const sessionStorageMock = makeStorageMock();

Object.defineProperty(window, 'localStorage', { value: localStorageMock });
Object.defineProperty(window, 'sessionStorage', { value: sessionStorageMock });