chore: Do some work on reimplementing the Rust side into the frontend side
This commit is contained in:
parent
e2495c78d4
commit
576c8f0d22
8 changed files with 129 additions and 46 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<Uuid, String> {
|
|||
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<Cluster, String> {
|
||||
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<HashMap<ClusterPath, Cluster>, String> {
|
||||
let mut map = HashMap::<ClusterPath, Cluster>::new();
|
||||
let cluster = placeholder_cluster();
|
||||
map.insert(cluster.cluster_path(), cluster);
|
||||
|
||||
Ok(map)
|
||||
|
||||
// cluster::list(None).await.map_err(|op| op.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<HTMLImageElement> & {
|
||||
cluster: Core.Cluster;
|
||||
cluster: Cluster;
|
||||
};
|
||||
|
||||
function ClusterCover(props: ClusterCoverProps) {
|
||||
const [split, rest] = splitProps(props, ['cluster']);
|
||||
return (
|
||||
<img {...rest} src={split.cluster.cover || defaultCover} />
|
||||
<img {...rest} src={split.cluster.meta.icon_url || defaultCover} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<HTMLImageElement> & {
|
||||
loader: Core.Loader;
|
||||
loader: Loader | undefined;
|
||||
};
|
||||
|
||||
function LoaderIcon(props: LoaderIconProp) {
|
||||
const [split, rest] = splitProps(props, ['loader']);
|
||||
const [{ loader = 'vanilla' }, rest] = splitProps(props, ['loader']);
|
||||
return (
|
||||
<img {...rest} src={getLoaderIcon(split.loader)} alt={split.loader} />
|
||||
<img {...rest} src={getLoaderIcon(loader)} alt={loader} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Core.ClusterWithManifest> | null {
|
||||
function useCluster(uuid: string | undefined | null): Resource<Cluster> | null {
|
||||
if (typeof uuid !== 'string' || uuid.length === 0)
|
||||
return null;
|
||||
|
||||
const [resource] = createResource(uuid, getCluster);
|
||||
const [resource] = useCommand(bridge.commands.getCluster, uuid);
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
|
|
|||
21
desktop/src/ui/hooks/useCommand.ts
Normal file
21
desktop/src/ui/hooks/useCommand.ts
Normal file
|
|
@ -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<R, E, Args extends unknown[]>(
|
||||
cmd: (...args: Args) => Promise<Result<R, E>>,
|
||||
...args: Args
|
||||
): ResourceReturn<R> {
|
||||
return createResource(async () => {
|
||||
const value = await cmd(...args);
|
||||
|
||||
if (value.status === 'ok')
|
||||
return value.data;
|
||||
|
||||
throw value.error;
|
||||
});
|
||||
}
|
||||
|
|
@ -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)' }}
|
||||
>
|
||||
<ClusterCover
|
||||
cluster={props.cluster}
|
||||
cluster={props}
|
||||
class="object-cover h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="z-10 p-3 flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<p class="h-4 font-semibold">{props.cluster.name}</p>
|
||||
<p class="h-4 font-semibold">{props.meta.name}</p>
|
||||
<p class="h-4 text-xs">
|
||||
{props.cluster.client.type}
|
||||
{props.meta.loader}
|
||||
{' '}
|
||||
{props.manifest.manifest.id}
|
||||
{props.meta.mc_version}
|
||||
{/* {' '}
|
||||
{props.instance.mods && `• ${props.mods} mods`} */}
|
||||
</p>
|
||||
|
|
@ -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() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* <Show when={clusters() !== undefined}>
|
||||
<For each={Array.from(clusters()!.entries())}>
|
||||
{([title, clusters]) => (
|
||||
<ClusterGroup title={title} clusters={clusters} />
|
||||
)}
|
||||
</For>
|
||||
</Show> */}
|
||||
<Show when={clusters() !== undefined}>
|
||||
<ClusterGroup title="Placeholder" clusters={Object.values(clusters()!)} />
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div class="flex flex-row bg-component-bg rounded-xl p-2.5 gap-x-2.5">
|
||||
<div class="w-48 rounded-lg overflow-hidden border border-gray-10">
|
||||
<ClusterCover cluster={cluster} />
|
||||
<ClusterCover cluster={props} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col flex-1 text-fg-primary">
|
||||
<div class="flex-1">
|
||||
<h2 class="text-2xl">{cluster.name}</h2>
|
||||
<h2 class="text-2xl">{props.meta.name}</h2>
|
||||
<span class="flex flex-row items-center gap-x-1">
|
||||
<LoaderIcon loader={cluster.client.type} class="w-5" />
|
||||
<span>{cluster.client.type}</span>
|
||||
<span>{manifest.manifest.id}</span>
|
||||
<LoaderIcon loader={props.meta.loader} class="w-5" />
|
||||
<span>{upperFirst(props.meta.loader || 'unknown')}</span>
|
||||
{props.meta.loader_version && <span>{props.meta.loader_version.id}</span>}
|
||||
<span>{props.meta.mc_version}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs text-fg-secondary">
|
||||
Played for
|
||||
{' '}
|
||||
<b>3.9</b>
|
||||
<b>{((props.meta.overall_played || 0n)).toString()}</b>
|
||||
{' '}
|
||||
hours
|
||||
{/* TODO: Ask Pauline if this is measured in seconds or milliseconds */}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue