import { useState, useEffect } from "react"; import { TauriService, Runner } from "../../services/TauriService"; import { usePlatform } from "../../hooks/usePlatform"; import { useConfig, useAudio } from "../../context/LauncherContext"; interface SetupViewProps { onComplete: () => void; } const SetupView: React.FC = ({ onComplete }) => { const { isLinux, isMac, isAndroid } = usePlatform(); const { username, setUsername, setHasCompletedSetup, setRpcEnabled: setConfigRpc, setLinuxRunner, linuxRunner: configLinuxRunner, rpcEnabled: configRpc, animationsEnabled, } = useConfig(); const { playPressSound, playSfx } = useAudio(); const titleImage = "/images/emerald_launcher.png"; const [currentStep, setCurrentStep] = useState(0); const [focusIndex, setFocusIndex] = useState(0); const [tempUsername, setTempUsername] = useState(username); const [runners, setRunners] = useState([]); const [selectedRunner, setSelectedRunner] = useState(""); const [isSettingUpRuntime, setIsSettingUpRuntime] = useState(false); const [setupProgress, setSetupProgress] = useState<{ stage: string; message: string; percent?: number; } | null>(null); const [runtimeAlreadyInstalled, setRuntimeAlreadyInstalled] = useState(false); const [enableDiscordRPC, setEnableDiscordRPC] = useState(configRpc); const totalSteps = 4; useEffect(() => { if (isLinux || isMac) { TauriService.getAvailableRunners().then((availableRunners) => { setRunners(availableRunners); if ( configLinuxRunner && availableRunners.find((r) => r.id === configLinuxRunner) ) { setSelectedRunner(configLinuxRunner); } }); } if (isMac) { checkMacOSRuntime(); const unlisten = TauriService.onMacosProgress((progress) => { setSetupProgress(progress); }); return () => { unlisten.then((f) => f?.()); }; } }, [isLinux, isMac]); const checkMacOSRuntime = async () => { try { const runtimeCheck = await TauriService.checkMacOSRuntimeInstalledFast(); setRuntimeAlreadyInstalled(runtimeCheck); if (runtimeCheck) { localStorage.setItem("lce-macos-runtime-installed", "true"); } else { localStorage.removeItem("lce-macos-runtime-installed"); } } catch { setRuntimeAlreadyInstalled(false); localStorage.removeItem("lce-macos-runtime-installed"); } }; const handleRunnerSelect = (runnerId: string) => { playPressSound(); setSelectedRunner(runnerId); }; const handleNext = async () => { playPressSound(); if (currentStep === 0) { setUsername(tempUsername); setCurrentStep(1); setFocusIndex(0); } else if (currentStep === 1) { if (isLinux && selectedRunner) setLinuxRunner(selectedRunner); setCurrentStep(2); setFocusIndex(0); } else if (currentStep === 2) { if (!isAndroid) setConfigRpc(enableDiscordRPC); setCurrentStep(3); setFocusIndex(0); } else if (currentStep === 3) { playSfx("levelup.ogg"); setHasCompletedSetup(true); onComplete(); } }; const handleBack = () => { playPressSound(); if (currentStep > 0) { setCurrentStep(currentStep - 1); setFocusIndex(0); } }; useEffect(() => { const handleKey = (e: KeyboardEvent) => { let count = 0; if (currentStep === 0) count = 2; else if (currentStep === 1) { if (isLinux) count = runners.length + 2; else if (isMac) count = 3; else count = 2; } else if (currentStep === 2) count = isAndroid ? 3 : 4; else if (currentStep === 3) count = 2; if (e.key === "ArrowDown" || e.key === "Tab") { e.preventDefault(); setFocusIndex((prev) => (prev + 1) % count); } else if (e.key === "ArrowUp") { e.preventDefault(); setFocusIndex((prev) => (prev - 1 + count) % count); } else if (e.key === "Enter") { if (currentStep === 0) { if (focusIndex === 0) handleNext(); else if (focusIndex === 1) handleNext(); } else if (currentStep === 1) { if (isLinux) { if (focusIndex < runners.length) handleRunnerSelect(runners[focusIndex].id); else if (focusIndex === runners.length) handleBack(); else if (focusIndex === runners.length + 1) handleNext(); } else if (isMac) { if (focusIndex === 0) handleMacosSetup(); else if (focusIndex === 1) handleBack(); else if (focusIndex === 2) handleNext(); } else { if (focusIndex === 0) handleBack(); else if (focusIndex === 1) handleNext(); } } else if (currentStep === 2) { if (isAndroid) { if (focusIndex === 0) { playPressSound(); } else if (focusIndex === 1) handleBack(); else if (focusIndex === 2) handleNext(); } else { if (focusIndex === 0) { setEnableDiscordRPC(!enableDiscordRPC); playPressSound(); } else if (focusIndex === 1) { playPressSound(); } else if (focusIndex === 2) handleBack(); else if (focusIndex === 3) handleNext(); } } else if (currentStep === 3) { if (focusIndex === 0) handleBack(); else if (focusIndex === 1) handleNext(); } } }; window.addEventListener("keydown", handleKey); return () => window.removeEventListener("keydown", handleKey); }, [ currentStep, focusIndex, runners, enableDiscordRPC, isLinux, isMac, isAndroid, tempUsername, ]); const handleMacosSetup = async () => { playPressSound(); setIsSettingUpRuntime(true); setSetupProgress({ stage: "preparing", message: "Preparing macOS runtime setup...", percent: 0, }); try { await TauriService.setupMacosRuntime(); setSetupProgress({ stage: "completed", message: "Setup completed successfully!", percent: 100, }); localStorage.setItem("lce-macos-runtime-installed", "true"); setRuntimeAlreadyInstalled(true); setTimeout(() => { setCurrentStep(2); setIsSettingUpRuntime(false); setSetupProgress(null); }, 2000); } catch (e) { setSetupProgress({ stage: "error", message: `Setup failed: ${e}`, percent: 0, }); setIsSettingUpRuntime(false); } }; const canProceed = () => { if (currentStep === 0) return tempUsername.trim().length > 0; if (currentStep === 1 && isMac) return runtimeAlreadyInstalled; return true; }; const navBtnStyle = (isFocused: boolean) => ({ backgroundImage: isFocused ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')", backgroundSize: "100% 100%", imageRendering: "pixelated" as const, }); return (
Emerald Legacy
{currentStep === 0 && (

Let's configure your launcher

)} {currentStep === 1 && (

Compatibility Runtime

)} {currentStep === 2 && (

Choose your preferred options and behaviors

)}
{currentStep === 0 && (
{tempUsername.trim().length === 0 && (

A username is required to continue

)}
)} {currentStep === 1 && isMac && (
{runtimeAlreadyInstalled ? ( checked ) : ( ⚠ )}

{runtimeAlreadyInstalled ? "Runtime Detected" : "Runtime Not Detected"}

{runtimeAlreadyInstalled ? "Game Porting Toolkit 3 and Wine installed" : "You must install the runtime before proceeding."}

{setupProgress && (

{setupProgress.stage}

{setupProgress.message}

{setupProgress.percent !== undefined && (
)}
)}
)} {currentStep === 1 && isLinux && (
{runners.length === 0 ? (

No compatible runners found. Please install Wine or Proton.

) : (
{runners.map((runner, idx) => ( ))}
)}

You can change this later in settings

)} {currentStep === 1 && !isMac && !isLinux && !isAndroid && (
✓

Windows Native Support

Emerald Legacy runs natively on Windows without additional requirements.

)} {currentStep === 1 && isAndroid && (
✓

DiamondRuntime Support

Emerald Launcher runs natively on Android without additional requirements. Powered by our own DiamondRuntime.

)} {currentStep === 2 && (
{!isAndroid && ( )}

You can change these later in settings

)} {currentStep === 3 && (

Emerald Launcher is now configured and ready to use!

Username {tempUsername}
{isMac && (
Runtime Ready
)} {isLinux && selectedRunner && (
Runner {runners.find((r) => r.id === selectedRunner)?.name}
)}
Animations
checkbox {animationsEnabled && ( checked )}
{!isAndroid && (
Discord RPC
checkbox {enableDiscordRPC && ( checked )}
)}
)}
{currentStep > 0 ? ( ) : (
)}
); }; export default SetupView;