|
1 | 1 | import { Head } from '@inertiajs/react'; |
2 | | -import moment from 'moment-timezone'; |
3 | 2 | import { motion } from 'framer-motion'; |
| 3 | +import moment from 'moment-timezone'; |
| 4 | +import { useMemo, useState } from 'react'; |
4 | 5 | import { Leaderboard } from '@/components/race/leaderboard'; |
5 | 6 | import { LpChart } from '@/components/race/lp-chart'; |
6 | 7 | import { MatchFeed } from '@/components/race/match-feed'; |
7 | 8 | import { RaceHeader } from '@/components/race/race-header'; |
| 9 | +import { TwitchEmbed } from '@/components/twitch-embed'; |
8 | 10 | import { useCountdown } from '@/hooks/use-countdown'; |
9 | 11 | import PublicLayout from '@/layouts/public-layout'; |
10 | | -import type { RaceData } from '@/types/race'; |
| 12 | +import type { LeaderboardRow, RaceData } from '@/types/race'; |
11 | 13 |
|
12 | 14 | interface UpcomingStreamer { |
13 | 15 | id: number; |
@@ -47,12 +49,49 @@ export default function Home({ race, upcoming, last }: Props) { |
47 | 49 | } |
48 | 50 |
|
49 | 51 | function RaceView({ race, isLast = false }: { race: RaceData; isLast?: boolean }) { |
| 52 | + const streamers = useMemo( |
| 53 | + () => race.leaderboard.filter((r) => !!r.stream_url), |
| 54 | + [race.leaderboard], |
| 55 | + ); |
| 56 | + const [activeStream, setActiveStream] = useState<LeaderboardRow | null>(() => { |
| 57 | + if (race.stream_url) return null; // handled separately |
| 58 | + if (streamers.length === 0) return null; |
| 59 | + return streamers[Math.floor(Math.random() * streamers.length)]; |
| 60 | + }); |
| 61 | + |
| 62 | + const embedUrl = race.stream_url ?? activeStream?.stream_url ?? null; |
| 63 | + |
50 | 64 | return ( |
51 | 65 | <div> |
52 | 66 | <RaceHeader race={race} isLast={isLast} /> |
53 | 67 | <div className="mx-auto max-w-5xl px-4 pb-24 pt-4 space-y-10"> |
54 | 68 | <SponsorBanner /> |
| 69 | + |
55 | 70 | <Leaderboard leaderboard={race.leaderboard} matches={race.matches} /> |
| 71 | + |
| 72 | + {embedUrl && ( |
| 73 | + <div className="space-y-0"> |
| 74 | + {/* Tabs — only shown when there is no dedicated race stream */} |
| 75 | + {!race.stream_url && streamers.length > 1 && ( |
| 76 | + <div className="flex gap-1 mb-2 flex-wrap"> |
| 77 | + {streamers.map((s) => ( |
| 78 | + <button |
| 79 | + key={s.streamer_id} |
| 80 | + onClick={() => setActiveStream(s)} |
| 81 | + className={`px-3 py-1 rounded text-xs font-semibold transition-colors ${ |
| 82 | + activeStream?.streamer_id === s.streamer_id |
| 83 | + ? 'bg-cyan-500/20 text-cyan-300 border border-cyan-500/40' |
| 84 | + : 'bg-white/5 text-[#4a4a60] border border-white/5 hover:text-white hover:border-white/20' |
| 85 | + }`} |
| 86 | + > |
| 87 | + {s.name} |
| 88 | + </button> |
| 89 | + ))} |
| 90 | + </div> |
| 91 | + )} |
| 92 | + <TwitchEmbed url={embedUrl} /> |
| 93 | + </div> |
| 94 | + )} |
56 | 95 | <LpChart series={race.lp_series} /> |
57 | 96 | <MatchFeed matches={race.matches} /> |
58 | 97 | </div> |
|
0 commit comments