diff --git a/apps/web/src/app/games/page.tsx b/apps/web/src/app/games/page.tsx index 776c456b..18c1cb69 100644 --- a/apps/web/src/app/games/page.tsx +++ b/apps/web/src/app/games/page.tsx @@ -6,6 +6,7 @@ import { useRouter } from 'next/navigation' import { useTranslations } from 'next-intl' import React, { useCallback, useEffect, useState } from 'react' import { PageWithNav } from '@/components/PageWithNav' +import { getAvailableGames } from '@/lib/arcade/game-registry' import { css } from '../../../styled-system/css' import { useFullscreen } from '../../contexts/FullscreenContext' import { useGameMode } from '../../contexts/GameModeContext' @@ -25,10 +26,21 @@ function GamesPageContent() { return aTime - bTime }) + // Get available games + const availableGames = getAvailableGames() + // Check if user has any stats to show const hasStats = profile.gamesPlayed > 0 - // Embla carousel setup for simple carousel + // Embla carousel setup for games hero carousel + const [gamesEmblaRef, gamesEmblaApi] = useEmblaCarousel({ + loop: true, + align: 'center', + containScroll: 'trimSnaps', + }) + const [gamesSelectedIndex, setGamesSelectedIndex] = useState(0) + + // Embla carousel setup for player carousel const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, align: 'center', @@ -36,6 +48,25 @@ function GamesPageContent() { }) const [selectedIndex, setSelectedIndex] = useState(0) + // Games carousel callbacks + const onGamesSelect = useCallback(() => { + if (!gamesEmblaApi) return + setGamesSelectedIndex(gamesEmblaApi.selectedScrollSnap()) + }, [gamesEmblaApi]) + + useEffect(() => { + if (!gamesEmblaApi) return + onGamesSelect() + gamesEmblaApi.on('select', onGamesSelect) + gamesEmblaApi.on('reInit', onGamesSelect) + + return () => { + gamesEmblaApi.off('select', onGamesSelect) + gamesEmblaApi.off('reInit', onGamesSelect) + } + }, [gamesEmblaApi, onGamesSelect]) + + // Player carousel callbacks const onSelect = useCallback(() => { if (!emblaApi) return setSelectedIndex(emblaApi.selectedScrollSnap()) @@ -85,6 +116,238 @@ function GamesPageContent() { position: 'relative', })} > + {/* Games Hero Carousel */} +
+

+ 🎮 Available Games +

+ + {/* Carousel */} +
+
+ {availableGames.map((game) => { + const gameIndex = availableGames.indexOf(game) + const isActive = gameIndex === gamesSelectedIndex + + return ( +
+ +
+ {/* Dark gradient overlay for readability */} +
+ + {/* Content */} +
+ {/* Icon and Title */} +
+
+ {game.manifest.icon} +
+
+

+ {game.manifest.displayName} +

+

+ {game.manifest.difficulty} •{' '} + {game.manifest.maxPlayers === 1 + ? 'Solo' + : `1-${game.manifest.maxPlayers} Players`} +

+
+
+ + {/* Description */} +

+ {game.manifest.description} +

+ + {/* Chips */} +
+ {game.manifest.chips.map((chip) => ( + + {chip} + + ))} +
+
+
+ +
+ ) + })} +
+
+ + {/* Navigation Dots */} +
+ {availableGames.map((game, index) => ( + + ))} +
+
+ {/* Enter Arcade Button */}