Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented takeback move, after the opponent approval #336

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ GITHUB_CLIENT_SECRET=your_github_client_secret

ALLOWED_HOSTS="http://localhost:5173,https://example.com"

AUTH_REDIRECT_URL="http://localhost:5173/game/random"
AUTH_REDIRECT_URL="http://localhost:5173/game/random"
183 changes: 115 additions & 68 deletions apps/frontend/src/components/MovesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
isBoardFlippedAtom,
movesAtom,
takebackAlertAtom,
userSelectedMoveIndexAtom,
} from '@repo/store/chessBoard';
import { Move } from 'chess.js';
Expand All @@ -14,9 +15,16 @@ import {
ChevronLeft,
ChevronRight,
RefreshCw,
Undo,
Check,
X
} from 'lucide-react';
import { useSocket } from '../hooks/useSocket';
import { PROPOSE_A_TAKEBACK, TAKEBACK } from '../screens/Game';

const MovesTable = () => {
const MovesTable = ({ gameId, userId }: { gameId: string; userId: string; }) => {
const socket = useSocket()
const [takebackAlert, settakebackAlert] = useRecoilState(takebackAlertAtom);
const [userSelectedMoveIndex, setUserSelectedMoveIndex] = useRecoilState(
userSelectedMoveIndexAtom,
);
Expand Down Expand Up @@ -81,75 +89,114 @@ const MovesTable = () => {
})}
</div>
{moves.length ? (
<div className="w-full p-2 bg-[#20211D] flex items-center justify-between">
<div className="flex gap-4">
<button className="flex items-center gap-2 hover:bg-[#32302E] rounded px-2.5 py-1">
{<HandshakeIcon size={16} />}
Draw
</button>
<button className="flex items-center gap-2 hover:bg-[#32302E] rounded px-2.5 py-1">
{<FlagIcon size={16} />}
Resign
</button>
</div>
<div className="flex gap-1">
<button
onClick={() => {
setUserSelectedMoveIndex(0);
}}
disabled={userSelectedMoveIndex === 0}
className="hover:text-white"
title="Go to first move"
>
<ChevronFirst />
</button>
<>
<div className="w-full p-2 bg-[#20211D] flex items-center justify-between">
<div className="flex gap-4">
<button className="flex items-center gap-2 hover:bg-[#32302E] rounded px-2.5 py-1" onClick={() => {
socket?.send(
JSON.stringify({
type: PROPOSE_A_TAKEBACK,
payload: {
gameId,
userId
}
}));
}}>
{<Undo size={16} />}
Takeback
</button>
<button className="flex items-center gap-2 hover:bg-[#32302E] rounded px-2.5 py-1">
{<HandshakeIcon size={16} />}
Draw
</button>
<button className="flex items-center gap-2 hover:bg-[#32302E] rounded px-2.5 py-1">
{<FlagIcon size={16} />}
Resign
</button>
</div>
<div className="flex gap-1">
<button
onClick={() => {
setUserSelectedMoveIndex(0);
}}
disabled={userSelectedMoveIndex === 0}
className="hover:text-white"
title="Go to first move"
>
<ChevronFirst />
</button>

<button
onClick={() => {
setUserSelectedMoveIndex((prev) =>
prev !== null ? prev - 1 : moves.length - 2,
);
}}
disabled={userSelectedMoveIndex === 0}
className="hover:text-white"
>
<ChevronLeft />
</button>
<button
onClick={() => {
setUserSelectedMoveIndex((prev) =>
prev !== null
? prev + 1 >= moves.length - 1
? moves.length - 1
: prev + 1
: null,
);
}}
disabled={userSelectedMoveIndex === null}
className="hover:text-white"
>
<ChevronRight />
</button>
<button
onClick={() => {
setUserSelectedMoveIndex(moves.length - 1);
}}
disabled={userSelectedMoveIndex === null}
className="hover:text-white"
title="Go to last move"
>
<ChevronLast />
</button>
<button
onClick={() => {
setIsFlipped((prev) => !prev);
}}
title="Flip the board"
>
<RefreshCw className="hover:text-white mx-2" size={18} />
</button>
<button
onClick={() => {
setUserSelectedMoveIndex((prev) =>
prev !== null ? prev - 1 : moves.length - 2,
);
}}
disabled={userSelectedMoveIndex === 0}
className="hover:text-white"
>
<ChevronLeft />
</button>
<button
onClick={() => {
setUserSelectedMoveIndex((prev) =>
prev !== null
? prev + 1 >= moves.length - 1
? moves.length - 1
: prev + 1
: null,
);
}}
disabled={userSelectedMoveIndex === null}
className="hover:text-white"
>
<ChevronRight />
</button>
<button
onClick={() => {
setUserSelectedMoveIndex(moves.length - 1);
}}
disabled={userSelectedMoveIndex === null}
className="hover:text-white"
title="Go to last move"
>
<ChevronLast />
</button>
<button
onClick={() => {
setIsFlipped((prev) => !prev);
}}
title="Flip the board"
>
<RefreshCw className="hover:text-white mx-2" size={18} />
</button>
</div>
</div>
</div>
{takebackAlert &&
<div className="flex items-center justify-center mt-4">
<div className="flex items-center justify-center bg-green-500 text-white w-8 h-8 rounded-full mr-2 cursor-pointer
" onClick={() => {
socket?.send(JSON.stringify({
type: TAKEBACK,
payload: {
gameId,
userId
}
}));
settakebackAlert(null)
}}>
<Check />
</div>
<div className="text-white mx-2">{takebackAlert}</div>
<div className="flex items-center justify-center bg-red-500 text-white w-8 h-8 rounded-full ml-2 cursor-pointer
" onClick={() => {
settakebackAlert(null)
}}>
<X />
</div>
</div>
}
</>
) : null}
</div>
);
Expand Down
19 changes: 16 additions & 3 deletions apps/frontend/src/screens/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export const GAME_ADDED = 'game_added';
export const USER_TIMEOUT = 'user_timeout';
export const GAME_TIME = 'game_time';
export const GAME_ENDED = 'game_ended';
export const PROPOSE_A_TAKEBACK = 'propose_a_takeback';
export const TAKEBACK = 'takeback';
export enum Result {
WHITE_WINS = 'WHITE_WINS',
BLACK_WINS = 'BLACK_WINS',
Expand All @@ -38,7 +40,7 @@ const GAME_TIME_MS = 10 * 60 * 1000;

import { useRecoilValue, useSetRecoilState } from 'recoil';

import { movesAtom, userSelectedMoveIndexAtom } from '@repo/store/chessBoard';
import { movesAtom, takebackAlertAtom, userSelectedMoveIndexAtom } from '@repo/store/chessBoard';
import GameEndModal from '@/components/GameEndModal';
import { Waitopponent } from '@/components/ui/waitopponent';

Expand Down Expand Up @@ -67,7 +69,7 @@ export const Game = () => {
>(null);
const [player1TimeConsumed, setPlayer1TimeConsumed] = useState(0);
const [player2TimeConsumed, setPlayer2TimeConsumed] = useState(0);

const settakebackAlert = useSetRecoilState(takebackAlertAtom);
const setMoves = useSetRecoilState(movesAtom);
const userSelectedMoveIndex = useRecoilValue(userSelectedMoveIndexAtom);
const userSelectedMoveIndexRef = useRef(userSelectedMoveIndex);
Expand Down Expand Up @@ -126,6 +128,17 @@ export const Game = () => {
console.log('Error', error);
}
break;
case PROPOSE_A_TAKEBACK:
settakebackAlert(message.payload.message);
break;
case TAKEBACK:
const { lastMove, player1TimeConsumed: p1, player2TimeConsumed: p2 } = message.payload;
setPlayer1TimeConsumed(p1);
setPlayer2TimeConsumed(p2);
setMoves((moves) => moves.slice(0, moves.length - 1));
if(!lastMove) chess.reset()
else chess.load(lastMove.after)
break;
case GAME_OVER:
setResult(message.payload.result);
break;
Expand Down Expand Up @@ -328,7 +341,7 @@ export const Game = () => {
</div>
)}
<div>
<MovesTable />
<MovesTable gameId={gameId ?? ''} userId={user.id}/>
</div>
</div>
</div>
Expand Down
Loading