import { useState, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { TauriService, Runner } from "../../services/TauriService"; import { usePlatform } from "../../hooks/usePlatform"; import { useConfig, useAudio, useGame } from "../../context/LauncherContext"; interface SetupViewProps { onComplete: () => void; } const SetupView: React.FC = ({ onComplete }) => { const { isLinux, isMac } = usePlatform(); const { username, setUsername, setHasCompletedSetup, profile, setEnableTrayIcon: setConfigTray, setVfxEnabled: setConfigVfx, setRpcEnabled: setConfigRpc, setKeepLauncherOpen: setConfigKeepOpen, setLinuxRunner, linuxRunner: configLinuxRunner, vfxEnabled: configVfx, enableTrayIcon: configTray, rpcEnabled: configRpc, keepLauncherOpen: configKeepOpen } = useConfig(); const { playClickSound, playSfx } = useAudio(); const { editions } = useGame(); const titleImage = editions.find(e => e.id === profile)?.titleImage || "/images/MenuTitle.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 [enableTrayIcon, setEnableTrayIcon] = useState(configTray); const [enableVfx, setEnableVfx] = useState(configVfx); const [enableDiscordRPC, setEnableDiscordRPC] = useState(configRpc); const [keepLauncherOpen, setKeepLauncherOpen] = useState(configKeepOpen); const totalSteps = isLinux ? 4 : 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) => { console.log("[macOS Setup Progress]", progress); setSetupProgress(progress); }); return () => { unlisten.then(f => f?.()); }; } }, [isLinux, isMac]); const checkMacOSRuntime = async () => { try { const localStorageInstalled = localStorage.getItem('lce-macos-runtime-installed') === 'true'; if (localStorageInstalled) { console.log("[macOS Runtime] Using cached installation status"); try { const runtimeCheck = await TauriService.checkMacOSRuntimeInstalledFast(); if (runtimeCheck) { setRuntimeAlreadyInstalled(true); return; } else { console.log("[macOS Runtime] Cache was wrong, clearing"); localStorage.removeItem('lce-macos-runtime-installed'); setRuntimeAlreadyInstalled(false); return; } } catch (error) { console.log("[macOS Runtime] Fast check failed, using cache"); setRuntimeAlreadyInstalled(true); return; } } else { console.log("[macOS Runtime] No installation detected"); setRuntimeAlreadyInstalled(false); } } catch (error) { console.error("[macOS Runtime] Error checking:", error); setRuntimeAlreadyInstalled(false); } }; const handleRunnerSelect = (runnerId: string) => { playClickSound(); setSelectedRunner(runnerId); }; const handleNext = async () => { playClickSound(); 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) { setConfigTray(enableTrayIcon); setConfigVfx(enableVfx); setConfigRpc(enableDiscordRPC); setConfigKeepOpen(keepLauncherOpen); setCurrentStep(3); setFocusIndex(0); } else if (currentStep === 3) { playSfx("levelup.ogg"); setHasCompletedSetup(true); onComplete(); } }; const handleBack = () => { playClickSound(); if (currentStep > 0) { setCurrentStep(currentStep - 1); setFocusIndex(0); } }; useEffect(() => { const handleKey = (e: KeyboardEvent) => { // Elements count per step let count = 0; if (currentStep === 0) count = 2; // Input, Next else if (currentStep === 1) { if (isLinux) count = runners.length + 2; // Runners, Back, Next else if (isMac) count = 3; // Install, Back, Next else count = 2; // Back, Next } else if (currentStep === 2) count = 6; // 4 Toggles, Back, Next else if (currentStep === 3) count = 2; // Back, Finish 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") { // Handle enter based on focusIndex and step if (currentStep === 0) { if (focusIndex === 0) handleNext(); // For input field else if (focusIndex === 1) handleNext(); // Next button } 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 (focusIndex === 0) { setEnableTrayIcon(!enableTrayIcon); playClickSound(); } else if (focusIndex === 1) { setEnableVfx(!enableVfx); playClickSound(); } else if (focusIndex === 2) { setEnableDiscordRPC(!enableDiscordRPC); playClickSound(); } else if (focusIndex === 3) { setKeepLauncherOpen(!keepLauncherOpen); playClickSound(); } else if (focusIndex === 4) handleBack(); else if (focusIndex === 5) 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, enableTrayIcon, enableVfx, enableDiscordRPC, keepLauncherOpen, isLinux, isMac, tempUsername]); const handleMacosSetup = async () => { playClickSound(); setIsSettingUpRuntime(true); setSetupProgress({ stage: "preparing", message: "Preparing macOS runtime setup...", percent: 0 }); try { console.log("[macOS Setup] Starting runtime installation..."); await TauriService.setupMacosRuntime(); console.log("[macOS Setup] Runtime installation completed successfully!"); 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) { console.error("[macOS Setup] Error:", 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; }; return (
Emerald Legacy
{Array.from({ length: totalSteps }, (_, i) => ( ))}
{currentStep === 0 && (

Welcome to Emerald Legacy

Let's configure your launcher

)} {currentStep === 1 && isMac && (

macOS Compatibility

{runtimeAlreadyInstalled ? "Emerald Legacy compatibility runtime is already installed" : "Emerald Legacy needs compatibility runtime for macOS" }

{setupProgress && (

{setupProgress.stage.toUpperCase()}

{setupProgress.message}

{setupProgress.percent !== undefined && (
)}
)}

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

{runtimeAlreadyInstalled ? "The compatibility runtime is properly installed and ready to use." : "You must install the compatibility runtime before proceeding to the next step." }

{!runtimeAlreadyInstalled && (

⚠ Installation required before proceeding to next step

)}
)} {currentStep === 1 && isLinux && (

Linux Compatibility

Choose your preferred compatibility layer

{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 && (

Windows Setup

Everything is ready to go!

✓ Native compatibility

✓ Windows Native Support

Emerald Legacy runs natively on Windows without additional requirements.

)} {currentStep === 2 && (

Customize Your Experience

Choose your preferred launcher settings

System Tray Icon

Keep launcher accessible in system tray

Visual Effects

Click particles and animations

Discord Rich Presence

Show your Emerald Legacy status on Discord

Keep Launcher Open

Keep launcher running after game launch

You can change these later in settings

)} {currentStep === 3 && (

Setup Complete!

Username: {tempUsername}

{isMac && (

Runtime: Ready

)} {isLinux && selectedRunner && (

Runner: {runners.find(r => r.id === selectedRunner)?.name}

)}

Customization:

{enableTrayIcon && Tray Icon} {enableVfx && Visual Effects} {enableDiscordRPC && Discord RPC} {keepLauncherOpen && Keep Open}

Emerald Legacy is now configured and ready to use!

)}
{currentStep > 0 && ( )}
); }; export default SetupView;