Implemented component version of authentication guard.
This commit is contained in:
		| @@ -1,6 +1,6 @@ | ||||
| import "./styles/Layout.css"; | ||||
| import "./styles/extra.css"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import { useContext } from "react"; | ||||
| import { NavLink, Route, Routes, useNavigate } from "react-router-dom"; | ||||
| import Welcome from "./pages/Welcome"; | ||||
| import Navbar from "react-bootstrap/Navbar"; | ||||
| @@ -9,30 +9,14 @@ import NavbarToggle from "react-bootstrap/esm/NavbarToggle"; | ||||
| import NavbarCollapse from "react-bootstrap/esm/NavbarCollapse"; | ||||
| import Dashboard from "./pages/Dashboard"; | ||||
| import Login from "./pages/Login"; | ||||
| import { apiClient } from "./utils/httpClients"; | ||||
| import { globalContext } from "./context.js"; | ||||
|  | ||||
|  | ||||
| export default function layout() { | ||||
|     const navigate = useNavigate(); | ||||
|     const [state, setState] = useState({ | ||||
|         user: null, | ||||
|     }); | ||||
|  | ||||
|     useEffect(async () => { | ||||
|         await updateAuthStatus(); | ||||
|     }); | ||||
|  | ||||
|     async function updateAuthStatus() { | ||||
|         const getUserResponse = await apiClient.get("/user"); | ||||
|         if (getUserResponse !== 200) { | ||||
|             setState({ user: null }); | ||||
|         } else { | ||||
|             setState({ user: getUserResponse.data }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let indentityDisplay = ( | ||||
|     const context = useContext(globalContext); | ||||
|     let identityDisplay = ( | ||||
|         <Nav> | ||||
|             <li className="nav-item"> | ||||
|                 <NavLink className="nav-link" to="/login" >Login</NavLink> | ||||
| @@ -43,8 +27,8 @@ export default function layout() { | ||||
|         </Nav> | ||||
|     ); | ||||
|  | ||||
|     if (state.user) { | ||||
|         indentityDisplay = ( | ||||
|     if (context.user) { | ||||
|         identityDisplay = ( | ||||
|             <Nav> | ||||
|                 <li className="nav-item"> | ||||
|                     <NavLink className="nav-link" to="/" >Hi, {this.state.user.firstName}</NavLink> | ||||
| @@ -58,7 +42,7 @@ export default function layout() { | ||||
|  | ||||
|     return ( | ||||
|         <div id="app"> | ||||
|             <globalContext.Provider value={{ navigate: navigate }}> | ||||
|             <globalContext.Provider value={{ navigate: navigate, user: null }}> | ||||
|                 <header> | ||||
|                     <Navbar bg="light" expand="md"> | ||||
|                         <Container> | ||||
| @@ -70,7 +54,7 @@ export default function layout() { | ||||
|                                         <NavLink className="nav-link" to="/" >Home</NavLink> | ||||
|                                     </li> | ||||
|                                 </Nav> | ||||
|                                 {indentityDisplay} | ||||
|                                 {identityDisplay} | ||||
|                             </NavbarCollapse> | ||||
|                         </Container> | ||||
|                     </Navbar> | ||||
|   | ||||
							
								
								
									
										30
									
								
								sports-matcher/client/src/components/AuthenticationGuard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								sports-matcher/client/src/components/AuthenticationGuard.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import React from "react"; | ||||
| import { Navigate } from "react-router-dom"; | ||||
| import { globalContext } from "../context"; | ||||
| import { apiClient } from "../utils/httpClients"; | ||||
|  | ||||
| export default class AuthenticationGuard extends React.Component { | ||||
|     constructor(props) { | ||||
|         super(props); | ||||
|     } | ||||
|  | ||||
|     async componentDidMount() { | ||||
|         if (!this.context.user) { | ||||
|             let userDataResponse = await apiClient.get("/user/"); | ||||
|             if (userDataResponse.status === 200) { | ||||
|                 this.context.user = userDataResponse.data; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static contextType = globalContext; | ||||
|  | ||||
|     render() { | ||||
|         if (!this.context.user) { | ||||
|             return ( | ||||
|                 <Navigate to="/signup" replace="true" /> | ||||
|             ); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| @@ -5,7 +5,7 @@ import { apiClient } from "../utils/httpClients.js"; | ||||
| import MatchInfoCardDisplay from "../components/MatchInfoCardDisplay"; | ||||
| import SportInfoCardDisplay from "../components/SportInfoCardDisplay"; | ||||
| import { globalContext } from "../context"; | ||||
| import { needUser } from "../utils/routing"; | ||||
| import AuthenticationGuard from "../components/AuthenticationGuard"; | ||||
|  | ||||
| export default class Dashboard extends React.Component { | ||||
|     constructor(props) { | ||||
| @@ -21,7 +21,7 @@ export default class Dashboard extends React.Component { | ||||
|     static contextType = globalContext; | ||||
|  | ||||
|     async componentDidMount() { | ||||
|         await needUser(this.context.navigate); | ||||
|         this.setState({ user: this.context.user }); | ||||
|         await this.latestMatches(); | ||||
|         await this.availableSports(); | ||||
|     } | ||||
| @@ -41,29 +41,32 @@ export default class Dashboard extends React.Component { | ||||
|  | ||||
|     render() { | ||||
|         return ( | ||||
|             <React.Fragment> | ||||
|                 <h1></h1> | ||||
|                 <InputGroup className="w-50"> | ||||
|                     <FormControl | ||||
|                         placeholder="Search for Matches" | ||||
|                         aria-label="Search Bar" | ||||
|                         aria-describedby="basic-addon2" | ||||
|                     /> | ||||
|                     <Button variant="outline-secondary" id="button-addon2"> | ||||
|                         Search | ||||
|                     </Button> | ||||
|                 </InputGroup> | ||||
|                 <div className="p-4"> | ||||
|                     <h2>Available Matches</h2> | ||||
|                     <MatchInfoCardDisplay recommendedmatches={this.state.displayedMatches} /> | ||||
|                 </div> | ||||
|                 <div className="p-4"> | ||||
|                     <h2>Available Sports</h2> | ||||
|                     <SportInfoCardDisplay recommendedsports={this.state.displayedSports} /> | ||||
|                 </div> | ||||
|             <div className="page-root"> | ||||
|                 <AuthenticationGuard /> | ||||
|                 <React.Fragment> | ||||
|                     <h1></h1> | ||||
|                     <InputGroup className="w-50"> | ||||
|                         <FormControl | ||||
|                             placeholder="Search for Matches" | ||||
|                             aria-label="Search Bar" | ||||
|                             aria-describedby="basic-addon2" | ||||
|                         /> | ||||
|                         <Button variant="outline-secondary" id="button-addon2"> | ||||
|                             Search | ||||
|                         </Button> | ||||
|                     </InputGroup> | ||||
|                     <div className="p-4"> | ||||
|                         <h2>Available Matches</h2> | ||||
|                         <MatchInfoCardDisplay recommendedmatches={this.state.displayedMatches} /> | ||||
|                     </div> | ||||
|                     <div className="p-4"> | ||||
|                         <h2>Available Sports</h2> | ||||
|                         <SportInfoCardDisplay recommendedsports={this.state.displayedSports} /> | ||||
|                     </div> | ||||
|  | ||||
|  | ||||
|             </React.Fragment> | ||||
|                 </React.Fragment> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -2,7 +2,7 @@ import React from "react"; | ||||
| import { Alert, Button, Card, Container, Form } from "react-bootstrap"; | ||||
| import { globalContext } from "../context"; | ||||
| import { apiClient } from "../utils/httpClients"; | ||||
| import { guard } from "../utils/routing"; | ||||
| import AuthenticationGuard from "../components/AuthenticationGuard"; | ||||
|  | ||||
| export default class Login extends React.Component { | ||||
|     constructor(props) { | ||||
| @@ -19,14 +19,6 @@ export default class Login extends React.Component { | ||||
|     static contextType = globalContext; | ||||
|  | ||||
|     async componentDidMount() { | ||||
|         try { | ||||
|             const getUserResponse = await apiClient.get("/user"); | ||||
|             guard(this.context.navigate, () => getUserResponse.status === 401, "/dashboard"); // If it's not 401, then we redirect to dashboard. | ||||
|         } catch (error) { | ||||
|             if (error.message !== "Request failed with status code 401") { | ||||
|                 throw error; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async attemptLogin(e) { | ||||
|   | ||||
| @@ -1,22 +0,0 @@ | ||||
| import { apiClient } from "./httpClients"; | ||||
|  | ||||
| export function guard(navigator, evaluator, redirect, navigateOptions, onRedirect) { | ||||
|     if (!evaluator) throw new Error("evaluator required."); | ||||
|     if (!redirect) throw new Error("redirect required."); | ||||
|     if (!navigateOptions) { | ||||
|         navigateOptions = { | ||||
|             replace: true | ||||
|         }; | ||||
|     } | ||||
|     let redirecting = !evaluator(); | ||||
|     if (redirecting) { | ||||
|         if (onRedirect) onRedirect(); | ||||
|         navigator(redirect, navigateOptions); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export async function needUser(navigator) { | ||||
|     let userDataResponse = await apiClient.get("/user"); | ||||
|     guard(navigator, () => userDataResponse.status === 200, "/login"); | ||||
|     return userDataResponse.data; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user