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

feat: setup logic for resign & offer draw #180

Closed
wants to merge 16 commits into from
Closed
9 changes: 8 additions & 1 deletion apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.7",
"@repo/store": "*",
"@repo/ui": "*",
"chess.js": "^1.0.0-beta.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"lucide-react": "^0.372.0",
"react": "^18.2.0",
"react-confetti": "^6.1.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"recoil": "^0.7.7"
"recoil": "^0.7.7",
"tailwind-merge": "^2.3.0"
},
"devDependencies": {
"@types/react": "^18.2.66",
Expand Down
96 changes: 96 additions & 0 deletions apps/frontend/src/components/GameActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Flag, Handshake } from 'lucide-react';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from './ui/tooltip';
import { OFFER_DRAW, RESIGN } from '../screens/Game';
import { Button } from './ui/button';

type GameActionsProps = {
socket?: WebSocket;
gameId: string;
myColor: 'black' | 'white';
myId: string;
};

export function GameActions({
socket,
gameId,
myColor,
myId,
}: GameActionsProps) {
const handleDrawClick = () => {
if (!socket) return;

socket.send(
JSON.stringify({
type: OFFER_DRAW,
payload: {
gameId,
myColor,
myId,
},
}),
);
};

const handleResignClick = () => {
if (!socket) return;

socket.send(
JSON.stringify({
type: RESIGN,
payload: {
gameId,
myColor,
},
}),
);
};

return (
<>
<div className="flex justify-center gap-5 items-center border-t-2 pt-5">
<button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button
onClick={handleResignClick}
className="flex gap-1 items-center border border-white"
variant={'secondary'}
>
<Flag className="text-white" />
<p className="text-white">Resign</p>
</Button>
</TooltipTrigger>
<TooltipContent className="bg-black">
<p className="text-white">Resign</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger className="text-white">
<Button
onClick={handleDrawClick}
className="flex gap-1 items-center border border-white"
variant={'secondary'}
>
<Handshake className="text-white" />
<span>Offer Draw</span>
</Button>
</TooltipTrigger>
<TooltipContent className="bg-black">
<p className="text-white">Offer Draw</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{/* TODO: Add Undo Feature */}
</div>
</>
);
}
2 changes: 1 addition & 1 deletion apps/frontend/src/components/chess-board/ChessSquare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const ChessSquare = ({
<div className="h-full justify-center flex flex-col ">
{square ? (
<img
className="w-14"
className="w-24"
src={`/${square?.color === 'b' ? `b${square.type}` : `w${square.type}`}.png`}
/>
) : null}
Expand Down
68 changes: 68 additions & 0 deletions apps/frontend/src/components/draw-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { DRAW_OFFER_ACCEPTED } from '../screens/Game';
import {
AlertDialog,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogAction,
AlertDialogCancel,
AlertDialogFooter,
} from './ui/alert-dialog';

type DrawDialogProps = {
drawReqSent: boolean;
myColor: string;
setDrawReq: React.Dispatch<React.SetStateAction<boolean>>;
socket: WebSocket;
gameId: string;
};

export function DrawDialog({
myColor,
drawReqSent,
setDrawReq,
socket,
gameId,
}: DrawDialogProps) {
if (!drawReqSent) return null;

return (
<>
<AlertDialog open={drawReqSent}>
<AlertDialogContent className="bg-green-600 text-white">
<AlertDialogHeader>
<AlertDialogTitle>
{myColor === 'black' ? 'white' : 'black'} is offering a draw? Do
you wish to accept it?
</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
onClick={() => {
setDrawReq(false);
}}
>
No!!!
</AlertDialogCancel>

<AlertDialogAction
onClick={() => {
socket.send(
JSON.stringify({
type: DRAW_OFFER_ACCEPTED,
payload: {
gameId,
},
}),
);
setDrawReq(false);
}}
>
Yes!
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}
60 changes: 60 additions & 0 deletions apps/frontend/src/components/game-over.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
AlertDialog,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogAction,
AlertDialogCancel,
AlertDialogFooter,
} from './ui/alert-dialog';
type GameOverProps = {
isGameOver: boolean;
setResult: React.Dispatch<
React.SetStateAction<
| 'WHITE_WINS'
| 'BLACK_WINS'
| 'DRAW'
| 'RESIGN_B'
| 'RESIGN_W'
| 'opponent_disconnected'
| 'user_timeout'
| null
>
>;
result: string;
};

export function GameOver({ setResult, isGameOver, result }: GameOverProps) {
return (
<>
<AlertDialog open={isGameOver}>
<AlertDialogContent className="bg-green-600 text-white">
<AlertDialogHeader>
<AlertDialogTitle className="flex flex-col items-center">
<p className="text-2xl font-semibold">
{result === 'WHITE_WINS' && 'White wins'}
{result === 'BLACK_WINS' && 'Black wins'}
{result === 'DRAW' && 'Draw'}
{result === 'RESIGN_B' && 'Black Resigned! White Wins!!!'}
{result === 'RESIGN_W' && 'White Resigned! Black Wins!!!'}
</p>
<img src="/wk.png" className="h-64 w-64" alt="" />
</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogFooter className="flex justify-center items-center">
<AlertDialogCancel
onClick={() => {
setResult(null);
}}
>
View Board
</AlertDialogCancel>
<AlertDialogAction onClick={() => {}}>
<a href="/game/random">Find next!</a>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}
139 changes: 139 additions & 0 deletions apps/frontend/src/components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import * as React from 'react';
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';

import { buttonVariants } from './button';
import { cn } from '../../utils/cn';

const AlertDialog = AlertDialogPrimitive.Root;

const AlertDialogTrigger = AlertDialogPrimitive.Trigger;

const AlertDialogPortal = AlertDialogPrimitive.Portal;

const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className,
)}
{...props}
ref={ref}
/>
));
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;

const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className,
)}
{...props}
/>
</AlertDialogPortal>
));
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;

const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className,
)}
{...props}
/>
);
AlertDialogHeader.displayName = 'AlertDialogHeader';

const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...props}
/>
);
AlertDialogFooter.displayName = 'AlertDialogFooter';

const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold', className)}
{...props}
/>
));
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;

const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
));
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName;

const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
));
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;

const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: 'outline' }),
'mt-2 sm:mt-0',
className,
)}
{...props}
/>
));
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;

export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
};