From 66668d85d603c5841d755a6023aa1925559fc6d4 Mon Sep 17 00:00:00 2001 From: 苏向夜 Date: Wed, 25 Feb 2026 01:32:51 +0800 Subject: chore(workspace): replace legacy codes --- packages/ui/src/components/bottom-bar.tsx | 231 ++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 packages/ui/src/components/bottom-bar.tsx (limited to 'packages/ui/src/components/bottom-bar.tsx') diff --git a/packages/ui/src/components/bottom-bar.tsx b/packages/ui/src/components/bottom-bar.tsx new file mode 100644 index 0000000..32eb852 --- /dev/null +++ b/packages/ui/src/components/bottom-bar.tsx @@ -0,0 +1,231 @@ +import { listen, type UnlistenFn } from "@tauri-apps/api/event"; +import { Play, User } from "lucide-react"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { toast } from "sonner"; +import { listInstalledVersions, startGame } from "@/client"; +import { cn } from "@/lib/utils"; +import { useAuthStore } from "@/models/auth"; +import { useInstancesStore } from "@/models/instances"; +import { useGameStore } from "@/stores/game-store"; +import { LoginModal } from "./login-modal"; +import { Button } from "./ui/button"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "./ui/select"; + +interface InstalledVersion { + id: string; + type: string; +} + +export function BottomBar() { + const authStore = useAuthStore(); + const gameStore = useGameStore(); + const instancesStore = useInstancesStore(); + + const [selectedVersion, setSelectedVersion] = useState(null); + const [installedVersions, setInstalledVersions] = useState< + InstalledVersion[] + >([]); + const [isLoadingVersions, setIsLoadingVersions] = useState(true); + const [showLoginModal, setShowLoginModal] = useState(false); + + const loadInstalledVersions = useCallback(async () => { + if (!instancesStore.activeInstance) { + setInstalledVersions([]); + setIsLoadingVersions(false); + return; + } + + setIsLoadingVersions(true); + try { + const versions = await listInstalledVersions( + instancesStore.activeInstance.id, + ); + + const installed = versions || []; + setInstalledVersions(installed); + + // If no version is selected but we have installed versions, select the first one + if (!gameStore.selectedVersion && installed.length > 0) { + gameStore.setSelectedVersion(installed[0].id); + } + } catch (error) { + console.error("Failed to load installed versions:", error); + } finally { + setIsLoadingVersions(false); + } + }, [ + instancesStore.activeInstance, + gameStore.selectedVersion, + gameStore.setSelectedVersion, + ]); + + useEffect(() => { + loadInstalledVersions(); + + // Listen for backend events that should refresh installed versions. + let unlistenDownload: UnlistenFn | null = null; + let unlistenVersionDeleted: UnlistenFn | null = null; + + (async () => { + try { + unlistenDownload = await listen("download-complete", () => { + loadInstalledVersions(); + }); + } catch (err) { + // best-effort: do not break UI if listening fails + // eslint-disable-next-line no-console + console.warn("Failed to attach download-complete listener:", err); + } + + try { + unlistenVersionDeleted = await listen("version-deleted", () => { + loadInstalledVersions(); + }); + } catch (err) { + // eslint-disable-next-line no-console + console.warn("Failed to attach version-deleted listener:", err); + } + })(); + + return () => { + try { + if (unlistenDownload) unlistenDownload(); + } catch { + // ignore + } + try { + if (unlistenVersionDeleted) unlistenVersionDeleted(); + } catch { + // ignore + } + }; + }, [loadInstalledVersions]); + + const handleStartGame = async () => { + if (!selectedVersion) { + toast.info("Please select a version!"); + return; + } + + if (!instancesStore.activeInstance) { + toast.info("Please select an instance first!"); + return; + } + // await gameStore.startGame( + // authStore.currentAccount, + // authStore.openLoginModal, + // instancesStore.activeInstanceId, + // uiStore.setView, + // ); + await startGame(instancesStore.activeInstance?.id, selectedVersion); + }; + + const getVersionTypeColor = (type: string) => { + switch (type) { + case "release": + return "bg-emerald-500"; + case "snapshot": + return "bg-amber-500"; + case "old_beta": + return "bg-rose-500"; + case "old_alpha": + return "bg-violet-500"; + default: + return "bg-gray-500"; + } + }; + + const versionOptions = useMemo( + () => + installedVersions.map((v) => ({ + label: `${v.id}${v.type !== "release" ? ` (${v.type})` : ""}`, + value: v.id, + type: v.type, + })), + [installedVersions], + ); + + return ( +
+
+
+
+
+ + Active Instance + + + {instancesStore.activeInstance?.name || "No instance selected"} + +
+ + +
+ +
+ {authStore.account ? ( + + ) : ( + + )} +
+
+
+ + setShowLoginModal(false)} + /> +
+ ); +} -- cgit v1.2.3-70-g09d2 From 120c0a460162226446cce4cfbc4c7e5859cd9d09 Mon Sep 17 00:00:00 2001 From: 苏向夜 Date: Thu, 26 Feb 2026 18:32:06 +0800 Subject: feat(ui): improve launching game --- .changes/process-monitor.md | 5 ++ packages/ui/src/components/bottom-bar.tsx | 104 ++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 .changes/process-monitor.md (limited to 'packages/ui/src/components/bottom-bar.tsx') diff --git a/.changes/process-monitor.md b/.changes/process-monitor.md new file mode 100644 index 0000000..478b822 --- /dev/null +++ b/.changes/process-monitor.md @@ -0,0 +1,5 @@ +--- +"@dropout/ui": "patch:feat" +--- + +Listen to `game-exited` event while launching game. diff --git a/packages/ui/src/components/bottom-bar.tsx b/packages/ui/src/components/bottom-bar.tsx index 32eb852..5489675 100644 --- a/packages/ui/src/components/bottom-bar.tsx +++ b/packages/ui/src/components/bottom-bar.tsx @@ -1,11 +1,11 @@ import { listen, type UnlistenFn } from "@tauri-apps/api/event"; -import { Play, User } from "lucide-react"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { Play, User, XIcon } from "lucide-react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { listInstalledVersions, startGame } from "@/client"; import { cn } from "@/lib/utils"; import { useAuthStore } from "@/models/auth"; -import { useInstancesStore } from "@/models/instances"; +import { useInstanceStore } from "@/models/instance"; import { useGameStore } from "@/stores/game-store"; import { LoginModal } from "./login-modal"; import { Button } from "./ui/button"; @@ -17,6 +17,7 @@ import { SelectTrigger, SelectValue, } from "./ui/select"; +import { Spinner } from "./ui/spinner"; interface InstalledVersion { id: string; @@ -26,8 +27,11 @@ interface InstalledVersion { export function BottomBar() { const authStore = useAuthStore(); const gameStore = useGameStore(); - const instancesStore = useInstancesStore(); + const instancesStore = useInstanceStore(); + const [isLaunched, setIsLaunched] = useState(false); + const gameUnlisten = useRef(null); + const [isLaunching, setIsLaunching] = useState(false); const [selectedVersion, setSelectedVersion] = useState(null); const [installedVersions, setInstalledVersions] = useState< InstalledVersion[] @@ -118,13 +122,25 @@ export function BottomBar() { toast.info("Please select an instance first!"); return; } - // await gameStore.startGame( - // authStore.currentAccount, - // authStore.openLoginModal, - // instancesStore.activeInstanceId, - // uiStore.setView, - // ); - await startGame(instancesStore.activeInstance?.id, selectedVersion); + + try { + gameUnlisten.current = await listen("game-exited", () => { + setIsLaunched(false); + }); + } catch (error) { + toast.warning(`Failed to listen to game-exited event: ${error}`); + } + + setIsLaunching(true); + try { + await startGame(instancesStore.activeInstance?.id, selectedVersion); + setIsLaunched(true); + } catch (error) { + console.error(`Failed to start game: ${error}`); + toast.error(`Failed to start game: ${error}`); + } finally { + setIsLaunching(false); + } }; const getVersionTypeColor = (type: string) => { @@ -152,6 +168,48 @@ export function BottomBar() { [installedVersions], ); + const renderButton = () => { + if (!authStore.account) { + return ( + + ); + } + + return isLaunched ? ( + + ) : ( + + ); + }; + return (
@@ -196,29 +254,7 @@ export function BottomBar() {
-
- {authStore.account ? ( - - ) : ( - - )} -
+
{renderButton()}
-- cgit v1.2.3-70-g09d2