From be7a616fb576148f19b5bef15daca93ae969e8c0 Mon Sep 17 00:00:00 2001 From: Alexander Polynomdivision Date: Sun, 23 Sep 2018 16:30:14 +0200 Subject: [PATCH] fix: Multiple API calls when bypassing /login --- frontend/src/actions/index.ts | 11 ++++++++++- frontend/src/components/app.tsx | 21 ++++++++++++--------- frontend/src/containers/Application.ts | 3 ++- frontend/src/containers/LoginPage.ts | 1 + frontend/src/pages/login.tsx | 7 ++++++- frontend/src/reducers/index.ts | 7 ++++++- frontend/src/security/Token.ts | 4 ++++ 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/frontend/src/actions/index.ts b/frontend/src/actions/index.ts index 884b9e2..b8a6309 100644 --- a/frontend/src/actions/index.ts +++ b/frontend/src/actions/index.ts @@ -2,6 +2,7 @@ import { IVocab, IReviewCard } from "../models/vocab"; import { IUser } from "../models/user"; import { ILevel } from "../models/level"; import { IReviewMetadata } from "../models/review"; +import { TopTen } from "../models/learner"; export const SET_DRAWER = "SET_DRAWER"; export function setDrawer(state: boolean) { @@ -161,7 +162,7 @@ export function setDashboardNLLoading(state: boolean) { }; export const SET_TOP_TEN = "SET_TOP_TEN"; -export function setTopTen(topTen: ILearner[]) { +export function setTopTen(topTen: TopTen[]) { return { type: SET_TOP_TEN, topTen, @@ -175,3 +176,11 @@ export function setDashboardTTLoading(state: boolean) { state, }; }; + +export const SET_DID_LOGIN = "SET_DID_LOGIN"; +export function setDidLogin(state: boolean) { + return { + type: SET_DID_LOGIN, + state, + }; +}; diff --git a/frontend/src/components/app.tsx b/frontend/src/components/app.tsx index 362d97e..16498fa 100644 --- a/frontend/src/components/app.tsx +++ b/frontend/src/components/app.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { BrowserRouter, Route, Redirect } from "react-router-dom"; import AuthRoute from "../security/AuthRoute"; -import { setSessionToken, removeSessionToken } from "../security/Token"; +import { setSessionToken, removeSessionToken, getSessionToken } from "../security/Token"; import Dashboard from "../containers/Dashboard"; import LoginPage from "../containers/LoginPage"; @@ -29,13 +29,21 @@ interface IProps { user: IUser; setAuthenticated: (status: boolean) => void; + setDidLogin: (status: boolean) => void; setUser: (user: IUser) => void; }; // TODO: Replace the sessionStorage with localStorage? -// TODO: Cache API-Calls -// TODO: When mounting without a login, check if the sessionToken is still valid export default class Application extends React.Component { + componentDidMount() { + // TODO: Ask the server if our session is still valid + // TODO: When asking the server if our session is still valid, a spinner + // should be shown + if (getSessionToken(window) !== null) { + this.props.setAuthenticated(true); + } + } + getLevels(): Promise { console.log("STUB: Application::getLevels"); @@ -143,8 +151,6 @@ export default class Application extends React.Component { }).then(resp => resp.json(), err => rej(err)) .then(data => { - console.log(data); - if (data.error === "0") { res(data.data.topTen); } else { @@ -164,8 +170,6 @@ export default class Application extends React.Component { }).then(resp => resp.json(), err => rej(err)) .then(data => { - console.log(data); - if (data.error === "0") { res(data.data); } else { @@ -215,6 +219,7 @@ export default class Application extends React.Component { if (resp.error === "0") { // Successful login this.props.setUser(resp.data); + this.props.setDidLogin(true); setSessionToken(window, resp.data.sessionToken); this.props.setAuthenticated(true); @@ -234,8 +239,6 @@ export default class Application extends React.Component { // Checks whether the user is logged in isAuthenticated = () => { - // TODO: Security? - // TODO: Implement return this.props.authenticated; } diff --git a/frontend/src/containers/Application.ts b/frontend/src/containers/Application.ts index f0485da..bf14a1c 100644 --- a/frontend/src/containers/Application.ts +++ b/frontend/src/containers/Application.ts @@ -4,7 +4,7 @@ import { IUser } from "../models/user"; import Application from "../components/app"; -import { setAuthenticated, setUser } from "../actions"; +import { setAuthenticated, setUser, setDidLogin } from "../actions"; const mapStateToProps = state => { return { @@ -15,6 +15,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { setAuthenticated: (status: boolean) => dispatch(setAuthenticated(status)), + setDidLogin: (state: boolean) => dispatch(setDidLogin(state)), setUser: (user: IUser) => dispatch(setUser(user)), }; }; diff --git a/frontend/src/containers/LoginPage.ts b/frontend/src/containers/LoginPage.ts index 6a3fa0e..32536fe 100644 --- a/frontend/src/containers/LoginPage.ts +++ b/frontend/src/containers/LoginPage.ts @@ -10,6 +10,7 @@ const mapStateToProps = state => { snackOpen: state.login.snackOpen, snackMsg: state.login.snackMsg, authenticated: state.authenticated, + didLogin: state.didLogin, }; }; const mapDispatchToProps = dispatch => { diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx index 9479719..b99b11a 100644 --- a/frontend/src/pages/login.tsx +++ b/frontend/src/pages/login.tsx @@ -18,6 +18,7 @@ interface IProps { authenticated: boolean; history: any; + didLogin: boolean; setLoading: (state: boolean) => void; setSnackbar: (state: boolean, msg: string) => void; loading: boolean; @@ -54,7 +55,11 @@ const LoginPageWithRouter = withRouter( componentDidMount() { // If we're already authenticated, we can skip the login page - if (this.props.authenticated) { + // NOTE: The '!this.props.didLogin' is here, as the Component gets + // remounted, when the auth status changes. Thus we would + // redirect to /dashboard, redirect back to /welcome + // (or /dashboard again) and cause mulitple API calls! + if (this.props.authenticated && !this.props.didLogin) { this.props.history.push("/dashboard"); } } diff --git a/frontend/src/reducers/index.ts b/frontend/src/reducers/index.ts index b30f72f..74cdd0c 100644 --- a/frontend/src/reducers/index.ts +++ b/frontend/src/reducers/index.ts @@ -11,6 +11,7 @@ interface IState { scorePopoverOpen: boolean; drawerButton: boolean; authenticated: boolean; + didLogin: boolean; // TODO: Rework this user: IUser | {}, @@ -64,6 +65,7 @@ const initialState: IState = { // Should we show the button to open the drawer? drawerButton: true, scorePopoverOpen: false, + didLogin: false, // Is the user authenticated? // TODO: Set this to false @@ -228,7 +230,6 @@ export function LateinicusApp(state: IState = initialState, action: any) { }), }); case Actions.SET_TOP_TEN: - console.log(action.topTen); return Object.assign({}, state, { topTen: action.topTen, }); @@ -238,6 +239,10 @@ export function LateinicusApp(state: IState = initialState, action: any) { loadingTT: action.state, }), }); + case Actions.SET_DID_LOGIN: + return Object.assign({}, state, { + didLogin: state, + }); default: // Ignore the initialization call to the reducer. By that we can // catch all actions that are not implemented diff --git a/frontend/src/security/Token.ts b/frontend/src/security/Token.ts index a6560eb..21d3b9c 100644 --- a/frontend/src/security/Token.ts +++ b/frontend/src/security/Token.ts @@ -2,6 +2,10 @@ export function setSessionToken(window: Window, token: string) { window.sessionStorage.setItem("sessionToken", token); }; +export function getSessionToken(window: Window) { + return window.sessionStorage.getItem("sessionToken"); +} + export function removeSessionToken(window: Window) { window.sessionStorage.removeItem("sessionToken"); }