feat: Launch button debounce + now in cluster sidebar
This commit is contained in:
parent
2231be828f
commit
59183e9124
7 changed files with 101 additions and 48 deletions
|
|
@ -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",
|
||||
|
|
|
|||
49
apps/frontend/src/ui/components/LaunchButton.tsx
Normal file
49
apps/frontend/src/ui/components/LaunchButton.tsx
Normal 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;
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
14
pnpm-lock.yaml
generated
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue