'use client' import { cn } from '@/lib/utils' import type { SelectGames } from '@/server/db/types' import type { LeaderboardEntry } from '@/server/services/neatqueue.service' import { RANKED_CHANNEL } from '@/shared/constants' import { api } from '@/trpc/react' import { Swords } from 'lucide-react' import { useParams } from 'next/navigation' import { type ComponentPropsWithoutRef, useEffect, useState } from 'react' function getPlayerData( playerLeaderboardEntry: LeaderboardEntry, games: SelectGames[] ) { const filteredGamesByLeaderboard = games.filter( (game) => game.gameType === 'ranked' ) const games_played = filteredGamesByLeaderboard.length let wins = 0 let losses = 0 let ties = 0 for (const game of filteredGamesByLeaderboard) { if (game.result === 'win') { wins++ } else if (game.result === 'loss') { losses++ } else if (game.result === 'tie') { ties++ } else { ties++ } } const lastGame = filteredGamesByLeaderboard.at(0) const currentName = lastGame?.playerName const meaningful_games = games_played - ties return { username: currentName, games: games_played, meaningful_games, wins, losses, ties, winRate: meaningful_games > 0 ? Math.ceil((wins / meaningful_games) * 100) : 0, lossRate: meaningful_games > 0 ? Math.floor((losses / meaningful_games) * 100) : 0, rank: playerLeaderboardEntry.rank, mmr: Math.round(playerLeaderboardEntry.mmr), mmrChangeRaw: lastGame?.mmrChange, mmrChange: `${(lastGame?.mmrChange ?? 0) >= 0 ? '+' : ''}${Math.round( lastGame?.mmrChange ?? 0 )}`, streak: playerLeaderboardEntry?.streak, } } export function StreamCardClient() { const { id } = useParams() if (!id || typeof id !== 'string') { return null } const [gamesQueryResult, gamesQuery] = api.history.user_games.useSuspenseQuery({ user_id: id }) const games = gamesQueryResult || [] const [rankedUserRank, rankedUserQuery] = api.leaderboard.get_user_rank.useSuspenseQuery({ channel_id: RANKED_CHANNEL, user_id: id, }) const result = api.playerState.onStateChange.useSubscription( { userId: id }, { onData: async () => { await Promise.all([gamesQuery.refetch(), rankedUserQuery.refetch()]) }, } ) const playerState = result.data?.data if (!rankedUserRank || !games?.length) { return null } const playerData = getPlayerData(rankedUserRank, games) const isQueuing = playerState?.status === 'queuing' const opponentId = playerState?.currentMatch?.opponentId let winsVsOpponent = 0 let lossesVsOpponent = 0 if (opponentId) { for (const game of games) { if (game.opponentId === opponentId) { if (game.result === 'win') { winsVsOpponent++ } else if (game.result === 'loss') { lossesVsOpponent++ } } } } console.log(winsVsOpponent, lossesVsOpponent) return (
{isQueuing && playerState.queueStartTime && ( )} {opponentId && ( <> {' '} )}
) } export function formatDuration(ms: number): string { const seconds = Math.floor(ms / 1000) const minutes = Math.floor(seconds / 60) const hours = Math.floor(minutes / 60) if (hours > 0) { return `${hours}h ${minutes % 60}m` } if (minutes > 0) { return `${minutes}m ${seconds % 60}s` } return `${seconds}s` } function QueueTimer({ startTime }: { startTime: number }) { const [queueTime, setQueueTime] = useState(Date.now() - startTime) useEffect(() => { const interval = setInterval(() => { setQueueTime(Date.now() - startTime) }, 1000) return () => clearInterval(interval) }) return (
Queueing for
{formatDuration(queueTime)}
) } function Opponent({ id, wins }: { id: string; wins?: number }) { const { data: gamesQueryResult } = api.history.user_games.useQuery({ user_id: id, }) const games = gamesQueryResult || [] const { data: rankedUserRank } = api.leaderboard.get_user_rank.useQuery({ channel_id: RANKED_CHANNEL, user_id: id, }) if (!rankedUserRank || !games?.length) { return null } const playerData = getPlayerData(rankedUserRank, games) return } function PlayerInfo({ playerData, className, children, isReverse = false, isInBattle = false, wins, ...rest }: { playerData: ReturnType isReverse?: boolean isInBattle?: boolean wins?: number } & ComponentPropsWithoutRef<'div'>) { return (
#{playerData.rank}
{/* Player Name */}
{playerData.username}
{/* MMR */}
MMR:
{playerData.mmr}
{playerData.mmrChange}
{/* Win Rate */} {!isInBattle && (
WR:
{playerData.winRate}%
)} {/* Win/Loss */}
{playerData.wins}W
|
{playerData.losses}L
{isInBattle && (
{wins}
)} {/* Streak */} {!isInBattle && (
Streak:
{playerData.streak}
)} {children}
) }