upgraded to router v6

This commit is contained in:
John O'Keefe 2024-09-12 19:20:26 -04:00
parent 3af14dee95
commit af3c779b32
16 changed files with 102 additions and 120 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -5,29 +5,29 @@
"dependencies": {
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@hookstate/core": "^4.0.1",
"@hookstate/localstored": "^4.0.2",
"axios": "^1.5.0",
"axios": "1.7.5",
"bson-objectid": "^2.0.4",
"chakra-react-select": "^4.7.2",
"chakra-react-select": "^4.9.1",
"chakra-ui-contextmenu": "^1.0.5",
"framer-motion": "^10.16.4",
"framer-motion": "^11.3.30",
"interweave": "^13.1.0",
"jodit-react": "^1.3.39",
"js-base64": "^3.7.5",
"react": "^18.2.0",
"jodit-react": "^4.1.2",
"js-base64": "^3.7.7",
"react": "^18.3.1",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
"react-hook-form": "^7.48.2",
"react-icons": "^4.11.0",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"react-icons": "^5.3.0",
"react-rating": "^2.0.5",
"react-responsive-carousel": "^3.2.23",
"react-router-dom": "^5.3.4",
"react-select": "^5.7.4",
"react-toastify": "^9.1.3",
"web-vitals": "^3.4.0"
"react-router-dom": "^6.26.1",
"react-select": "^5.8.0",
"react-toastify": "^10.0.5",
"web-vitals": "^4.2.3"
},
"scripts": {
"start": "bunx --bun vite",
@ -60,17 +60,16 @@
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",
"@hookstate/devtools": "^4.0.1",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.4",
"@types/node": "^20.6.0",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.12",
"@types/node": "^22.5.1",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/react-router-dom": "^5.3.3",
"@types/react-table": "^7.7.15",
"@vitejs/plugin-react": "^4.0.4",
"typescript": "^5.2.2",
"vite": "^4.4.9"
"@types/react-table": "^7.7.20",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.5.4",
"vite": "^5.4.2"
}
}

View File

@ -1,11 +1,10 @@
import React, { useEffect } from 'react'
import GameDashboard from './components/GamesDashboard'
import { Redirect, Route, Switch, useLocation } from 'react-router-dom'
import {Navigate, Route, Routes, useLocation, useNavigate} from 'react-router-dom'
import Header from './components/Header'
import Home from './components/Home'
import GameDetails from './components/GameDetails'
import GameForm from './components/GameForm'
import NotFound from './errors/NotFound'
import TestErrors from './errors/TestError'
import Footer from './components/Footer'
import LoginForm from './components/authComponents/LoginForm'
@ -20,21 +19,22 @@ import { ToastContainer } from 'react-toastify'
import NoGames from './components/NoGames'
import CreateUser from './components/userComponents/CreateUser'
import LoadingModal from './components/LoadingModal'
import SomethingWentWrong from './errors/SomethingWentWrong'
import ForgotPassword from './components/authComponents/ForgotPassword'
import ResetPassword from './components/authComponents/ResetPassword'
import UserProfile from './components/userComponents/UserProfile'
import EditUser from './components/userComponents/EditUser'
import {Box, useColorMode} from "@chakra-ui/react";
import globalRouter from "./api/globalRouter";
function App() {
const location = useLocation<Location>()
const location = useLocation()
const appLoaded = useHookstate(appLoadedState)
const appLoad = appLoaded.get()
const isLoggedIn = useHookstate(loggedInState)
const loggedIn = isLoggedIn.get()
const originalToken = window.localStorage.getItem('jwt')
const { colorMode, toggleColorMode } = useColorMode()
globalRouter.navigate = useNavigate()
useEffect(() => {
if (originalToken) getUser(originalToken).finally(() => setAppLoaded(true))
@ -55,54 +55,27 @@ function App() {
<>
<ToastContainer position='bottom-right' hideProgressBar />
{loggedIn ? <Header /> : ''}
<Route exact path='/'>
{loggedIn ? <Redirect to='/games' /> : <Home />}
</Route>
<Box mt={loadedPreferences.get().stickyNav ? "20" : "4"} mb={'4'}>
{/*<Box mt={loadedPreferences.get().stickyNav ? isSmallerThan768 ? "15%" : "9%" : "4"} mb={'4'}>*/}
<Switch>
<Routes>
<Route path='/*' element={loggedIn ? <Navigate to='games' /> : <Home />} />
<Route path={'games/create'} element={<GameForm />} />
<Route
key={location.key}
path={['/games/create', '/games/:id/edit']}
component={() => <GameForm />}
path={'games/:id/edit'}
element={<GameForm />}
/>
<Route path='/games/:id'>
<GameDetails />
</Route>
<Route path='/games'>
<GameDashboard />
</Route>
<Route path='/nogames'>
<NoGames />
</Route>
<Route path='/login'>
<LoginForm />
</Route>
<Route path='/profile'>
<UserProfile />
</Route>
<Route path='/forgotpassword'>
<ForgotPassword />
</Route>
<Route path='/resetpassword/:token'>
<ResetPassword />
</Route>
<Route path='/create-user'>
<CreateUser />
</Route>
<Route path='/edit-user'>
<EditUser />
</Route>
<Route path='/errors'>
<TestErrors />
</Route>
<Route path='/something-went-wrong'>
<SomethingWentWrong />
</Route>
<Route>
<NotFound />
</Route>
</Switch>
<Route path='games/:id' element={<GameDetails />} />
<Route path='games' element={<GameDashboard />} />
<Route path='nogames' element={<NoGames />} />
<Route path='login' element={<LoginForm />} />
<Route path='profile' element={<UserProfile />} />
<Route path='forgotpassword' element={<ForgotPassword />} />
<Route path='resetpassword/:token' element={<ResetPassword />} />
<Route path='create-user' element={<CreateUser />} />
<Route path='edit-user' element={<EditUser />} />
<Route path='errors' element={<TestErrors />} />
</Routes>
</Box>
{loggedIn ? <Footer /> : ''}
</>

View File

@ -1,7 +1,7 @@
import axios, {AxiosError, AxiosResponse} from 'axios'
import Game, {Data, GameList} from '../models/game'
import {toast} from 'react-toastify'
import {history} from '..'
import globalRouter from "./globalRouter";
import User, {IForgotPassword, IResetPassword, UserAPI, UserFormValues, UserUpdateValues} from '../models/user'
import {userToken} from '../stateManagement/userState'
import {RecursivePartial} from "../@types/game";
@ -11,7 +11,6 @@ const sleep = (delay: number) => {
setTimeout(resolve, delay)
})
}
axios.defaults.baseURL = import.meta.env.VITE_API_URL
axios.interceptors.request.use((config) => {
@ -31,7 +30,7 @@ axios.interceptors.response.use(
case 400:
// @ts-ignore
if (typeof data.error === 'string') toast.error(data.error)
if (config.method === 'get') history.push('/not-found')
if (config.method === 'get' && globalRouter.navigate) globalRouter.navigate('/not-found')
// @ts-ignore
if (data.errors) {
const modalStateErrors = []
@ -45,13 +44,13 @@ axios.interceptors.response.use(
break
case 401:
toast.error('Unauthorized')
history.push('/')
if (globalRouter.navigate) globalRouter.navigate('/')
break
case 404:
history.push('/not-found')
if (globalRouter.navigate) globalRouter.navigate('/not-found')
break
case 429:
history.push('/something-went-wrong')
if (globalRouter.navigate) globalRouter.navigate('/not-found')
break
case 500:
toast.error('Server Error')

7
src/api/globalRouter.ts Normal file
View File

@ -0,0 +1,7 @@
import { NavigateFunction } from "react-router-dom";
const globalRouter = { navigate: null } as {
navigate: null | NavigateFunction
}
export default globalRouter

View File

@ -1,6 +1,6 @@
import React, { useRef } from 'react'
import agent from '../api/agent'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import { Data } from '../models/game'
import {
AlertDialog,
@ -11,7 +11,7 @@ import {
Button, Icon, Text
} from '@chakra-ui/react'
interface Props {
type Props = {
id: string
open: boolean
setOpen: Function
@ -26,7 +26,7 @@ export default function DeleteDialogue({
setLoading,
game,
}: Props) {
const history = useHistory()
const history = useNavigate()
const handleClose = () => {
setOpen(false)
}
@ -37,7 +37,7 @@ export default function DeleteDialogue({
await agent.Games.delete(id)
setLoading(false)
handleClose()
history.push(`/games/`)
history(`/games/`)
} catch (err) {
console.error(err)
setLoading(false)

View File

@ -1,5 +1,5 @@
import React, {FormEvent, useEffect, useRef} from 'react'
import {useHistory, useParams} from 'react-router-dom'
import {useNavigate, useParams} from 'react-router-dom'
import agent from '../api/agent'
import ObjectID from 'bson-objectid'
import {AccessedBy, Data, Os, SystemRequirements, WindowsOrMacOrLinux,} from '../models/game'
@ -48,7 +48,7 @@ const GameForm = () => {
const loaded = loadState.get()
const submitLoadingState = useHookstate(loadingAndSaveState)
const submitLoading = submitLoadingState.get()
const history = useHistory()
const history = useNavigate()
const openState = useHookstate(openAndCloseDeleteDialogue)
const open = openState.get()
const moveOnState = useHookstate(loadingAndContinueState)
@ -227,7 +227,7 @@ const GameForm = () => {
if (event.nativeEvent.submitter.dataset.name === 'save-and-view') {
moveOnState.set(() => false)
changeLoadState(false)
return history.push(`/games/${gameId}`)
return history(`/games/${gameId}`)
}
await getGame(gameId, admin)
changeLoadState(false)
@ -248,11 +248,11 @@ const GameForm = () => {
// @ts-ignore
} else if (event.nativeEvent.submitter.dataset.name === 'save') {
changeLoadState(false)
return history.push(`/games/${newGame._id}/edit`)
return history(`/games/${newGame._id}/edit`)
// @ts-ignore
} else if (event.nativeEvent.submitter.dataset.name === 'save-and-view') {
changeLoadState(false)
return history.push(`/games/${newGame._id}`)
return history(`/games/${newGame._id}`)
}
},
)
@ -303,6 +303,7 @@ const GameForm = () => {
if (loaded) return <LoadingModal/>
if (open) return <DeleteDialogue
//@ts-ignore
id={id}
open={open}
setOpen={openState.set}

View File

@ -1,6 +1,6 @@
import React, { useCallback, useEffect } from 'react'
import { useHookstate } from '@hookstate/core'
import { Link as RouteLink, useHistory, useLocation } from 'react-router-dom'
import { Link as RouteLink, useNavigate, useLocation } from 'react-router-dom'
import {loadedPreferences, loggedInUser, logout} from '../stateManagement/userState'
import {
Box,
@ -48,7 +48,7 @@ export default function NavBar() {
const bgHeader = useColorModeValue('gray.100', 'gray.900')
const bgLink = useColorModeValue('gray.200', 'gray.700')
const location = useLocation()
const history = useHistory()
const history = useNavigate()
const { isOpen: mobileIsOpen, onOpen: mobileOnOpen, onClose: mobileOnClose } = useDisclosure()
const { isOpen: filterIsOpen, onOpen: filterOnOpen, onClose: filterOnClose } = useDisclosure()
const btnRef = React.useRef(null)
@ -62,12 +62,12 @@ export default function NavBar() {
if (event.ctrlKey && event.key === '.' && location.pathname !== '/games') {
event.preventDefault()
history.push('/games')
history('/games')
}
if (event.ctrlKey && event.key === '/' && location.pathname !== '/games/create') {
event.preventDefault()
history.push('/games/create')
history('/games/create')
}
}, [setSearchModalOpen, location.pathname, history])
@ -239,8 +239,8 @@ export default function NavBar() {
</Center>
<br/>
<MenuDivider/>
<MenuItem onMouseDown={() => history.push('/profile')}>Preferences</MenuItem>
<MenuItem onMouseDown={() => logout()}><Icon as={MdOutlinePowerSettingsNew} mr={'2'}/> <Text
<MenuItem onClick={() => history('/profile')}>Preferences</MenuItem>
<MenuItem onClick={() => logout()}><Icon as={MdOutlinePowerSettingsNew} mr={'2'}/> <Text
mt={'1'}>Logout</Text></MenuItem>
</MenuList>
</Menu>

View File

@ -9,7 +9,7 @@ import {
loadingState,
openAndCloseSearchDialogue, searchCompletedState, showFiltersModuleState,
} from '../stateManagement/loadingState'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import { adminMode } from '../stateManagement/userState'
import getWithFilters from '../componentUtils/getWithFilters'
import {ChangeActiveFilterHeader} from "../componentUtils/changeActiveFilterHeader";
@ -27,7 +27,7 @@ export default function Search() {
const limit = games.count.limit
const setIsOpen = useHookstate(openAndCloseSearchDialogue)
const admin = useHookstate(adminMode).get()
const history = useHistory()
const history = useNavigate()
const useRatingState = useHookstate(ratingState).get()
const useSteamRatingState = useHookstate(steamRatingState).get()
const searchFinished= useHookstate(searchCompletedState)
@ -42,7 +42,7 @@ export default function Search() {
submitLoadingState.set(false)
games.searchParams.length > 0 && searchFinished.set(true)
handleClose()
return history.push('/games')
return history('/games')
}
const handleInputChange = <G extends keyof GameList>(name: G, value: string) => {

View File

@ -9,7 +9,7 @@ import {
Input, useToast, useColorModeValue, FormErrorMessage,
} from '@chakra-ui/react'
import agent from '../../api/agent'
import { useHistory, useParams } from 'react-router-dom'
import { useNavigate, useParams } from 'react-router-dom'
interface Message {
success: boolean
@ -28,7 +28,7 @@ export default function ResetPassword() {
formState: { errors, isSubmitting, isDirty },
} = useForm()
const { token } = useParams<URL>()
const history = useHistory()
const history = useNavigate()
const toast = useToast()
const password = useRef({})
password.current = watch('password', '')
@ -36,7 +36,7 @@ export default function ResetPassword() {
const onSubmit = handleSubmit(async (data) => {
const message = await agent.Account.reset(token, data as IResetPassword) as Message
if (message.success) {
setTimeout(() => history.push('/login'), 3000)
setTimeout(() => history('/login'), 3000)
toast({
title: 'Password Reset',
status: 'success',

View File

@ -38,8 +38,7 @@ const GameThumbnailContextMenu = ({ game }: Props) => {
<MenuList>
<MenuItem><Link to={`/games/${gameLink}`} target="_blank" rel="noopener noreferrer">Open in New Tab</Link></MenuItem>
<MenuGroup title={'Change PlayStatus'}>
{playStatusValues.map((value, index) => <MenuItem
key={index}
{playStatusValues.map(value => <MenuItem
onClick={() => rightClick(value.label, value.value)}
>{value.label}</MenuItem>)}
</MenuGroup>

View File

@ -4,7 +4,7 @@ import { Data } from '../../models/game'
import { gameDashboardState } from '../../stateManagement/gameState'
import { ImmutableObject, useHookstate } from '@hookstate/core'
import { loadingState, showFiltersModuleState } from '../../stateManagement/loadingState'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import { adminMode } from '../../stateManagement/userState'
import getWithFilters from '../../componentUtils/getWithFilters'
import { ChangeActiveFilterHeader } from "../../componentUtils/changeActiveFilterHeader";
@ -24,7 +24,7 @@ export default function Features({ game }: Props) {
const setShowFiltersModuleState = useHookstate(showFiltersModuleState)
const loadedState = useHookstate(loadingState)
const admin = useHookstate(adminMode).get()
const history = useHistory()
const history = useNavigate()
const [editMode, setEditMode] = reactUseState(false)
const [playState, setPlayState] = reactUseState(game.accessedBy[0].playStatus);
const useRatingState = useHookstate(ratingState).get()
@ -33,7 +33,7 @@ export default function Features({ game }: Props) {
const handleOnClick = async () => {
await ChangeActiveFilterHeader(games.filters, games.searchParams, setShowFiltersModuleState)
await getWithFilters(gamesState, loadedState, admin, games.count.currentPage, games.count.limit, games.searchParams, games.filters, useRatingState, useSteamRatingState)
return history.push('/games')
return history('/games')
}
return (

View File

@ -39,7 +39,7 @@ const GameRating = ({game, size}: Props) => {
stop={10}
step={2}
fractions={2}
onMouseDown={handleClick}
onClick={handleClick}
initialRating={finalRating}
emptySymbol={<IoGameControllerOutline size={size}/>}
fullSymbol={<IoGameController size={size} color={rating === 0 ? '#2761A7' : '#64ABDE'}/>}

View File

@ -15,7 +15,7 @@ import {
} from '@chakra-ui/react'
import { useHookstate } from '@hookstate/core'
import agent from '../../api/agent'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
export default function EditUser() {
const userState = useHookstate(loggedInUser)
@ -40,7 +40,7 @@ export default function EditUser() {
stickyNav: preferences.stickyNav
},
})
const history = useHistory()
const history = useNavigate()
useEffect(() => {
reset(user)
@ -61,7 +61,7 @@ export default function EditUser() {
const newPreferenceData: UserPreferences = { id: preferences.id, theme, stickyNav: data.stickyNav }
await agent.Account.update(newUserData)
await updateUserPreferences(newPreferenceData)
return history.push('/edit-user')
return history('/edit-user')
})
const engageChangePassword = async (values: IForgotPassword) => {

View File

@ -8,24 +8,28 @@ import "./styles/typography.css";
import "./styles/videoBackground.css";
import "./styles/overrides.css";
import "react-responsive-carousel/lib/styles/carousel.min.css";
import {Router} from "react-router-dom";
import {createBrowserHistory} from "history";
import {createBrowserRouter, RouterProvider} from "react-router-dom";
import {ChakraProvider, ColorModeScript} from "@chakra-ui/react";
import theme from "./styles/theme";
export const history = createBrowserHistory();
import NotFound from "./errors/NotFound";
const container = document.getElementById("root");
const root = createRoot(container!);
const router = createBrowserRouter([
{
path: "/*",
element: <App />,
errorElement: <NotFound />
}
])
root.render(
<React.StrictMode>
<Router history={history}>
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode}/>
<App/>
<RouterProvider router={router} />
</ChakraProvider>
</Router>
</React.StrictMode>
);

View File

@ -1,9 +1,9 @@
import defaultUser from '../models/defaultUser'
import User, {UserFormValues, UserPreferences} from '../models/user'
import {hookstate, useHookstate} from '@hookstate/core'
import {hookstate} from '@hookstate/core'
import {localstored} from '@hookstate/localstored'
import agent from '../api/agent'
import {history} from '..'
import globalRouter from "../api/globalRouter";
export const loggedInUser = hookstate(defaultUser as User)
export const userToken = hookstate('')
@ -22,7 +22,7 @@ export const createUser = async (creds: UserFormValues) => {
setToken(user.token)
await getUser(user.token)
loggedInState.set(true)
history.push('/games')
if (globalRouter.navigate) globalRouter.navigate('/games')
} catch (error) {
throw error
}
@ -34,7 +34,7 @@ export const login = async (creds: UserFormValues) => {
setToken(user.token)
await getUser(user.token)
loggedInState.set(true)
history.push('/games')
if (globalRouter.navigate) globalRouter.navigate('/games')
} catch (error) {
throw error
}
@ -46,7 +46,7 @@ export const logout = () => {
window.localStorage.removeItem('jwt')
loggedInUser.set(defaultUser)
loggedInState.set(false)
history.push('/')
if (globalRouter.navigate) globalRouter.navigate('/')
}
export const setToken = (token: string) => {