diff --git a/webpack/account/actions.ts b/webpack/account/actions.ts index 2fafcb16f..5397ca64b 100644 --- a/webpack/account/actions.ts +++ b/webpack/account/actions.ts @@ -18,7 +18,7 @@ export function deleteUser(payload: DeletionRequest): Thunk { }) .then((resp: HttpData<{}>) => { alert("We're sorry to see you go. :("); - Session.clear(true); + Session.clear(); }) .catch((err: UnsafeError) => { toastErrors({ err }); diff --git a/webpack/auth/actions.ts b/webpack/auth/actions.ts index 984970632..7f868d175 100644 --- a/webpack/auth/actions.ts +++ b/webpack/auth/actions.ts @@ -30,7 +30,7 @@ export function didLogin(authState: AuthState, dispatch: Function) { function onLogin(dispatch: Function) { return (response: HttpData) => { let { data } = response; - Session.put(data); + Session.replace(data); didLogin(data, dispatch); push("/app/controls"); }; @@ -126,8 +126,8 @@ export function logout() { // In those cases, seeing a logout message may confuse the user. // To circumvent this, we must check if the user had a token. // If there was infact a token, we can safely show the message. - if (Session.get()) { success("You have been logged out."); } - Session.clear(true); + if (Session.getAll()) { success("You have been logged out."); } + Session.clear(); // Technically this is unreachable code: return { type: "LOGOUT", diff --git a/webpack/config/actions.ts b/webpack/config/actions.ts index e5186aee6..7d5b5b1e8 100644 --- a/webpack/config/actions.ts +++ b/webpack/config/actions.ts @@ -5,7 +5,7 @@ import { Session } from "../session"; /** Lets Redux know that the app is ready to bootstrap. */ export function ready(): Thunk { return (dispatch, getState) => { - let state = Session.get() || getState().auth; + let state = Session.getAll() || getState().auth; if (state) { didLogin(state, dispatch); } }; } diff --git a/webpack/devices/reducer.ts b/webpack/devices/reducer.ts index 8a6f6da9e..49392a0b3 100644 --- a/webpack/devices/reducer.ts +++ b/webpack/devices/reducer.ts @@ -1,16 +1,10 @@ import { BotState, HardwareState, Xyz, ControlPanelState } from "./interfaces"; import { generateReducer } from "../redux/generate_reducer"; import { SyncStatus } from "farmbot/dist"; -import { localStorageBoolFetch } from "../util"; import { Actions } from "../constants"; import { EncoderDisplay } from "../controls/interfaces"; import { EXPECTED_MAJOR, EXPECTED_MINOR } from "./actions"; - -export const X_AXIS_INVERTED = "x_axis_inverted"; -export const Y_AXIS_INVERTED = "y_axis_inverted"; -export const Z_AXIS_INVERTED = "z_axis_inverted"; -export const RAW_ENCODERS = "raw_encoders"; -export const SCALED_ENCODERS = "scaled_encoders"; +import { Session, BooleanSetting } from "../session"; /** * TODO: Refactor this method to use semverCompare() now that it is a thing. @@ -73,11 +67,11 @@ export let initialState: BotState = { dirty: false, currentOSVersion: undefined, currentFWVersion: undefined, - x_axis_inverted: !localStorageBoolFetch(X_AXIS_INVERTED), - y_axis_inverted: !localStorageBoolFetch(Y_AXIS_INVERTED), - z_axis_inverted: !localStorageBoolFetch(Z_AXIS_INVERTED), - raw_encoders: !localStorageBoolFetch(RAW_ENCODERS), - scaled_encoders: !localStorageBoolFetch(SCALED_ENCODERS) + x_axis_inverted: !Session.getBool(BooleanSetting.X_AXIS_INVERTED), + y_axis_inverted: !Session.getBool(BooleanSetting.Y_AXIS_INVERTED), + z_axis_inverted: !Session.getBool(BooleanSetting.Z_AXIS_INVERTED), + raw_encoders: !Session.getBool(BooleanSetting.RAW_ENCODERS), + scaled_encoders: !Session.getBool(BooleanSetting.SCALED_ENCODERS), }; export let botReducer = generateReducer(initialState) @@ -120,18 +114,15 @@ export let botReducer = generateReducer(initialState) switch (payload) { case "x": s.x_axis_inverted = !s.x_axis_inverted; - localStorage.setItem(X_AXIS_INVERTED, - JSON.stringify(localStorageBoolFetch(X_AXIS_INVERTED))); + Session.setBool(BooleanSetting.X_AXIS_INVERTED, s.x_axis_inverted); return s; case "y": s.y_axis_inverted = !s.y_axis_inverted; - localStorage.setItem(Y_AXIS_INVERTED, - JSON.stringify(localStorageBoolFetch(Y_AXIS_INVERTED))); + Session.setBool(BooleanSetting.Y_AXIS_INVERTED, s.y_axis_inverted); return s; case "z": s.z_axis_inverted = !s.z_axis_inverted; - localStorage.setItem(Z_AXIS_INVERTED, - JSON.stringify(localStorageBoolFetch(Z_AXIS_INVERTED))); + Session.setBool(BooleanSetting.Z_AXIS_INVERTED, s.z_axis_inverted); return s; default: throw new Error("Attempted to invert invalid jog button direction."); @@ -141,13 +132,11 @@ export let botReducer = generateReducer(initialState) switch (payload) { case "raw_encoders": s.raw_encoders = !s.raw_encoders; - localStorage.setItem(RAW_ENCODERS, - JSON.stringify(localStorageBoolFetch(RAW_ENCODERS))); + Session.setBool(BooleanSetting.RAW_ENCODERS, s.raw_encoders); return s; case "scaled_encoders": s.scaled_encoders = !s.scaled_encoders; - localStorage.setItem(SCALED_ENCODERS, - JSON.stringify(localStorageBoolFetch(SCALED_ENCODERS))); + Session.setBool(BooleanSetting.SCALED_ENCODERS, s.scaled_encoders); return s; default: throw new Error("Attempted to toggle display of invalid data."); diff --git a/webpack/farm_designer/reducer.ts b/webpack/farm_designer/reducer.ts index 63d956b33..7fbf14408 100644 --- a/webpack/farm_designer/reducer.ts +++ b/webpack/farm_designer/reducer.ts @@ -9,16 +9,12 @@ import { } from "./interfaces"; import { cloneDeep } from "lodash"; import { TaggedResource } from "../resources/tagged_resources"; -import { localStorageNumFetch } from "../util"; import { Actions } from "../constants"; +import { Session, NumericSetting } from "../session"; -export const BOT_ORIGIN_QUADRANT = "bot_origin_quadrant"; -export const ZOOM_LEVEL = "zoom_level"; - -let botOriginVal = localStorageNumFetch(BOT_ORIGIN_QUADRANT); +let botOriginVal = Session.getNum(NumericSetting.BOT_ORIGIN_QUADRANT); let botOriginQuadrant = isBotOriginQuadrant(botOriginVal) ? botOriginVal : 2; - -let zoomLevelVal = localStorageNumFetch(ZOOM_LEVEL); +let zoomLevelVal = Session.getNum(NumericSetting.ZOOM_LEVEL); let zoomLevel = zoomLevelVal ? zoomLevelVal : 1; export let initialState: DesignerState = { @@ -48,14 +44,14 @@ export let designer = generateReducer(initialState) return s; }) .add(Actions.UPDATE_BOT_ORIGIN_QUADRANT, (s, a) => { - localStorage.setItem(BOT_ORIGIN_QUADRANT, JSON.stringify(a.payload)); + Session.setNum(NumericSetting.BOT_ORIGIN_QUADRANT, a.payload); s.botOriginQuadrant = a.payload; return s; }) .add(Actions.UPDATE_MAP_ZOOM_LEVEL, (s, { payload }) => { let value = s.zoomLevel + payload; s.zoomLevel = value; - localStorage.setItem(ZOOM_LEVEL, value.toString()); + Session.setNum(NumericSetting.ZOOM_LEVEL, value); return s; }) .add(Actions.OF_SEARCH_RESULTS_OK, (s, a) => { diff --git a/webpack/front_page/front_page.tsx b/webpack/front_page/front_page.tsx index b4fba6dea..0ec59fb3d 100644 --- a/webpack/front_page/front_page.tsx +++ b/webpack/front_page/front_page.tsx @@ -30,7 +30,7 @@ export class FrontPage extends React.Component<{}, Partial> { } componentDidMount() { - if (Session.get()) { window.location.href = "/app/controls"; } + if (Session.getAll()) { window.location.href = "/app/controls"; } logInit(); API.setBaseUrl(API.fetchBrowserLocation()); this.setState({ @@ -63,7 +63,7 @@ export class FrontPage extends React.Component<{}, Partial> { API.setBaseUrl(url); axios.post(API.current.tokensPath, payload) .then((resp: HttpData) => { - Session.put(resp.data); + Session.replace(resp.data); window.location.href = "/app/controls"; }).catch((error: Error) => { if (_.get(error, "response.status") === 451) { @@ -346,5 +346,5 @@ export class FrontPage extends React.Component<{}, Partial> { ); } - render() { return Session.get() ?
: this.defaultContent(); } + render() { return Session.getAll() ?
: this.defaultContent(); } } diff --git a/webpack/nav/index.tsx b/webpack/nav/index.tsx index b8cea6eff..083d60f6f 100644 --- a/webpack/nav/index.tsx +++ b/webpack/nav/index.tsx @@ -24,7 +24,7 @@ export class NavBar extends React.Component> { tickerListOpen: false }; - logout = () => Session.clear(true); + logout = () => Session.clear(); toggle = (name: keyof NavBarState) => () => this.setState({ [name]: !this.state[name] }); diff --git a/webpack/redux/root_reducer.ts b/webpack/redux/root_reducer.ts index d56b4ecbc..07927f77e 100644 --- a/webpack/redux/root_reducer.ts +++ b/webpack/redux/root_reducer.ts @@ -23,7 +23,7 @@ export function rootReducer( state: any, action: ReduxAction<{}>) { if (action.type === Actions.LOGOUT) { - Session.clear(true); + Session.clear(); } // TODO: Get rid of this nasty type case / hack. Resulted from TSC 2.4 upgrade // - RC 30 JUN 17 diff --git a/webpack/routes.tsx b/webpack/routes.tsx index 5a6cd4f6b..03c4fa824 100644 --- a/webpack/routes.tsx +++ b/webpack/routes.tsx @@ -70,14 +70,14 @@ export class RootComponent extends React.Component { requireAuth(_discard: RouterState, replace: RedirectFunction) { let { store } = this.props; - if (Session.get()) { // has a previous session in cache + if (Session.getAll()) { // has a previous session in cache if (store.getState().auth) { // Has session, logged in. return; } else { // Has session but not logged in (returning visitor). store.dispatch(ready()); } } else { // Not logged in yet. - Session.clear(true); + Session.clear(); } } @@ -281,7 +281,7 @@ export class RootComponent extends React.Component { render() { // ==== TEMPORARY HACK. TODO: Add a before hook, if such a thing exists in // React Router. Or switch routing libs. - let notLoggedIn = !Session.get(); + let notLoggedIn = !Session.getAll(); let restrictedArea = window.location.pathname.includes("/app/"); if (notLoggedIn && restrictedArea) { window.location.href = "/"; diff --git a/webpack/session.ts b/webpack/session.ts index 4f5037afd..a1e94c737 100644 --- a/webpack/session.ts +++ b/webpack/session.ts @@ -1,16 +1,30 @@ import { AuthState } from "./auth/interfaces"; import { box } from "boxed_value"; +import { get, isNumber } from "lodash"; + +export enum BooleanSetting { + X_AXIS_INVERTED = "x_axis_inverted", + Y_AXIS_INVERTED = "y_axis_inverted", + Z_AXIS_INVERTED = "z_axis_inverted", + RAW_ENCODERS = "raw_encoders", + SCALED_ENCODERS = "scaled_encoders" +} + +export enum NumericSetting { + BOT_ORIGIN_QUADRANT = "bot_origin_quadrant", + ZOOM_LEVEL = "zoom_level", +} export namespace Session { const KEY = "session"; /** Replace the contents of session storage. */ - export function put(nextState: AuthState) { + export function replace(nextState: AuthState) { localStorage[KEY] = JSON.stringify(nextState); } /** Fetch the previous session. */ - export function get(): AuthState | undefined { + export function getAll(): AuthState | undefined { try { let v: AuthState = JSON.parse(localStorage[KEY]); if (box(v).kind === "object") { @@ -24,9 +38,27 @@ export namespace Session { } /** Clear localstorage and sessionstorage. */ - export function clear(_redirectToFrontPage = false) { + export function clear() { localStorage.clear(); sessionStorage.clear(); window.location.href = window.location.origin; } + + export function getBool(key: BooleanSetting): boolean { + let output = JSON.parse(get(localStorage, key, "false")); + return !output; + } + + export function setBool(key: BooleanSetting, val: boolean): void { + localStorage.setItem(key, JSON.stringify(val)); + } + + export function getNum(key: NumericSetting): number | undefined { + let output = JSON.parse(get(localStorage, key, "null")); + return (isNumber(output)) ? output : undefined; + } + + export function setNum(key: NumericSetting, val: number): void { + localStorage.setItem(key, JSON.stringify(val)); + } } diff --git a/webpack/tos_update/index.tsx b/webpack/tos_update/index.tsx index fe28685e5..a995d78a1 100644 --- a/webpack/tos_update/index.tsx +++ b/webpack/tos_update/index.tsx @@ -52,7 +52,7 @@ export class Wow extends React.Component> { axios .post(API.current.tokensPath, payload) .then((resp: HttpData) => { - Session.put(resp.data); + Session.replace(resp.data); window.location.href = "/app/controls"; }) .catch(error => { diff --git a/webpack/util.ts b/webpack/util.ts index c2129dd04..e1edbc2c4 100644 --- a/webpack/util.ts +++ b/webpack/util.ts @@ -146,16 +146,6 @@ export function safeStringFetch(obj: any, key: string): string { } } -export function localStorageNumFetch(key: string): number | undefined { - let output = JSON.parse(_.get(localStorage, key, "null")); - return (_.isNumber(output)) ? output : undefined; -} - -export function localStorageBoolFetch(key: string): boolean { - let output = JSON.parse(_.get(localStorage, key, "false")); - return !output; -} - /** We don't support IE. This method stops users from trying to use the site. * It's unfortunate that we need to do this, but the site simply won't work on * old browsers and our error logs were getting full of IE related bugs. */ diff --git a/webpack/verification.ts b/webpack/verification.ts index 2b65aa18e..1abc8f7fc 100644 --- a/webpack/verification.ts +++ b/webpack/verification.ts @@ -13,7 +13,7 @@ export async function verify() { const url = API.fetchBrowserLocation(); try { let r: HttpData = await axios.put(url + "/api/users/verify/" + token); - Session.put(r.data); + Session.replace(r.data); window.location.href = window.location.origin + "/app/controls"; } catch (e) { document.write(`