feat: Launch button debounce + now in cluster sidebar

This commit is contained in:
LynithDev 2025-02-16 11:03:38 +01:00
parent 2231be828f
commit 59183e9124
7 changed files with 101 additions and 48 deletions

View file

@ -25,6 +25,7 @@
"@solid-primitives/i18n": "^2.1.1",
"@solid-primitives/map": "^0.4.13",
"@solid-primitives/refs": "^1.0.8",
"@solid-primitives/scheduled": "^1.5.0",
"@solidjs/router": "^0.14.7",
"@t3-oss/env-core": "^0.11.0",
"@tanstack/solid-query": "^5.56.2",

View file

@ -0,0 +1,49 @@
import type { Cluster } from '@onelauncher/client/bindings';
import type { ButtonProps } from './base/Button';
import { debounce } from '@solid-primitives/scheduled';
import { PlayIcon } from '@untitled-theme/icons-solid';
import { useLaunchCluster } from '~ui/hooks/useCluster';
import useProcessor from '~ui/hooks/useProcessor';
import useSettings from '~ui/hooks/useSettings';
import { Match, splitProps, Switch } from 'solid-js';
import Button from './base/Button';
function LaunchButton(props: ButtonProps & { cluster: Cluster; iconOnly?: boolean }) {
const [split, rest] = splitProps(props, ['cluster']);
const innerLaunch = useLaunchCluster(() => split.cluster?.uuid);
// eslint-disable-next-line solid/reactivity -- a
const { running: runningProcesses } = useProcessor(split.cluster);
const { settings } = useSettings();
const launch = (e: MouseEvent) => {
e.stopImmediatePropagation();
e.preventDefault();
debounce(innerLaunch, 300)();
};
return (
<Switch>
<Match when={props.iconOnly === true}>
<Button
buttonStyle="iconSecondary"
children={<PlayIcon class="h-4! w-4!" />}
disabled={settings().allow_parallel_running_clusters !== true && (runningProcesses()?.length || 0) > 0}
onClick={launch}
{...rest}
/>
</Match>
<Match when>
<Button
buttonStyle="primary"
children="Launch"
disabled={settings().allow_parallel_running_clusters !== true && (runningProcesses()?.length || 0) > 0}
iconLeft={<PlayIcon />}
onClick={launch}
{...rest}
/>
</Match>
</Switch>
);
}
export default LaunchButton;

View file

@ -2,7 +2,7 @@ import type { Accessor, JSX, ParentProps } from 'solid-js';
import { createEffect, createSignal, mergeProps, on, splitProps, untrack } from 'solid-js';
import styles from './Button.module.scss';
type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & ParentProps & {
export type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & ParentProps & {
iconLeft?: JSX.Element;
iconRight?: JSX.Element;
buttonStyle?: 'primary' | 'secondary' | 'danger' | 'icon' | 'iconPrimary' | 'iconSecondary' | 'iconDanger' | 'ghost';

View file

@ -9,10 +9,11 @@ import {
import { bridge } from '~imports';
import ClusterCover from '~ui/components/game/ClusterCover';
import PlayerHead from '~ui/components/game/PlayerHead';
import LaunchButton from '~ui/components/LaunchButton';
import { useClusterCreator } from '~ui/components/overlay/cluster/ClusterCreationModal';
import { useLaunchCluster, useRecentCluster } from '~ui/hooks/useCluster';
import useCommand from '~ui/hooks/useCommand';
import useCommand from '~ui/hooks/useCommand';
import useProcessor from '~ui/hooks/useProcessor';
import { formatAsDuration, upperFirst } from '~utils';
import { For, onMount, Show } from 'solid-js';
@ -178,8 +179,6 @@ function ClusterCard(props: Cluster) {
navigate(`/clusters/?id=${props.uuid}`);
}
const launch = useLaunchCluster(() => props.uuid);
return (
<>
<div
@ -222,16 +221,8 @@ function ClusterCard(props: Cluster) {
{props.packages.mods && `• ${props.mods} mods`} */}
</p>
</div>
<Button
buttonStyle="iconSecondary"
onClick={(e) => {
e.preventDefault();
e.stopImmediatePropagation();
launch();
}}
>
<PlayIcon class="h-4! w-4!" />
</Button>
<LaunchButton cluster={props} iconOnly />
</div>
</div>
</>

View file

@ -2,14 +2,15 @@ import type { Cluster } from '@onelauncher/client/bindings';
import { useNavigate } from '@solidjs/router';
import * as dialog from '@tauri-apps/plugin-dialog';
import { open } from '@tauri-apps/plugin-shell';
import { Edit02Icon, FolderIcon, ImagePlusIcon, LinkExternal01Icon, PlayIcon, Save01Icon, Share07Icon, Tool02Icon, Trash01Icon } from '@untitled-theme/icons-solid';
import { Edit02Icon, FolderIcon, ImagePlusIcon, LinkExternal01Icon, Save01Icon, Share07Icon, Tool02Icon, Trash01Icon } from '@untitled-theme/icons-solid';
import { bridge } from '~imports';
import TextField from '~ui/components/base/TextField';
import LaunchButton from '~ui/components/LaunchButton';
import Modal, { createModal, type ModalProps } from '~ui/components/overlay/Modal';
import ScrollableContainer from '~ui/components/ScrollableContainer';
import SettingsRow from '~ui/components/SettingsRow';
import Sidebar from '~ui/components/Sidebar';
import useClusterContext, { useLaunchCluster } from '~ui/hooks/useCluster';
import useClusterContext from '~ui/hooks/useCluster';
import usePreventLeave from '~ui/hooks/usePreventLeave';
import useSettings from '~ui/hooks/useSettings';
import { formatAsDuration, upperFirst } from '~utils';
@ -264,8 +265,6 @@ interface BannerProps {
}
function Banner(props: BannerProps) {
const launch = useLaunchCluster(() => props.cluster.uuid);
async function launchFilePicker() {
const selected = await dialog.open({
multiple: false,
@ -361,14 +360,7 @@ function Banner(props: BannerProps) {
disabled={true}
/>
<Button
buttonStyle="primary"
children="Launch"
class="!w-auto"
disabled={props.editMode()}
iconLeft={<PlayIcon />}
onClick={launch}
/>
<LaunchButton cluster={props.cluster} />
</div>
</div>

View file

@ -1,6 +1,7 @@
import { type Navigator, Route, useIsRouting, useSearchParams } from '@solidjs/router';
import { EyeIcon, File06Icon, Globe04Icon, Image03Icon, PackagePlusIcon, Settings04Icon } from '@untitled-theme/icons-solid';
import PlayerHead from '~ui/components/game/PlayerHead';
import LaunchButton from '~ui/components/LaunchButton';
import useClusterContext, { ClusterProvider } from '~ui/hooks/useCluster';
import useProcessor from '~ui/hooks/useProcessor';
import { supportsMods } from '~utils';
@ -70,7 +71,6 @@ ClusterRoot.open = function (navigate: Navigator, uuid: string) {
function ClusterSidebar() {
const [cluster, { refetch: refetchCluster }] = useClusterContext();
const { running: runningProcesses } = useProcessor(cluster()!);
const isRouting = useIsRouting();
createEffect(() => {
@ -79,28 +79,34 @@ function ClusterSidebar() {
});
return (
<Sidebar
base="/clusters"
links={{
Cluster: [
[<EyeIcon />, 'Overview', '/'],
(supportsMods(cluster?.()) ? [<PackagePlusIcon />, 'Mods', '/mods'] : undefined),
[<Image03Icon />, 'Screenshots', '/screenshots'],
[<Globe04Icon />, 'Worlds', '/worlds'],
[<File06Icon />, 'Logs', '/logs'],
[<Settings04Icon />, 'Game Settings', '/settings'],
],
...(runningProcesses() && runningProcesses()!.length > 0
? {
Running: runningProcesses()!.map((details, index) => {
const icon = <PlayerHead uuid={details.user} />; ;
<div class="h-full flex flex-col items-center justify-between">
<Show when={cluster()}>
<Sidebar
base="/clusters"
links={{
Cluster: [
[<EyeIcon />, 'Overview', '/'],
(supportsMods(cluster()) ? [<PackagePlusIcon />, 'Mods', '/mods'] : undefined),
[<Image03Icon />, 'Screenshots', '/screenshots'],
[<Globe04Icon />, 'Worlds', '/worlds'],
[<File06Icon />, 'Logs', '/logs'],
[<Settings04Icon />, 'Game Settings', '/settings'],
],
...(runningProcesses() && runningProcesses()!.length > 0
? {
Running: runningProcesses()!.map((details, index) => {
const icon = <PlayerHead uuid={details.user} />; ;
return [icon, `Process #${index + 1}`, '/game', ClusterGame.buildUrl(cluster()!.uuid, details)];
}),
}
: {}),
}}
/>
return [icon, `Process #${index + 1}`, '/game', ClusterGame.buildUrl(cluster()!.uuid, details)];
}),
}
: {}),
}}
/>
<LaunchButton class="!w-full" cluster={cluster()!} />
</Show>
</div>
);
}

14
pnpm-lock.yaml generated
View file

@ -106,6 +106,9 @@ importers:
'@solid-primitives/refs':
specifier: ^1.0.8
version: 1.0.8(solid-js@1.9.3)
'@solid-primitives/scheduled':
specifier: ^1.5.0
version: 1.5.0(solid-js@1.9.3)
'@solidjs/router':
specifier: ^0.14.7
version: 0.14.10(solid-js@1.9.3)
@ -876,6 +879,7 @@ packages:
'@flowr/eslint-config@4.4.1':
resolution: {integrity: sha512-orefWDMD5tGnShE+fS5b5XjfU7X0vrRORLUmouyBuRVmHbhgZEXMn4EJF4AW/Ej8p2+AV3UlybrTjU3hBVsxLg==}
engines: {node: '>=20'}
deprecated: use `@flowr/eslint` instead.
peerDependencies:
'@eslint-react/eslint-plugin': ^1.12.2
'@prettier/plugin-xml': ^3.4.1
@ -940,6 +944,7 @@ packages:
'@flowr/utils@4.4.1':
resolution: {integrity: sha512-9tWJ5TmFy0RaFtxOmuLvTmuluNoi7H1FbBxuIjO3mQatwRMz7ZAv+9uWRcB2+UUCr3IwbCcH4vC9nARZb9CsIg==}
engines: {node: '>=20'}
deprecated: use @flowr/utilities instead.
'@fontsource/poppins@5.1.0':
resolution: {integrity: sha512-tpLXlnNi2fwQjiipvuj4uNFHCdoLA8izRsKdoexZuEzjx0r/g1aKLf4ta6lFgF7L+/+AFdmaXFlUwwvmDzYH+g==}
@ -1374,6 +1379,11 @@ packages:
peerDependencies:
solid-js: ^1.6.12
'@solid-primitives/scheduled@1.5.0':
resolution: {integrity: sha512-RVw24IRNh1FQ4DCMb3OahB70tXIwc5vH8nhR4nNPsXwUPQeuOkLsDI5BlxaPk0vyZgqw9lDpufgI3HnPwplgDw==}
peerDependencies:
solid-js: ^1.6.12
'@solid-primitives/transition-group@1.0.5':
resolution: {integrity: sha512-G3FuqvL13kQ55WzWPX2ewiXdZ/1iboiX53195sq7bbkDbXqP6TYKiadwEdsaDogW5rPnPYAym3+xnsNplQJRKQ==}
peerDependencies:
@ -5311,6 +5321,10 @@ snapshots:
dependencies:
solid-js: 1.9.3
'@solid-primitives/scheduled@1.5.0(solid-js@1.9.3)':
dependencies:
solid-js: 1.9.3
'@solid-primitives/transition-group@1.0.5(solid-js@1.9.3)':
dependencies:
solid-js: 1.9.3