LAUNCHER-5 feat: Disable mod buttons when the cluster is running

LAUNCHER-16 feat: Running cluster indicator on home page
This commit is contained in:
LynithDev 2024-12-27 12:50:19 +01:00
parent d4a33ad348
commit de70230645
4 changed files with 73 additions and 32 deletions

View file

@ -0,0 +1,40 @@
import type { Cluster, DetailedProcess } from '@onelauncher/client/bindings';
import type { UnlistenFn } from '@tauri-apps/api/event';
import { bridge } from '~imports';
import { type Accessor, createEffect, createSignal, onCleanup, onMount, type Resource } from 'solid-js';
import useCommand, { tryResult } from './useCommand';
interface ProcessorHookType {
running: Resource<DetailedProcess[]>;
stop: (id: string) => Promise<unknown>;
isRunning: Accessor<boolean>;
};
export default function useProcessor(cluster: Cluster): ProcessorHookType {
const [runningProcesses, { refetch: refetchProcesses }] = useCommand(() => bridge.commands.getProcessesDetailedByPath(cluster.path!));
const [unlisten, setUnlisten] = createSignal<UnlistenFn>();
const [isRunning, setIsRunning] = createSignal<boolean>(false);
onMount(async () => {
const unlisten = await bridge.events.processPayload.listen((e) => {
if (e.payload.event === 'started' || e.payload.event === 'finished')
refetchProcesses();
});
setUnlisten(() => unlisten);
});
onCleanup(() => {
unlisten()?.();
});
createEffect(() => {
setIsRunning((runningProcesses()?.length ?? 0) > 0);
});
return {
running: runningProcesses,
stop: async id => await tryResult(() => bridge.commands.killProcess(id)),
isRunning,
};
}

View file

@ -8,11 +8,13 @@ import {
} from '@untitled-theme/icons-solid';
import { bridge } from '~imports';
import ClusterCover from '~ui/components/game/ClusterCover';
import PlayerHead from '~ui/components/game/PlayerHead';
import { useClusterCreator } from '~ui/components/overlay/cluster/ClusterCreationModal';
import { useLaunchCluster, useRecentCluster } from '~ui/hooks/useCluster';
import useCommand from '~ui/hooks/useCommand';
import { formatAsDuration, upperFirst } from '~utils';
import useProcessor from '~ui/hooks/useProcessor';
import { formatAsDuration, upperFirst } from '~utils';
import { For, onMount, Show } from 'solid-js';
import BannerBackground from '../../assets/images/header.png';
import Button from '../components/base/Button';
@ -170,6 +172,7 @@ function Banner() {
function ClusterCard(props: Cluster) {
const navigate = useNavigate();
const { running, isRunning } = useProcessor(props);
function openClusterPage(_e: MouseEvent) {
navigate(`/clusters/?id=${props.uuid}`);
@ -180,7 +183,11 @@ function ClusterCard(props: Cluster) {
return (
<>
<div
class="group relative h-[152px] flex flex-col border border-border/05 rounded-xl bg-component-bg active:bg-component-bg-pressed hover:bg-component-bg-hover"
class="group relative h-[152px] flex flex-col border rounded-xl"
classList={{
'border-brand bg-brand/20 active:bg-brand/30 hover:bg-brand/10': isRunning(),
'border-border/05 bg-component-bg active:bg-component-bg-pressed hover:bg-component-bg-hover': !isRunning(),
}}
onClick={e => openClusterPage(e)}
>
<div class="relative flex-1 overflow-hidden rounded-t-xl">
@ -193,6 +200,16 @@ function ClusterCard(props: Cluster) {
cluster={props}
/>
</div>
<Show when={isRunning()}>
<div class="absolute left-1 top-1 flex flex-row gap-1 rounded-md bg-brand/70 p-1">
<For each={running()}>
{process => (
<PlayerHead class="h-4 w-4 rounded-[4px]" uuid={process.user} />
)}
</For>
</div>
</Show>
</div>
<div class="z-10 flex flex-row items-center justify-between gap-x-3 p-3">
<div class="h-8 flex flex-col gap-1.5 overflow-hidden">

View file

@ -9,11 +9,13 @@ import Sidebar from '~ui/components/Sidebar';
import useBrowser from '~ui/hooks/useBrowser';
import useClusterContext from '~ui/hooks/useCluster';
import useCommand from '~ui/hooks/useCommand';
import { createEffect, createSignal, For, Match, Show, Switch } from 'solid-js';
import useProcessor from '~ui/hooks/useProcessor';
import { type Accessor, createEffect, createSignal, For, Match, Show, Switch } from 'solid-js';
// TODO: This needs a rewrite.
function ClusterMods() {
const [cluster] = useClusterContext();
const { isRunning } = useProcessor(cluster()!);
const [mods, { refetch: refetchMods }] = useCommand(cluster, () => bridge.commands.getClusterPackages(cluster()?.path || '', 'mod'));
// Mods to display, should be a clone of the mods array but sorted
@ -101,7 +103,7 @@ function ClusterMods() {
<Match when={displayedMods().length > 0}>
<For each={displayedMods()}>
{mod => (
<ModEntry pkg={mod} refetch={refetchMods} />
<ModEntry isRunning={isRunning} pkg={mod} refetch={refetchMods} />
)}
</For>
</Match>
@ -120,6 +122,7 @@ export default ClusterMods;
interface ModEntryProps {
pkg: Package;
refetch: () => void;
isRunning: Accessor<boolean>;
};
function ModEntry(props: ModEntryProps) {
@ -241,9 +244,12 @@ function ModEntry(props: ModEntryProps) {
<Edit02Icon />
</Button> */}
<Button buttonStyle="iconDanger" onClick={deletePackage}>
<Trash03Icon />
</Button>
<Button
buttonStyle="iconDanger"
children={<Trash03Icon />}
disabled={props.isRunning()}
onClick={deletePackage}
/>
</div>
</div>
</div>

View file

@ -1,12 +1,10 @@
import type { UnlistenFn } from '@tauri-apps/api/event';
import { type Navigator, Route, useIsRouting, useSearchParams } from '@solidjs/router';
import { EyeIcon, File06Icon, Globe04Icon, Image03Icon, PackagePlusIcon, Settings04Icon } from '@untitled-theme/icons-solid';
import { bridge } from '~imports';
import PlayerHead from '~ui/components/game/PlayerHead';
import useClusterContext, { ClusterProvider } from '~ui/hooks/useCluster';
import { tryResult } from '~ui/hooks/useCommand';
import useProcessor from '~ui/hooks/useProcessor';
import { supportsMods } from '~utils';
import { createEffect, createResource, createSignal, onCleanup, onMount, type ParentProps, Show } from 'solid-js';
import { createEffect, type ParentProps, Show } from 'solid-js';
import AnimatedRoutes from '../../components/AnimatedRoutes';
import ErrorBoundary from '../../components/ErrorBoundary';
import Sidebar from '../../components/Sidebar';
@ -71,14 +69,7 @@ ClusterRoot.open = function (navigate: Navigator, uuid: string) {
function ClusterSidebar() {
const [cluster, { refetch: refetchCluster }] = useClusterContext();
const [listener, setListener] = createSignal<UnlistenFn>();
const [runningProcesses, { refetch: refetchProcesses }] = createResource([], async () => {
const c = cluster();
if (!c || typeof c.path !== 'string')
return [];
return tryResult(() => bridge.commands.getProcessesDetailedByPath(c.path!));
});
const { running: runningProcesses } = useProcessor(cluster()!);
const isRouting = useIsRouting();
@ -87,19 +78,6 @@ function ClusterSidebar() {
refetchCluster();
});
onMount(async () => {
const unlisten = await bridge.events.processPayload.listen(({ payload }) => {
if (payload.event === 'started' || payload.event === 'finished')
refetchProcesses();
});
setListener(() => unlisten);
});
onCleanup(() => {
listener?.()?.();
});
return (
<Sidebar
base="/clusters"