Skip to content

Commit

Permalink
Merge pull request #91 from LaumiH/feature/page-refresh
Browse files Browse the repository at this point in the history
feat: Implement page refresh
  • Loading branch information
ianchen0119 authored Aug 1, 2024
2 parents 943f49c + 92a9ade commit 69b0f1a
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 23 deletions.
2 changes: 1 addition & 1 deletion backend/webui_service/webui_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (a *WebuiApp) Start(tlsKeyLogPath string) {
AllowMethods: []string{"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE"},
AllowHeaders: []string{
"Origin", "Content-Length", "Content-Type", "User-Agent",
"Referrer", "Host", "Token", "X-Requested-With",
"Referrer", "Host", "Authorization", "Token", "X-Requested-With",
},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
Expand Down
34 changes: 31 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import StatusList from "./pages/StatusList";
import StatusRead from "./pages/StatusRead";
Expand All @@ -19,10 +19,38 @@ import { ProtectedRoute } from "./ProtectedRoute";
import { LoginContext, User } from "./LoginContext";

export default function App() {
const [user, setUser] = useState<User | null>(null);
const [user, setUser] = useState<User | null>(() => {
// retrieve from local storage on initial load (if available)
const storedUser = localStorage.getItem('username');
const storedToken = localStorage.getItem('token');
if (storedUser && storedToken) {
return { username: storedUser, token: storedToken };
} else {
console.warn('no user stored!');
}
return null;
});

useEffect(() => {
if (user && user.token) {
console.log('setting user related state');
localStorage.setItem('username', user.username);
localStorage.setItem('token', user.token);
} else {
console.log('deleting user related state');
localStorage.removeItem('username');
localStorage.removeItem('token');
}
}, [user])

// performance optimization, skip re-rendering of children if user did not change
const contextValue = useMemo(() => ({
user,
setUser
}), [user]);

return (
<LoginContext.Provider value={{ user, setUser }}>
<LoginContext.Provider value={contextValue}>
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ function Dashboard(props: DashboardProps) {
const toggleDrawer = () => {
setOpen(!open);
};
const { user, setUser } = useContext(LoginContext);
const context = useContext(LoginContext);
if (context === undefined) {
throw new Error("LoginContext must be used within a LoginContext.Provider");
}
const { user } = context;

const navigation = useNavigate();

const [time, setTime] = useState<Date>(new Date());
Expand Down Expand Up @@ -115,7 +120,7 @@ function Dashboard(props: DashboardProps) {
navigation("/password");
break;
case 1:
setUser(null);
// setUser(null);
navigation("/login");
break;
default:
Expand Down Expand Up @@ -151,7 +156,6 @@ function Dashboard(props: DashboardProps) {
break;
}
};

return (
<ThemeProvider theme={mdTheme}>
<Box sx={{ display: "flex" }}>
Expand Down
11 changes: 3 additions & 8 deletions frontend/src/LoginContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext } from "react";
import { Dispatch, SetStateAction, createContext } from "react";

export interface User {
username: string;
Expand All @@ -7,12 +7,7 @@ export interface User {

export interface UserContext {
user: User | null;
setUser: (user: User | null) => void;
setUser: Dispatch<SetStateAction<User | null>>;
}

export const LoginContext = createContext<UserContext>({
user: null,
setUser: (user: User | null) => {
console.log(user);
},
});
export const LoginContext = createContext<UserContext | undefined>(undefined);
8 changes: 6 additions & 2 deletions frontend/src/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React, { useContext } from "react";
import { useContext } from "react";
import { Navigate } from "react-router-dom";

import { LoginContext } from "./LoginContext";

export const ProtectedRoute = (props: any) => {
const { user } = useContext(LoginContext);
const context = useContext(LoginContext);
if (context === undefined) {
throw new Error("LoginContext must be used within a LoginContext.Provider");
}
const { user } = context;

if (user === null) {
return <Navigate to="/login" />;
Expand Down
35 changes: 34 additions & 1 deletion frontend/src/SimpleListMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useState } from "react";
import React, { useContext, useState } from "react";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import MenuItem from "@mui/material/MenuItem";
import Menu from "@mui/material/Menu";
import { useNavigate } from "react-router-dom";
import { LoginContext } from "./LoginContext";

export interface SimpleListMenuProps {
title: string | undefined;
Expand All @@ -19,6 +21,37 @@ export default function SimpleListMenu(props: SimpleListMenuProps) {
setAnchorEl(event.currentTarget);
};

const navigation = useNavigate();
const context = useContext(LoginContext);
if (context === undefined) {
throw new Error("LoginContext must be used within a LoginContext.Provider");
}
const { setUser } = context;

function onChangePassword() {
navigation("/password");
}

function onLogout() {
setUser(null);
navigation("/login");
}

const handleMenuItemClick = (event: React.MouseEvent<HTMLElement>, index: number) => {
setSelectedIndex(index);
setAnchorEl(null);
switch (index) {
case 0:
onChangePassword();
break;
case 1:
onLogout();
break;
default:
break;
}
};

const handleClose = () => {
setAnchorEl(null);
};
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/axios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,27 @@ if (process.env.NODE_ENV === "development") {
} else {
apiConfig.API_URL = process.env.REACT_APP_HTTP_API_URL ? process.env.REACT_APP_HTTP_API_URL : "";
}

const instance = axios.create({
baseURL: apiConfig.API_URL,
});

// attach the token to every request
instance.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
// add the token to the header
console.log('adding token to axios header');
config.headers.Token = `${token}`;
} else {
console.warn('no token in local storage!');
}
return config;
},
error => {
return Promise.reject(error);
}
);

export default instance;
9 changes: 6 additions & 3 deletions frontend/src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ const theme = createTheme();
export default function SignIn() {
const navigation = useNavigate();
const [error, setError] = useState<string>("");
const { setUser } = useContext(LoginContext);
const context = useContext(LoginContext);
if (context === undefined) {
throw new Error("LoginContext must be used within a LoginContext.Provider");
}
const { setUser } = context;

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
Expand All @@ -28,12 +32,11 @@ export default function SignIn() {
if (data.get("email") !== null) {
setUser({ username: data.get("email")!.toString(), token: res.data.access_token });
}
axios.defaults.headers.common.Token = res.data.access_token;
setError("");
navigation("/");
})
.catch((err) => {
console.log(err);
console.log(err.message);
setError("Wrong credentials");
});
};
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/pages/Logout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from "react";
import React, { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
Expand All @@ -9,7 +9,11 @@ import { LoginContext } from "../LoginContext";

export const Logout = () => {
const navigation = useNavigate();
const { setUser } = useContext(LoginContext);
const context = useContext(LoginContext);
if (context === undefined) {
throw new Error("LoginContext must be used within a LoginContext.Provider");
}
const { setUser } = context;

function onLogout() {
setUser(null);
Expand Down

0 comments on commit 69b0f1a

Please sign in to comment.