bug fixes

This commit is contained in:
Moon Patel
2023-07-25 03:01:45 +05:30
parent f86846004f
commit bd13e2b129
33 changed files with 1582 additions and 865 deletions
+2 -2
View File
@@ -44,8 +44,8 @@ app.use((error, req, res, next) => {
const status = error.status || 500; const status = error.status || 500;
console.log(error); console.log(error);
res.status(status).json({ res.status(status).json({
userMessage: "Something went wrong", message: "Something went wrong",
devMessage: error?.message || "Internal server error", description: error?.message || "Internal server error",
}); });
}); });
+6 -6
View File
@@ -67,7 +67,7 @@ router.post("/signup", async (req, res, next) => {
}); });
} catch (err) { } catch (err) {
if (err instanceof ZodError) { if (err instanceof ZodError) {
return res.status(400).json({ userMessage: "Invalid data submitted", devMessage: "Invalid schema" }); return res.status(400).json({ message: "Invalid data submitted", description: "Invalid schema" });
} }
next(err); next(err);
} }
@@ -84,16 +84,16 @@ router.post("/login", async (req, res, next) => {
user = await User.findOne({ username }); user = await User.findOne({ username });
if (!user) if (!user)
return res.status(404).json({ return res.status(404).json({
userMessage: "User does not exist", message: "User does not exist",
devMessage: "'username' not found in db", description: "'username' not found in db",
}); });
const pwIsValid = await isValidPassword(password, user.password_hash); const pwIsValid = await isValidPassword(password, user.password_hash);
if (!pwIsValid) { if (!pwIsValid) {
return res.status(401).json({ return res.status(401).json({
success: false, success: false,
userMessage: "Invalid credentials", message: "Invalid credentials",
devMessage: "Invalid credentials", description: "Invalid credentials",
}); });
} }
@@ -104,7 +104,7 @@ router.post("/login", async (req, res, next) => {
.json({ token, user: { id: user.id, username: user.username, email: user.email }, success: true }); .json({ token, user: { id: user.id, username: user.username, email: user.email }, success: true });
} catch (error) { } catch (error) {
if (error instanceof ZodError) { if (error instanceof ZodError) {
return res.status(401).json({ userMessage: "Invalid Credentials", devMessage: "Invalid schema" }); return res.status(401).json({ message: "Invalid Credentials", description: "Invalid schema" });
} }
next(error); next(error);
} }
+8 -5
View File
@@ -4,20 +4,22 @@ const { createRoom } = require("../socket");
const { checkAuth } = require("../util/auth"); const { checkAuth } = require("../util/auth");
const { Challenge } = require("../models/challenge"); const { Challenge } = require("../models/challenge");
async function fetchChallenge({ challenger, challenged }) {} // async function fetchChallenge({ challenger, challenged }) {}
// rooms can only be created through HTTP requests and destroyed only by socket.io server // rooms can only be created through HTTP requests and destroyed only by socket.io server
// and vice versa is not true // and vice versa is not true
router.post("/create", checkAuth, async (req, res, next) => { router.post("/", checkAuth, async (req, res, next) => {
console.log(req.body); console.log(req.body);
// challenger and challenged are username, color is the color played by challenger, timeLimit is the timeLimit for one player // challenger and challenged are username, color is the color played by challenger, timeLimit is the timeLimit for one player
const { challenger, challenged, color, timeLimit } = req.body; const { challenger, challenged, color, timeLimit } = req.body;
let challenge = await Challenge.findOne({ challenger }); let challenge = await Challenge.findOne({ challenger });
// a user can create only one challenge at a time // a user can create only one challenge at a time
if (challenge) { if (challenge) {
return res.status(405).json({ success: false, error: { message: "Cannot create new challenge" } }); return res.status(405).json({
message: "Cannot create new challenge",
description: "User already created a challenge",
});
} }
// get email of the challenged person // get email of the challenged person
@@ -45,7 +47,8 @@ router.post("/create", checkAuth, async (req, res, next) => {
// `Challenge from ${challenger}`, // `Challenge from ${challenger}`,
// `To accept the challenge follow the link: http://192.168.136.99:5173/game/challenges/${challenged}/${roomID} \n login through: http://192.168.136.99:5173/login \n roomid:${roomID}` // `To accept the challenge follow the link: http://192.168.136.99:5173/game/challenges/${challenged}/${roomID} \n login through: http://192.168.136.99:5173/login \n roomid:${roomID}`
// ); // );
res.json({ roomID }); console.log(roomID);
res.status(201).json({ roomID });
}); });
module.exports = router; module.exports = router;
+186 -135
View File
@@ -5,177 +5,228 @@ const { User } = require("../models/user");
const { checkAuth } = require("../util/auth"); const { checkAuth } = require("../util/auth");
const { catchAsync } = require("../util/errors"); const { catchAsync } = require("../util/errors");
// TO BE TESTED // get the logged in user details
// attaches user data to the route object router.get("/", checkAuth, async (req, res, next) => {
router.use( try {
catchAsync(async (req, res, next) => { let { userId } = req;
let userID = req.url.split("/")[1]; const user = await User.findById(userId);
if (userID?.length !== 24) let { id, username, email, fname, lname, country, location } = user;
return res.status(404).json({ userMessage: "User not found", devMessage: "Invalid user ID" }); let friends = await user.getFriends();
let userData = await User.findById(userID); let games = await user.getGames();
if (userData) {
req.userData = userData;
next();
} else {
res.status(404).json({
success: false,
userMessage: "User not found",
devMessage: "User ID does not exists",
});
}
})
);
// TO BE TESTED
// get user details
router.get(
"/:userid",
catchAsync(async (req, res, next) => {
let userData = req.userData;
let { id, username, email, fname, lname, country, location } = userData;
let friends = await userData.getFriends();
let games = await userData.getGames();
return res.status(200).json({ id, username, email, friends, fname, lname, country, location, games }); return res.status(200).json({ id, username, email, friends, fname, lname, country, location, games });
}) } catch (err) {
); next(err);
}
});
router.get("/friends", checkAuth, async (req, res, next) => {
try {
let { userId } = req;
let user = await User.findById(userId);
let friends = await user.getFriends();
return res.status(200).json(friends);
} catch (err) {
next(err);
}
});
router.get("/challenges", checkAuth, async (req, res, next) => {
try {
let { userId } = req;
let user = await User.findById(userId);
let challenges = await Challenge.find({ challenged: user.username });
challenges = challenges.map((challenge) => {
let { id, challenged, challenger, color, roomID, timeLimit } = challenge;
return { id, challenged, challenger, color, roomID, timeLimit };
});
console.log(challenges);
res.status(200).json(challenges);
} catch (err) {
next(err);
}
});
// TODO
// get history of games played
router.get("/games", checkAuth, async (req, res, next) => {
try {
let { userId } = req;
const user = await User.findById(userId);
let games = await user.getGames();
if (!games) games = [];
return res.status(200).json(gamesData);
} catch (err) {
next(err);
}
});
// TODO
router.get("/games/:gameid", checkAuth, async (req, res, next) => {
try {
} catch (err) {
next(err);
}
});
// TODO
router.get("");
// TO BE TESTED // TO BE TESTED
// update user details // update user details
router.patch( router.patch("/", checkAuth, async (req, res, next) => {
"/:userid", try {
checkAuth, let { userId } = req;
catchAsync(async (req, res, next) => {
let { userid } = req.params;
let updatedData = req.body; let updatedData = req.body;
console.log("Updated data: ", updatedData); await User.findByIdAndUpdate(userId, { ...updatedData });
// console.log(updatedData) let { id, username, email, fname, lname, location, country, fullName } = await User.findById(userId);
await User.findByIdAndUpdate(userid, { ...updatedData }); return res.status(200).json({ user: { id, username, email, fname, lname, location, country, fullName } });
let { id, username, email, fname, lname, location, country, fullName } = await User.findById(userid); } catch (err) {
// console.log(req.userData); next(err);
console.log({ id, username, email, fname, lname, location, country, fullName }); }
return res.json({ success: true, user: { id, username, email, fname, lname, location, country, fullName } }); });
})
);
// TO BE TESTED // TO BE TESTED
// delete the user // delete the user
router.delete( router.delete("/", checkAuth, async (req, res, next) => {
"/:userid", try {
checkAuth, let { userId } = req;
catchAsync(async (req, res, next) => { let user = await User.findById(userId);
let { userData } = req; await user.deleteOne();
await userData.deleteOne(); return res.status(204).json({ message: "Account deleted succesfully" });
}) } catch (err) {
); next(err);
}
});
// get friends of the user // TO BE TESTED
router.get( // TODO: add some logic to notify the challenger if the challenged user declines the challenge
"/:userid/friends", // accept or decline a challenge
checkAuth, // challengeID here refers to the roomID associated with the challenge
catchAsync(async (req, res, next) => { router.delete("/challenges/:challengeID", checkAuth, async (req, res, next) => {
const friends = await req.userData.getFriends(); try {
return res.json({ success: true, friends }); let { challengeID } = req.params;
}) let challenge = await Challenge.findById(challengeID);
); if (!challenge)
return res
.status(404)
.json({ message: "Challenge not found", description: "Challenge ID does not exists" });
await challenge.deleteOne();
return res.status(200).json({});
} catch (err) {
next(err);
}
});
// TO BE TESTED // TO BE TESTED
// add a friend // add a friend
router.post( router.post("/friends/:friendusername", checkAuth, async (req, res, next) => {
"/:userid/friends/:friendusername", let { friendusername } = req.params;
checkAuth, let { userId } = req;
catchAsync(async (req, res, next) => { const user = await User.findById(userId);
let { friendusername } = req.params; if (req.user.username === friendusername)
if (req.userData.username === friendusername) res.json({
res.json({ success: false, error: { message: "Cannot add yourself as friend" } }); error: { description: "Cannot add yourself as friend", message: "Cannot add this user as friends" },
let friendData = await User.findOne({ username: friendusername }); });
if (friendData) { let friendData = await User.findOne({ username: friendusername });
if (friendData.friends.includes(req.userData._id)) { if (friendData) {
res.json({ success: false, error: { message: "User is already added as a friend" } }); if (friendData.friends.includes(req.user._id)) {
} else { res.json({
friendData.friends.push(req.userData._id); error: {
await friendData.save(); message: "User is already added as a friend",
req.userData.friends.push(friendData._id); description: "User is already added as a friend",
await req.userData.save(); },
res.json({ success: true }); });
}
} else { } else {
res.status(404).json({ success: false, error: { message: "username does not exist" } }); friendData.friends.push(req.user._id);
await friendData.save();
req.user.friends.push(friendData._id);
await req.user.save();
res.status(204).json(null);
} }
}) } else {
); res.status(404).json({
error: { message: "User not found", description: "username not found in DB" },
});
}
});
// TODO // TODO
// remove a user from friends list // remove a user from friends list
router.delete( router.delete(
"/:userid/friends/:friendid", "/friends/:friendid",
checkAuth, checkAuth,
catchAsync(async (req, res, next) => { catchAsync(async (req, res, next) => {
const { friendid } = req.params; const { friendid } = req.params;
const { userData } = req; const { userId } = req;
const user = await User.findById(userId);
// Find the friend user to be removed // Find the friend user to be removed
const friendData = await User.findById(friendid); const friendData = await User.findById(friendid);
if (!friendData) { if (!friendData) {
return res.status(404).json({ success: false, error: { message: "Friend user not found" } }); return res.status(404).json({ error: { message: "Friend user not found" } });
} }
// Remove the friend from the user's friends list // Remove the friend from the user's friends list
const friendIndex = userData.friends.indexOf(friendData._id); const friendIndex = user.friends.indexOf(friendData._id);
if (friendIndex === -1) { if (friendIndex === -1) {
return res return res.status(400).json({ error: { message: "Friend user not found in the friends list" } });
.status(400)
.json({ success: false, error: { message: "Friend user not found in the friends list" } });
} }
userData.friends.splice(friendIndex, 1); user.friends.splice(friendIndex, 1);
await userData.save(); await user.save();
// Remove the user from the friend's friends list // Remove the user from the friend's friends list
const userIndex = friendData.friends.indexOf(userData._id); const userIndex = friendData.friends.indexOf(user._id);
if (userIndex === -1) { if (userIndex === -1) {
return res return res.status(400).json({ error: { message: "User not found in the friend's friends list" } });
.status(400)
.json({ success: false, error: { message: "User not found in the friend's friends list" } });
} }
friendData.friends.splice(userIndex, 1); friendData.friends.splice(userIndex, 1);
await friendData.save(); await friendData.save();
return res.json({ success: true }); return res.json({});
}) })
); );
// get current challenges of the user // =============================================================
router.get(
"/:userid/challenges",
checkAuth,
catchAsync(async (req, res, next) => {
let { userData } = req;
let challenges = await Challenge.find({ challenged: userData.username });
if (!challenges) challenges = [];
console.log("Challenges to", userData.username, challenges);
res.json({ success: true, challenges: challenges });
})
);
// TO BE TESTED // TO BE TESTED
// accept or decline a challenge // get user details
// challengeID here refers to the roomID associated with the challenge router.get("/:userid", async (req, res, next) => {
router.delete( try {
"/:userid/challenges/:challengeID", let userId = req.params.userid;
checkAuth, const user = await User.findById(userId);
catchAsync(async (req, res) => { let { id, username, email, fname, lname, country, location } = user;
let challengeResponse = req.query.response; let friends = await user.getFriends();
let { challengeID } = req.params; let games = await user.getGames();
if (challengeResponse === "accept") { return res.status(200).json({ id, username, email, friends, fname, lname, country, location, games });
let { deletedCount } = await Challenge.deleteOne({ roomID: challengeID }); } catch (err) {
return res.json({ success: true }); next(err);
} else if (challengeResponse === "decline") { }
let { deletedCount } = await Challenge.deleteOne({ roomID: challengeID }); });
return res.json({ success: true });
} else { // get friends of the user
res.json({ success: false, error: { message: "Invalid query parameter" } }); router.get("/:userid/friends", async (req, res, next) => {
} try {
}) const user = await User.findById(req.params.userid);
); const friends = await user.getFriends();
return res.json({ friends });
} catch (err) {
next(err);
}
});
// get current challenges of the user
router.get("/:userid/challenges", checkAuth, async (req, res, next) => {
try {
let { userId } = req;
const user = await user.findById(userId);
let challenges = await Challenge.find({ challenged: user.username });
if (!challenges) challenges = [];
console.log("Challenges to", user.username, challenges);
res.json({ challenges: challenges });
} catch (err) {
next(err);
}
});
// TODO // TODO
// get history of games played // get history of games played
@@ -183,10 +234,10 @@ router.get(
"/:userid/games", "/:userid/games",
checkAuth, checkAuth,
catchAsync(async (req, res, next) => { catchAsync(async (req, res, next) => {
const userData = await User.findOne(); const user = await User.findOne();
let gamesData = await userData.getGames(); let gamesData = await user.getGames();
if (!gamesData) gamesData = []; if (!gamesData) gamesData = [];
return res.status(200).json({ success: true, data: gamesData }); return res.status(200).json({ data: gamesData });
}) })
); );
@@ -195,7 +246,7 @@ router.get(
router.post("/:userid/game", checkAuth, async (req, res, next) => { router.post("/:userid/game", checkAuth, async (req, res, next) => {
const gameData = req.body; const gameData = req.body;
const gameDoc = await Game.create(gameData); const gameDoc = await Game.create(gameData);
return res.json({ success: true, data: gameDoc }); return res.json({ data: gameDoc });
}); });
// TODO // TODO
@@ -207,9 +258,9 @@ router.get(
const { gameid } = req.params; const { gameid } = req.params;
const gameData = await Game.findById(gameid); const gameData = await Game.findById(gameid);
if (gameData) { if (gameData) {
return res.status(200).json({ success: true, data: gameData }); return res.status(200).json({ data: gameData });
} else { } else {
return res.status(404).json({ success: false, error: { message: "Game not found" } }); return res.status(404).json({ error: { message: "Game not found" } });
} }
}) })
); );
-1
View File
@@ -6,7 +6,6 @@ const { Game } = require("./models/game");
const { const {
CHESS_MOVE, CHESS_MOVE,
CHESS_OPPONENT_MOVE, CHESS_OPPONENT_MOVE,
CONNECTION,
JOIN_ROOM, JOIN_ROOM,
JOIN_ROOM_ERROR, JOIN_ROOM_ERROR,
JOIN_ROOM_SUCCESS, JOIN_ROOM_SUCCESS,
+4 -3
View File
@@ -52,14 +52,15 @@ function checkAuthMiddleware(req, res, next) {
} }
let authToken = req.cookies["auth-token"]; let authToken = req.cookies["auth-token"];
if (!authToken) { if (!authToken) {
return res.status(401).json({ userMessage: "Not authenticated", devMessage: "Auth token not found" }); return res.status(401).json({ message: "Not authenticated", description: "Auth token not found" });
} }
try { try {
const validatedToken = validateJSONToken(authToken); const validatedToken = validateJSONToken(authToken);
req.userid = validatedToken; req.userId = validatedToken.id;
req.isAuthenticated = true;
} catch (error) { } catch (error) {
console.log("NOT AUTH. TOKEN INVALID."); console.log("NOT AUTH. TOKEN INVALID.");
return res.status(401).json({ userMessage: "Not authenticated", devMessage: "Invalid auth token" }); return res.status(401).json({ message: "Not authenticated", description: "Invalid auth token" });
} }
next(); next();
} }
+5
View File
@@ -0,0 +1,5 @@
node_modules/
dist/
.prettierrc.js
.eslintrc.js
env.d.ts
+36 -14
View File
@@ -1,15 +1,37 @@
module.exports = { module.exports = {
env: { browser: true, es2020: true }, extends: [
extends: [ // By extending from a plugin config, we can get recommended rules without having to add them manually.
'eslint:recommended', 'eslint:recommended',
'plugin:react/recommended', 'plugin:react/recommended',
'plugin:react/jsx-runtime', 'plugin:import/recommended',
'plugin:react-hooks/recommended', 'plugin:jsx-a11y/recommended',
], 'plugin:@typescript-eslint/recommended',
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, // This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.
settings: { react: { version: '18.2' } }, // Make sure it's always the last config, so it gets the chance to override other configs.
plugins: ['react-refresh'], 'eslint-config-prettier',
rules: { ],
'react-refresh/only-export-components': 'warn', settings: {
}, react: {
} // Tells eslint-plugin-react to automatically detect the version of React to use.
version: 'detect',
},
// Tells eslint how to resolve imports
'import/resolver': {
node: {
paths: ['src'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
rules: {
// Add your own rules here to override ones from the extended configs.
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
"no-console": "off",
},
"env":{
"browser":true,
"node":true
}
};
+1169 -510
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -17,8 +17,16 @@
"@mantine/hooks": "^6.0.14", "@mantine/hooks": "^6.0.14",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@tabler/icons-react": "^2.23.0", "@tabler/icons-react": "^2.23.0",
"@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^6.2.0",
"chess.js": "^1.0.0-beta.6", "chess.js": "^1.0.0-beta.6",
"eslint": "^8.45.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"prettier": "^3.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.45.2", "react-hook-form": "^7.45.2",
@@ -30,8 +38,6 @@
"@types/react": "^18.0.37", "@types/react": "^18.0.37",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^4.0.0", "@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.38.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4", "eslint-plugin-react-refresh": "^0.3.4",
"vite": "^4.3.9" "vite": "^4.3.9"
+3 -6
View File
@@ -1,9 +1,7 @@
import './App.css'
import { createBrowserRouter, redirect, RouterProvider } from 'react-router-dom' import { createBrowserRouter, redirect, RouterProvider } from 'react-router-dom'
import Home from './pages/Home' import Home from './pages/Home'
import MainLayout from './layout/MainLayout' import MainLayout from './layout/MainLayout'
import Settings from './pages/Settings/Settings' import Settings from './pages/Settings/Settings'
import Profile from './pages/Settings/Profile'
import Friends from './pages/Settings/Friends' import Friends from './pages/Settings/Friends'
import Password from './pages/Settings/Password' import Password from './pages/Settings/Password'
import Themes from './pages/Settings/Themes' import Themes from './pages/Settings/Themes'
@@ -11,12 +9,11 @@ import PlayLayout from './pages/Play/Layout'
import PlayFriend from './pages/Play/PlayFriend' import PlayFriend from './pages/Play/PlayFriend'
import Play from './pages/Play/Play' import Play from './pages/Play/Play'
import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication' import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication'
import { getAuthToken, getUserData } from '../utils/auth' import { getAuthToken, getUserData } from './utils/auth'
import { logoutAction } from './components/Logout'
import ChallengeFriend, { playFriendAction } from './pages/Play/ChallengeFriend' import ChallengeFriend, { playFriendAction } from './pages/Play/ChallengeFriend'
import ChessGame from './pages/Chess/ChessGame' import ChessGame from './pages/Chess/ChessGame'
import ChessGameContextProvider from './context/chess-game-context' import ChessGameContextProvider from './context/chess-game-context'
import { action as profileAction } from './pages/Settings/Profile' import Profile, { action as profileAction } from './pages/Settings/Profile'
const router = createBrowserRouter([{ const router = createBrowserRouter([{
path: '/', path: '/',
@@ -58,7 +55,7 @@ const router = createBrowserRouter([{
}, { }, {
path: '/signup', element: <AuthenticationPage isLogin={false} />, action: signupAction, loader: () => { if (getAuthToken()) return redirect('/signup'); else return null; } path: '/signup', element: <AuthenticationPage isLogin={false} />, action: signupAction, loader: () => { if (getAuthToken()) return redirect('/signup'); else return null; }
}, { }, {
path: '/logout', loader: () => { getAuthToken() || redirect('/home') }, action: logoutAction path: '/logout', loader: () => { getAuthToken() || redirect('/home') }
}]); }]);
function App() { function App() {
+11 -6
View File
@@ -1,7 +1,7 @@
import React, { useContext, useState } from 'react' import React, { useContext } from 'react'
import Piece from './Piece'; import Piece from './Piece';
import { socket } from '../socket'; import { socket } from '../socket';
import { Box, Flex, Modal } from '@mantine/core'; import { Box, Flex } from '@mantine/core';
import { useDroppable } from '@dnd-kit/core' import { useDroppable } from '@dnd-kit/core'
import { ChessGameContext } from '../context/chess-game-context'; import { ChessGameContext } from '../context/chess-game-context';
import { SOCKET_EVENTS } from '../constants'; import { SOCKET_EVENTS } from '../constants';
@@ -9,7 +9,7 @@ const { CHESS_MOVE } = SOCKET_EVENTS
const Cell = ({ cell }) => { const Cell = ({ cell }) => {
let roomID = localStorage.getItem('roomID'); let roomID = localStorage.getItem('roomID');
let { square, type, color } = cell; let { square, type } = cell;
const { getSquareColor, isSquareMarked, handleSquareClick } = useContext(ChessGameContext) const { getSquareColor, isSquareMarked, handleSquareClick } = useContext(ChessGameContext)
const { isOver, setNodeRef } = useDroppable({ id: square }); const { isOver, setNodeRef } = useDroppable({ id: square });
let squareColor = getSquareColor(square); let squareColor = getSquareColor(square);
@@ -24,11 +24,16 @@ const Cell = ({ cell }) => {
}); });
} }
let content = marked && !type ? <Mark /> : <Piece cell={cell} />; let content = null;
if (marked) {
content = <Mark />
} else if (type) {
content = <Piece cell={cell} />
}
return ( return (
<Flex ref={setNodeRef} style={{ aspectRatio: '1/1', position: 'relative' }} sx={theme => { <Flex ref={setNodeRef} style={{ aspectRatio: '1/1' }} sx={theme => {
let color = theme.colors.lime let color = theme.colors.lime;
return { backgroundColor: squareColor === 'b' ? color[8] : color[1], filter: 'saturate(0.5)' } return { backgroundColor: squareColor === 'b' ? color[8] : color[1], filter: 'saturate(0.5)' }
}} onClick={handleClick} bg={squareColor === 'w' ? "white" : "gray"} > }} onClick={handleClick} bg={squareColor === 'w' ? "white" : "gray"} >
{ {
+62 -69
View File
@@ -1,88 +1,35 @@
import { Button, Group, Stack, Text, Title } from '@mantine/core'; import { Button, Group, Stack, Text, Title } from '@mantine/core';
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { getAuthToken, getUserData } from '../../utils/auth'
const Challenges = () => { const Challenges = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [challenges, setChallenges] = useState([]); const [challenges, setChallenges] = useState([]);
const { id: userid } = getUserData();
const [error, setError] = useState(null); const [error, setError] = useState(null);
console.log(challenges)
useEffect(() => { useEffect(() => {
const abortController = new AbortController(); if (error) return;
let response = null;
const fetchData = async () => { const fetchData = async () => {
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${userid}/challenges`; let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/challenges`;
try { try {
response = await fetch(url, { const response = await fetch(url, {
signal: abortController.signal, headers: { credentials: 'include'
'Authorization': `Bearer ${getAuthToken()}` });
}
})
const data = await response.json(); const data = await response.json();
if (data.success) { if (response.ok) {
setChallenges(data.challenges); setChallenges(data);
} else { } else {
throw data.error; setError('Cannot')
} }
} catch (error) { } catch (error) {
if (error.message === 'The user aborted a request.'); console.log(error);
else { setError("Something went wrong")
console.log('Error fetching data');
throw error;
}
} }
} }
fetchData(); fetchData();
}, [error])
return () => {
if (!response) {
abortController.abort();
}
}
}, [])
const acceptChallengeHandler = ({ challenger, roomID, color, timeLimit }) => {
async function handler() {
const res = await deleteChallenge(roomID, 'accept');
if (res?.success) {
localStorage.setItem('myColor', color === 'b' ? 'w' : 'b');
localStorage.setItem('roomID', roomID);
localStorage.setItem('opponent', challenger);
localStorage.setItem('timeLimit', timeLimit);
navigate(`/game/friend/${roomID}`);
}
}
return handler;
}
const declineChallengeHandler = ({ challenger, roomID, color, timeLimit }) => {
async function handler() {
const res = await deleteChallenge(roomID, 'decline');
}
return handler;
}
const deleteChallenge = async (challengeID, response) => {
try {
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${userid}/challenges/${challengeID}?response=${response}`
console.log(url)
let res = await fetch(url, {
method: 'DELETE', headers: {
Authorization: `Bearer ${getAuthToken()}`,
}
})
const resData = await res.json();
return resData;
} catch (err) {
console.log(err)
setError(err)
}
}
if (!challenges || challenges.length === 0) { if (!challenges || challenges.length === 0) {
return ( return (
@@ -93,19 +40,65 @@ const Challenges = () => {
) )
} }
if (error) {
return (
<>
<Title mt="20px" mb="10px" order={3}>Challenges</Title>
<Text>{error}</Text><Text onClick={() => setError(null)} fw={800} sx={{ cursor: 'pointer' }}>Retry</Text>
</>
)
}
return ( return (
<div> <div>
<Title mt="20px" mb="10px" order={3}>Challenges</Title> <Title mt="20px" mb="10px" order={3}>Challenges</Title>
<Stack> <Stack>
{ {
challenges.map(({ challenger, roomID, color, timeLimit }) => { challenges.map(({ id, challenger, roomID, color, timeLimit }, index) => {
console.log(challenger, roomID, color, timeLimit); console.log(challenger, roomID, color, timeLimit);
return ( return (
<Group position='apart'> <Group position='apart' key={id}>
<Text>Challenge by {challenger}</Text> <Text>Challenge by {challenger}</Text>
<Group position='center'> <Group position='center'>
<Button color='lime' onClick={acceptChallengeHandler({ challenger, roomID, color, timeLimit })}>Accept</Button> <Button color='lime' onClick={async () => {
<Button color='gray' onClick={declineChallengeHandler({ challenger, roomID, color, timeLimit })}>Decline</Button> try {
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/challenges/${id}`
const response = await fetch(url, { method: 'DELETE', credentials: 'include' });
console.log(id, challenger);
const resData = await response.json();
if (response.ok) {
localStorage.setItem('myColor', color === 'b' ? 'w' : 'b');
localStorage.setItem('roomID', roomID);
localStorage.setItem('opponent', challenger);
localStorage.setItem('timeLimit', timeLimit);
navigate(`/game/friend/${roomID}`);
} else {
console.log(resData)
}
} catch (err) {
setError("Something went wrong");
console.log(err);
}
}}>Accept</Button>
<Button color='gray' onClick={async () => {
try {
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/challenges/${id}`
const response = await fetch(url, { method: 'DELETE', credentials: 'include' });
console.log(id, challenger);
if (response.ok) {
challenges.splice(index, 1);
setChallenges(challenges);
} else {
const resData = await response.json();
console.log(resData)
}
} catch (err) {
setError("Something went wrong");
console.log(err);
}
}}>Decline</Button>
</Group> </Group>
</Group> </Group>
) )
+1 -1
View File
@@ -21,7 +21,7 @@ const FriendsList = () => {
No friends No friends
</Text> </Text>
: :
friends.map((friend, index) => <NavLink key={index} component={Link} to={`/play/friend/${friend.username}`} p='5px' icon={<Avatar size='sm' color='blue' children={friend.username[0].toUpperCase()} />} label={<Text fw={700}>{friend.username}</Text>} />) friends.map((friend, index) => <NavLink key={index} component={Link} to={`/play/friend/${friend.username}`} p='5px' icon={<Avatar size='sm' color='blue' >{friend.username[0].toUpperCase()}</Avatar>} label={<Text fw={700}>{friend.username}</Text>} />)
} }
</Flex> </Flex>
) )
+3 -3
View File
@@ -1,9 +1,9 @@
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { ChessGameContext } from '../context/chess-game-context' import { ChessGameContext } from '../context/chess-game-context'
import { Button, Flex, Group, ScrollArea, Text, Tooltip, createStyles } from '@mantine/core'; import { Button, Flex, ScrollArea, Tooltip, createStyles } from '@mantine/core';
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react' import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'
const useStyles = createStyles(theme => { const useStyles = createStyles(() => {
return { return {
movebtn: { movebtn: {
fontSize: '14px', fontSize: '14px',
@@ -26,7 +26,7 @@ const useStyles = createStyles(theme => {
const GameHistory = () => { const GameHistory = () => {
let { classes } = useStyles(); let { classes } = useStyles();
const { gameHistory, jumpTo, currentIndex, goBack, goAhead } = useContext(ChessGameContext) const { gameHistory, jumpTo, goBack, goAhead } = useContext(ChessGameContext)
let gameHistoryJSX = []; let gameHistoryJSX = [];
for (let i = 0; i < gameHistory.length;) { for (let i = 0; i < gameHistory.length;) {
+2 -9
View File
@@ -1,6 +1,6 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { Button, Flex, Modal, Text, Title } from '@mantine/core' import { Button, Flex, Modal, Text, Title } from '@mantine/core'
import { Form, redirect, useNavigate } from 'react-router-dom' import { redirect, useNavigate } from 'react-router-dom'
import { useDisclosure } from '@mantine/hooks' import { useDisclosure } from '@mantine/hooks'
const Logout = () => { const Logout = () => {
@@ -27,7 +27,7 @@ const Logout = () => {
localStorage.removeItem('loggedIn'); localStorage.removeItem('loggedIn');
return navigate('/login'); return navigate('/login');
} else { } else {
return setErrorMsg(resData.userMessage || "Something went wrong") return setErrorMsg(resData.message || "Something went wrong")
} }
} catch (err) { } catch (err) {
setIsLoading(false) setIsLoading(false)
@@ -54,11 +54,4 @@ const Logout = () => {
</> </>
) )
} }
export const logoutAction = ({ request }) => {
localStorage.removeItem('token');
localStorage.removeItem('user');
return redirect('/login');
}
export default Logout export default Logout
+2 -2
View File
@@ -1,4 +1,4 @@
import { Group, NavLink, Text, ThemeIcon, UnstyledButton } from '@mantine/core' import { NavLink, Text, ThemeIcon } from '@mantine/core'
import { GearIcon, HomeIcon, PlayIcon } from '@radix-ui/react-icons' import { GearIcon, HomeIcon, PlayIcon } from '@radix-ui/react-icons'
import React, { useState } from 'react' import React, { useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@@ -31,7 +31,7 @@ const NavbarLink = ({ label, icon, to, index, active, setActive }) => {
component={Link} component={Link}
to={to} to={to}
icon={ icon={
<ThemeIcon variant="filled" color={active===index?'gray':'lime'}> <ThemeIcon variant="filled" color={active === index ? 'gray' : 'lime'}>
{icon} {icon}
</ThemeIcon> </ThemeIcon>
} }
+5 -5
View File
@@ -9,7 +9,7 @@ const useStyles = createStyles((theme) => ({
boxShadow: 'none', boxShadow: 'none',
borderColor: 'transparent' borderColor: 'transparent'
} }
})) }));
const Piece = ({ cell }) => { const Piece = ({ cell }) => {
const { classes } = useStyles(); const { classes } = useStyles();
@@ -50,7 +50,7 @@ const Piece = ({ cell }) => {
const style = transform ? { const style = transform ? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
cursor: isDragging ? 'grabbing' : 'pointer', cursor: isDragging ? 'grabbing' : 'pointer',
zIndex: isDragging ? 100 : 20, zIndex: isDragging ? 1000 : 10,
aspectRatio: '1', aspectRatio: '1',
touchAction: 'none', touchAction: 'none',
borderRadius: '10px', borderRadius: '10px',
@@ -66,15 +66,15 @@ const Piece = ({ cell }) => {
if (logo) { if (logo) {
return ( return (
<div style={{ position: 'relative', zIndex: 100 }}> <div style={{ position: 'relative' }}>
<div style={{ position: 'absolute', borderRadius: '50%', boxSizing: 'border-box', borderWidth: '8px', width: '100%', height: '100%', borderStyle: 'solid', borderColor }}> <div style={{ position: 'absolute', borderRadius: '50%', boxSizing: 'border-box', borderWidth: '8px', width: '100%', height: '100%', borderStyle: 'solid', borderColor }}>
</div> </div>
<Image classNames={{ root: classes['chess-piece'] }} ref={setNodeRef} style={style} sx={{ cursor: 'pointer' }} {...listeners} {...attributes} src={`/src/assets/images/pieces/${logo}.png`} /> <Image classNames={{ root: classes['chess-piece'] }} ref={setNodeRef} style={style} sx={{ cursor: 'pointer' }} {...listeners} {...attributes} src={`/src/assets/images/${logo}.png`} />
</div> </div>
) )
} else { } else {
return ( return (
<div style={{ width: '100%' }}></div> <div style={{ width: '100%', zIndex: 100 }}></div>
) )
} }
} }
+3 -3
View File
@@ -1,9 +1,9 @@
import React, { createContext, useReducer, useRef, useState } from 'react' import React, { createContext, useReducer, useRef, useState } from 'react'
import { ChessModified, chessInit } from '../../utils/chess'; import { ChessModified, chessInit } from '../utils/chess';
import { DISPATCH_EVENTS, SOCKET_EVENTS } from '../constants'; import { DISPATCH_EVENTS, SOCKET_EVENTS } from '../constants';
import { socket } from '../socket'; import { socket } from '../socket';
const { CAPTURE_PIECE, MOVE_PIECE, SELECT_PIECE, JUMP_TO, SET_GAME_HISTORY, END_GAME } = DISPATCH_EVENTS const { CAPTURE_PIECE, MOVE_PIECE, SELECT_PIECE, JUMP_TO, SET_GAME_HISTORY, END_GAME } = DISPATCH_EVENTS
const { GAME_END, CHESS_MOVE, CHESS_OPPONENT_MOVE } = SOCKET_EVENTS; const { GAME_END, CHESS_MOVE } = SOCKET_EVENTS;
export const ChessGameContext = createContext(); 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 // myColor: null, chess: null, chessBoard: null, moveHints: null, selected: null, dispatch: null, handleOpponentMove: null, handleSquareClick: null, getSquareColor: null, isSquareMarked: null, selectPiece: null, handleDrop: null
@@ -171,7 +171,7 @@ const ChessGameContextProvider = ({ children }) => {
} }
} }
function selectPiece({ square, type, color: pieceColor }) { function selectPiece({ square, color: pieceColor }) {
if (pieceColor === myColor && myColor === chess.turn()) { if (pieceColor === myColor && myColor === chess.turn()) {
dispatch({ type: SELECT_PIECE, val: square }); dispatch({ type: SELECT_PIECE, val: square });
} }
+3 -6
View File
@@ -1,14 +1,12 @@
import React, { createContext, useEffect, useState } from 'react' import React, { createContext, useEffect, useState } from 'react'
import Cookie from 'js-cookie'
export const UserDataContext = createContext();
export const UserDataContext = createContext();
const UserDataContextProvider = ({ children }) => { const UserDataContextProvider = ({ children }) => {
// TODO: use more secure mechanism insted of localstorage // TODO: use more secure mechanism insted of localstorage
const [isLoggedIn, setIsLoggedIn] = useState(JSON.parse(localStorage.getItem('loggedIn'))); const [isLoggedIn, setIsLoggedIn] = useState(JSON.parse(localStorage.getItem('loggedIn')));
const [user, setUser] = useState(null); const [user, setUser] = useState(null);
const [errorMessage, setErrorMessage] = useState(""); const [errorMessage, setErrorMessage] = useState("");
console.log(user)
async function fetchUserDetails() { async function fetchUserDetails() {
try { try {
@@ -22,8 +20,7 @@ const UserDataContextProvider = ({ children }) => {
if (response.ok) { if (response.ok) {
setUser(resData); setUser(resData);
} else { } else {
console.log(resData.devMessage); setErrorMessage(resData.message);
setErrorMessage(resData.userMessage);
} }
} }
} catch (err) { } catch (err) {
@@ -36,7 +33,7 @@ const UserDataContextProvider = ({ children }) => {
}, []); }, []);
return ( return (
<UserDataContext.Provider value={{ user, friends: user?.friends , games: user?.games, errorMessage, isLoggedIn, setIsLoggedIn }}> <UserDataContext.Provider value={{ user, friends: user?.friends, games: user?.games, errorMessage, isLoggedIn, setIsLoggedIn }}>
{children} {children}
</UserDataContext.Provider> </UserDataContext.Provider>
) )
+3 -3
View File
@@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
const useCountDown = (timeLimit,isTimerOn) => { const useCountDown = (timeLimit, isTimerOn) => {
const [timeLeft, setTimeLeft] = useState(timeLimit * 60 * 1000); const [timeLeft, setTimeLeft] = useState(timeLimit * 60 * 1000);
useEffect(() => { useEffect(() => {
if(!isTimerOn) { if (!isTimerOn) {
return; return;
} }
if (timeLeft > 0) { if (timeLeft > 0) {
@@ -73,7 +73,7 @@ const LoginForm = () => {
setIsLoggedIn(true); setIsLoggedIn(true);
return navigate('/'); return navigate('/');
} else { } else {
setErrorMsg(resData.userMessage || "Something went wrong"); setErrorMsg(resData.message || "Something went wrong");
} }
} catch (err) { } catch (err) {
setIsLoading(false); setIsLoading(false);
@@ -123,8 +123,8 @@ const SignupForm = () => {
} else { } else {
setIsLoading(false); setIsLoading(false);
console.log(resData); console.log(resData);
console.log(resData.devMessage); console.log(resData.description);
setErrorMsg(resData.userMessage); setErrorMsg(resData.message);
resData?.error?.username && setError('username', { message: resData.error.username }); resData?.error?.username && setError('username', { message: resData.error.username });
resData?.error?.email && setError('email', { message: resData.error.email }); resData?.error?.email && setError('email', { message: resData.error.email });
} }
+16 -12
View File
@@ -1,18 +1,18 @@
import { Avatar, Button, Flex, Group, Image, Loader, MediaQuery, Modal, NavLink, Text, Title } from '@mantine/core' import { Avatar, Button, Flex, Group, Image, Loader, MediaQuery, Modal, NavLink, Text, Title } from '@mantine/core'
import React, { useContext, useEffect, useState } from 'react' import React, { useContext, useEffect, useState } from 'react'
import ChessBoard from '../Chess/ChessBoard' import ChessBoard from '../Chess/ChessBoard'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { socket } from '../../socket' import { socket } from '../../socket'
import { getUserData } from '../../../utils/auth' import { getUserData } from '../../utils/auth'
import { ChessGameContext } from '../../context/chess-game-context' import { ChessGameContext } from '../../context/chess-game-context'
import GameHistory from '../../components/GameHistory' import GameHistory from '../../components/GameHistory'
import Timer from './Timer' import MainLoader from '../../components/MainLoader'
import { useDisclosure } from '@mantine/hooks' import { useDisclosure } from '@mantine/hooks'
import { SOCKET_EVENTS } from '../../constants' import { SOCKET_EVENTS } from '../../constants'
const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, CONNECTION, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM } = SOCKET_EVENTS; const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM } = SOCKET_EVENTS;
const ChessGame = () => { const ChessGame = () => {
const { setGameHistory, isTimerOn, setIsTimerOn, hasGameEnded, gameEndedReason, endGame } = useContext(ChessGameContext); const { setGameHistory, hasGameEnded, gameEndedReason, endGame } = useContext(ChessGameContext);
const [gameEndedModalOpen, modalFunctions] = useDisclosure(true); const [gameEndedModalOpen, modalFunctions] = useDisclosure(true);
const user = getUserData(); const user = getUserData();
@@ -67,7 +67,7 @@ const ChessGame = () => {
console.log('Socket disconnected due to', reason); console.log('Socket disconnected due to', reason);
}); });
socket.on(CHESS_OPPONENT_MOVE, (data) => { socket.on(CHESS_OPPONENT_MOVE, () => {
// console.log(data); // console.log(data);
// setIsTimerOn(true); // setIsTimerOn(true);
}) })
@@ -76,9 +76,9 @@ const ChessGame = () => {
setIsWaiting(false); setIsWaiting(false);
}); });
socket.on(USER_RESIGNED, (roomID, username) => { socket.on(USER_RESIGNED, () => {
endGame('RESIGN'); endGame('RESIGN');
}) });
return () => { return () => {
socket.offAny(); socket.offAny();
@@ -87,7 +87,7 @@ const ChessGame = () => {
}, []); }, []);
if (!hasJoinedRoom) return ( if (!hasJoinedRoom) return (
<Loader variant='bars' /> <MainLoader />
) )
return ( return (
@@ -104,7 +104,9 @@ const ChessGame = () => {
style={{ width: "500px" }} style={{ width: "500px" }}
p="2px" p="2px"
label={isWaiting ? "Waiting for opponent..." : opponent} label={isWaiting ? "Waiting for opponent..." : opponent}
icon={<Avatar radius="3px" children={opponent[0].toUpperCase()} />} icon={<Avatar radius="3px" >
{opponent[0].toUpperCase}
</Avatar>}
description={"description"} description={"description"}
/> />
{/* <Timer on={!isTimerOn} /> */} {/* <Timer on={!isTimerOn} /> */}
@@ -128,7 +130,9 @@ const ChessGame = () => {
style={{ width: "500px" }} style={{ width: "500px" }}
p="2px" p="2px"
label={username} label={username}
icon={<Avatar radius="3px" children={username[0].toUpperCase()} />} icon={<Avatar radius="3px" >
{username[0].toUpperCase()}
</Avatar>}
description={"description"} description={"description"}
/> />
{/* <Timer on={isTimerOn} /> */} {/* <Timer on={isTimerOn} /> */}
@@ -140,7 +144,7 @@ const ChessGame = () => {
height: '100%', height: '100%',
textAlign: 'center', textAlign: 'center',
borderRadius: '10px', borderRadius: '10px',
backgroundColor:'#272623' backgroundColor: '#272623'
}} bg='gray' justify='start' py='md' align='center' direction='column' h="600px"> }} bg='gray' justify='start' py='md' align='center' direction='column' h="600px">
<Title my='20px'>Game Data</Title> <Title my='20px'>Game Data</Title>
<Flex direction='column' w='100%'> <Flex direction='column' w='100%'>
+1 -1
View File
@@ -4,7 +4,7 @@ import { Box } from '@mantine/core';
import { ChessGameContext } from '../../context/chess-game-context'; import { ChessGameContext } from '../../context/chess-game-context';
const Timer = ({ on }) => { const Timer = ({ on }) => {
const { isTimerOn } = useContext(ChessGameContext) // const { isTimerOn } = useContext(ChessGameContext)
const timeLimit = localStorage.getItem('timeLimit'); const timeLimit = localStorage.getItem('timeLimit');
const [seconds, minutes] = useCountDown(timeLimit, on); const [seconds, minutes] = useCountDown(timeLimit, on);
return ( return (
+12 -13
View File
@@ -1,7 +1,7 @@
import { Avatar, Button, Card, Flex, Image, Select, Text, TextInput, Title } from '@mantine/core' import { Avatar, Button, Card, Flex, Select, Text, Title } from '@mantine/core'
import React from 'react' import React from 'react'
import { Form, redirect, useParams } from 'react-router-dom' import { Form, redirect, useParams } from 'react-router-dom'
import { getAuthToken, getUserData } from '../../../utils/auth' import { getUserData } from '../../utils/auth'
const ChallengeFriend = () => { const ChallengeFriend = () => {
const params = useParams(); const params = useParams();
@@ -9,12 +9,12 @@ const ChallengeFriend = () => {
return ( return (
<Card <Card
maw={450} sx={{ maw={450} sx={{
width: '100%', width: '100%',
height: '600px', height: '600px',
textAlign: 'center', textAlign: 'center',
backgroundColor:'#262523' backgroundColor: '#262523'
}} }}
> >
<Form action={`/play/friend/${friend_username}`} method='POST'> <Form action={`/play/friend/${friend_username}`} method='POST'>
<Flex align="center" direction="column" justify="center" gap="xs" my="lg"> <Flex align="center" direction="column" justify="center" gap="xs" my="lg">
@@ -42,20 +42,19 @@ export const playFriendAction = async ({ request, params }) => {
let timeLimit = formData.get('timeLimit'); let timeLimit = formData.get('timeLimit');
let username = getUserData().username; let username = getUserData().username;
let challenged = params.friend_username; let challenged = params.friend_username;
console.log(color, timeLimit, username, challenged);
let roomIDURL = `${import.meta.env.VITE_BACKEND_HOST}/api/room/create`; let roomIDURL = `${import.meta.env.VITE_BACKEND_HOST}/api/room`;
let reqBody = { challenger: username, challenged, timeLimit, color } let reqBody = { challenger: username, challenged, timeLimit, color }
try { try {
console.log(reqBody);
const response = await fetch(roomIDURL, { const response = await fetch(roomIDURL, {
method: 'POST', body: JSON.stringify(reqBody), headers: { method: 'POST', body: JSON.stringify(reqBody), headers: {
'Authorization': `Bearer ${getAuthToken()}`,
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} },
credentials: 'include'
}); });
console.log(response.status);
const resJSON = await response.json(); const resJSON = await response.json();
const { roomID } = resJSON; const { roomID } = resJSON;
console.log('Room ID:', roomID); console.log('Room ID:', roomID);
+3 -3
View File
@@ -1,7 +1,7 @@
import { Avatar, Box, Card, Divider, Flex, Image, MediaQuery, NavLink, Text, Title } from '@mantine/core' import { Avatar, Flex, Image, MediaQuery, NavLink } from '@mantine/core'
import React from 'react' import React from 'react'
import { Link, Outlet } from 'react-router-dom' import { Outlet } from 'react-router-dom'
import { getUserData } from '../../../utils/auth'; import { getUserData } from '../../utils/auth';
const Layout = () => { const Layout = () => {
const user = getUserData(); const user = getUserData();
+11 -29
View File
@@ -1,48 +1,30 @@
import React, { useState } from 'react' import React from 'react'
import { Button, Card, CopyButton, Flex, Group, Image, Modal, NativeSelect, NavLink, Select, Text, TextInput, Title } from '@mantine/core' import { Button, Card, Flex, Image, Modal, NativeSelect, Text, TextInput, Title } from '@mantine/core'
import { useDisclosure } from '@mantine/hooks' import { useDisclosure } from '@mantine/hooks'
import { IconSearch, IconUserCircle } from '@tabler/icons-react' import { IconSearch } from '@tabler/icons-react'
import FriendsList from '../../components/FriendsList' import FriendsList from '../../components/FriendsList'
import { Form } from 'react-router-dom'
import Challenges from '../../components/Challenges' import Challenges from '../../components/Challenges'
const createChallengeLink = (color) => {
let challengeLink = Math.floor(Math.random() * 100_000_000).toString();
if (color === 'RANDOM') {
challengeLink = challengeLink.concat(Math.random() < 0.5 ? 'W' : 'B');
} else {
challengeLink = challengeLink.concat(color);
}
return challengeLink;
}
const PlayFriend = () => { const PlayFriend = () => {
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
const [joinChallengeModalState, modalFunctions] = useDisclosure(false);
return ( return (
<> <>
<Modal zIndex={10} opened={opened} onClose={close} title={<Text mx="auto" size="xl">Create Challenge Link</Text>} centered> <Modal zIndex={10} opened={opened} onClose={close} title={<Text mx="auto" size="xl">Create Challenge Link</Text>} centered>
<Text>Start a game with anyone</Text> <Text>Start a game with anyone</Text>
<div> <div>
<NativeSelect onChange={(evt) => setColor(evt.target.value)} my="20px" label={<Text mx="auto" order={3}>I play as</Text>} placeholder='choose your color' data={[ <NativeSelect my="20px" label={<Text mx="auto" order={3}>I play as</Text>} placeholder='choose your color' data={[
{ value: 'W', label: 'White' }, { value: 'W', label: 'White' },
{ value: 'B', label: 'Black' }, { value: 'B', label: 'Black' },
{ value: 'RANDOM', label: 'Random' } { value: 'RANDOM', label: 'Random' }
]} /> ]} />
</div> </div>
{/* TODO: update createChallengeLink function */}
{/* <CopyButton>
{
({ copied, copy }) => <Button disabled onClick={copy} color={copied ? 'gray' : 'lime'}> {copied ? 'Copied' : 'Copy Link'} </Button>
}
</CopyButton> */}
</Modal> </Modal>
<Card <Card
maw={450} sx={{ maw={450} sx={{
width: '100%', width: '100%',
height: '600px', height: '600px',
textAlign: 'center', textAlign: 'center',
backgroundColor:'#262523' backgroundColor: '#262523'
}} }}
> >
<Flex align="center" justify="center" gap="xs" my="lg"> <Flex align="center" justify="center" gap="xs" my="lg">
@@ -61,11 +43,11 @@ const PlayFriend = () => {
) )
} }
const friends = [ // const friends = [
{ avatar: <IconUserCircle />, username: "friend", rating: 100 }, // { avatar: <IconUserCircle />, username: "friend", rating: 100 },
{ avatar: <IconUserCircle />, username: "friend", rating: 100 }, // { avatar: <IconUserCircle />, username: "friend", rating: 100 },
{ avatar: <IconUserCircle />, username: "friend", rating: 100 }, // { avatar: <IconUserCircle />, username: "friend", rating: 100 },
{ avatar: <IconUserCircle />, username: "friend", rating: 100 }, // { avatar: <IconUserCircle />, username: "friend", rating: 100 },
] // ]
export default PlayFriend export default PlayFriend
+3 -7
View File
@@ -1,20 +1,16 @@
import React from 'react' import React from 'react'
import { Button, Card, Flex, List, Stack, TextInput, Title } from '@mantine/core' import { Button, Card, Flex, TextInput, Title } from '@mantine/core'
import { IconSearch } from '@tabler/icons-react' import { IconSearch } from '@tabler/icons-react'
import { useForm } from '@mantine/form' import { useForm } from '@mantine/form'
import { getAuthToken, getUserData } from '../../../utils/auth'
import FriendsList from '../../components/FriendsList' import FriendsList from '../../components/FriendsList'
const Friends = () => { const Friends = () => {
let { id: userid } = getUserData();
const form = useForm({ initialValues: { username: '' }, }) const form = useForm({ initialValues: { username: '' }, })
const addFriend = async () => { const addFriend = async () => {
const response = await fetch(`${import.meta.env.VITE_BACKEND_HOST}/api/user/${userid}/friends/${form.values.username}`, { const response = await fetch(`${import.meta.env.VITE_BACKEND_HOST}/api/user/friends/${form.values.username}`, {
method: 'POST', method: 'POST',
headers: { credentials: 'include'
'Authorization': `Bearer ${getAuthToken()}`
}
}); });
const resData = await response.json(); const resData = await response.json();
if (resData.success === false) { if (resData.success === false) {
+4 -4
View File
@@ -1,6 +1,6 @@
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { Avatar, Button, Flex, Grid, Group, Loader, Stack, Text, TextInput, Title } from '@mantine/core' import { Avatar, Button, Flex, Grid, Group, Stack, Text, TextInput, Title } from '@mantine/core'
import { getAuthToken, getUserData } from '../../../utils/auth' import { getUserData } from '../../utils/auth'
import { UserDataContext } from '../../context/user-data-context' import { UserDataContext } from '../../context/user-data-context'
import { useForm } from '@mantine/form' import { useForm } from '@mantine/form'
import { Form } from 'react-router-dom' import { Form } from 'react-router-dom'
@@ -83,9 +83,9 @@ export const action = async ({ request }) => {
body: JSON.stringify(reqBody), body: JSON.stringify(reqBody),
method: 'PATCH', method: 'PATCH',
headers: { headers: {
Authorization: `Bearer ${getAuthToken()}`,
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} },
credentials: 'include'
}) })
const resData = await response.json(); const resData = await response.json();
console.log(resData) console.log(resData)
+1 -2
View File
@@ -1,10 +1,9 @@
import { Box, Flex, NavLink, Title } from '@mantine/core' import { Box, Flex, NavLink, Title } from '@mantine/core'
import { ColorWheelIcon, GearIcon, GlobeIcon, LockOpen1Icon, PersonIcon } from '@radix-ui/react-icons' import { ColorWheelIcon, GearIcon, GlobeIcon, LockOpen1Icon, PersonIcon } from '@radix-ui/react-icons'
import React, { useState } from 'react' import React from 'react'
import { Link, Outlet } from 'react-router-dom'; import { Link, Outlet } from 'react-router-dom';
const Settings = () => { const Settings = () => {
const [active, setActive] = useState(null);
return ( return (
<Box ml='45px'> <Box ml='45px'>
<Flex align="center" my="lg"> <Flex align="center" my="lg">
+6
View File
@@ -0,0 +1,6 @@
{
"name": "_chess_",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}