From 5e6c628f556a2cb667b69f1415dcc6d14dec4c10 Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:43:24 +0200 Subject: [PATCH] feat: Less empty home page + work on integrating backend with frontend --- apps/desktop/src-tauri/src/api/commands.rs | 106 +++++++++-------- apps/desktop/src/ui/App.tsx | 51 ++++---- .../overlay/cluster/ClusterCreationModal.tsx | 110 ++++++++++++------ .../overlay/cluster/ClusterStepOne.tsx | 10 +- .../overlay/cluster/ClusterStepTwo.tsx | 19 ++- apps/desktop/src/ui/pages/Home.tsx | 108 +++++++++-------- crates/core/src/api/cluster/create.rs | 6 +- crates/core/src/api/cluster/mod.rs | 11 +- crates/core/src/api/package/import/mod.rs | 1 + crates/testing/Cargo.toml | 2 +- crates/testing/src/main.rs | 9 +- 11 files changed, 253 insertions(+), 180 deletions(-) diff --git a/apps/desktop/src-tauri/src/api/commands.rs b/apps/desktop/src-tauri/src/api/commands.rs index 5c97f099..25318b25 100644 --- a/apps/desktop/src-tauri/src/api/commands.rs +++ b/apps/desktop/src-tauri/src/api/commands.rs @@ -1,13 +1,9 @@ -use std::collections::HashMap; use std::path::PathBuf; -use std::str::FromStr; -use anyhow::anyhow; -use chrono::DateTime; use interpulse::api::minecraft::Version; use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION}; -use onelauncher::data::{ClusterMeta, Loader, MinecraftCredentials, PackageData, Settings}; -use onelauncher::store::{Cluster, ClusterPath, MinecraftLogin}; +use onelauncher::data::{Loader, MinecraftCredentials, PackageData, Settings}; +use onelauncher::store::{Cluster, MinecraftLogin}; use onelauncher::{cluster, minecraft, settings}; use serde::{Deserialize, Serialize}; use specta::Type; @@ -81,58 +77,56 @@ pub async fn create_cluster(props: CreateCluster) -> Result { // } -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, +// 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 { + 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_cluster(uuid: Uuid) -> Result { - Ok(placeholder_cluster()) +pub async fn get_clusters() -> Result, String> { + // let mut map = HashMap::::new(); + // let cluster = placeholder_cluster(); + // map.insert(cluster.cluster_path(), cluster); - // match cluster::get_by_uuid(uuid, None).await? { - // Some(cluster) => Ok(cluster), - // None => Err("Cluster does not exist".into()) - // } -} + // Ok(map) -#[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()) + Ok(cluster::list(None).await?) } #[specta::specta] @@ -212,3 +206,13 @@ pub async fn finish_msa( pub async fn remove_user(uuid: Uuid) -> Result<(), String> { Ok(minecraft::remove_user(uuid).await?) } + +// #[specta::specta] +// #[tauri::command] +// pub async fn import_instance() -> Result<(), String> { +// onelauncher::api::package::import::import_instance() +// } + +// #[specta::specta] +// #[tauri::command] +// pub async fn diff --git a/apps/desktop/src/ui/App.tsx b/apps/desktop/src/ui/App.tsx index fce3a0b0..2ded5981 100644 --- a/apps/desktop/src/ui/App.tsx +++ b/apps/desktop/src/ui/App.tsx @@ -16,31 +16,40 @@ function App(props: ParentProps) { }); return ( - - -
- -
- -
+ +
+ +
+ +
-
-
- - - - {props.children} - - - -
+
+
+ + + + {props.children} + + +
+
- -
- - + +
+ ); } export default App; + +function GlobalContexts(props: ParentProps) { + // TODO(refactor): Something something lots of nesting will look awful soon + return ( + + + {props.children} + + + ); +} diff --git a/apps/desktop/src/ui/components/overlay/cluster/ClusterCreationModal.tsx b/apps/desktop/src/ui/components/overlay/cluster/ClusterCreationModal.tsx index 37895ad6..6c41fcb8 100644 --- a/apps/desktop/src/ui/components/overlay/cluster/ClusterCreationModal.tsx +++ b/apps/desktop/src/ui/components/overlay/cluster/ClusterCreationModal.tsx @@ -1,13 +1,15 @@ -import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createEffect, createSignal, useContext } from 'solid-js'; +import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createEffect, createSignal, untrack, useContext } from 'solid-js'; import { ArrowRightIcon, Server01Icon } from '@untitled-theme/icons-solid'; import HeaderImage from '../../../../assets/images/header.png'; -import FullscreenOverlay, { type FullscreenOverlayProps } from '../FullscreenOverlay'; +import FullscreenOverlay from '../FullscreenOverlay'; import ClusterStepOne from './ClusterStepOne'; import { ClusterStepTwo } from './ClusterStepTwo'; import Button from '~ui/components/base/Button'; import type { CreateCluster } from '~bindings'; +import { bridge } from '~imports'; type PartialCluster = Partial; +type PartialClusterUpdateFunc = (key: K, value: PartialCluster[K]) => any; // Why the fuck do I need to use a context for all this??? // TODO: Rewrite for use with the new Modal stacking system @@ -16,7 +18,9 @@ interface ClusterModalContextFunc { setStep: Setter; partialCluster: Accessor; setPartialCluster: Setter; - updatePartialCluster: (key: K, value: PartialCluster[K]) => any; + updatePartialCluster: PartialClusterUpdateFunc; + finish: () => void; + visible: Accessor; setVisible: Setter; } @@ -34,19 +38,43 @@ export function ClusterModalController(props: ParentProps) { const [visible, setVisible] = createSignal(false); const [partialCluster, setPartialCluster] = createSignal({}); + const updatePartialCluster: PartialClusterUpdateFunc = (key, value) => { + setPartialCluster((prev) => { + return { + ...prev, + [key]: value, + }; + }); + }; + + const finish = () => { + const untracked = untrack(partialCluster); + + if (untracked.name === undefined || untracked.mc_version === undefined || untracked.mod_loader === undefined) + throw new Error('Cluster is missing required fields'); + + bridge.commands.createCluster({ + name: untracked.name!, + mod_loader: untracked.mod_loader!, + mc_version: untracked.mc_version!, + // TODO: Implement the rest of the fields + icon: null, + icon_url: null, + loader_version: null, + package_data: null, + skip: null, + skip_watch: null, + }); + }; + const stepper: ClusterModalContextFunc = { step, setStep, partialCluster, setPartialCluster, - updatePartialCluster(key, value) { - setPartialCluster((prev) => { - return { - ...prev, - [key]: value, - }; - }); - }, + updatePartialCluster, + finish, + visible, setVisible, }; @@ -60,13 +88,7 @@ export function ClusterModalController(props: ParentProps) { {props.children} {/* Makes sure theres a new instance of the modal */} - + ); @@ -81,13 +103,10 @@ export interface ClusterStepProps { setCanGoForward: Setter; }; -type ClusterCreationModalProps = FullscreenOverlayProps & { - step: Accessor; - setStep: Setter; - buttonIsNext: boolean; -}; +function ClusterCreationModal() { + const controller = useClusterModalController(); + const buttonIsNext = () => controller.step() !== ((Object.keys(ClusterModalStages).length / 2) - 1); -function ClusterCreationModal(props: ClusterCreationModalProps) { const [canGoForward, setCanGoForward] = createSignal(false); // Indexed by the current step const messages: FixedArray = [ @@ -101,18 +120,32 @@ function ClusterCreationModal(props: ClusterCreationModalProps) { ].map((Component, index) => ( Component({ setCanGoForward, - isVisible: () => props.visible() && props.step() === index, + isVisible: () => controller.visible() && controller.step() === index, }) )); const Step = (props: { step: number }) => <>{stepComponents[props.step]}; + function cancel() { + controller.setVisible(false); + } + + function prev() { + controller.setStep(prev => prev - 1); + } + + function next() { + controller.setStep(prev => prev + 1); + } + + function finish() { + controller.finish(); + } + return (
@@ -127,49 +160,50 @@ function ClusterCreationModal(props: ClusterCreationModalProps) {
{/** weird positioning?? */}

New Cluster

- {messages[props.step()]} + {messages[controller.step()]}
- +
- +
+
+ + + +
+ No clusters were found. + Create one now with the New Cluster button. +
+
+ + + +
+ + ); +} + +export default HomePage; + // TODO: Replace this into it's own component // possibly with the core ui lirbary with autogenerated and optimized svgs pls function OneConfigLogo() { @@ -40,6 +83,15 @@ function OneConfigLogo() { } function Banner() { + // TODO: Banner information + /** + * If there are any clusters, display the most recent cluster name + some statistics as the "description". + * The background would prioritise + * any screenshots taken from the cluster, if there are none, it would display the cluster cover if it has been set. + * The button would launch the cluster. + * + * If there are no clusters, display a generic background with the button action creating a new cluster. + */ return (
@@ -50,10 +102,15 @@ function Banner() {
- - +
} /> @@ -184,44 +241,3 @@ function ClusterGroup(props: ClusterGroupProps) {
); } - -function HomePage() { - const [clusters, { refetch }] = useCommand(bridge.commands.getClusters); - const { setVisible } = useClusterModalController(); - - async function newInstance() { - setVisible(true); - } - - return ( -
- - -
-
- } placeholder="Search for something..." /> -
-
-
-
- - - - -
- ); -} - -export default HomePage; diff --git a/crates/core/src/api/cluster/create.rs b/crates/core/src/api/cluster/create.rs index 22940ebf..3dc92788 100644 --- a/crates/core/src/api/cluster/create.rs +++ b/crates/core/src/api/cluster/create.rs @@ -107,9 +107,9 @@ pub async fn create_cluster( .await?; } - if !skip.unwrap_or(false) { - crate::game::install_minecraft(&cluster, None, false).await?; - } + // if !skip.unwrap_or(false) { + // crate::game::install_minecraft(&cluster, None, false).await?; + // } State::sync().await?; diff --git a/crates/core/src/api/cluster/mod.rs b/crates/core/src/api/cluster/mod.rs index 83c74c6a..297c18c0 100644 --- a/crates/core/src/api/cluster/mod.rs +++ b/crates/core/src/api/cluster/mod.rs @@ -45,7 +45,7 @@ pub async fn get(path: &ClusterPath, clear: Option) -> crate::Result) -> crate::Result> { +pub async fn list(clear: Option) -> crate::Result> { let state = State::get().await?; let clusters = state.clusters.read().await; Ok(clusters @@ -56,11 +56,18 @@ pub async fn list(clear: Option) -> crate::Result) -> crate::Result> { +// let clusters = list(clear).await?; +// let mut map = HashMap::new(); + +// } + /// run a Minecraft [`Cluster`] using the default credentials. #[tracing::instrument] pub async fn run(path: &ClusterPath) -> crate::Result>> { diff --git a/crates/core/src/api/package/import/mod.rs b/crates/core/src/api/package/import/mod.rs index 8150cf59..d6571ed8 100644 --- a/crates/core/src/api/package/import/mod.rs +++ b/crates/core/src/api/package/import/mod.rs @@ -21,6 +21,7 @@ pub mod technic; pub mod tlauncher; /// List of launcher types we support importing from. +#[cfg_attr(feature = "tauri", derive(specta::Type))] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum ImportType { /// MultiMC based launchers diff --git a/crates/testing/Cargo.toml b/crates/testing/Cargo.toml index c71a53cf..35c98890 100644 --- a/crates/testing/Cargo.toml +++ b/crates/testing/Cargo.toml @@ -15,4 +15,4 @@ onelauncher = { path = "../core", features = [ "cli" ] } tokio = { workspace = true } webbrowser = { version = "0.8" } -url = { version = "2.5" } +url = { version = "2.5.2" } diff --git a/crates/testing/src/main.rs b/crates/testing/src/main.rs index 422ff2bb..84d53f75 100644 --- a/crates/testing/src/main.rs +++ b/crates/testing/src/main.rs @@ -23,11 +23,12 @@ pub async fn authenticate_mc() -> onelauncher::Result { let parsed = url::Url::parse(input.trim()).expect("idk"); let code = if let Some((_, code)) = parsed.query_pairs().find(|x| x.0 == "code") { - &code.clone() + let code = code.clone(); + code.to_string() } else { panic!() }; - let creds = minecraft::finish(code, login).await?; + let creds = minecraft::finish(code.as_str(), login).await?; println!("logged in {}", creds.username); @@ -53,8 +54,8 @@ async fn main() -> onelauncher::Result<()> { println!("clearing clusters"); { let c = cluster::list(None).await?; - for (id, _) in c.into_iter() { - cluster::remove(&id).await?; + for cluster in c.into_iter() { + cluster::remove(&cluster.cluster_path()).await?; } }