import refactoring
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { createBrowserRouter, redirect, RouterProvider } from 'react-router-dom'
|
||||
import Home from './pages/Home'
|
||||
|
||||
import MainLayout from './layout/MainLayout'
|
||||
import Home from './pages/Home'
|
||||
import Settings from './pages/Settings/Settings'
|
||||
import Friends from './pages/Settings/Friends'
|
||||
import Password from './pages/Settings/Password'
|
||||
@@ -9,11 +10,11 @@ import PlayLayout from './pages/Play/Layout'
|
||||
import PlayFriend from './pages/Play/PlayFriend'
|
||||
import Play from './pages/Play/Play'
|
||||
import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication'
|
||||
import { getAuthToken, getUserData } from './utils/auth'
|
||||
import ChallengeFriend, { playFriendAction } from './pages/Play/ChallengeFriend'
|
||||
import ChessGame from './pages/Chess/ChessGame'
|
||||
import ChessGameContextProvider from './context/chess-game-context'
|
||||
import Profile, { action as profileAction } from './pages/Settings/Profile'
|
||||
import ChessGameContextProvider from './context/chess-game-context'
|
||||
import { getAuthToken, getUserData } from './utils/auth'
|
||||
|
||||
const router = createBrowserRouter([{
|
||||
path: '/',
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import React, { useContext } from 'react'
|
||||
import Piece from './Piece';
|
||||
|
||||
import { socket } from '../socket';
|
||||
import { Box, Flex } from '@mantine/core';
|
||||
import { useDroppable } from '@dnd-kit/core'
|
||||
|
||||
import Piece from './Piece';
|
||||
import { ChessGameContext } from '../context/chess-game-context';
|
||||
|
||||
import { SOCKET_EVENTS } from '../constants';
|
||||
const { CHESS_MOVE } = SOCKET_EVENTS
|
||||
|
||||
|
||||
const Cell = ({ cell }) => {
|
||||
let roomID = localStorage.getItem('roomID');
|
||||
let { square, type } = cell;
|
||||
const { getSquareColor, isSquareMarked, handleSquareClick } = useContext(ChessGameContext)
|
||||
const { isOver, setNodeRef } = useDroppable({ id: square });
|
||||
let squareColor = getSquareColor(square);
|
||||
|
||||
let marked = isSquareMarked(square);
|
||||
let borderColor = isOver ? '#77777777' : 'transparent';
|
||||
let borderWidth = type ? '3px' : '5px'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Button, Group, Stack, Text, Title } from '@mantine/core';
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button, Group, Stack, Text, Title } from '@mantine/core';
|
||||
|
||||
const Challenges = () => {
|
||||
const navigate = useNavigate();
|
||||
const [challenges, setChallenges] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
console.log(challenges)
|
||||
|
||||
useEffect(() => {
|
||||
if (error) return;
|
||||
@@ -20,7 +20,7 @@ const Challenges = () => {
|
||||
if (response.ok) {
|
||||
setChallenges(data);
|
||||
} else {
|
||||
setError('Cannot')
|
||||
setError('Cannot fetch challenges')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -33,10 +33,10 @@ const Challenges = () => {
|
||||
|
||||
if (!challenges || challenges.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<Title mt="20px" mb="10px" order={3}>Challenges</Title>
|
||||
<Text>No challenges found</Text>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Avatar, Flex, Loader, NavLink, Text, Title } from '@mantine/core';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { UserDataContext } from '../context/user-data-context';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Avatar, Flex, Loader, NavLink, Text, Title } from '@mantine/core';
|
||||
|
||||
import { UserDataContext } from '../context/user-data-context';
|
||||
|
||||
const FriendsList = () => {
|
||||
const { friends } = useContext(UserDataContext)
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { ChessGameContext } from '../context/chess-game-context'
|
||||
|
||||
import { Button, Flex, ScrollArea, Tooltip, createStyles } from '@mantine/core';
|
||||
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'
|
||||
|
||||
import { ChessGameContext } from '../context/chess-game-context'
|
||||
|
||||
const useStyles = createStyles(() => {
|
||||
return {
|
||||
movebtn: {
|
||||
@@ -48,7 +50,7 @@ const GameHistory = () => {
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
// console.log(currentIndex)
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', userSelect: 'none' }}>
|
||||
<ScrollArea h={400} scrollbarSize={6} >
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Button, Flex, Modal, Text, Title } from '@mantine/core'
|
||||
import { redirect, useNavigate } from 'react-router-dom'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
|
||||
const Logout = () => {
|
||||
@@ -21,17 +22,16 @@ const Logout = () => {
|
||||
const resData = await response.json();
|
||||
setIsLoading(false);
|
||||
if (response.ok) {
|
||||
console.log('Logged out')
|
||||
localStorage.removeItem('user');
|
||||
close();
|
||||
localStorage.removeItem('loggedIn');
|
||||
close();
|
||||
return navigate('/login');
|
||||
} else {
|
||||
return setErrorMsg(resData.message || "Something went wrong")
|
||||
}
|
||||
} catch (err) {
|
||||
setIsLoading(false)
|
||||
console.log(err)
|
||||
console.error(err)
|
||||
setErrorMsg("Something went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react'
|
||||
|
||||
import { createPortal } from 'react-dom'
|
||||
import loaderImage from '../assets/images/chess_board_loader.png'
|
||||
import { Loader, Title } from '@mantine/core'
|
||||
|
||||
import loaderImage from '../assets/images/chess_board_loader.png'
|
||||
|
||||
const MainLoader = ({ errorMessage }) => {
|
||||
return (
|
||||
<>
|
||||
@@ -22,10 +24,6 @@ const MainLoader = ({ errorMessage }) => {
|
||||
|
||||
}
|
||||
</>
|
||||
// <div style={{ backgroundColor: '#272321', width: '100%', height: '100vh', display: 'flex', flexDirection: 'column', placeItems: 'center' }}>
|
||||
// <img draggable='false' src={loaderImage} style={{ width: '300px', height: '300px', marginTop: '180px', display: 'block' }} />
|
||||
// <Loader variant='bars' />
|
||||
// </div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { Link } from 'react-router-dom'
|
||||
import { NavLink, Text, ThemeIcon } from '@mantine/core'
|
||||
import { GearIcon, HomeIcon, PlayIcon } from '@radix-ui/react-icons'
|
||||
import React, { useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
const NavbarLinks = () => {
|
||||
const [active, setActive] = useState(null);
|
||||
|
||||
@@ -17,6 +17,7 @@ const Piece = ({ cell }) => {
|
||||
let { square, type, color } = cell;
|
||||
let marked = isSquareMarked(square);
|
||||
let logo = null;
|
||||
|
||||
switch (type) {
|
||||
case 'p':
|
||||
logo = color === 'w' ? 'pawn_white' : 'pawn_black';
|
||||
@@ -46,7 +47,6 @@ const Piece = ({ cell }) => {
|
||||
|
||||
let borderColor = marked ? '#77777766' : 'transparent'
|
||||
|
||||
|
||||
const style = transform ? {
|
||||
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||
cursor: isDragging ? 'grabbing' : 'pointer',
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { createContext, useReducer, useRef, useState } from 'react'
|
||||
import { ChessModified, chessInit } from '../utils/chess';
|
||||
import { DISPATCH_EVENTS, SOCKET_EVENTS } from '../constants';
|
||||
|
||||
import { socket } from '../socket';
|
||||
import { ChessModified, chessInit } from '../utils/chess';
|
||||
|
||||
import { DISPATCH_EVENTS, SOCKET_EVENTS } from '../constants';
|
||||
const { CAPTURE_PIECE, MOVE_PIECE, SELECT_PIECE, JUMP_TO, SET_GAME_HISTORY, END_GAME } = DISPATCH_EVENTS
|
||||
const { GAME_END, CHESS_MOVE } = SOCKET_EVENTS;
|
||||
export const ChessGameContext = createContext();
|
||||
// myColor: null, chess: null, chessBoard: null, moveHints: null, selected: null, dispatch: null, handleOpponentMove: null, handleSquareClick: null, getSquareColor: null, isSquareMarked: null, selectPiece: null, handleDrop: null
|
||||
|
||||
export const ChessGameContext = createContext();
|
||||
|
||||
const reducer = (state, action) => {
|
||||
// console.log('Before', state);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { AppShell, Burger, Container, Header, MediaQuery, Navbar, Paper, Text, createStyles, useMantineTheme } from '@mantine/core'
|
||||
import React, { useContext, useState } from 'react'
|
||||
import NavbarLinks from '../components/NavbarLinks';
|
||||
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import Logout from '../components/Logout';
|
||||
import { AppShell, Burger, Container, Header, MediaQuery, Navbar, Paper, Text, createStyles, useMantineTheme } from '@mantine/core'
|
||||
|
||||
import UserDataContextProvider, { UserDataContext } from '../context/user-data-context';
|
||||
import MainLoader from '../components/MainLoader';
|
||||
import NavbarLinks from '../components/NavbarLinks';
|
||||
import Logout from '../components/Logout';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
body: {
|
||||
@@ -27,7 +29,7 @@ const MainLayout = () => {
|
||||
const { classes } = useStyles();
|
||||
const theme = useMantineTheme();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { isLoggedIn,errorMessage } = useContext(UserDataContext);
|
||||
const { isLoggedIn, errorMessage } = useContext(UserDataContext);
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return <MainLoader errorMessage={errorMessage} />
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
import App from './App.jsx'
|
||||
import './index.css'
|
||||
import { MantineProvider } from '@mantine/styles'
|
||||
import UserDataContextProvider from './context/user-data-context.jsx'
|
||||
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<MantineProvider theme={{ colorScheme: 'dark', fontFamily: 'monospace', primaryColor: 'lime' }}>
|
||||
<UserDataContextProvider>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import { redirect, useNavigate } from 'react-router-dom';
|
||||
import { Button, Card, Container, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Form, redirect, useNavigate } from 'react-router-dom';
|
||||
import { ZodError, z } from 'zod';
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { ZodError, z } from 'zod';
|
||||
|
||||
import { UserDataContext } from '../../context/user-data-context';
|
||||
|
||||
let host = import.meta.env.VITE_BACKEND_HOST;
|
||||
@@ -122,8 +124,6 @@ const SignupForm = () => {
|
||||
return navigate('/');
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
console.log(resData);
|
||||
console.log(resData.description);
|
||||
setErrorMsg(resData.message);
|
||||
resData?.error?.username && setError('username', { message: resData.error.username });
|
||||
resData?.error?.email && setError('email', { message: resData.error.email });
|
||||
@@ -157,67 +157,4 @@ const SignupForm = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const loginAction = async ({ request }) => {
|
||||
try {
|
||||
const data = await request.formData();
|
||||
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/auth/login`;
|
||||
|
||||
const authData = {
|
||||
username: data.get('username'),
|
||||
password: data.get('password')
|
||||
}
|
||||
|
||||
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(authData),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.status >= 400) return response;
|
||||
else {
|
||||
const responseBody = await response.json();
|
||||
localStorage.setItem('token', responseBody.token);
|
||||
localStorage.setItem('user', JSON.stringify(responseBody.user))
|
||||
return redirect('/home');
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ZodError) {
|
||||
|
||||
} else {
|
||||
console.er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const signupAction = async ({ request }) => {
|
||||
const data = await request.formData();
|
||||
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/auth/signup`;
|
||||
|
||||
const authData = {
|
||||
username: data.get('username'),
|
||||
email: data.get('email'),
|
||||
password: data.get('password')
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(authData),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.status >= 400) return response;
|
||||
else {
|
||||
const responseBody = await response.json();
|
||||
localStorage.setItem('token', responseBody.token);
|
||||
localStorage.setItem('user', JSON.stringify(responseBody.user))
|
||||
return redirect('/home');
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthenticationPage;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import React, { useContext, useEffect, useReducer, useRef } from 'react';
|
||||
import Cell from '../../components/Cell';
|
||||
import { socket } from '../../socket';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
|
||||
import { Flex, createStyles } from '@mantine/core';
|
||||
import { DndContext } from '@dnd-kit/core'
|
||||
|
||||
import Cell from '../../components/Cell';
|
||||
import { ChessGameContext } from '../../context/chess-game-context';
|
||||
import { socket } from '../../socket';
|
||||
import { SOCKET_EVENTS } from '../../constants';
|
||||
const { CHESS_OPPONENT_MOVE, CHESS_MOVE } = SOCKET_EVENTS
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
chessboard: {
|
||||
[theme.fn.largerThan('md')]: {
|
||||
@@ -35,15 +38,9 @@ const useStyles = createStyles((theme) => ({
|
||||
const ChessBoard = () => {
|
||||
const { classes } = useStyles();
|
||||
const { getChessBoard, handleOpponentMove, handleDrop, hasGameEnded, gameEndedReason } = useContext(ChessGameContext)
|
||||
let roomID = localStorage.getItem('roomID');
|
||||
const roomID = localStorage.getItem('roomID');
|
||||
const chessBoard = getChessBoard();
|
||||
let myColor = localStorage.getItem('myColor')
|
||||
|
||||
// if (hasGameEnded) {
|
||||
// console.log('Game ended due to', gameEndedReason)
|
||||
// } else {
|
||||
// console.log('Game not ended yet')
|
||||
// }
|
||||
const myColor = localStorage.getItem('myColor')
|
||||
|
||||
useEffect(() => {
|
||||
socket.on(CHESS_OPPONENT_MOVE, handleOpponentMove)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { Avatar, Button, Flex, Group, Image, Loader, MediaQuery, Modal, NavLink, Text, Title } from '@mantine/core'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import ChessBoard from '../Chess/ChessBoard'
|
||||
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { Avatar, Button, Flex, Image, MediaQuery, Modal, NavLink, Text, Title } from '@mantine/core'
|
||||
|
||||
import { socket } from '../../socket'
|
||||
import { getUserData } from '../../utils/auth'
|
||||
import { ChessGameContext } from '../../context/chess-game-context'
|
||||
import ChessBoard from '../Chess/ChessBoard'
|
||||
import GameHistory from '../../components/GameHistory'
|
||||
import MainLoader from '../../components/MainLoader'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { SOCKET_EVENTS } from '../../constants'
|
||||
const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM } = SOCKET_EVENTS;
|
||||
|
||||
@@ -91,7 +93,7 @@ const ChessGame = () => {
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<Modal onClose={modalFunctions.close} opened={hasGameEnded && gameEndedModalOpen} centered>
|
||||
<Text>Game ended due to {gameEndedReason}</Text>
|
||||
<Button color='lime' onClick={exitGame}>Go back</Button>
|
||||
@@ -156,7 +158,7 @@ const ChessGame = () => {
|
||||
</Flex>
|
||||
</MediaQuery>
|
||||
</Flex>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useContext } from 'react'
|
||||
import useCountDown from '../../hooks/useCountDown'
|
||||
import React from 'react'
|
||||
|
||||
import { Box } from '@mantine/core';
|
||||
import { ChessGameContext } from '../../context/chess-game-context';
|
||||
|
||||
import useCountDown from '../../hooks/useCountDown'
|
||||
|
||||
const Timer = ({ on }) => {
|
||||
// const { isTimerOn } = useContext(ChessGameContext)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Text } from '@mantine/core'
|
||||
import React from 'react'
|
||||
|
||||
import { Text } from '@mantine/core'
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<Text>Home</Text>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Avatar, Button, Card, Flex, Select, Text, Title } from '@mantine/core'
|
||||
import React from 'react'
|
||||
|
||||
import { Form, redirect, useParams } from 'react-router-dom'
|
||||
import { Avatar, Button, Card, Flex, Select, Text, Title } from '@mantine/core'
|
||||
|
||||
import { getUserData } from '../../utils/auth'
|
||||
|
||||
const ChallengeFriend = () => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Avatar, Flex, Image, MediaQuery, NavLink } from '@mantine/core'
|
||||
import React from 'react'
|
||||
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import { Avatar, Flex, Image, MediaQuery, NavLink } from '@mantine/core'
|
||||
|
||||
import { getUserData } from '../../utils/auth';
|
||||
|
||||
const Layout = () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Card, Flex, Image, NavLink, Title } from '@mantine/core'
|
||||
import React from 'react'
|
||||
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Card, Flex, Image, NavLink, Title } from '@mantine/core'
|
||||
|
||||
const Play = () => {
|
||||
return (
|
||||
@@ -8,7 +9,7 @@ const Play = () => {
|
||||
width: '100%',
|
||||
height: '600px',
|
||||
textAlign: 'center',
|
||||
backgroundColor:'#262523'
|
||||
backgroundColor: '#262523'
|
||||
}}>
|
||||
<Flex gap="5px" px="20px" justify='center' align='center' wrap='nowrap' direction='column'>
|
||||
<Title order={2} >Play Chess</Title>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react'
|
||||
import { Button, Card, Flex, Image, Modal, NativeSelect, Text, TextInput, Title } from '@mantine/core'
|
||||
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { IconSearch } from '@tabler/icons-react'
|
||||
import { Button, Card, Flex, Image, Modal, NativeSelect, Text, TextInput, Title } from '@mantine/core'
|
||||
|
||||
import FriendsList from '../../components/FriendsList'
|
||||
import Challenges from '../../components/Challenges'
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useForm } from '@mantine/form'
|
||||
import { Button, Card, Flex, TextInput, Title } from '@mantine/core'
|
||||
import { IconSearch } from '@tabler/icons-react'
|
||||
import { useForm } from '@mantine/form'
|
||||
|
||||
import FriendsList from '../../components/FriendsList'
|
||||
|
||||
const Friends = () => {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { Avatar, Button, Flex, Grid, Group, Stack, Text, TextInput, Title } from '@mantine/core'
|
||||
import { getUserData } from '../../utils/auth'
|
||||
import { UserDataContext } from '../../context/user-data-context'
|
||||
|
||||
import { useForm } from '@mantine/form'
|
||||
import { Form } from 'react-router-dom'
|
||||
import { Avatar, Button, Flex, Grid, Group, Stack, Text, TextInput, Title } from '@mantine/core'
|
||||
|
||||
import { getUserData } from '../../utils/auth'
|
||||
import { UserDataContext } from '../../context/user-data-context'
|
||||
import MainLoader from '../../components/MainLoader'
|
||||
|
||||
const Profile = () => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Link, Outlet } from 'react-router-dom';
|
||||
import { Box, Flex, NavLink, Title } from '@mantine/core'
|
||||
import { ColorWheelIcon, GearIcon, GlobeIcon, LockOpen1Icon, PersonIcon } from '@radix-ui/react-icons'
|
||||
import React from 'react'
|
||||
import { Link, Outlet } from 'react-router-dom';
|
||||
|
||||
const Settings = () => {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user