import { useMemo, useState, useEffect } from "react"; type Status = | "ready" | "saved successfully" | "injection started" | "validating key..." | "invalid key" | "injecting..." | "RL Running" | "RL Closed" | string; interface UserInfo { userId: string | null; discordId: string | null; epicId: string | null; username: string | null; globalName: string | null; logins?: number; } interface KeyValidationResponse { status: string; user: UserInfo | null; } const LS_KEYS = { spoofed: "neonGlass.spoofedUsername", apiKey: "neonGlass.apiKey", minimizeToTray: "neonGlass.minimizeToTray", platform: "neonGlass.platform", autoInject: "neonGlass.autoInject", } as const; const GITHUB_URL = "https://github.com/RLidentity"; const FAQ_URL = "https://rlidentity.me/faq"; function isTauriRuntime() { return typeof window !== "undefined" && typeof (window as any).__TAURI_INTERNALS__ !== "undefined"; } async function tryWindowApi(action: (win: any) => Promise) { if (!isTauriRuntime()) return; const mod: any = await import("@tauri-apps/api/window"); const win = mod.getCurrentWindow(); await action(win); } async function tryInvoke(cmd: string, args?: Record) { if (!isTauriRuntime()) return null; const mod: any = await import("@tauri-apps/api/core"); return (await mod.invoke(cmd, args)) as T; } async function openUrl(url: string) { const fallback = () => window.open(url, "_blank", "noopener,noreferrer"); if (!isTauriRuntime()) return fallback(); try { const mod: any = await import("@tauri-apps/plugin-opener"); if (typeof mod.openUrl === "function") { await mod.openUrl(url); return; } fallback(); } catch { fallback(); } } export default function App() { const initialApiKey = useMemo(() => localStorage.getItem(LS_KEYS.apiKey) ?? "", []); const initialSpoofed = useMemo(() => localStorage.getItem(LS_KEYS.spoofed) ?? "", []); const initialMinToTray = useMemo(() => localStorage.getItem(LS_KEYS.minimizeToTray) === "true", []); const initialPlatform = useMemo(() => localStorage.getItem(LS_KEYS.platform) ?? "Epic", []); const initialAutoInject = useMemo(() => localStorage.getItem(LS_KEYS.autoInject) === "true", []); const [apiKey, setApiKey] = useState(initialApiKey); const [spoofedUsername, setSpoofedUsername] = useState(initialSpoofed); const [isAuthorized, setIsAuthorized] = useState(false); const [isRevoked, setIsRevoked] = useState(false); const [userData, setUserData] = useState(null); const [status, setStatus] = useState("ready"); const [rlStatus, setRlStatus] = useState("Checking..."); const [settingsOpen, setSettingsOpen] = useState(false); const [logsOpen, setLogsOpen] = useState(false); const [lastLog, setLastLog] = useState(""); const [minimizeToTray, setMinimizeToTray] = useState(initialMinToTray); const [platform, setPlatform] = useState(initialPlatform); const [autoInject, setAutoInject] = useState(initialAutoInject); const [platformPickerOpen, setPlatformPickerOpen] = useState(false); // Easter Egg State const [debugOpen, setDebugOpen] = useState(false); const [logoClicks, setLogoClicks] = useState(0); const handleLogoClick = (e: React.MouseEvent) => { e.stopPropagation(); setLogoClicks(prev => prev + 1); }; useEffect(() => { if (logoClicks >= 5) { setDebugOpen(true); setLogoClicks(0); } const timer = setTimeout(() => { if (logoClicks > 0) setLogoClicks(0); }, 1000); return () => clearTimeout(timer); }, [logoClicks]); // Tutorial State const [tutorialStep, setTutorialStep] = useState(-1); // Startup Authorization & Update Check useEffect(() => { if (initialApiKey) { authorize(initialApiKey); } syncAssetsAndCheckUpdates(); }, []); async function syncAssetsAndCheckUpdates() { if (!isTauriRuntime()) return; // 1. Download DLL and Injector try { await tryInvoke("download_assets"); console.log("Assets synced successfully"); } catch (e) { console.error("Failed to sync assets:", e); } // 2. Check for App updates checkForUpdates(); } async function checkForUpdates() { if (!isTauriRuntime()) return; try { const { check } = await import("@tauri-apps/plugin-updater"); const update = await check(); if (update) { console.log(`Update available: ${update.version}`); const confirmed = window.confirm(`A new version (${update.version}) is available. Would you like to update?`); if (confirmed) { setStatus("Updating..."); await update.downloadAndInstall(); // The app will restart automatically after install on some platforms, // or we might need to relaunch. Tauri v2 updater usually handles this. const { relaunch } = await import("@tauri-apps/plugin-process"); await relaunch(); } } } catch (e) { console.error("Failed to check for updates:", e); } } // Sync revoked background to body useEffect(() => { if (isRevoked) { document.body.classList.add('revoked-bg'); } else { document.body.classList.remove('revoked-bg'); } }, [isRevoked]); // Poll for Rocket League status & Auto Inject useEffect(() => { if (!isAuthorized || isRevoked) return; const interval = setInterval(async () => { try { const res = await tryInvoke<{is_running: boolean}>("check_status"); if (res) { const wasRunning = rlStatus === "RL Running"; const isRunning = res.is_running; setRlStatus(isRunning ? "RL Running" : "RL Closed"); // Auto Inject Logic: if it just started running and autoInject is on if (!wasRunning && isRunning && autoInject) { inject(); } } } catch (e) { console.error(e); } }, 2000); return () => clearInterval(interval); }, [isAuthorized, isRevoked, rlStatus, autoInject]); async function authorize(keyToTry: string) { if (!keyToTry.trim()) { setStatus("Please enter a key"); return; } setStatus("validating key..."); setIsRevoked(false); try { const hwid = await tryInvoke("get_hwid") || "UNKNOWN"; const res = await tryInvoke("validate_key", { key: keyToTry.trim(), hwid }); if (res && res.status === "valid") { localStorage.setItem(LS_KEYS.apiKey, keyToTry.trim()); setUserData(res.user); setIsAuthorized(true); setIsRevoked(false); setStatus("ready"); // Check for tutorial if (res.user?.logins === 0) { setTutorialStep(0); } } else if (res && res.status === "revoked") { setIsRevoked(true); setIsAuthorized(false); setStatus("Error: Key Revoked"); } else if (res && res.status === "invalid_hwid") { setStatus("Error: Key locked to another PC"); setIsAuthorized(false); } else { setStatus("Error: Invalid key"); setIsAuthorized(false); } } catch (e) { setStatus("Network Error: Check connection"); } } async function saveConfig() { localStorage.setItem(LS_KEYS.spoofed, spoofedUsername.trim()); localStorage.setItem(LS_KEYS.platform, platform); try { await tryInvoke("save_config", { name: spoofedUsername.trim(), platform }); setStatus("saved successfully"); window.setTimeout(() => setStatus("ready"), 1400); } catch (e) { setStatus("Save error: " + String(e)); } } async function inject() { setStatus("injecting..."); try { const res = await tryInvoke("inject_dll", { discordId: userData?.discordId }); setLastLog(res || "Successfully Injected!"); setStatus("Successfully Injected!"); window.setTimeout(() => setStatus("ready"), 1400); } catch (e) { setStatus("Injection Failed!"); setLastLog(String(e)); setLogsOpen(true); } } function toggleMinimizeToTray(next: boolean) { setMinimizeToTray(next); localStorage.setItem(LS_KEYS.minimizeToTray, String(next)); } function toggleAutoInject(next: boolean) { setAutoInject(next); localStorage.setItem(LS_KEYS.autoInject, String(next)); } async function handleMinimizeClick() { if (!isTauriRuntime()) return; if (minimizeToTray) { await tryInvoke("minimize_to_tray"); } else { await tryWindowApi((w) => w.minimize()); } } const logout = () => { localStorage.removeItem(LS_KEYS.apiKey); window.location.reload(); }; const tutorialSteps = [ { title: "Welcome to RLidentity", text: "Let's show you around. First, enter your spoofed username here.", target: "input" }, { title: "Injection", text: "Once Rocket League is running, click Inject to start. Or enable Auto-Inject in settings!", target: "btn-primary" }, { title: "Settings", text: "Customize your experience here. Change your platform or toggle Auto-Injection.", target: "tb-action" }, { title: "All Set!", text: "You're ready to win. Happy gaming!", target: "none" } ]; if (!isAuthorized) { return (
); } return (