From 84ee743eae16e87eaa91969393bebf01e2d15a44 Mon Sep 17 00:00:00 2001 From: Sabir Hassan Date: Thu, 21 Apr 2022 04:34:27 +0500 Subject: [PATCH 1/3] feat: create AppContext --- web/src/context/appContext.tsx | 139 +++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 web/src/context/appContext.tsx diff --git a/web/src/context/appContext.tsx b/web/src/context/appContext.tsx new file mode 100644 index 0000000..a46352a --- /dev/null +++ b/web/src/context/appContext.tsx @@ -0,0 +1,139 @@ +import React, { + createContext, + Dispatch, + SetStateAction, + useState, + useEffect, + useCallback, + ReactNode +} from 'react' + +import axios from 'axios' + +const NODE_ENV = process.env.NODE_ENV +const PORT_API = process.env.PORT_API +const baseUrl = + NODE_ENV === 'development' ? `http://localhost:${PORT_API ?? 5000}` : '' + +const isAbsoluteURLRegex = /^(?:\w+:)\/\// + +const setAxiosRequestHeader = (accessToken: string) => { + axios.interceptors.request.use(function (config) { + if (baseUrl && !isAbsoluteURLRegex.test(config.url as string)) { + config.url = baseUrl + config.url + } + console.log('axios.interceptors.request.use', accessToken) + config.headers!['Authorization'] = `Bearer ${accessToken}` + config.withCredentials = true + + return config + }) +} + +const setAxiosResponse = (setTokens: Function) => { + // Add a response interceptor + axios.interceptors.response.use( + function (response) { + // Any status code that lie within the range of 2xx cause this function to trigger + return response + }, + async function (error) { + if (error.response?.status === 401) { + // refresh token + // const { accessToken, refreshToken: newRefresh } = await refreshMyToken( + // refreshToken + // ) + + // if (accessToken && newRefresh) { + // setTokens(accessToken, newRefresh) + // error.config.headers['Authorization'] = 'Bearer ' + accessToken + // error.config.baseURL = undefined + + // return axios.request(error.config) + // } + console.log(53) + setTokens(undefined) + } + + return Promise.reject(error) + } + ) +} + +const getTokens = () => { + const accessToken = localStorage.getItem('accessToken') + const refreshToken = localStorage.getItem('refreshToken') + + if (accessToken && refreshToken) { + setAxiosRequestHeader(accessToken) + return { accessToken, refreshToken } + } + return undefined +} + +interface AppContextProps { + userName: string + setUserName: Dispatch> | null + tokens?: { accessToken: string; refreshToken: string } + setTokens: ((accessToken: string, refreshToken: string) => void) | null + logout: (() => void) | null +} + +export const AppContext = createContext({ + userName: '', + tokens: getTokens(), + setUserName: null, + setTokens: null, + logout: null +}) + +const AppContextProvider = (props: { children: ReactNode }) => { + const { children } = props + const [userName, setUserName] = useState('') + const [tokens, setTokens] = useState(getTokens()) + + useEffect(() => { + setAxiosResponse(setTokens) + }, []) + + useEffect(() => { + console.log(97) + if (tokens === undefined) { + console.log(99) + localStorage.removeItem('accessToken') + localStorage.removeItem('refreshToken') + } + }, [tokens]) + + const saveTokens = useCallback( + (accessToken: string, refreshToken: string) => { + localStorage.setItem('accessToken', accessToken) + localStorage.setItem('refreshToken', refreshToken) + console.log(accessToken) + setAxiosRequestHeader(accessToken) + setTokens({ accessToken, refreshToken }) + }, + [] + ) + + const logout = useCallback(() => { + setUserName('') + setTokens(undefined) + }, []) + + return ( + + {children} + + ) +} + +export default AppContextProvider From 23cf8fa06fe10b8ee9495a30af125fa4f3865412 Mon Sep 17 00:00:00 2001 From: Sabir Hassan Date: Thu, 21 Apr 2022 04:36:20 +0500 Subject: [PATCH 2/3] chore(web): add user name at top right --- web/src/components/header.tsx | 70 ++++++++++++++++++++++++++++++--- web/src/components/userName.tsx | 30 ++++++++++++++ 2 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 web/src/components/userName.tsx diff --git a/web/src/components/header.tsx b/web/src/components/header.tsx index 3c514d8..f0654cc 100644 --- a/web/src/components/header.tsx +++ b/web/src/components/header.tsx @@ -1,13 +1,20 @@ -import React, { useState } from 'react' +import React, { useState, useContext } from 'react' import { Link, useHistory, useLocation } from 'react-router-dom' -import AppBar from '@mui/material/AppBar' -import Toolbar from '@mui/material/Toolbar' -import Tabs from '@mui/material/Tabs' -import Tab from '@mui/material/Tab' -import Button from '@mui/material/Button' +import { + AppBar, + Toolbar, + Tabs, + Tab, + Button, + Menu, + MenuItem +} from '@mui/material' import OpenInNewIcon from '@mui/icons-material/OpenInNew' +import UserName from './userName' +import { AppContext } from '../context/appContext' + const NODE_ENV = process.env.NODE_ENV const PORT_API = process.env.PORT_API const baseUrl = @@ -16,11 +23,29 @@ const baseUrl = const Header = (props: any) => { const history = useHistory() const { pathname } = useLocation() + const appContext = useContext(AppContext) const [tabValue, setTabValue] = useState(pathname) + const [anchorEl, setAnchorEl] = useState< + (EventTarget & HTMLButtonElement) | null + >(null) + + const handleMenu = ( + event: React.MouseEvent + ) => { + setAnchorEl(event.currentTarget) + } + + const handleClose = () => { + setAnchorEl(null) + } const handleTabChange = (event: React.SyntheticEvent, value: string) => { setTabValue(value) } + + const handleLogout = () => { + if (appContext.logout) appContext.logout() + } return ( { > App Stream +
+ + + + + + +
) diff --git a/web/src/components/userName.tsx b/web/src/components/userName.tsx new file mode 100644 index 0000000..10093fe --- /dev/null +++ b/web/src/components/userName.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import { Typography, IconButton } from '@mui/material' +import AccountCircle from '@mui/icons-material/AccountCircle' + +const UserName = (props: any) => { + return ( + + {props.avatarContent ? ( + user-avatar + ) : ( + + )} + + {props.userName} + + + ) +} + +export default UserName From 36628551ae577598568ebf7864ddee00a60f4a79 Mon Sep 17 00:00:00 2001 From: Sabir Hassan Date: Thu, 21 Apr 2022 04:37:40 +0500 Subject: [PATCH 3/3] chore(web): use AppContext instead of useTokens Hook --- web/src/App.tsx | 10 +++++----- web/src/components/login.tsx | 12 +++++++----- web/src/index.tsx | 5 ++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index e298fe0..02a493b 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useContext } from 'react' import { Route, HashRouter, Switch } from 'react-router-dom' import { ThemeProvider } from '@mui/material/styles' import { theme } from './theme' @@ -9,12 +9,12 @@ import Home from './components/home' import Drive from './containers/Drive' import Studio from './containers/Studio' -import useTokens from './components/useTokens' +import { AppContext } from './context/appContext' function App() { - const { tokens, setTokens } = useTokens() + const appContext = useContext(AppContext) - if (!tokens) { + if (!appContext.tokens) { return ( @@ -24,7 +24,7 @@ function App() { - + diff --git a/web/src/components/login.tsx b/web/src/components/login.tsx index 68b067c..acccd35 100644 --- a/web/src/components/login.tsx +++ b/web/src/components/login.tsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react' +import React, { useState, useContext } from 'react' import { useLocation } from 'react-router-dom' import PropTypes from 'prop-types' import { CssBaseline, Box, TextField, Button, Typography } from '@mui/material' +import { AppContext } from '../context/appContext' const headers = { Accept: 'application/json', @@ -33,8 +34,9 @@ const getTokens = async (payload: any) => { }).then((data) => data.json()) } -const Login = ({ setTokens, getCodeOnly }: any) => { +const Login = ({ getCodeOnly }: any) => { const location = useLocation() + const appContext = useContext(AppContext) const [username, setUserName] = useState('') const [password, setPassword] = useState('') const [errorMessage, setErrorMessage] = useState('') @@ -71,7 +73,8 @@ const Login = ({ setTokens, getCodeOnly }: any) => { code }) - setTokens(accessToken, refreshToken) + if (appContext.setTokens) appContext.setTokens(accessToken, refreshToken) + if (appContext.setUserName) appContext.setUserName(username) } } @@ -126,7 +129,7 @@ const Login = ({ setTokens, getCodeOnly }: any) => { required /> {errorMessage && {errorMessage}} - @@ -134,7 +137,6 @@ const Login = ({ setTokens, getCodeOnly }: any) => { } Login.propTypes = { - setTokens: PropTypes.func, getCodeOnly: PropTypes.bool } diff --git a/web/src/index.tsx b/web/src/index.tsx index 606a3cf..23b1a75 100644 --- a/web/src/index.tsx +++ b/web/src/index.tsx @@ -2,10 +2,13 @@ import React from 'react' import ReactDOM from 'react-dom' import './index.css' import App from './App' +import AppContextProvider from './context/appContext' ReactDOM.render( - + + + , document.getElementById('root') )