Skip to content

Commit 6d20c3b

Browse files
feat: clean up carousel component
1 parent 1e00ffe commit 6d20c3b

3 files changed

Lines changed: 42 additions & 99 deletions

File tree

src/components/Home/LiveChessBoardShowcase.tsx renamed to src/components/Home/GameCarousel.tsx

Lines changed: 40 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ interface GameData {
2222
url?: string
2323
}
2424

25-
// Sample predefined games
2625
const SAMPLE_GAMES: GameData[] = [
2726
{
2827
id: 'sample1',
@@ -109,7 +108,6 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
109108
}
110109
}
111110

112-
// Preset rotation options
113111
const rotationOptions = [
114112
'rotate(-2deg)',
115113
'rotate(-1deg)',
@@ -118,17 +116,14 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
118116
'rotate(2deg)',
119117
]
120118

121-
// Chess icon options
122119
const chessIcons = [
123120
'chess_knight',
124121
'chess_bishop',
125-
'chess_king',
126122
'chess_rook',
127123
'chess_pawn',
128124
'chess',
129125
]
130126

131-
// Select rotation and icon based on game ID hash
132127
const hash = (() => {
133128
let h = 0
134129
for (let i = 0; i < game.id.length; i++) {
@@ -140,7 +135,7 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
140135
const rotation = rotationOptions[hash % rotationOptions.length]
141136
const chessIcon = chessIcons[hash % chessIcons.length]
142137

143-
const truncateName = (name: string, maxLength = 12) => {
138+
const truncateName = (name: string, maxLength = 15) => {
144139
return name.length > maxLength
145140
? name.substring(0, maxLength - 1) + '…'
146141
: name
@@ -162,52 +157,48 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
162157
className="group relative flex h-14 min-w-48 max-w-48 cursor-pointer flex-row items-center gap-3 rounded bg-white/[3%] px-4 py-2 backdrop-blur-sm transition-all duration-200 hover:bg-white/[6%] focus:outline-none focus:ring-2 focus:ring-white/20"
163158
style={{ transform: rotation }}
164159
>
165-
{/* Live Indicator */}
166160
{game.isLive && (
167161
<div className="absolute -right-1 -top-1 flex items-center gap-1 rounded-full bg-red-500/90 px-2 py-0.5">
168162
<div className="h-1 w-1 animate-pulse rounded-full bg-white" />
169163
<span className="text-[8px] font-semibold text-white">LIVE</span>
170164
</div>
171165
)}
172-
{/* Chess Icon */}
166+
173167
<span className="material-symbols-outlined material-symbols-filled text-2xl text-white/30 transition-all duration-200 group-hover:text-white/60">
174168
{chessIcon}
175169
</span>
176170

177-
{/* Text Content */}
178171
{isBroadcast ? (
179172
<div className="flex flex-1 flex-col justify-center gap-0.5">
180173
<span className="text-[10px] font-medium text-white/30 transition-all duration-200 group-hover:text-white/70">
181174
Broadcast
182175
</span>
183176
<span className="truncate text-xs font-medium text-white/40 transition-all duration-200 group-hover:text-white">
184-
{truncateName(game.black.name, 16)}
177+
{truncateName(game.black.name, 18)}
185178
</span>
186179
</div>
187180
) : (
188181
<div className="flex flex-1 flex-col justify-center gap-0.5">
189182
<span className="truncate text-xs font-medium text-white/40 transition-all duration-200 group-hover:text-white">
190-
{truncateName(game.white.name, 13)}
183+
{truncateName(game.white.name, 16)}
191184
</span>
192185
<span className="truncate text-xs font-medium text-white/40 transition-all duration-200 group-hover:text-white">
193-
vs {truncateName(game.black.name, 10)}
186+
vs {truncateName(game.black.name, 14)}
194187
</span>
195188
</div>
196189
)}
197190
</div>
198191
)
199192
}
200193

201-
export const LiveChessBoardShowcase: React.FC = () => {
194+
export const GameCarousel: React.FC = () => {
202195
const router = useRouter()
203-
const [error, setError] = useState<string | null>(null)
204196
const [games, setGames] = useState<GameData[]>(SAMPLE_GAMES)
205197
const [isPaused, setIsPaused] = useState(false)
206198
const abortController = useRef<AbortController | null>(null)
207199
const carouselRef = useRef<HTMLDivElement>(null)
208200

209201
const handleGameStart = useCallback((gameData: StreamedGame) => {
210-
// Update the games list with live game data
211202
setGames((prevGames) => {
212203
const newGames = [...prevGames]
213204
const liveGameData: GameData = {
@@ -224,15 +215,13 @@ export const LiveChessBoardShowcase: React.FC = () => {
224215
url: `/analysis/stream/${gameData.id}`,
225216
}
226217

227-
// Check if this live game already exists
228218
const existingLiveIndex = newGames.findIndex(
229219
(g) => g.id === `live-${gameData.id}`,
230220
)
221+
231222
if (existingLiveIndex !== -1) {
232-
// Update existing live game
233223
newGames[existingLiveIndex] = liveGameData
234224
} else {
235-
// Replace a sample game at index 2 (3rd position) with live game data
236225
const targetIndex = 2
237226
if (targetIndex < newGames.length && !newGames[targetIndex].isLive) {
238227
newGames[targetIndex] = liveGameData
@@ -247,21 +236,17 @@ export const LiveChessBoardShowcase: React.FC = () => {
247236
}, [])
248237

249238
const handleStreamComplete = useCallback(() => {
250-
console.log('Live board showcase - Stream completed')
251239
fetchNewGame()
252240
}, [])
253241

254242
const fetchNewGame = useCallback(async () => {
255243
try {
256-
setError(null)
257244
const tvGame = await fetchLichessTVGame()
258245

259-
// Stop current stream if any
260246
if (abortController.current) {
261247
abortController.current.abort()
262248
}
263249

264-
// Start new stream
265250
abortController.current = new AbortController()
266251

267252
streamLichessGameMoves(
@@ -272,13 +257,11 @@ export const LiveChessBoardShowcase: React.FC = () => {
272257
abortController.current.signal,
273258
).catch((err) => {
274259
if (err.name !== 'AbortError') {
275-
console.error('Live board streaming error:', err)
276-
setError('Connection lost')
260+
console.error('Live game streaming error:', err)
277261
}
278262
})
279263
} catch (err) {
280-
console.error('Error fetching new live game:', err)
281-
setError('Failed to load live game')
264+
console.error('Error fetching live game:', err)
282265
}
283266
}, [handleGameStart, handleMove, handleStreamComplete])
284267

@@ -291,7 +274,6 @@ export const LiveChessBoardShowcase: React.FC = () => {
291274
topBroadcasts.active[0],
292275
)
293276

294-
// Add broadcast to games list
295277
setGames((prevGames) => {
296278
const newGames = [...prevGames]
297279
const broadcastGameData: GameData = {
@@ -308,15 +290,13 @@ export const LiveChessBoardShowcase: React.FC = () => {
308290
url: `/broadcast/${broadcastData.tour.id}/${broadcastData.rounds[0]?.id}`,
309291
}
310292

311-
// Check if this broadcast already exists
312293
const existingBroadcastIndex = newGames.findIndex(
313294
(g) => g.id === `broadcast-${broadcastData.tour.id}`,
314295
)
296+
315297
if (existingBroadcastIndex !== -1) {
316-
// Update existing broadcast
317298
newGames[existingBroadcastIndex] = broadcastGameData
318299
} else {
319-
// Replace a sample game at index 7 (8th position) with broadcast data
320300
const targetIndex = 7
321301
if (
322302
targetIndex < newGames.length &&
@@ -333,7 +313,6 @@ export const LiveChessBoardShowcase: React.FC = () => {
333313
}
334314
}, [])
335315

336-
// Auto-scroll effect
337316
useEffect(() => {
338317
if (isPaused || !carouselRef.current) return
339318

@@ -343,10 +322,8 @@ export const LiveChessBoardShowcase: React.FC = () => {
343322
const halfWidth = scrollWidth / 2
344323

345324
if (scrollLeft >= halfWidth - 10) {
346-
// Reset to beginning without animation when we reach the duplicate section
347325
carouselRef.current.scrollTo({ left: 0, behavior: 'auto' })
348326
} else {
349-
// Smooth continuous scroll
350327
carouselRef.current.scrollBy({ left: 0.5, behavior: 'auto' })
351328
}
352329
}
@@ -357,11 +334,9 @@ export const LiveChessBoardShowcase: React.FC = () => {
357334
}, [isPaused])
358335

359336
useEffect(() => {
360-
// Initial fetch
361337
fetchNewGame()
362338
fetchBroadcast()
363339

364-
// Cleanup on unmount
365340
return () => {
366341
if (abortController.current) {
367342
abortController.current.abort()
@@ -379,58 +354,44 @@ export const LiveChessBoardShowcase: React.FC = () => {
379354
)
380355

381356
return (
382-
<div className="relative mb-10 w-full overflow-y-visible">
383-
<motion.div
384-
ref={carouselRef}
385-
className="flex gap-10 overflow-x-hidden overflow-y-visible py-2 pl-4"
386-
initial={{ opacity: 0 }}
387-
animate={{ opacity: 1 }}
388-
transition={{ duration: 0.5 }}
389-
onMouseEnter={() => setIsPaused(true)}
390-
onMouseLeave={() => setIsPaused(false)}
391-
style={{
392-
scrollbarWidth: 'none',
393-
msOverflowStyle: 'none',
394-
}}
395-
>
396-
{/* Render games twice for seamless loop */}
397-
{[...games, ...games].map((game, index) => (
398-
<GameChip
399-
key={`${game.id}-${index}`}
400-
game={game}
401-
onClick={() => handleGameClick(game)}
402-
/>
403-
))}
404-
</motion.div>
405-
406-
{error && (
407-
<div className="mt-2 text-center">
408-
<p className="text-xs text-red-400">{error}</p>
409-
<button
410-
onClick={fetchNewGame}
411-
className="mt-1 text-[10px] font-medium text-white/60 hover:text-white/80 hover:underline"
412-
>
413-
Retry
414-
</button>
415-
</div>
416-
)}
417-
</div>
357+
<section className="relative w-full overflow-y-visible py-6">
358+
<div className="relative mb-10 w-full overflow-y-visible">
359+
<motion.div
360+
ref={carouselRef}
361+
className="flex gap-10 overflow-x-hidden overflow-y-visible py-2 pl-4"
362+
initial={{ opacity: 0 }}
363+
animate={{ opacity: 1 }}
364+
transition={{ duration: 0.5 }}
365+
onMouseEnter={() => setIsPaused(true)}
366+
onMouseLeave={() => setIsPaused(false)}
367+
style={{
368+
scrollbarWidth: 'none',
369+
msOverflowStyle: 'none',
370+
}}
371+
>
372+
{[...games, ...games].map((game, index) => (
373+
<GameChip
374+
key={`${game.id}-${index}`}
375+
game={game}
376+
onClick={() => handleGameClick(game)}
377+
/>
378+
))}
379+
</motion.div>
380+
</div>
381+
</section>
418382
)
419383
}
420384

421-
// Hide scrollbar for webkit browsers
422-
const styles = `
423-
.carousel-container::-webkit-scrollbar {
424-
display: none;
425-
}
426-
`
427-
428385
if (
429386
typeof document !== 'undefined' &&
430387
!document.getElementById('carousel-styles')
431388
) {
432389
const styleSheet = document.createElement('style')
433390
styleSheet.id = 'carousel-styles'
434-
styleSheet.innerText = styles
391+
styleSheet.innerText = `
392+
.carousel-container::-webkit-scrollbar {
393+
display: none;
394+
}
395+
`
435396
document.head.appendChild(styleSheet)
436397
}

src/components/Home/LiveChessShowcase.tsx

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/pages/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
AdditionalFeaturesSection,
1313
PageNavigation,
1414
} from 'src/components'
15-
import { LiveChessShowcase } from 'src/components/Home/LiveChessShowcase'
15+
import { GameCarousel } from 'src/components/Home/GameCarousel'
1616

1717
const Home: NextPage = () => {
1818
const { setPlaySetupModalProps } = useContext(ModalContext)
@@ -41,7 +41,7 @@ const Home: NextPage = () => {
4141
/>
4242
</Head>
4343
<HomeHero scrollHandler={scrollHandler} />
44-
<LiveChessShowcase />
44+
<GameCarousel />
4545
<PageNavigation />
4646
<div ref={featuresRef}>
4747
<div className="bg-background-1">

0 commit comments

Comments
 (0)