https://checkmate.social
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Wire MoveDrawer into GameScreen and switch breakpoint to md

Mobile (<768px) now uses the MoveDrawerPanel triggered from the bottom
PlayerBar; desktop (>=768px) keeps the side-by-side sidebar layout.
Outer padding reduced to p-2 on mobile so the board reclaims ~16px of
horizontal space on small phones. Drawer auto-closes when activeGame.id
changes to avoid a stale-open state across games.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

jcalabro bd66874e 55110e6a

+34 -7
+34 -7
client/src/components/game/GameScreen.tsx
··· 23 23 import { ChessBoard } from './ChessBoard'; 24 24 import { ClockDisplay } from './ClockDisplay'; 25 25 import { MoveList } from './MoveList'; 26 + import { MoveDrawerPanel, MoveDrawerTrigger } from './MoveDrawer'; 26 27 import { PlayerBar } from './PlayerBar'; 27 28 import { GameStatus } from './GameStatus'; 28 29 ··· 35 36 const { displayName, avatarUrl, handle } = useAuth(); 36 37 const { activeGame, moves, players, makeMove, resignGame, claimTimeoutVictory } = useGame(); 37 38 const [showResignConfirm, setShowResignConfirm] = useState(false); 39 + const [moveDrawerOpen, setMoveDrawerOpen] = useState(false); 40 + 41 + // Auto-close the drawer when the active game changes so a stale "open" 42 + // state doesn't carry over into the next game. 43 + useEffect(() => { 44 + setMoveDrawerOpen(false); 45 + }, [activeGame?.id]); 38 46 39 47 // Tracks the game ID for which we have already sent a claimTimeoutVictory 40 48 // call, so we don't spam the server while waiting for confirmation. ··· 191 199 <span className="hidden text-xs font-medium text-wood-300 sm:inline">Resign?</span> 192 200 <button 193 201 onClick={handleResign} 194 - className="rounded-md bg-red-700 px-2.5 py-1 text-xs font-semibold uppercase tracking-wide text-white shadow-sm transition-colors hover:bg-red-600" 202 + className="min-h-[44px] rounded-md bg-red-700 px-2.5 py-1 text-xs font-semibold uppercase tracking-wide text-white shadow-sm transition-colors hover:bg-red-600" 195 203 aria-label="Confirm resign" 196 204 > 197 205 Yes 198 206 </button> 199 207 <button 200 208 onClick={() => setShowResignConfirm(false)} 201 - className="rounded-md border border-wood-500 bg-wood-800 px-2.5 py-1 text-xs font-medium text-wood-200 transition-colors hover:border-wood-300 hover:text-wood-100" 209 + className="min-h-[44px] rounded-md border border-wood-500 bg-wood-800 px-2.5 py-1 text-xs font-medium text-wood-200 transition-colors hover:border-wood-300 hover:text-wood-100" 202 210 aria-label="Cancel resign" 203 211 > 204 212 No ··· 207 215 ) : ( 208 216 <button 209 217 onClick={() => setShowResignConfirm(true)} 210 - className="rounded-md border border-red-900/70 bg-wood-900 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-red-300 shadow-sm transition-colors hover:border-red-600 hover:bg-red-900/50 hover:text-red-100" 218 + className="min-h-[44px] rounded-md border border-red-900/70 bg-wood-900 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-red-300 shadow-sm transition-colors hover:border-red-600 hover:bg-red-900/50 hover:text-red-100" 211 219 > 212 220 Resign 213 221 </button> 214 222 ) 215 223 ) : undefined; 216 224 225 + // Bottom-bar action: moves trigger (mobile-only via md:hidden in MoveDrawerTrigger) 226 + // followed by the resign control. Wrapped in a flex group so they align nicely. 227 + const bottomBarAction = activeGame.status === 'active' ? ( 228 + <div className="flex items-center gap-2"> 229 + <MoveDrawerTrigger 230 + onOpen={() => setMoveDrawerOpen(true)} 231 + count={moves.length} 232 + /> 233 + {resignAction} 234 + </div> 235 + ) : undefined; 236 + 217 237 return ( 218 - <div className="flex flex-1 flex-col items-center justify-center gap-4 p-4 lg:flex-row"> 238 + <div className="flex flex-1 flex-col items-center justify-center gap-0 p-2 md:flex-row md:gap-4 md:p-4"> 219 239 {/* Board + player bars column */} 220 240 <div className="relative flex w-full max-w-[600px] flex-col gap-2"> 221 241 {/* Opponent (top) */} ··· 257 277 isActive={bottomIsActive} 258 278 color={bottomColor} 259 279 clock={bottomClock} 260 - action={resignAction} 280 + action={bottomBarAction} 261 281 /> 262 282 </div> 263 283 264 - {/* Move list sidebar */} 265 - <div className="h-32 w-full overflow-hidden rounded-xl border border-wood-600 bg-wood-800 p-3 shadow-lg lg:h-[500px] lg:w-56"> 284 + {/* Move list sidebar — desktop only; mobile uses MoveDrawerPanel below */} 285 + <div className="hidden h-[500px] w-56 overflow-hidden rounded-xl border border-wood-600 bg-wood-800 p-3 shadow-lg md:block"> 266 286 <h3 className="mb-2 text-xs font-medium uppercase tracking-widest text-wood-500"> 267 287 Moves 268 288 </h3> ··· 270 290 <MoveList moves={moves} /> 271 291 </div> 272 292 </div> 293 + 294 + {/* Move drawer — mobile only; renders nothing when closed */} 295 + <MoveDrawerPanel 296 + open={moveDrawerOpen} 297 + onClose={() => setMoveDrawerOpen(false)} 298 + moves={moves} 299 + /> 273 300 </div> 274 301 ); 275 302 }