From 19ade5dc810e2d03e70c787bf8706b25ac7ce711 Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Sun, 16 Jun 2024 21:54:27 +0200 Subject: [PATCH] feat: SettingsContext + Temporary fix for failing to fetch manifest --- core/src/api/cluster/create.rs | 5 ++ core/src/api/cluster/mod.rs | 6 +- core/src/api/metadata.rs | 13 +-- core/src/game/mod.rs | 23 ++--- core/src/store/java_versions.rs | 1 + core/src/store/metadata.rs | 88 ++++++++++--------- core/src/store/settings.rs | 2 + desktop/src-tauri/build.rs | 2 + .../permissions/onelauncher/default.toml | 2 + desktop/src-tauri/src/api/commands.rs | 20 ++++- desktop/src-tauri/src/lib.rs | 4 +- desktop/src/ui/App.tsx | 39 ++++---- desktop/src/ui/components/WindowFrame.tsx | 9 +- desktop/src/ui/hooks/useSettings.tsx | 34 +++++++ .../settings/launcher/SettingsAppearance.tsx | 10 +-- .../settings/launcher/SettingsGeneral.tsx | 18 ++-- desktop/src/ui/state/appSettings.ts | 37 -------- 17 files changed, 175 insertions(+), 138 deletions(-) create mode 100644 desktop/src/ui/hooks/useSettings.tsx delete mode 100644 desktop/src/ui/state/appSettings.ts diff --git a/core/src/api/cluster/create.rs b/core/src/api/cluster/create.rs index d9bcfea3..f014aab8 100644 --- a/core/src/api/cluster/create.rs +++ b/core/src/api/cluster/create.rs @@ -7,6 +7,7 @@ use crate::proxy::ClusterPayloadType; pub use crate::store::{Cluster, ClusterPath, JavaOptions, PackageData, State}; use crate::utils::io::{self, canonicalize}; use crate::{cluster, package}; +use anyhow::anyhow; use interpulse::api::modded::LoaderVersion; use std::path::PathBuf; @@ -226,6 +227,10 @@ pub(crate) async fn get_loader_version( _ => return Err(CreateClusterError::MissingManifest(loader.to_string()).into()), }; + let loader_meta = loader_meta + .to_owned() + .ok_or(anyhow!("missing loader meta"))?; + let loaders = &loader_meta .game_versions .iter() diff --git a/core/src/api/cluster/mod.rs b/core/src/api/cluster/mod.rs index b9a50054..bcc69b33 100644 --- a/core/src/api/cluster/mod.rs +++ b/core/src/api/cluster/mod.rs @@ -19,6 +19,7 @@ use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::Arc; +use anyhow::anyhow; use tokio::process::Command; use tokio::sync::RwLock; @@ -311,8 +312,9 @@ pub async fn get_optimal_java_version(path: &ClusterPath) -> crate::Result crate::Result { let state = State::get().await?; - let meta = state.metadata.read().await.minecraft.clone(); + let meta = state.metadata.read().await.minecraft.clone().ok_or(anyhow!("missing minecraft metadata"))?; Ok(meta) } @@ -17,7 +18,7 @@ pub async fn get_minecraft_versions() -> crate::Result { #[tracing::instrument] pub async fn get_fabric_versions() -> crate::Result { let state = State::get().await?; - let meta = state.metadata.read().await.fabric.clone(); + let meta = state.metadata.read().await.fabric.clone().ok_or(anyhow!("missing fabric metadata"))?; Ok(meta) } @@ -26,7 +27,7 @@ pub async fn get_fabric_versions() -> crate::Result { #[tracing::instrument] pub async fn get_quilt_versions() -> crate::Result { let state = State::get().await?; - let meta = state.metadata.read().await.quilt.clone(); + let meta = state.metadata.read().await.quilt.clone().ok_or(anyhow!("missing quilt metadata"))?; Ok(meta) } @@ -35,7 +36,7 @@ pub async fn get_quilt_versions() -> crate::Result { #[tracing::instrument] pub async fn get_forge_versions() -> crate::Result { let state = State::get().await?; - let meta = state.metadata.read().await.forge.clone(); + let meta = state.metadata.read().await.forge.clone().ok_or(anyhow!("missing forge metadata"))?; Ok(meta) } @@ -44,7 +45,7 @@ pub async fn get_forge_versions() -> crate::Result { #[tracing::instrument] pub async fn get_neoforge_versions() -> crate::Result { let state = State::get().await?; - let meta = state.metadata.read().await.neoforge.clone(); + let meta = state.metadata.read().await.neoforge.clone().ok_or(anyhow!("missing neoforce metadata"))?; Ok(meta) } @@ -53,7 +54,7 @@ pub async fn get_neoforge_versions() -> crate::Result { #[tracing::instrument] pub async fn get_legacy_fabric_versions() -> crate::Result { let state = State::get().await?; - let meta = state.metadata.read().await.legacy_fabric.clone(); + let meta = state.metadata.read().await.legacy_fabric.clone().ok_or(anyhow!("missing legacyfabric metadata"))?; Ok(meta) } diff --git a/core/src/game/mod.rs b/core/src/game/mod.rs index b908d868..3eaead10 100644 --- a/core/src/game/mod.rs +++ b/core/src/game/mod.rs @@ -11,6 +11,7 @@ use crate::store::{ }; use crate::utils::io::{self, IOError}; +use anyhow::anyhow; use chrono::Utc; use interpulse as ip; use interpulse::api::minecraft::{RuleAction, VersionInfo}; @@ -71,8 +72,9 @@ pub async fn install_minecraft( let instance_path = &io::canonicalize(cluster.get_full_path().await?)?; let metadata = state.metadata.read().await; - let version_idx = metadata - .minecraft + let version_manifest = metadata.minecraft.to_owned().ok_or(anyhow!("missing metadata"))?; + + let version_idx = version_manifest .versions .iter() .position(|g| g.id == cluster.meta.mc_version) @@ -81,11 +83,10 @@ pub async fn install_minecraft( cluster.meta.mc_version ))?; - let version = &metadata.minecraft.versions[version_idx]; + let version = &version_manifest.versions[version_idx]; let updated = version_idx - <= metadata - .minecraft - .versions + <= version_manifest + .versions .iter() .position(|g| g.id == "22w16a") .unwrap_or(0); // LWJGL patching @@ -273,8 +274,9 @@ pub async fn launch_minecraft( let instance_path = cluster.get_full_path().await?; let instance_path = &io::canonicalize(instance_path)?; - let version_index = metadata - .minecraft + let version_manifest = metadata.minecraft.to_owned().ok_or(anyhow!("missing minecraft metadata"))?; + + let version_index = version_manifest .versions .iter() .position(|it| it.id == cluster.meta.mc_version) @@ -282,10 +284,9 @@ pub async fn launch_minecraft( "invalid game version {}", cluster.meta.mc_version ))?; - let version = &metadata.minecraft.versions[version_index]; + let version = &version_manifest.versions[version_index]; let updated = version_index - <= metadata - .minecraft + <= version_manifest .versions .iter() .position(|x| x.id == "22w16a") diff --git a/core/src/store/java_versions.rs b/core/src/store/java_versions.rs index f4865350..ed4b8ded 100644 --- a/core/src/store/java_versions.rs +++ b/core/src/store/java_versions.rs @@ -8,6 +8,7 @@ use crate::utils::java; use crate::utils::java::JavaVersion; /// A HashMap of all located and installed available Java versions +#[cfg_attr(feature = "tauri", derive(specta::Type))] #[derive(Debug, Serialize, Deserialize, Clone)] pub struct JavaVersions(HashMap); diff --git a/core/src/store/metadata.rs b/core/src/store/metadata.rs index 38e88c5f..53bdfe43 100644 --- a/core/src/store/metadata.rs +++ b/core/src/store/metadata.rs @@ -24,17 +24,17 @@ const METADATA_URL: &str = "localhost:5543"; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Metadata { /// The [`MinecraftManifest`] associated with core Minecraft versions. - pub minecraft: MinecraftManifest, + pub minecraft: Option, /// The [`ModdedManifest`] associated with core Fabric versions. - pub fabric: ModdedManifest, + pub fabric: Option, /// The [`ModdedManifest`] associated with core Quilt versions. - pub quilt: ModdedManifest, + pub quilt: Option, /// The [`ModdedManifest`] associated with core NeoForge versions. - pub neoforge: ModdedManifest, + pub neoforge: Option, /// The [`ModdedManifest`] associated with core Forge versions. - pub forge: ModdedManifest, + pub forge: Option, /// The [`ModdedManifest`] associated with core Legacy Fabric versions. - pub legacy_fabric: ModdedManifest, + pub legacy_fabric: Option, } impl Metadata { @@ -45,41 +45,49 @@ impl Metadata { /// Fetch all available metadata types and get a new [`Metadata`] structure. pub async fn fetch() -> crate::Result { - let (minecraft, fabric, quilt, neoforge, forge, legacy_fabric) = tokio::try_join! { - async { - let url = Self::get_manifest("minecraft"); - fetch_version_manifest(Some(&url)).await - }, - async { - let url = Self::get_manifest("fabric"); - fetch_modded_manifest(&url).await - }, - async { - let url = Self::get_manifest("quilt"); - fetch_modded_manifest(&url).await - }, - async { - let url = Self::get_manifest("neo"); - fetch_modded_manifest(&url).await - }, - async { - let url = Self::get_manifest("forge"); - fetch_modded_manifest(&url).await - }, - async { - let url = Self::get_manifest("legacy-fabric"); - fetch_modded_manifest(&url).await - }, - }?; + // let (minecraft, fabric, quilt, neoforge, forge, legacy_fabric) = tokio::try_join! { + // async { + // let url = Self::get_manifest("minecraft"); + // fetch_version_manifest(Some(&url)).await + // }, + // async { + // let url = Self::get_manifest("fabric"); + // fetch_modded_manifest(&url).await + // }, + // async { + // let url = Self::get_manifest("quilt"); + // fetch_modded_manifest(&url).await + // }, + // async { + // let url = Self::get_manifest("neo"); + // fetch_modded_manifest(&url).await + // }, + // async { + // let url = Self::get_manifest("forge"); + // fetch_modded_manifest(&url).await + // }, + // async { + // let url = Self::get_manifest("legacy-fabric"); + // fetch_modded_manifest(&url).await + // }, + // }?; - Ok(Self { - minecraft, - fabric, - quilt, - neoforge, - forge, - legacy_fabric, - }) + // Ok(Self { + // minecraft, + // fabric, + // quilt, + // neoforge, + // forge, + // legacy_fabric, + // }) + Ok(Self { + minecraft: None, + fabric: None, + quilt: None, + neoforge: None, + forge: None, + legacy_fabric: None + }) } /// Initialize the core Metadata manager. diff --git a/core/src/store/settings.rs b/core/src/store/settings.rs index c5073d61..7b045b31 100644 --- a/core/src/store/settings.rs +++ b/core/src/store/settings.rs @@ -10,6 +10,7 @@ use std::path::{Path, PathBuf}; const CURRENT_FORMAT_VERSION: u32 = 1; /// A global settings state for the launcher. +#[cfg_attr(feature = "tauri", derive(specta::Type))] #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Settings { /// A OneLauncher [`Theme`] managed by the core GUI. @@ -123,6 +124,7 @@ impl Settings { } /// A OneLauncher theme managed by the GUI. +#[cfg_attr(feature = "tauri", derive(specta::Type))] #[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Theme { diff --git a/desktop/src-tauri/build.rs b/desktop/src-tauri/build.rs index a460c8cf..4c3f0e27 100644 --- a/desktop/src-tauri/build.rs +++ b/desktop/src-tauri/build.rs @@ -12,6 +12,8 @@ fn main() { "get_clusters", "get_cluster", "get_manifest", + "get_settings", + "change_settings" ]), ), ) diff --git a/desktop/src-tauri/permissions/onelauncher/default.toml b/desktop/src-tauri/permissions/onelauncher/default.toml index df2bea2d..eb58e990 100644 --- a/desktop/src-tauri/permissions/onelauncher/default.toml +++ b/desktop/src-tauri/permissions/onelauncher/default.toml @@ -8,4 +8,6 @@ permissions = [ "allow-get-cluster", "allow-get-manifest", "allow-login-msa", + "allow-get-settings", + "allow-change-settings" ] diff --git a/desktop/src-tauri/src/api/commands.rs b/desktop/src-tauri/src/api/commands.rs index 1ad7b513..a75edb71 100644 --- a/desktop/src-tauri/src/api/commands.rs +++ b/desktop/src-tauri/src/api/commands.rs @@ -1,9 +1,10 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr}; use chrono::DateTime; -use onelauncher::{cluster, data::{ClusterMeta, Loader, PackageData}, store::{Cluster, ClusterPath}, State}; +use onelauncher::{cluster, data::{ClusterMeta, Loader, PackageData, Settings}, store::{Cluster, ClusterPath}, State}; use serde::{Deserialize, Serialize}; use specta::Type; +use tokio::sync::RwLock; use uuid::Uuid; #[macro_export] @@ -17,7 +18,9 @@ macro_rules! collect_commands { is_dev, create_cluster, get_cluster, - get_clusters + get_clusters, + get_settings, + change_settings ]) } }; @@ -117,3 +120,16 @@ pub async fn get_clusters() -> Result, String> { // cluster::list(None).await.map_err(|op| op.into()) } + +#[specta::specta] +#[tauri::command] +pub async fn get_settings() -> Result { + Ok(State::get().await?.settings.read().await.clone()) +} + +#[specta::specta] +#[tauri::command] +pub async fn change_settings(settings: Settings) -> Result<(), String> { + State::get().await?.settings.write().await.clone_from(&settings); + Ok(()) +} diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index d344c28f..4c0732be 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -70,7 +70,9 @@ pub async fn run_app( .build(tauri::tauri_build_context!()) .expect("failed to build tauri application"); - initialize_state(app.app_handle().clone()).await; + if let Err(err) = initialize_state(app.app_handle().clone()).await { + tracing::error!("{err}"); + }; app.run(|_app_handle, _event| {}) } diff --git a/desktop/src/ui/App.tsx b/desktop/src/ui/App.tsx index cbf0b623..91593949 100644 --- a/desktop/src/ui/App.tsx +++ b/desktop/src/ui/App.tsx @@ -5,6 +5,7 @@ import Navbar from './components/Navbar'; import ErrorBoundary from './components/ErrorBoundary'; import AnimatedRoutes from './components/AnimatedRoutes'; import NotificationOverlay from './components/overlay/notifications/NotificationOverlay'; +import { SettingsProvider } from './hooks/useSettings'; import { bridge } from '~index'; function App(props: ParentProps) { @@ -15,26 +16,28 @@ function App(props: ParentProps) { } return ( -
- -
- -
- -
-
- - - - {props.children} - - - + +
+ +
+
-
- -
+
+
+ + + + {props.children} + + + +
+
+ + + + ); } diff --git a/desktop/src/ui/components/WindowFrame.tsx b/desktop/src/ui/components/WindowFrame.tsx index 9f682220..9ee955ca 100644 --- a/desktop/src/ui/components/WindowFrame.tsx +++ b/desktop/src/ui/components/WindowFrame.tsx @@ -4,7 +4,6 @@ import type { JSX } from 'solid-js'; import { createSignal, onMount } from 'solid-js'; import Modal from './overlay/Modal'; import Button from './base/Button'; -import appSettings from '~ui/state/appSettings'; interface TitlebarButtonProps { icon: (any: any) => JSX.Element; @@ -32,10 +31,10 @@ function WindowFrame() { onMount(() => { Window.getCurrent().onCloseRequested((event) => { - if (appSettings.settings.closeDialog) { - event.preventDefault(); - setModalVisible(true); - } + // if (appSettings.settings.closeDialog) { + // event.preventDefault(); + // setModalVisible(true); + // } }); }); diff --git a/desktop/src/ui/hooks/useSettings.tsx b/desktop/src/ui/hooks/useSettings.tsx new file mode 100644 index 00000000..27952828 --- /dev/null +++ b/desktop/src/ui/hooks/useSettings.tsx @@ -0,0 +1,34 @@ +import { type Context, type ParentProps, type Resource, Show, createContext, useContext } from 'solid-js'; +import useCommand from './useCommand'; +import type { Settings } from '~bindings'; +import { bridge } from '~index'; + +const SettingsContext = createContext() as Context; + +export function getSettings(): Resource | undefined { + const [resource] = useCommand(bridge.commands.getSettings); + return resource; +} + +export function SettingsProvider(props: ParentProps) { + const settings = getSettings(); + + return ( + + + {props.children} + + + ); +} + +export function useSettingsContext() { + const context = useContext(SettingsContext); + + if (!context) + throw new Error('useSettingsContext should be called inside its SettingsProvider'); + + return context; +} + +export default useSettingsContext; diff --git a/desktop/src/ui/pages/settings/launcher/SettingsAppearance.tsx b/desktop/src/ui/pages/settings/launcher/SettingsAppearance.tsx index 2c7270b5..fec38aa9 100644 --- a/desktop/src/ui/pages/settings/launcher/SettingsAppearance.tsx +++ b/desktop/src/ui/pages/settings/launcher/SettingsAppearance.tsx @@ -3,12 +3,9 @@ import SettingsRow from '../../../components/SettingsRow'; import ScrollableContainer from '~ui/components/ScrollableContainer'; import Button from '~ui/components/base/Button'; import Toggle from '~ui/components/base/Toggle'; -import appSettings from '~ui/state/appSettings'; import Sidebar from '~ui/components/Sidebar'; function SettingsAppearance() { - const { settings, setSettings } = appSettings; - return (

Appearance

@@ -30,12 +27,7 @@ function SettingsAppearance() { description="Toggle all animations in the launcher." icon={} > - { - setSettings('reducedMotion', checked); - }} - /> +
diff --git a/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx b/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx index e784389c..12bfe99c 100644 --- a/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx +++ b/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx @@ -1,11 +1,20 @@ import { XIcon } from '@untitled-theme/icons-solid'; +import { createEffect } from 'solid-js'; import SettingsRow from '../../../components/SettingsRow'; import ScrollableContainer from '~ui/components/ScrollableContainer'; import Toggle from '~ui/components/base/Toggle'; -import appSettings from '~ui/state/appSettings'; import Sidebar from '~ui/components/Sidebar'; +import { bridge } from '~index'; +import useCommand from '~ui/hooks/useCommand'; +import useSettingsContext from '~ui/hooks/useSettings'; function SettingsGeneral() { + const settings = useSettingsContext(); + + createEffect(() => { + console.log(settings); + }); + return (

General

@@ -15,12 +24,7 @@ function SettingsGeneral() { description="Show a confirmation dialog when closing the launcher." icon={} > - { - appSettings.setSettings('closeDialog', checked); - }} - /> +
diff --git a/desktop/src/ui/state/appSettings.ts b/desktop/src/ui/state/appSettings.ts deleted file mode 100644 index 1f8a956c..00000000 --- a/desktop/src/ui/state/appSettings.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createEffect, createRoot, onMount } from 'solid-js'; -import { createStore } from 'solid-js/store'; - -export interface AppSettingsStore { - reducedMotion: boolean; - closeDialog: boolean; -}; - -function globalStore() { - // TODO: Much better management of this, currently temporary - const [settings, setSettings] = createStore({ - reducedMotion: false, - closeDialog: true, - }); - - function set(key: K, value: AppSettingsStore[K]) { - window.localStorage.setItem(key, value.toString()); - } - - onMount(() => { - setSettings('reducedMotion', window.localStorage.getItem('reducedMotion') === 'true'); - setSettings('closeDialog', window.localStorage.getItem('closeDialog') === 'true'); - }); - - createEffect(() => { - // Reduced Motion - set('reducedMotion', settings.reducedMotion); - document.body.classList.toggle('reduce-motion', settings.reducedMotion); - - // Close Dialog - set('closeDialog', settings.closeDialog); - }); - - return { settings, setSettings }; -} - -export default createRoot(globalStore);