aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author苏向夜 <fu050409@163.com>2026-02-26 18:32:06 +0800
committer苏向夜 <fu050409@163.com>2026-02-26 18:32:06 +0800
commit120c0a460162226446cce4cfbc4c7e5859cd9d09 (patch)
tree91739f044507c2bf0ef8d9956f46cbb99fdb890f
parentd95ca2801c19a89a2a845f43b6e0133bf4e9be50 (diff)
downloadDropOut-120c0a460162226446cce4cfbc4c7e5859cd9d09.tar.gz
DropOut-120c0a460162226446cce4cfbc4c7e5859cd9d09.zip
feat(ui): improve launching game
-rw-r--r--.changes/process-monitor.md5
-rw-r--r--packages/ui/src/components/bottom-bar.tsx104
2 files changed, 75 insertions, 34 deletions
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<boolean>(false);
+ const gameUnlisten = useRef<UnlistenFn | null>(null);
+ const [isLaunching, setIsLaunching] = useState<boolean>(false);
const [selectedVersion, setSelectedVersion] = useState<string | null>(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 (
+ <Button
+ className="px-4 py-2"
+ size="lg"
+ onClick={() => setShowLoginModal(true)}
+ >
+ <User /> Login
+ </Button>
+ );
+ }
+
+ return isLaunched ? (
+ <Button
+ variant="destructive"
+ onClick={() => {
+ toast.warning(
+ "Minecraft Process will not be terminated, please close it manually.",
+ );
+ setIsLaunched(false);
+ }}
+ >
+ <XIcon />
+ Game started
+ </Button>
+ ) : (
+ <Button
+ className={cn(
+ "px-4 py-2 shadow-xl",
+ "bg-emerald-600! hover:bg-emerald-500!",
+ )}
+ size="lg"
+ onClick={handleStartGame}
+ disabled={isLaunching}
+ >
+ {isLaunching ? <Spinner /> : <Play />}
+ Start
+ </Button>
+ );
+ };
+
return (
<div className="absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/30 via-transparent to-transparent p-4 z-10">
<div className="max-w-7xl mx-auto">
@@ -196,29 +254,7 @@ export function BottomBar() {
</Select>
</div>
- <div className="flex items-center gap-3">
- {authStore.account ? (
- <Button
- className={cn(
- "px-4 py-2 shadow-xl",
- "bg-emerald-600! hover:bg-emerald-500!",
- )}
- size="lg"
- onClick={handleStartGame}
- >
- <Play />
- Start
- </Button>
- ) : (
- <Button
- className="px-4 py-2"
- size="lg"
- onClick={() => setShowLoginModal(true)}
- >
- <User /> Login
- </Button>
- )}
- </div>
+ <div className="flex items-center gap-3">{renderButton()}</div>
</div>
</div>