import type { LoaderFunctionArgs, MetaFunction } from "react-router"; import { useLoaderData } from "react-router"; import { ensureUserLoggedIn } from "../.server/auth"; import { createApiClient } from "../api/client"; import type { components } from "../api/schema"; import BorderedContainer from "../components/BorderedContainer"; import UserIcon from "../components/UserIcon"; export const meta: MetaFunction = () => [ { title: "Tournament | iOSDC Japan 2025 Albatross" }, ]; export async function loader({ request }: LoaderFunctionArgs) { const { token } = await ensureUserLoggedIn(request); const apiClient = createApiClient(token); const url = new URL(request.url); const game1Param = url.searchParams.get("game1"); const game2Param = url.searchParams.get("game2"); const game3Param = url.searchParams.get("game3"); const game4Param = url.searchParams.get("game4"); const game5Param = url.searchParams.get("game5"); const player1Param = url.searchParams.get("player1"); const player2Param = url.searchParams.get("player2"); const player3Param = url.searchParams.get("player3"); const player4Param = url.searchParams.get("player4"); const player5Param = url.searchParams.get("player5"); const player6Param = url.searchParams.get("player6"); if (!game1Param || !game2Param || !game3Param || !game4Param || !game5Param) { throw new Response( "Missing required query parameters: game1, game2, game3, game4, game5", { status: 400, }, ); } if ( !player1Param || !player2Param || !player3Param || !player4Param || !player5Param || !player6Param ) { throw new Response( "Missing required query parameters: player1, player2, player3, player4, player5, player6", { status: 400, }, ); } const game1 = Number(game1Param); const game2 = Number(game2Param); const game3 = Number(game3Param); const game4 = Number(game4Param); const game5 = Number(game5Param); if (!game1 || !game2 || !game3 || !game4 || !game5) { throw new Response("Invalid game IDs: must be positive integers", { status: 400, }); } const { tournament } = await apiClient.getTournament( game1, game2, game3, game4, game5, ); return { tournament, playerIDs: [ Number(player1Param), Number(player2Param), Number(player3Param), Number(player4Param), Number(player5Param), Number(player6Param), ], }; } type TournamentMatch = components["schemas"]["TournamentMatch"]; type User = components["schemas"]["User"]; function Player({ player, rank }: { player: User | null; rank: number }) { return (
予選 {rank} 位 {player?.display_name} {player?.icon_path && ( )}
); } function BranchVL({ className = "" }: { className?: string }) { return (
); } function BranchVR({ className = "" }: { className?: string }) { return (
); } function BranchVL2({ score, className = "", }: { score: number | null; className?: string }) { return (
{score}
); } function BranchVR2({ score, className = "", }: { score: number | null; className?: string }) { return (
{score}
); } function BranchV3({ className = "" }: { className?: string }) { return
; } function BranchH({ score, className1, className2, className3, }: { score?: number | null; className1: string; className2: string; className3: string; }) { return (
{score}
); } function BranchH2({ score, className1, className2, className3, }: { score?: number | null; className1: string; className2: string; className3: string; }) { return (
{score}
); } function BranchL({ score, className = "", }: { score: number | null; className?: string }) { return (
{score}
); } function BranchR({ score, className = "", }: { score: number | null; className?: string }) { return (
{score}
); } function BranchL2({ className = "" }: { className?: string }) { return (
); } function BranchR2({ className = "" }: { className?: string }) { return (
); } function getPlayer(match: TournamentMatch, playerID: number): User | null { if (match.player1?.user_id === playerID) return match.player1; else if (match.player2?.user_id === playerID) return match.player2; else return null; } function getScore(match: TournamentMatch, playerIDs: number[]): number | null { if (match.player1 && playerIDs.includes(match.player1.user_id)) return match.player1_score ?? null; if (match.player2 && playerIDs.includes(match.player2.user_id)) return match.player2_score ?? null; else return null; } function getBorderColor(match: TournamentMatch, playerIDs: number[]): string { if (!match.winner) { return "border-black"; } else if (playerIDs.includes(match.winner)) { return "border-pink-700"; } else { return "border-gray-400"; } } export default function Tournament() { const { tournament, playerIDs } = useLoaderData(); const match1 = tournament.matches[0]!; const match2 = tournament.matches[1]!; const match3 = tournament.matches[2]!; const match4 = tournament.matches[3]!; const match5 = tournament.matches[4]!; const playerID1 = playerIDs[0]!; const playerID2 = playerIDs[1]!; const playerID3 = playerIDs[2]!; const playerID4 = playerIDs[3]!; const playerID5 = playerIDs[4]!; const playerID6 = playerIDs[5]!; const player5 = getPlayer(match1, playerID5); const player4 = getPlayer(match1, playerID4); const player3 = getPlayer(match2, playerID3); const player6 = getPlayer(match2, playerID6); const player1 = getPlayer(match3, playerID1); const player2 = getPlayer(match4, playerID2); return (

iOSDC Japan 2025 Swift Code Battle

); }