-
Notifications
You must be signed in to change notification settings - Fork 626
Feat/Add Exit Game Functionality #356
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
Changes from 4 commits
ff4d5fc
c1cf8a2
80c6fff
5ce256d
0c19cf3
57d6609
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { | ||
| AlertDialogAction, | ||
| AlertDialogCancel, | ||
| AlertDialogTitle, | ||
| AlertDialogHeader, | ||
| AlertDialogFooter, | ||
| AlertDialog, | ||
| AlertDialogTrigger, | ||
| AlertDialogContent, | ||
| AlertDialogDescription, | ||
| } from './ui/alert-dialog'; | ||
|
|
||
| const ExitGameModel = ({ onClick } : {onClick : () => void}) => { | ||
|
|
||
| return ( | ||
| <AlertDialog> | ||
| <AlertDialogTrigger className='w-24 h-12 bg-stone-800 rounded-md font-semibold text-white'>Exit</AlertDialogTrigger> | ||
| <AlertDialogContent className='bg-stone-800 border-stone-800'> | ||
| <AlertDialogHeader> | ||
| <AlertDialogTitle className='text-white font-mono'>Are you absolutely sure?</AlertDialogTitle> | ||
| <AlertDialogDescription className='text-white font-mono'> | ||
| This action cannot be undone. This will be considered as a loss. | ||
| </AlertDialogDescription> | ||
| </AlertDialogHeader> | ||
| <AlertDialogFooter> | ||
| <AlertDialogCancel className='font-mono bg-[#739552] text-white font-semibold hover:bg-[#b2e084] hover:text-gray-700 border-none'>Continue</AlertDialogCancel> | ||
| <AlertDialogAction | ||
| onClick={onClick} | ||
| className='bg-[#e2e6aa] min-w-20 text-gray-900 hover:text-slate-100 hover:bg-[#bbc259] font-semibold' | ||
| > | ||
| Exit | ||
| </AlertDialogAction> | ||
| </AlertDialogFooter> | ||
| </AlertDialogContent> | ||
| </AlertDialog> | ||
| ); | ||
| }; | ||
|
|
||
| export default ExitGameModel; |
| 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 { cn } from '@/lib/utils'; | ||
| import { buttonVariants } from '@/components/ui/button'; | ||
|
|
||
| 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, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ 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 EXIT_GAME = 'exit_game'; | ||
| export enum Result { | ||
| WHITE_WINS = 'WHITE_WINS', | ||
| BLACK_WINS = 'BLACK_WINS', | ||
|
|
@@ -33,14 +34,14 @@ export interface GameResult { | |
| by: string; | ||
| } | ||
|
|
||
|
|
||
| const GAME_TIME_MS = 10 * 60 * 1000; | ||
|
|
||
| import { useRecoilValue, useSetRecoilState } from 'recoil'; | ||
|
|
||
| import { movesAtom, userSelectedMoveIndexAtom } from '@repo/store/chessBoard'; | ||
| import GameEndModal from '@/components/GameEndModal'; | ||
| import { Waitopponent } from '@/components/ui/waitopponent'; | ||
| import ExitGameModel from '@/components/ExitGameModel'; | ||
|
|
||
| const moveAudio = new Audio(MoveSound); | ||
|
|
||
|
|
@@ -61,10 +62,7 @@ export const Game = () => { | |
| const [added, setAdded] = useState(false); | ||
| const [started, setStarted] = useState(false); | ||
| const [gameMetadata, setGameMetadata] = useState<Metadata | null>(null); | ||
| const [result, setResult] = useState< | ||
| GameResult | ||
| | null | ||
| >(null); | ||
| const [result, setResult] = useState<GameResult | null>(null); | ||
| const [player1TimeConsumed, setPlayer1TimeConsumed] = useState(0); | ||
| const [player2TimeConsumed, setPlayer2TimeConsumed] = useState(0); | ||
|
|
||
|
|
@@ -132,7 +130,8 @@ export const Game = () => { | |
|
|
||
| case GAME_ENDED: | ||
| const wonBy = message.payload.status === 'COMPLETED' ? | ||
| message.payload.result !== 'DRAW' ? 'CheckMate' : 'Draw' : 'Timeout'; | ||
| message.payload.result !== 'DRAW' ? 'CheckMate' : 'Draw' | ||
| : message.payload.status === 'PLAYER_EXIT'? 'Player Exit' : 'Timeout'; | ||
| setResult({ | ||
| result: message.payload.result, | ||
| by: wonBy, | ||
|
|
@@ -148,8 +147,8 @@ export const Game = () => { | |
| blackPlayer: message.payload.blackPlayer, | ||
| whitePlayer: message.payload.whitePlayer, | ||
| }); | ||
|
|
||
| setStarted(false); | ||
|
|
||
| break; | ||
|
|
||
| case USER_TIMEOUT: | ||
|
|
@@ -226,6 +225,18 @@ export const Game = () => { | |
| ); | ||
| }; | ||
|
|
||
| const handleExit = () => { | ||
| socket?.send( | ||
| JSON.stringify({ | ||
| type: EXIT_GAME, | ||
| payload: { | ||
| gameId, | ||
| }, | ||
| }), | ||
| ); | ||
| navigate('/'); | ||
| }; | ||
|
|
||
| if (!socket) return <div>Connecting...</div>; | ||
|
|
||
| return ( | ||
|
|
@@ -253,26 +264,24 @@ export const Game = () => { | |
| <div> | ||
| {started && ( | ||
| <div className="mb-4"> | ||
| <div className="flex justify-between"> | ||
| <UserAvatar | ||
| name={ | ||
| <div className="flex justify-between"> | ||
| <UserAvatar | ||
| name={ | ||
| user.id === gameMetadata?.whitePlayer?.id | ||
| ? gameMetadata?.blackPlayer?.name | ||
| : gameMetadata?.whitePlayer?.name ?? '' | ||
| } | ||
| /> | ||
| {getTimer( | ||
| user.id === gameMetadata?.whitePlayer?.id | ||
| ? gameMetadata?.blackPlayer?.name | ||
| : gameMetadata?.whitePlayer?.name ?? '' | ||
| } | ||
| /> | ||
| {getTimer( | ||
| user.id === gameMetadata?.whitePlayer?.id | ||
| ? player2TimeConsumed | ||
| : player1TimeConsumed, | ||
| )} | ||
| </div> | ||
| ? player2TimeConsumed | ||
| : player1TimeConsumed, | ||
| )} | ||
| </div> | ||
| </div> | ||
| )} | ||
| <div> | ||
| <div | ||
| className={`w-full flex justify-center text-white`} | ||
| > | ||
| <div className={`w-full flex justify-center text-white`}> | ||
| <ChessBoard | ||
| started={started} | ||
| gameId={gameId ?? ''} | ||
|
|
@@ -306,10 +315,12 @@ export const Game = () => { | |
| </div> | ||
| </div> | ||
| <div className="rounded-md pt-2 bg-bgAuxiliary3 flex-1 overflow-auto h-[95vh]"> | ||
| {!started && ( | ||
| <div className="pt-8 flex justify-center w-full"> | ||
| {!started? ( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this, not getting this condition. why we show if not started
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the game is not started the Play button and Waiting loader will render, if it's started the Exit Button will render. you have mentioned above is the component of Exit button.
Please let me know if it seems complex at first glance, i'll replace the ternary with if else. |
||
| <div className="p-8 flex justify-center w-full"> | ||
| {added ? ( | ||
| <div className="text-white"><Waitopponent/></div> | ||
| <div className="text-white"> | ||
| <Waitopponent /> | ||
| </div> | ||
| ) : ( | ||
| gameId === 'random' && ( | ||
| <Button | ||
|
|
@@ -326,6 +337,10 @@ export const Game = () => { | |
| ) | ||
| )} | ||
| </div> | ||
| ) : ( | ||
| <div className="p-8 flex justify-center w-full"> | ||
| <ExitGameModel onClick={() => handleExit()} /> | ||
| </div> | ||
| )} | ||
| <div> | ||
| <MovesTable /> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| -- AlterEnum | ||
| ALTER TYPE "GameStatus" ADD VALUE 'PLAYER_EXIT'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we improve this? it's too nasted...