import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { motion } from "framer-motion"; import { TauriService } from "../../services/TauriService"; import { lceOnlineService } from "../../services/LceOnlineService"; import type { Edition } from "../../types/edition"; interface GameInvite { inviteId: string; from: string; hostIp: string; hostPort: number; hostName: string; sessionId?: string; status: string; } export default function ChooseInstanceModal({ isOpen, onClose, playPressSound, playBackSound, editions, installs, invite, }: { isOpen: boolean; onClose: () => void; playPressSound: (s?: string) => void; playBackSound: (s?: string) => void; editions: Edition[]; installs: string[]; invite: GameInvite | null; }) { const { t } = useTranslation(); const [selectedInstance, setSelectedInstance] = useState(""); const [status, setStatus] = useState(""); const [error, setError] = useState(""); const [isJoining, setIsJoining] = useState(false); const [focusIndex, setFocusIndex] = useState(0); const validInstances = editions.filter((e: Edition) => installs.includes(e.instanceId), ); useEffect(() => { if (!isOpen) { setSelectedInstance(""); setStatus(""); setError(""); setIsJoining(false); setFocusIndex(0); } }, [isOpen]); useEffect(() => { if (isOpen && validInstances.length > 0 && !selectedInstance) { setSelectedInstance(validInstances[0].instanceId); } }, [isOpen, validInstances, selectedInstance]); const handleJoin = async () => { if (!invite || !selectedInstance) return; playPressSound(); setIsJoining(true); setError(""); setStatus(t("modals.chooseInstance.launching")); try { const sessionId = invite.sessionId || ""; if (sessionId) { setStatus(t("modals.chooseInstance.connectingRelay")); const accessToken = lceOnlineService.accessToken ?? ""; const relayPromise = TauriService.startRelayProxy( accessToken, sessionId, ); setStatus(t("modals.chooseInstance.launching")); await TauriService.launchGame( selectedInstance, [ { name: invite.hostName || "LCEOnline Game", ip: "127.0.0.1", port: 61000, }, ], ["-ip", "127.0.0.1", "-port", "61000", "-quitondisconnect"], ); await relayPromise; } else { await TauriService.stopAllProxies(); await TauriService.launchGame(selectedInstance, [ { name: invite.hostName || "LCEOnline Game", ip: invite.hostIp, port: invite.hostPort, }, ]); } onClose(); } catch (e: unknown) { setError(e instanceof Error ? e.message : String(e)); setStatus(""); setIsJoining(false); } }; useEffect(() => { if (!isOpen) return; const handleKey = (e: KeyboardEvent) => { if (e.key === "Escape") { playBackSound(); if (isJoining) return; onClose(); } else if (e.key === "ArrowDown" || e.key === "Tab") { e.preventDefault(); const max = 1 + (validInstances.length > 0 ? 1 : 0) + 1; setFocusIndex((prev) => (prev + 1) % max); } else if (e.key === "ArrowUp") { e.preventDefault(); const max = 1 + (validInstances.length > 0 ? 1 : 0) + 1; setFocusIndex((prev) => (prev - 1 + max) % max); } else if (e.key === "Enter") { if (focusIndex === 0 && validInstances.length > 0) { const currentIdx = validInstances.findIndex( (i: Edition) => i.instanceId === selectedInstance, ); const next = (currentIdx + 1) % validInstances.length; setSelectedInstance(validInstances[next].instanceId); playPressSound(); } else if (focusIndex === 1 && !isJoining) { handleJoin(); } else if ( focusIndex === (validInstances.length > 0 ? 2 : 1) && !isJoining ) { onClose(); } } }; window.addEventListener("keydown", handleKey); return () => window.removeEventListener("keydown", handleKey); }, [isOpen, focusIndex, selectedInstance, validInstances, isJoining]); if (!isOpen) return null; const hostName = invite ? typeof invite.from === "string" ? invite.from : invite.hostName : ""; return (
{!isJoining ? ( <>

{t("modals.chooseInstance.title")}

{t("modals.chooseInstance.joinPrompt", { name: hostName, })}

{validInstances.length > 0 ? (
{validInstances.map((inst: Edition) => { const isSelected = selectedInstance === inst.instanceId; return (
{ playPressSound(); setSelectedInstance(inst.instanceId); }} onMouseEnter={() => setFocusIndex(0)} className={`w-full px-4 py-3 cursor-pointer flex items-center gap-3 transition-all outline-none border-none ${isSelected ? "bg-white/15 border-l-4 border-[#FFFF55]" : "bg-black/20 hover:bg-black/30 border-l-4 border-transparent"}`} style={{ imageRendering: "pixelated" }} >
{isSelected && (
)}
{inst.name} {inst.selectedBranch && ( {inst.selectedBranch} )}
); })}
) : (

{t("modals.chooseInstance.noInstances")}

)} {error && (
{error}
)}
{validInstances.length > 0 && ( )}
) : ( <>

{t("modals.chooseInstance.joiningTitle")}

{status}

{error && (
{error}
)} )}
); }