Skip to content

Commit

Permalink
feat(ui): add monitor list
Browse files Browse the repository at this point in the history
  • Loading branch information
skjsjhb committed Jan 30, 2025
1 parent def5fcb commit 6d05ed6
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Navigator } from "@components/Navigator";
import { HeroUIProvider } from "@heroui/react";
import { AboutView } from "@pages/about/AboutView";
import { GamesView } from "@pages/games/GamesView";
import { MonitorView } from "@pages/monitor/MonitorView";
import { MonitorListView, MonitorView } from "@pages/monitor/MonitorView";
import { pages } from "@pages/pages";
import { SettingsView } from "@pages/settings/SettingsView";
import React from "react";
Expand Down Expand Up @@ -65,6 +65,7 @@ function Routes() {
<Route path="/settings" component={SettingsView}/>
<Route path="/games" component={GamesView}/>
<Route path="/monitor/:procId" component={MonitorView}/>
<Route path="/monitor" component={MonitorListView}/>
<Route path="/" component={DefaultPageRedirect}/>
</Switch>;
}
Expand Down
33 changes: 33 additions & 0 deletions src/renderer/lib/remote-game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ function syncEvents(emitter: EventTarget, proc: RemoteGameProcess): RemoteGamePr
const procs = new Map<string, RemoteGameProcess>();
const emitters = new Map<string, EventTarget>();

const globalEmitter = new EventTarget();

async function create(detail: GameProfileDetail): Promise<string> {
const meta = await native.launcher.launch(detail.id);

Expand Down Expand Up @@ -111,8 +113,17 @@ async function create(detail: GameProfileDetail): Promise<string> {
et.addEventListener(ch, () => em.dispatchEvent(new CustomEvent("change")));
});

function emitGlobal() {
globalEmitter.dispatchEvent(new CustomEvent("change"));
}

["exit", "crash"].forEach(ch => {
et.addEventListener(ch, () => emitGlobal());
});

procs.set(meta.id, proc);
emitters.set(meta.id, em);
emitGlobal();

return meta.id;
}
Expand All @@ -135,6 +146,13 @@ function slice(procId: string): RemoteGameProcess {
throw `Process not found: ${procId}`;
}

/**
* Gets deep copy of all statuses.
*/
function sliceAll(): RemoteGameProcess[] {
return structuredClone([...procs.values()]);
}

/**
* Retrieves an auto-updating game process object.
*
Expand Down Expand Up @@ -163,6 +181,21 @@ export function useGameProc(procId: string, wait = 0): RemoteGameProcess {
return proc;
}

export function useGameProcList(): RemoteGameProcess[] {
const [procs, setProcs] = useState<RemoteGameProcess[]>(sliceAll());

useEffect(() => {
function handler() {
setProcs(sliceAll());
}

globalEmitter.addEventListener("change", handler);
return () => globalEmitter.removeEventListener("change", handler);
}, []);

return procs;
}

export const remoteGame = {
create
};
2 changes: 1 addition & 1 deletion src/renderer/pages/games/GamesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function GamesView() {

const sortedGames = toSortedGames(games, sortMethod!);

return <div className="flex flex-col w-full h-full mx-auto">
return <div className="flex flex-col w-full h-full">
<div className="flex gap-2">
<Button fullWidth color="primary" size="lg" startContent={<PlusIcon/>}>
{t("new")}
Expand Down
75 changes: 67 additions & 8 deletions src/renderer/pages/monitor/MonitorView.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,76 @@
import { type RemoteGameProcess, type RemoteGameStatus, useGameProcList } from "@/renderer/lib/remote-game";
import { GameTypeImage } from "@components/GameTypeImage";
import { Button, Card, CardBody, Chip } from "@heroui/react";
import { Monitor } from "@pages/monitor/Monitor";
import { useParams } from "wouter";
import { ArrowRightIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
import { useLocation, useParams } from "wouter";

export function MonitorView() {
const { procId } = useParams<{ procId: string }>();
return <Monitor procId={procId} key={procId}/>;
}

if (procId) {
return <Monitor procId={procId} key={procId}/>;
}
export function MonitorListView() {
const procs = useGameProcList();

return <MonitorList/>;
return <div className="w-full h-full overflow-y-auto">
<div className="grid grid-cols-2 gap-4 w-full">
{
procs.map(p => <MonitorItem proc={p} key={p.id}/>)
}
</div>
</div>;
}

function MonitorList() {
// TODO add game processes
return <div className="w-full"></div>;
const statusColors = {
running: "success",
crashed: "danger",
exited: "default"
} as const;

function StatusChip({ status }: { status: RemoteGameStatus }) {
const { t } = useTranslation("pages", { keyPrefix: "monitor.status" });

return <Chip
variant="dot"
color={statusColors[status]}
>
{t(status)}
</Chip>;
}

function MonitorItem({ proc }: { proc: RemoteGameProcess }) {
const { detail: { modLoader, stable, name, versionId }, status } = proc;
const [, nav] = useLocation();

function revealProc() {
nav(`/monitor/${proc.id}`);
}

return <Card>
<CardBody>
<div className="flex gap-4 items-center h-16 px-3">
<div className="h-full p-3 bg-content2 rounded-full">
<GameTypeImage loader={modLoader} stable={stable}/>
</div>

<div className="flex flex-col gap-1">
<div className="font-bold text-xl">{name}</div>
<div className="text-foreground-400">{versionId}</div>
</div>

<div className="ml-auto flex gap-2 items-center">
<StatusChip status={status}/>
</div>


<div className="ml-4">
<Button isIconOnly color="primary" onPress={revealProc}>
<ArrowRightIcon/>
</Button>
</div>
</div>
</CardBody>
</Card>;
}

0 comments on commit 6d05ed6

Please sign in to comment.