From 7c8e5139aa62c8d8a1fecbb0bbf5d4256f1c5c4b Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Sat, 11 May 2024 22:06:36 +0200 Subject: [PATCH] feat: ClusterLogs + ClusterOverview --- desktop/src/assets/logos/fabric.png | Bin 0 -> 5867 bytes desktop/src/assets/logos/forge.png | Bin 0 -> 4542 bytes desktop/src/assets/logos/quilt.png | Bin 0 -> 26249 bytes desktop/src/assets/logos/vanilla.png | Bin 0 -> 10411 bytes desktop/src/bridge/game.ts | 84 +++++++++++++++--- desktop/src/globals.css | 8 +- desktop/src/index.tsx | 6 +- desktop/src/ui/App.tsx | 65 ++------------ desktop/src/ui/components/AnimatedRoutes.tsx | 57 ++++++++++++ desktop/src/ui/components/Sidebar.tsx | 70 +++++++++++++++ .../src/ui/components/game/ClusterCover.tsx | 15 ++++ desktop/src/ui/components/game/LoaderIcon.tsx | 31 +++++++ desktop/src/ui/hooks/useCluster.ts | 13 +++ desktop/src/ui/pages/Home.tsx | 8 +- desktop/src/ui/pages/cluster/ClusterLogs.tsx | 61 +++++++++++++ .../src/ui/pages/cluster/ClusterOverview.tsx | 65 +++++++++++++- desktop/src/ui/pages/cluster/ClusterRoot.tsx | 38 ++++++++ desktop/src/ui/pages/cluster/index.tsx | 34 ------- 18 files changed, 438 insertions(+), 117 deletions(-) create mode 100644 desktop/src/assets/logos/fabric.png create mode 100644 desktop/src/assets/logos/forge.png create mode 100644 desktop/src/assets/logos/quilt.png create mode 100644 desktop/src/assets/logos/vanilla.png create mode 100644 desktop/src/ui/components/AnimatedRoutes.tsx create mode 100644 desktop/src/ui/components/Sidebar.tsx create mode 100644 desktop/src/ui/components/game/ClusterCover.tsx create mode 100644 desktop/src/ui/components/game/LoaderIcon.tsx create mode 100644 desktop/src/ui/hooks/useCluster.ts create mode 100644 desktop/src/ui/pages/cluster/ClusterLogs.tsx create mode 100644 desktop/src/ui/pages/cluster/ClusterRoot.tsx delete mode 100644 desktop/src/ui/pages/cluster/index.tsx diff --git a/desktop/src/assets/logos/fabric.png b/desktop/src/assets/logos/fabric.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab8370ff4b4de7b4ab665a6628e07b3f94cb44d GIT binary patch literal 5867 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 BTp|Df literal 0 HcmV?d00001 diff --git a/desktop/src/assets/logos/forge.png b/desktop/src/assets/logos/forge.png new file mode 100644 index 0000000000000000000000000000000000000000..5b8e835cc42dd60e674c718056e5405cf1f2e458 GIT binary patch literal 4542 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 literal 0 HcmV?d00001 diff --git a/desktop/src/bridge/game.ts b/desktop/src/bridge/game.ts index 24b06a94..048e5443 100644 --- a/desktop/src/bridge/game.ts +++ b/desktop/src/bridge/game.ts @@ -1,5 +1,5 @@ import { invoke } from '@tauri-apps/api/core'; -import { listen } from '@tauri-apps/api/event'; +import { type UnlistenFn, listen } from '@tauri-apps/api/event'; const _test_cluster = { cluster: { @@ -32,6 +32,28 @@ export async function getCluster(uuid: string): Promise { + return ['test log']; + try { + return await invoke( + 'plugin:onelauncher|get_cluster_logs', + { uuid }, + ); + } + catch (e) { + console.error(e); + return []; + } +} + +export async function getClusterLog(uuid: string, log: string): Promise { + return 'test log'; + return await invoke( + 'plugin:onelauncher|get_cluster_log', + { uuid, log }, + ); +} + export async function getClustersGrouped(): Promise[]>> { const clusters = await getClusters(); const map = new Map[]>(); @@ -80,19 +102,55 @@ interface LaunchCallbacks { on_stderr: (line: string) => any; }; -export async function launchCluster(uuid: string, callbacks: LaunchCallbacks): Promise { - const unlisten_launch = await listen('game:launch', e => callbacks.on_launch(e.payload)); - const unlisten_stdout = await listen('game:stdout', e => callbacks.on_stdout(e.payload)); - const unlisten_stderr = await listen('game:stderr', e => callbacks.on_stderr(e.payload)); +export function launchCluster(uuid: string, callbacks?: LaunchCallbacks): Promise { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve) => { + let unlisten_stdout: UnlistenFn | undefined; + let unlisten_stderr: UnlistenFn | undefined; + let pid: number = -1; - const exit_code = await invoke( - 'plugin:onelauncher|launch_cluster', - { uuid }, - ); + function guard(passed_pid: number, fn: () => any | undefined) { + if (passed_pid !== pid) + return; - unlisten_launch(); - unlisten_stdout(); - unlisten_stderr(); + if (fn) + fn(); + } - return exit_code; + const unlisten_launch = await listen('game:launch', async (e) => { + pid = e.payload; + + if (callbacks) + callbacks.on_launch(pid); + + unlisten_launch!(); + }); + + const unlisten_exit = await listen<[number, number]>(`game:exit`, e => guard(e.payload[0], () => { + if (callbacks) { + unlisten_stdout!(); + unlisten_stderr!(); + } + + unlisten_exit!(); + resolve(e.payload[1]); + })); + + if (callbacks) { + unlisten_stdout = await listen<[number, string]>(`game:stdout`, e => guard( + e.payload[0], + callbacks.on_stdout(e.payload[1]), + )); + + unlisten_stderr = await listen<[number, string]>(`game:stderr`, e => guard( + e.payload[0], + callbacks.on_stderr(e.payload[1]), + )); + } + + await invoke( + 'plugin:onelauncher|launch_cluster', + { uuid }, + ); + }); } diff --git a/desktop/src/globals.css b/desktop/src/globals.css index d7738ecf..eaf54c27 100644 --- a/desktop/src/globals.css +++ b/desktop/src/globals.css @@ -19,8 +19,8 @@ img { } /* end lynith's native experience (tm) */ -h1 { @apply text-2xl font-semibold; } -h2 { @apply text-xxl font-semibold; } +h1 { @apply text-2xl font-semibold; line-height: 1.5 !important; } +h2 { @apply text-xxl font-semibold; line-height: 1.25 !important; } h3 { @apply text-xl font-medium; } h4 { @apply text-lg font-medium; } @@ -31,3 +31,7 @@ h4 { @apply text-lg font-medium; } --os-handle-bg-hover: theme(colors.gray[0.10]); */ /* --os-handle-bg-active: theme(colors.gray[0.05]); */ } + +.os-hide-horizontal-scrollbar > .os-scrollbar-horizontal { + display: none; +} diff --git a/desktop/src/index.tsx b/desktop/src/index.tsx index 160d74a0..10b26960 100644 --- a/desktop/src/index.tsx +++ b/desktop/src/index.tsx @@ -8,17 +8,19 @@ import App from './ui/App'; import HomePage from './ui/pages/Home'; import BrowserPage from './ui/pages/Browser'; import UpdatesPage from './ui/pages/Updates'; -import ClusterPage from './ui/pages/cluster'; import ClusterOverview from './ui/pages/cluster/ClusterOverview'; import ClusterMods from './ui/pages/cluster/ClusterMods'; +import ClusterRoot from '~ui/pages/cluster/ClusterRoot'; +import ClusterLogs from '~ui/pages/cluster/ClusterLogs'; render(() => ( - + + diff --git a/desktop/src/ui/App.tsx b/desktop/src/ui/App.tsx index 3fe479f8..83e5f422 100644 --- a/desktop/src/ui/App.tsx +++ b/desktop/src/ui/App.tsx @@ -1,10 +1,10 @@ import type { ParentProps } from 'solid-js'; -import { Transition } from 'solid-transition-group'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid'; import environment from '../utils/environment'; import WindowFrame from './components/WindowFrame'; import Navbar from './components/Navbar'; import ErrorBoundary from './components/ErrorBoundary'; +import AnimatedRoutes from './components/AnimatedRoutes'; function App(props: ParentProps) { if (!environment.isDev()) { @@ -14,8 +14,8 @@ function App(props: ParentProps) { } return ( -
- +
+
@@ -23,71 +23,16 @@ function App(props: ParentProps) {
- + {props.children} -
-
); } -export default App; - -function AnimatedRoutes(props: ParentProps) { - const keyframesEnter = [ - { - opacity: 0, - transform: 'translateX(-100px)', - }, - { - opacity: 1, - transform: 'translateX(0px)', - }, - ]; - - const keyframesExit = [ - { - opacity: 1, - transform: 'translateX(0px)', - }, - { - opacity: 0, - transform: 'translateX(100px)', - }, - ]; - - const properties: KeyframeAnimationOptions = { - duration: 100, - easing: 'cubic-bezier(0.22, 1, 0.36, 1)', - }; - - return ( - { - const animation = element.animate( - keyframesEnter, - properties, - ); - - animation.onfinish = done; - }} - onExit={(element, done) => { - const animation = element.animate( - keyframesExit, - properties, - ); - - animation.onfinish = done; - }} - > - {props.children} - - ); -} +export default App; \ No newline at end of file diff --git a/desktop/src/ui/components/AnimatedRoutes.tsx b/desktop/src/ui/components/AnimatedRoutes.tsx new file mode 100644 index 00000000..a9929feb --- /dev/null +++ b/desktop/src/ui/components/AnimatedRoutes.tsx @@ -0,0 +1,57 @@ +import type { ParentProps } from 'solid-js'; +import { Transition } from 'solid-transition-group'; + +function AnimatedRoutes(props: ParentProps) { + const keyframesEnter = [ + { + opacity: 0, + transform: 'translateX(-100px)', + }, + { + opacity: 1, + transform: 'translateX(0px)', + }, + ]; + + const keyframesExit = [ + { + opacity: 1, + transform: 'translateX(0px)', + }, + { + opacity: 0, + transform: 'translateX(100px)', + }, + ]; + + const properties: KeyframeAnimationOptions = { + duration: 100, + easing: 'cubic-bezier(0.22, 1, 0.36, 1)', + }; + + return ( + { + const animation = element.animate( + keyframesEnter, + properties, + ); + + animation.onfinish = done; + }} + onExit={(element, done) => { + const animation = element.animate( + keyframesExit, + properties, + ); + + animation.onfinish = done; + }} + > + {props.children} + + ); +} + +export default AnimatedRoutes; diff --git a/desktop/src/ui/components/Sidebar.tsx b/desktop/src/ui/components/Sidebar.tsx new file mode 100644 index 00000000..d0550e6b --- /dev/null +++ b/desktop/src/ui/components/Sidebar.tsx @@ -0,0 +1,70 @@ +import { useLocation, useNavigate } from '@solidjs/router'; +import type { JSX, ParentProps } from 'solid-js'; +import { For, createEffect } from 'solid-js'; + +type SidebarProps = ParentProps & { + state: { + [key: string]: any; + }; + base: string; + links: { + [key: string]: [JSX.Element, string, string][]; + }; +}; + +function Sidebar(props: SidebarProps) { + const searchParams = new URLSearchParams(); + const navigate = useNavigate(); + const location = useLocation(); + + createEffect(() => { + if (props.base.endsWith('/')) + throw new Error('Base should not end with a slash'); + + Object.keys(props.state).forEach((key) => { + searchParams.set(key, props.state[key]); + }); + }); + + function goto(href: string) { + const url = `${props.base}${href}?${searchParams.toString()}`; + navigate(url); + } + + function isActive(link: string) { + return location.pathname === `${props.base}${link}`; + } + + return ( + + ); +} + +export default Sidebar; diff --git a/desktop/src/ui/components/game/ClusterCover.tsx b/desktop/src/ui/components/game/ClusterCover.tsx new file mode 100644 index 00000000..e8b22c69 --- /dev/null +++ b/desktop/src/ui/components/game/ClusterCover.tsx @@ -0,0 +1,15 @@ +import { type JSX, splitProps } from 'solid-js'; +import defaultCover from '~assets/images/default_instance_cover.jpg'; + +type ClusterCoverProps = JSX.HTMLAttributes & { + cluster: Core.Cluster; +}; + +function ClusterCover(props: ClusterCoverProps) { + const [split, rest] = splitProps(props, ['cluster']); + return ( + + ); +} + +export default ClusterCover; diff --git a/desktop/src/ui/components/game/LoaderIcon.tsx b/desktop/src/ui/components/game/LoaderIcon.tsx new file mode 100644 index 00000000..0b3c7860 --- /dev/null +++ b/desktop/src/ui/components/game/LoaderIcon.tsx @@ -0,0 +1,31 @@ +import { type JSX, splitProps } from 'solid-js'; +import fabric from '~assets/logos/fabric.png'; +import forge from '~assets/logos/forge.png'; +import quilt from '~assets/logos/quilt.png'; +import vanilla from '~assets/logos/vanilla.png'; + +export function getLoaderIcon(loader: Core.Loader) { + switch (loader) { + case 'Vanilla': + return vanilla; + case 'Forge': + return forge; + case 'Fabric': + return fabric; + case 'Quilt': + return quilt; + } +} + +type LoaderIconProp = JSX.HTMLAttributes & { + loader: Core.Loader; +}; + +function LoaderIcon(props: LoaderIconProp) { + const [split, rest] = splitProps(props, ['loader']); + return ( + {split.loader} + ); +} + +export default LoaderIcon; diff --git a/desktop/src/ui/hooks/useCluster.ts b/desktop/src/ui/hooks/useCluster.ts new file mode 100644 index 00000000..9d0276c8 --- /dev/null +++ b/desktop/src/ui/hooks/useCluster.ts @@ -0,0 +1,13 @@ +import type { Resource } from 'solid-js'; +import { createResource } from 'solid-js'; +import { getCluster } from '../../bridge/game'; + +function useCluster(uuid: string | undefined | null): Resource | null { + if (typeof uuid !== 'string' || uuid.length === 0) + return null; + + const [resource] = createResource(uuid, getCluster); + return resource; +} + +export default useCluster; diff --git a/desktop/src/ui/pages/Home.tsx b/desktop/src/ui/pages/Home.tsx index 55bc3334..51d2ae44 100644 --- a/desktop/src/ui/pages/Home.tsx +++ b/desktop/src/ui/pages/Home.tsx @@ -10,12 +10,12 @@ import type { Accessor, Setter } from 'solid-js'; import { For, Show, createResource, createSignal } from 'solid-js'; import { useNavigate } from '@solidjs/router'; import image from '../../assets/images/header.png'; -import defaultCover from '../../assets/images/default_instance_cover.jpg'; import ContextMenu from '../components/overlay/ContextMenu'; import { createCluster, getClustersGrouped, launchCluster } from '../../bridge/game'; import Button from '../components/base/Button'; import Tag from '../components/base/Tag'; import TextField from '../components/base/TextField'; +import ClusterCover from '~ui/components/game/ClusterCover'; // TODO: Replace this into it's own component // possibly with the core ui lirbary with autogenerated and optimized svgs pls @@ -142,9 +142,9 @@ function ClusterCard(props: Core.ClusterWithManifest) { class="absolute h-full w-full group-hover:!scale-110 transition-transform" style={{ '-webkit-transform': 'translateZ(0)' }} > - diff --git a/desktop/src/ui/pages/cluster/ClusterLogs.tsx b/desktop/src/ui/pages/cluster/ClusterLogs.tsx new file mode 100644 index 00000000..046ae7c2 --- /dev/null +++ b/desktop/src/ui/pages/cluster/ClusterLogs.tsx @@ -0,0 +1,61 @@ +import { useSearchParams } from '@solidjs/router'; +import { For, Show, createEffect, createResource, createSignal } from 'solid-js'; +import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid'; +import { getClusterLog, getClusterLogs } from '../../../bridge/game'; + +function ClusterLogs() { + const [params] = useSearchParams(); + const [logs] = createResource(params.id, getClusterLogs); + + const [activeLogFile, setActiveLogFile] = createSignal(null); + const [log, setLog] = createSignal(null); + + createEffect(() => { + const log = logs()?.[0]; + + // Set default log to the first file + if (log && activeLogFile() === null) + setActiveLogFile(log); + + // Fetch log content + if (activeLogFile() !== null) { + getClusterLog(params.id!, activeLogFile()!).then((log) => { + setLog(log); + }); + } + }); + + return ( +
+

Logs

+ +
+
+ + {(log) => { + const pretty = log.split('/').pop(); + return ( +
+ + {/* TODO: Try fix this when using OverlayScrollbarComponent https://github.com/KingSora/OverlayScrollbars/issues/627 */} + No logs found}> +
+ + {log()} + +
+
+
+
+ ); +} + +export default ClusterLogs; diff --git a/desktop/src/ui/pages/cluster/ClusterOverview.tsx b/desktop/src/ui/pages/cluster/ClusterOverview.tsx index 9ac16c57..88b243bf 100644 --- a/desktop/src/ui/pages/cluster/ClusterOverview.tsx +++ b/desktop/src/ui/pages/cluster/ClusterOverview.tsx @@ -1,7 +1,68 @@ +import { useSearchParams } from '@solidjs/router'; +import { Show } from 'solid-js'; +import { PlayIcon, Share07Icon } from '@untitled-theme/icons-solid'; +import useCluster from '../../hooks/useCluster'; +import ClusterCover from '../../components/game/ClusterCover'; +import LoaderIcon from '../../components/game/LoaderIcon'; +import Button from '../../components/base/Button'; +import { launchCluster } from '../../../bridge/game'; + function ClusterOverview() { + const [params] = useSearchParams(); + const cluster = useCluster(params.id); + if (cluster === null) + throw new Error('Cluster doesn\'t exist'); + return ( -
-

This is the overview

+
+

Overview

+ + + + +
+ ); +} + +// eslint-disable-next-line solid/no-destructure +function Banner({ cluster, manifest }: Core.ClusterWithManifest) { + return ( +
+
+ +
+ +
+
+

{cluster.name}

+ + + {cluster.client.type} + {manifest.manifest.id} + +
+ + Played for + {' '} + 3.9 + {' '} + hours + +
+ +
+
); } diff --git a/desktop/src/ui/pages/cluster/ClusterRoot.tsx b/desktop/src/ui/pages/cluster/ClusterRoot.tsx new file mode 100644 index 00000000..41254d84 --- /dev/null +++ b/desktop/src/ui/pages/cluster/ClusterRoot.tsx @@ -0,0 +1,38 @@ +import { useSearchParams } from '@solidjs/router'; +import type { ParentProps } from 'solid-js'; +import { EyeIcon, File06Icon, PackagePlusIcon } from '@untitled-theme/icons-solid'; +import Sidebar from '../../components/Sidebar'; +import AnimatedRoutes from '../../components/AnimatedRoutes'; +import ErrorBoundary from '../../components/ErrorBoundary'; + +function ClusterRoot(props: ParentProps) { + const [searchParams] = useSearchParams(); + + return ( +
+
+ , 'Overview', '/'], + [, 'Mods', '/mods'], + [, 'Logs', '/logs'], + ], + }} + /> +
+ +
+ + + {props.children} + + +
+
+ ); +} + +export default ClusterRoot; diff --git a/desktop/src/ui/pages/cluster/index.tsx b/desktop/src/ui/pages/cluster/index.tsx deleted file mode 100644 index 1205de77..00000000 --- a/desktop/src/ui/pages/cluster/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { A, type AnchorProps, useNavigate, useSearchParams } from '@solidjs/router'; -import { type ParentProps, createEffect, splitProps } from 'solid-js'; - -function Link(props: AnchorProps) { - const [split, rest] = splitProps(props, ['href']); - const [searchParams] = useSearchParams(); - - return ( - - ); -} - -function ClusterPage(props: ParentProps) { - const [searchParams] = useSearchParams(); - const navigate = useNavigate(); - - createEffect(() => { - if (typeof searchParams.id !== 'string') - navigate('/'); - }); - - return ( -
-
- Overview - mods -
- {props.children} - {searchParams.id} -
- ); -} - -export default ClusterPage;