diff --git a/core/src/store/clusters.rs b/core/src/store/clusters.rs index 4093030a..d9886fc9 100644 --- a/core/src/store/clusters.rs +++ b/core/src/store/clusters.rs @@ -42,6 +42,7 @@ pub enum ClusterStage { } /// Relative Path wrapper to be used as an identifer for a cluster path. +#[cfg_attr(feature = "tauri", derive(specta::Type))] #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)] #[serde(transparent)] pub struct ClusterPath(pub PathBuf); diff --git a/desktop/src-tauri/src/api/commands.rs b/desktop/src-tauri/src/api/commands.rs index 607befbe..1ad7b513 100644 --- a/desktop/src-tauri/src/api/commands.rs +++ b/desktop/src-tauri/src/api/commands.rs @@ -1,6 +1,7 @@ -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf, str::FromStr}; -use onelauncher::{cluster, data::{Loader, PackageData}}; +use chrono::DateTime; +use onelauncher::{cluster, data::{ClusterMeta, Loader, PackageData}, store::{Cluster, ClusterPath}, State}; use serde::{Deserialize, Serialize}; use specta::Type; use uuid::Uuid; @@ -11,9 +12,12 @@ macro_rules! collect_commands { { use $crate::api::commands::*; tauri_specta::ts::builder() + .config(specta::ts::ExportConfig::default().bigint(specta::ts::BigIntExportBehavior::BigInt)) .commands(tauri_specta::collect_commands![ is_dev, - create_cluster + create_cluster, + get_cluster, + get_clusters ]) } }; @@ -59,3 +63,57 @@ pub async fn create_cluster(props: CreateCluster) -> Result { Err("Cluster does not exist".to_string()) } } + +fn placeholder_cluster() -> Cluster { + let path = ClusterPath("test".into()); + Cluster { + uuid: Uuid::from_str("56d1cbcf-1961-4477-b263-80e3b1c7a9d1").unwrap(), + stage: onelauncher::store::ClusterStage::Installed, + path: path.0, + meta: ClusterMeta { + created_at: DateTime::from_timestamp_millis(1718297861712).unwrap(), + modified_at: DateTime::from_timestamp_millis(1718297861712).unwrap(), + group: vec![], + icon: None, + icon_url: None, + loader: Loader::Vanilla, + loader_version: None, + mc_version: "1.8.9".into(), + name: "Test Cluster".into(), + overall_played: 58195, + recently_played: 0, + package_data: None, + played_at: None + }, + memory: None, + java: None, + resolution: None, + force_fullscreen: None, + init_hooks: None, + packages: HashMap::new(), + update: None + } +} + +#[specta::specta] +#[tauri::command] +pub async fn get_cluster(uuid: Uuid) -> Result { + Ok(placeholder_cluster()) + + // match cluster::get_by_uuid(uuid, None).await? { + // Some(cluster) => Ok(cluster), + // None => Err("Cluster does not exist".into()) + // } +} + +#[specta::specta] +#[tauri::command] +pub async fn get_clusters() -> Result, String> { + let mut map = HashMap::::new(); + let cluster = placeholder_cluster(); + map.insert(cluster.cluster_path(), cluster); + + Ok(map) + + // cluster::list(None).await.map_err(|op| op.into()) +} diff --git a/desktop/src/ui/components/game/ClusterCover.tsx b/desktop/src/ui/components/game/ClusterCover.tsx index e8b22c69..df21e969 100644 --- a/desktop/src/ui/components/game/ClusterCover.tsx +++ b/desktop/src/ui/components/game/ClusterCover.tsx @@ -1,14 +1,15 @@ import { type JSX, splitProps } from 'solid-js'; import defaultCover from '~assets/images/default_instance_cover.jpg'; +import type { Cluster } from '~bindings'; type ClusterCoverProps = JSX.HTMLAttributes & { - cluster: Core.Cluster; + cluster: Cluster; }; function ClusterCover(props: ClusterCoverProps) { const [split, rest] = splitProps(props, ['cluster']); return ( - + ); } diff --git a/desktop/src/ui/components/game/LoaderIcon.tsx b/desktop/src/ui/components/game/LoaderIcon.tsx index 0b3c7860..b4a5d7a5 100644 --- a/desktop/src/ui/components/game/LoaderIcon.tsx +++ b/desktop/src/ui/components/game/LoaderIcon.tsx @@ -3,28 +3,30 @@ 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'; +import type { Loader } from '~bindings'; -export function getLoaderIcon(loader: Core.Loader) { +export function getLoaderIcon(loader: Loader): string { switch (loader) { - case 'Vanilla': - return vanilla; - case 'Forge': + case 'forge': return forge; - case 'Fabric': + case 'fabric': return fabric; - case 'Quilt': + case 'quilt': return quilt; + case 'vanilla': + default: + return vanilla; } } type LoaderIconProp = JSX.HTMLAttributes & { - loader: Core.Loader; + loader: Loader | undefined; }; function LoaderIcon(props: LoaderIconProp) { - const [split, rest] = splitProps(props, ['loader']); + const [{ loader = 'vanilla' }, rest] = splitProps(props, ['loader']); return ( - {split.loader} + {loader} ); } diff --git a/desktop/src/ui/hooks/useCluster.ts b/desktop/src/ui/hooks/useCluster.ts index b580d098..adc644f3 100644 --- a/desktop/src/ui/hooks/useCluster.ts +++ b/desktop/src/ui/hooks/useCluster.ts @@ -1,15 +1,13 @@ import type { Resource } from 'solid-js'; -import { createResource } from 'solid-js'; +import useCommand from './useCommand'; +import type { Cluster } from '~bindings'; +import { bridge } from '~index'; -function getCluster(uuid: string): Core.ClusterWithManifest { - throw new Error('get cluster'); -} - -function useCluster(uuid: string | undefined | null): Resource | null { +function useCluster(uuid: string | undefined | null): Resource | null { if (typeof uuid !== 'string' || uuid.length === 0) return null; - const [resource] = createResource(uuid, getCluster); + const [resource] = useCommand(bridge.commands.getCluster, uuid); return resource; } diff --git a/desktop/src/ui/hooks/useCommand.ts b/desktop/src/ui/hooks/useCommand.ts new file mode 100644 index 00000000..44ff2484 --- /dev/null +++ b/desktop/src/ui/hooks/useCommand.ts @@ -0,0 +1,21 @@ +import { type Resource, type ResourceReturn, createResource } from 'solid-js'; +import type { Result } from '~bindings'; + +/** + * A helper function which handles a Rust Result properly + * @param cmd The command to call + * @throws + */ +export default function useCommand( + cmd: (...args: Args) => Promise>, + ...args: Args +): ResourceReturn { + return createResource(async () => { + const value = await cmd(...args); + + if (value.status === 'ok') + return value.data; + + throw value.error; + }); +} diff --git a/desktop/src/ui/pages/Home.tsx b/desktop/src/ui/pages/Home.tsx index 75456d16..cf856f40 100644 --- a/desktop/src/ui/pages/Home.tsx +++ b/desktop/src/ui/pages/Home.tsx @@ -7,7 +7,7 @@ import { SearchMdIcon, } from '@untitled-theme/icons-solid'; import type { Accessor, Setter } from 'solid-js'; -import { For, createSignal } from 'solid-js'; +import { For, Show, createSignal } from 'solid-js'; import { useNavigate } from '@solidjs/router'; import image from '../../assets/images/header.png'; import ContextMenu from '../components/overlay/ContextMenu'; @@ -15,6 +15,9 @@ 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'; +import type { Cluster } from '~bindings'; +import useCommand from '~ui/hooks/useCommand'; +import { bridge } from '~index'; // TODO: Replace this into it's own component // possibly with the core ui lirbary with autogenerated and optimized svgs pls @@ -97,14 +100,14 @@ function ClusterCardContextMenu(props: ClusterCardContextMenuProps) { ); } -function ClusterCard(props: Core.ClusterWithManifest) { +function ClusterCard(props: Cluster) { const navigate = useNavigate(); const [pos, setPos] = createSignal({ x: 0, y: 0 }); const [contextMenuVisible, setContextMenuVisible] = createSignal(false); let ref!: HTMLDivElement; function openClusterPage(_e: MouseEvent) { - navigate(`/clusters/?id=${props.cluster.id}`); + navigate(`/clusters/?id=${props.uuid}`); } function openContextMenu(e: MouseEvent) { @@ -132,18 +135,18 @@ function ClusterCard(props: Core.ClusterWithManifest) { style={{ '-webkit-transform': 'translateZ(0)' }} >
-

{props.cluster.name}

+

{props.meta.name}

- {props.cluster.client.type} + {props.meta.loader} {' '} - {props.manifest.manifest.id} + {props.meta.mc_version} {/* {' '} {props.instance.mods && `• ${props.mods} mods`} */}

@@ -158,7 +161,7 @@ function ClusterCard(props: Core.ClusterWithManifest) { contextMenuVisible={contextMenuVisible} setContextMenuVisible={setContextMenuVisible} pos={pos} - id={props.cluster.id} + id={props.uuid} /> ); @@ -166,7 +169,7 @@ function ClusterCard(props: Core.ClusterWithManifest) { interface ClusterGroupProps { title: string; - clusters: Core.ClusterWithManifest[]; + clusters: Cluster[]; } function ClusterGroup(props: ClusterGroupProps) { @@ -181,7 +184,7 @@ function ClusterGroup(props: ClusterGroupProps) { } function HomePage() { - // const [clusters, { refetch }] = createResource(getClustersGrouped); + const [clusters, { refetch }] = useCommand(bridge.commands.getClusters); async function newInstance() { // bridge.commands.createCluster({ @@ -225,13 +228,9 @@ function HomePage() {
- {/* - - {([title, clusters]) => ( - - )} - - */} + + + ); } diff --git a/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx b/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx index e88da4aa..a3d20eed 100644 --- a/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx +++ b/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx @@ -7,6 +7,8 @@ import LoaderIcon from '../../../components/game/LoaderIcon'; import Button from '../../../components/base/Button'; import ScrollableContainer from '~ui/components/ScrollableContainer'; import Sidebar from '~ui/components/Sidebar'; +import type { Cluster } from '~bindings'; +import { upperFirst } from '~utils/string'; function ClusterOverview() { const [params] = useSearchParams(); @@ -26,29 +28,30 @@ function ClusterOverview() { ); } -// eslint-disable-next-line solid/no-destructure -function Banner({ cluster, manifest }: Core.ClusterWithManifest) { +function Banner(props: Cluster) { return (
- +
-

{cluster.name}

+

{props.meta.name}

- - {cluster.client.type} - {manifest.manifest.id} + + {upperFirst(props.meta.loader || 'unknown')} + {props.meta.loader_version && {props.meta.loader_version.id}} + {props.meta.mc_version}
Played for {' '} - 3.9 + {((props.meta.overall_played || 0n)).toString()} {' '} hours + {/* TODO: Ask Pauline if this is measured in seconds or milliseconds */}