Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 'use client' import dynamic from 'next/dynamic' import { Component, useEffect, useMemo, useState } from 'react' import type { ReactNode } from 'react' import type { GameComponent, GameProviderComponent } from '@/lib/arcade/game-sdk/types' import { MockArcadeEnvironment } from './MockArcadeEnvironment' import { PreviewModeContext } from '@/contexts/PreviewModeContext' import { ViewportProvider } from '@/contexts/ViewportContext' import { getMockGameState } from './MockGameStates' // Re-export for backwards compatibility export { PreviewModeContext } from '@/contexts/PreviewModeContext' // Dynamic import breaks webpack's import chain, preventing useRoomData // from being bundled with useUserPlayers in shared chunks const GameModeProviderWithHooks = dynamic( () => import('@/contexts/GameModeProviderWithHooks').then((m) => m.GameModeProviderWithHooks), { ssr: false } ) interface GamePreviewProps { GameComponent: GameComponent Provider: GameProviderComponent gameName: string } /** * Error boundary to prevent game errors from crashing the page */ class GameErrorBoundary extends Component< { children: ReactNode; fallback: ReactNode }, { hasError: boolean } > { constructor(props: { children: ReactNode; fallback: ReactNode }) { super(props) this.state = { hasError: false } } static getDerivedStateFromError() { return { hasError: true } } componentDidCatch(error: Error) { console.error(`Game preview error (${error.message}):`, error) } render() { if (this.state.hasError) { return this.props.fallback } return this.props.children } } /** * Wrapper for displaying games in demo/preview mode * Provides mock arcade contexts so games can render */ export function GamePreview({ GameComponent, Provider, gameName }: GamePreviewProps) { // Don't render on first mount to avoid hydration issues const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) // Get mock state for this game const mockState = useMemo(() => getMockGameState(gameName), [gameName]) // Preview mode context value const previewModeValue = useMemo( () => ({ isPreview: true, mockState, }), [mockState] ) if (!mounted) { return null } return ( <GameErrorBoundary fallback={ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'rgba(255, 255, 255, 0.4)', fontSize: '14px', textAlign: 'center', padding: '20px', }} > <span style={{ fontSize: '48px', marginBottom: '10px' }}>🎮</span> Game Demo </div> } > <PreviewModeContext.Provider value={previewModeValue}> <MockArcadeEnvironment gameName={gameName}> <GameModeProviderWithHooks> {/* Mock viewport: Provide 1440x900 dimensions to games via ViewportContext This prevents layout issues when games check viewport size */} <ViewportProvider width={1440} height={900}> <div style={{ width: '1440px', height: '900px', position: 'relative', overflow: 'hidden', }} > <Provider> <GameComponent /> </Provider> </div> </ViewportProvider> </GameModeProviderWithHooks> </MockArcadeEnvironment> </PreviewModeContext.Provider> </GameErrorBoundary> ) } |