@@ -82,11 +82,35 @@ interface Props {
8282 sampleMoves ?: boolean
8383 simulateMaiaTime ?: boolean
8484 startFen ?: string
85+ returnTo ?: string
86+ challengeId ?: string
87+ forcedPlayerColor ?: Color
88+ modalTitle ?: string
89+ modalSubtitle ?: string
8590}
8691
8792export const PlaySetupModal : React . FC < Props > = ( props : Props ) => {
8893 const { setPlaySetupModalProps } = useContext ( ModalContext )
89- const { push } = useRouter ( )
94+ const router = useRouter ( )
95+ const { push } = router
96+
97+ const dismissModal = useCallback ( ( ) => {
98+ setPlaySetupModalProps ( undefined )
99+
100+ if (
101+ props . returnTo &&
102+ router . pathname === '/play' &&
103+ router . asPath !== props . returnTo
104+ ) {
105+ push ( props . returnTo )
106+ }
107+ } , [
108+ props . returnTo ,
109+ push ,
110+ router . asPath ,
111+ router . pathname ,
112+ setPlaySetupModalProps ,
113+ ] )
90114
91115 const [ timeControl , setTimeControl ] = useState < TimeControl > (
92116 props . timeControl || TimeControlOptions [ 0 ] ,
@@ -118,9 +142,22 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
118142 const [ fen , setFen ] = useState < string | undefined > (
119143 props . startFen ? props . startFen : undefined ,
120144 )
145+ const forcedPlayerColor = props . forcedPlayerColor
146+ const colorSelectionLocked = forcedPlayerColor !== undefined
147+ const positionLocked = forcedPlayerColor !== undefined
121148
122149 const [ openMoreOptions , setMoreOptionsOpen ] = useState < boolean > ( true )
123150 const compactHandBrainLayout = props . playType === 'handAndBrain'
151+ const modalTitle =
152+ props . modalTitle ||
153+ ( props . playType == 'againstMaia'
154+ ? 'Play Against Maia'
155+ : 'Play Hand and Brain' )
156+ const modalSubtitle =
157+ props . modalSubtitle ||
158+ ( props . playType == 'againstMaia'
159+ ? 'Configure your game settings and choose your side'
160+ : 'Team up with Maia in Hand and Brain chess' )
124161
125162 const handlePresetSelect = useCallback ( ( preset : TimeControl ) => {
126163 setTimeControl ( preset )
@@ -156,7 +193,16 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
156193
157194 const start = useCallback (
158195 ( color : Color | undefined ) => {
196+ if (
197+ forcedPlayerColor &&
198+ color !== undefined &&
199+ color !== forcedPlayerColor
200+ ) {
201+ return
202+ }
203+
159204 const player = color ?? [ 'white' , 'black' ] [ Math . floor ( Math . random ( ) * 2 ) ]
205+ const resolvedPlayer = forcedPlayerColor ?? player
160206
161207 if ( fen && ! new Chess ( ) . validateFen ( fen ) . valid ) {
162208 toast . error ( 'Invalid Starting FEN provided' )
@@ -169,20 +215,25 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
169215 push ( {
170216 pathname : '/play/maia' ,
171217 query : {
172- player : player ,
218+ player : resolvedPlayer ,
173219 //maiaPartnerVersion: maiaPartnerVersion,
174220 maiaVersion : maiaVersion ,
175221 timeControl : timeControl ,
176222 sampleMoves : sampleMoves ,
177223 simulateMaiaTime : simulateMaiaTime ,
178224 startFen : fen ,
225+ returnTo : props . returnTo ,
226+ challengeId : props . challengeId ,
227+ forcedColor : forcedPlayerColor ,
228+ modalTitle : props . modalTitle ,
229+ modalSubtitle : props . modalSubtitle ,
179230 } ,
180231 } )
181232 } else {
182233 push ( {
183234 pathname : '/play/hb' ,
184235 query : {
185- player : player ,
236+ player : resolvedPlayer ,
186237 maiaPartnerVersion : maiaPartnerVersion ,
187238 maiaVersion : maiaVersion ,
188239 timeControl : timeControl ,
@@ -204,13 +255,18 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
204255 sampleMoves ,
205256 simulateMaiaTime ,
206257 fen ,
258+ forcedPlayerColor ,
207259 isBrain ,
260+ props . challengeId ,
261+ props . modalSubtitle ,
262+ props . modalTitle ,
263+ props . returnTo ,
208264 ] ,
209265 )
210266
211267 return (
212268 < AnimatePresence >
213- < ModalContainer dismiss = { ( ) => setPlaySetupModalProps ( undefined ) } >
269+ < ModalContainer dismiss = { dismissModal } >
214270 < motion . div
215271 initial = { { opacity : 0 } }
216272 animate = { { opacity : 1 } }
@@ -231,7 +287,7 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
231287 < button
232288 className = "absolute right-4 top-4 z-10 text-secondary transition-colors hover:text-primary"
233289 title = "Close"
234- onClick = { ( ) => setPlaySetupModalProps ( undefined ) }
290+ onClick = { dismissModal }
235291 >
236292 < span className = "material-symbols-outlined" > close</ span >
237293 </ button >
@@ -242,16 +298,8 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
242298 compactHandBrainLayout ? 'px-4 py-3' : 'p-4'
243299 } `}
244300 >
245- < h2 className = "text-xl font-bold text-primary" >
246- { props . playType == 'againstMaia'
247- ? 'Play Against Maia'
248- : 'Play Hand and Brain' }
249- </ h2 >
250- < p className = "text-xs text-secondary" >
251- { props . playType == 'againstMaia'
252- ? 'Configure your game settings and choose your side'
253- : 'Team up with Maia in Hand and Brain chess' }
254- </ p >
301+ < h2 className = "text-xl font-bold text-primary" > { modalTitle } </ h2 >
302+ < p className = "text-xs text-secondary" > { modalSubtitle } </ p >
255303 </ div >
256304
257305 { /* Settings Section */ }
@@ -484,11 +532,18 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
484532 type = "text"
485533 value = { fen }
486534 placeholder = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
535+ readOnly = { positionLocked }
487536 onChange = { ( e ) => setFen ( e . target . value ) }
488- className = "w-full rounded border border-glass-border bg-glass px-3 py-2 font-mono text-xs text-white/90 placeholder-white/60 focus:outline-none"
537+ className = { `w-full rounded border border-glass-border px-3 py-2 font-mono text-xs placeholder-white/60 focus:outline-none ${
538+ positionLocked
539+ ? 'bg-glass text-white/90'
540+ : 'bg-glass text-white/90'
541+ } `}
489542 />
490543 < p className = "mt-1 text-xs text-secondary" >
491- Enter a valid FEN string to start from a specific position
544+ { positionLocked
545+ ? 'This challenge uses a fixed starting position.'
546+ : 'Enter a valid FEN string to start from a specific position' }
492547 </ p >
493548 </ div >
494549 ) }
@@ -508,11 +563,22 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
508563 >
509564 Choose your color:
510565 </ p >
566+ { colorSelectionLocked ? (
567+ < p className = "mb-3 text-center text-xs text-secondary" >
568+ This challenge starts with{ ' ' }
569+ { forcedPlayerColor === 'white' ? 'White' : 'Black' } to move.
570+ </ p >
571+ ) : null }
511572 < div className = "flex items-center justify-center gap-4" >
512573 < button
513574 onClick = { ( ) => start ( 'black' ) }
514575 title = "Play as black"
515- className = "flex h-16 w-16 cursor-pointer items-center justify-center rounded border border-glass-border bg-glass transition-colors hover:bg-glass-stronger"
576+ disabled = { colorSelectionLocked && forcedPlayerColor !== 'black' }
577+ className = { `flex h-16 w-16 items-center justify-center rounded border border-glass-border transition-colors ${
578+ colorSelectionLocked && forcedPlayerColor !== 'black'
579+ ? 'cursor-not-allowed bg-glass/40 opacity-40'
580+ : 'cursor-pointer bg-glass hover:bg-glass-stronger'
581+ } `}
516582 >
517583 < div className = "relative h-10 w-10" >
518584 < Image
@@ -525,7 +591,12 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
525591 < button
526592 onClick = { ( ) => start ( undefined ) }
527593 title = "Play as random color"
528- className = "flex h-20 w-20 cursor-pointer items-center justify-center rounded border border-glass-border bg-glass transition-colors hover:bg-glass-stronger"
594+ disabled = { colorSelectionLocked }
595+ className = { `flex h-20 w-20 items-center justify-center rounded border border-glass-border transition-colors ${
596+ colorSelectionLocked
597+ ? 'cursor-not-allowed bg-glass/40 opacity-40'
598+ : 'cursor-pointer bg-glass hover:bg-glass-stronger'
599+ } `}
529600 >
530601 < div className = "relative h-12 w-12" >
531602 < Image
@@ -538,7 +609,12 @@ export const PlaySetupModal: React.FC<Props> = (props: Props) => {
538609 < button
539610 onClick = { ( ) => start ( 'white' ) }
540611 title = "Play as white"
541- className = "flex h-16 w-16 cursor-pointer items-center justify-center rounded border border-glass-border bg-glass transition-colors hover:bg-glass-stronger"
612+ disabled = { colorSelectionLocked && forcedPlayerColor !== 'white' }
613+ className = { `flex h-16 w-16 items-center justify-center rounded border border-glass-border transition-colors ${
614+ colorSelectionLocked && forcedPlayerColor !== 'white'
615+ ? 'cursor-not-allowed bg-glass/40 opacity-40'
616+ : 'cursor-pointer bg-glass hover:bg-glass-stronger'
617+ } `}
542618 >
543619 < div className = "relative h-10 w-10" >
544620 < Image
0 commit comments