From 2f80658db98e7ba45cf6fbd359df7d779dee95ab Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:57:33 +0200 Subject: [PATCH] feat: Somewhat working Browser UI --- apps/desktop/src-tauri/build.rs | 4 + .../permissions/onelauncher/default.toml | 5 + apps/desktop/src-tauri/src/api/commands.rs | 43 +++++++-- apps/desktop/src/types/global.d.ts | 13 --- .../src/ui/components/content/ModCard.tsx | 19 ++-- .../src/ui/hooks/useBrowserController.tsx | 32 +++---- apps/desktop/src/ui/hooks/useCommand.ts | 7 +- .../src/ui/pages/browser/BrowserMain.tsx | 69 ++++++++------ .../src/ui/pages/browser/BrowserMod.tsx | 92 ++++++++++++++++--- .../src/ui/pages/cluster/ClusterRoot.tsx | 4 +- .../core/src/api/package/content/modrinth.rs | 3 + 11 files changed, 198 insertions(+), 93 deletions(-) diff --git a/apps/desktop/src-tauri/build.rs b/apps/desktop/src-tauri/build.rs index dc84b0c7..2f9caabf 100644 --- a/apps/desktop/src-tauri/build.rs +++ b/apps/desktop/src-tauri/build.rs @@ -26,6 +26,10 @@ fn main() { "set_settings", // Metadata "get_minecraft_versions", + // Package + "random_mods", + "get_mod", + "download_mod", // Other "get_program_info", ]), diff --git a/apps/desktop/src-tauri/permissions/onelauncher/default.toml b/apps/desktop/src-tauri/permissions/onelauncher/default.toml index 9e4b1469..c9fd51d7 100644 --- a/apps/desktop/src-tauri/permissions/onelauncher/default.toml +++ b/apps/desktop/src-tauri/permissions/onelauncher/default.toml @@ -27,6 +27,11 @@ permissions = [ # Metadata "allow-get-minecraft-versions", + # Package + "allow-random-mods", + "allow-get-mod", + "allow-download-mod", + # Other "allow-get-program-info", ] diff --git a/apps/desktop/src-tauri/src/api/commands.rs b/apps/desktop/src-tauri/src/api/commands.rs index 71d5ce22..186ed495 100644 --- a/apps/desktop/src-tauri/src/api/commands.rs +++ b/apps/desktop/src-tauri/src/api/commands.rs @@ -2,7 +2,8 @@ use std::path::PathBuf; use interpulse::api::minecraft::Version; use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION}; -use onelauncher::data::{Loader, MinecraftCredentials, PackageData, Settings}; +use onelauncher::data::{Loader, ManagedPackage, MinecraftCredentials, PackageData, Settings}; +use onelauncher::package::content; use onelauncher::store::{Cluster, ClusterPath, MinecraftLogin}; use onelauncher::{cluster, minecraft, processor, settings}; use serde::{Deserialize, Serialize}; @@ -40,6 +41,10 @@ macro_rules! collect_commands { set_settings, // Metadata get_minecraft_versions, + // Package + random_mods, + get_mod, + download_mod, // Other get_program_info, ]) @@ -244,8 +249,34 @@ pub async fn remove_user(uuid: Uuid) -> Result<(), String> { Ok(minecraft::remove_user(uuid).await?) } -// #[specta::specta] -// #[tauri::command] -// pub async fn searchMods(query: String) -> Result, String> { -// // Ok(onelauncher::api::) -// } +#[specta::specta] +#[tauri::command] +pub async fn random_mods() -> Result, String> { + let provider = content::Providers::Modrinth; + Ok(provider.list().await?) +} + +#[specta::specta] +#[tauri::command] +pub async fn get_mod(project_id: String) -> Result { + let provider = content::Providers::Modrinth; + Ok(provider.get(&project_id).await?) +} + +#[specta::specta] +#[tauri::command] +pub async fn download_mod(cluster_id: Uuid, version_id: String) -> Result<(), String> { + let cluster = cluster::get_by_uuid(cluster_id, None).await?.ok_or("cluster not found")?; + let provider = content::Providers::Modrinth; + let game_version = cluster.meta.mc_version.clone(); + + provider.get_version_for_game_version(&version_id, &game_version) + .await? + .files + .first() + .ok_or("no files found")? + .download_to_cluster(&cluster) + .await?; + + Ok(()) +} diff --git a/apps/desktop/src/types/global.d.ts b/apps/desktop/src/types/global.d.ts index dff6020d..75f8eba6 100644 --- a/apps/desktop/src/types/global.d.ts +++ b/apps/desktop/src/types/global.d.ts @@ -21,17 +21,4 @@ declare namespace globalThis { // } // type Result = UnionToArray['length'] // = 5 -} - -// TODO remove in place for specta-generated types -interface Mod { - id: string; - name: string; - description: string; - author: string; - icon_url: string; - provider: Provider; - page_url: string; - downloads: number; - ratings: number; } \ No newline at end of file diff --git a/apps/desktop/src/ui/components/content/ModCard.tsx b/apps/desktop/src/ui/components/content/ModCard.tsx index 01bc3e0a..06cc1e2a 100644 --- a/apps/desktop/src/ui/components/content/ModCard.tsx +++ b/apps/desktop/src/ui/components/content/ModCard.tsx @@ -2,6 +2,7 @@ import { Download01Icon, HeartIcon } from '@untitled-theme/icons-solid'; import { useNavigate } from '@solidjs/router'; import { abbreviateNumber } from '../../../utils/primitives'; import BrowserMod from '~ui/pages/browser/BrowserMod'; +import type { ManagedPackage } from '~bindings'; // TODO: Remove in place for specta generated types export enum Provider { @@ -11,37 +12,37 @@ export enum Provider { Skyclient, } -function ModCard(props: Mod) { +function ModCard(props: ManagedPackage) { const navigate = useNavigate(); function redirect() { navigate(BrowserMod.getUrl({ id: props.id, - provider: Provider[props.provider] || 'unknown', + provider: 'modrinth', })); } return (
- {`Icon - {`Icon + {`Icon + {`Icon
-
{props.name}
+
{props.title}

By {' '} - {props.author} + Author TODO {' '} on {' '} - {Provider[props.provider]} + Modrinth

-

{props.description}

+

{props.description}

@@ -51,7 +52,7 @@ function ModCard(props: Mod) {
- {abbreviateNumber(props.ratings)} + {abbreviateNumber(props.followers)}
diff --git a/apps/desktop/src/ui/hooks/useBrowserController.tsx b/apps/desktop/src/ui/hooks/useBrowserController.tsx index 6cdc060e..ec18dc88 100644 --- a/apps/desktop/src/ui/hooks/useBrowserController.tsx +++ b/apps/desktop/src/ui/hooks/useBrowserController.tsx @@ -1,20 +1,20 @@ -import { type Context, type ParentProps, createContext, useContext } from 'solid-js'; +// import { type Context, type ParentProps, createContext, useContext } from 'solid-js'; -interface BrowserContextType { - getMod: (id: string) => void; - getMods: (category: string) => void; -}; +// interface BrowserContextType { +// getMod: (id: string) => void; +// getMods: (category: string) => void; +// }; -const BrowserContext = createContext() as Context; +// const BrowserContext = createContext() as Context; -export function BrowserContextProvider(props: ParentProps) { - return ( - - {props.children} - - ); -} +// export function BrowserContextProvider(props: ParentProps) { +// return ( +// +// {props.children} +// +// ); +// } -export default function useBrowserController() { - return useContext(BrowserContext); -} +// export default function useBrowserController() { +// return useContext(BrowserContext); +// } diff --git a/apps/desktop/src/ui/hooks/useCommand.ts b/apps/desktop/src/ui/hooks/useCommand.ts index f33f8c3f..53e4777c 100644 --- a/apps/desktop/src/ui/hooks/useCommand.ts +++ b/apps/desktop/src/ui/hooks/useCommand.ts @@ -10,12 +10,7 @@ export default function useCommand( cmd: (...args: Args) => Promise>, ...args: Args ): ResourceReturn { - return createResource(async () => { - console.log('aaaa', cmd); - const test = await tryResult(cmd, ...args); - console.log('bbbb'); - return test; - }); + return createResource(async () => await tryResult(cmd, ...args)); } export async function tryResult( diff --git a/apps/desktop/src/ui/pages/browser/BrowserMain.tsx b/apps/desktop/src/ui/pages/browser/BrowserMain.tsx index 028050fb..3d94bd72 100644 --- a/apps/desktop/src/ui/pages/browser/BrowserMain.tsx +++ b/apps/desktop/src/ui/pages/browser/BrowserMain.tsx @@ -1,10 +1,13 @@ import { A } from '@solidjs/router'; import { Download01Icon, FileCode01Icon, FilterLinesIcon, SearchMdIcon } from '@untitled-theme/icons-solid'; -import { For, createSignal } from 'solid-js'; +import { For, createEffect, createSignal, onMount } from 'solid-js'; +import type { ManagedPackage } from '~bindings'; +import { bridge } from '~imports'; import Button from '~ui/components/base/Button'; import Dropdown from '~ui/components/base/Dropdown'; import TextField from '~ui/components/base/TextField'; import ModCard, { Provider } from '~ui/components/content/ModCard'; +import useCommand, { tryResult } from '~ui/hooks/useCommand'; import createSortable from '~utils/sorting'; interface CardProps { @@ -24,6 +27,36 @@ function BrowserMain() { provider: [Provider.Curseforge], }); + const [modRowData, setModRowData] = createSignal([]); + + onMount(() => { + Promise.all([ + tryResult(bridge.commands.randomMods), + tryResult(bridge.commands.getMod, 'oneconfig'), + tryResult(bridge.commands.getMod, 'chatting'), + tryResult(bridge.commands.getMod, 'patcher'), + ]).then((res) => { + const list: ModsRowProps[] = [ + { + header: 'Polyfrost', + category: 'polyfrost', + packages: [ + res[1], + res[2], + res[3], + ], + }, + { + header: 'Random Mods', + category: 'random', + packages: res[0], + }, + ]; + + setModRowData(list); + }); + }); + const sortable = createSortable([], { 'A-Z': (a, b) => a.name.localeCompare(b.name), 'Z-A': (a, b) => b.name.localeCompare(a.name), @@ -33,7 +66,7 @@ function BrowserMain() { return (
-
+
-
- +
+ {row => ( )} @@ -78,32 +111,10 @@ function BrowserMain() { ); } -// export const modStore: Mod[] = [ -// { -// id: '1yIQcc2b', -// name: 'EvergreenHUD', -// description: 'Improves your heads up display.', -// author: 'Polyfrost', -// icon_url: 'https://cdn.modrinth.com/data/1yIQcc2b/icon.png', -// page_url: 'https://modrinth.com/mod/evergreenhud', -// provider: Provider.Modrinth, -// downloads: 281700, -// ratings: 220, -// }, -// ]; - -// const _modRowData: ModsRowProps[] = [ -// { -// header: 'Highly Endorsed', -// category: 'endorsed', -// mods: modStore, -// }, -// ]; - interface ModsRowProps { header: string; category: string; - mods: Mod[]; + packages: ManagedPackage[]; }; function ModsRow(props: ModsRowProps) { @@ -114,8 +125,8 @@ function ModsRow(props: ModsRowProps) { See all
-
- +
+ {mod => }
diff --git a/apps/desktop/src/ui/pages/browser/BrowserMod.tsx b/apps/desktop/src/ui/pages/browser/BrowserMod.tsx index 9091ec95..5c8a4484 100644 --- a/apps/desktop/src/ui/pages/browser/BrowserMod.tsx +++ b/apps/desktop/src/ui/pages/browser/BrowserMod.tsx @@ -1,32 +1,100 @@ import { type Params, useSearchParams } from '@solidjs/router'; -import { Match, Switch } from 'solid-js'; +import { Download01Icon } from '@untitled-theme/icons-solid'; +import { For, Match, Switch, createSignal, onMount } from 'solid-js'; +import { bridge } from '~imports'; +import Button from '~ui/components/base/Button'; +import Dropdown from '~ui/components/base/Dropdown'; +import ModCard from '~ui/components/content/ModCard'; +import Modal from '~ui/components/overlay/Modal'; +import useCommand, { tryResult } from '~ui/hooks/useCommand'; interface BrowserModParams extends Params { id: string; provider: string; } +// TODO: Refactor the entire browser page and subpages function BrowserMod() { const [params] = useSearchParams(); const isInvalid = !params.id || !params.provider; + const [visible, setVisible] = createSignal(false); + const [cluster, setCluster] = createSignal(); + const [clusters, setClusters] = createSignal<[string, string][]>(); + const [pkg] = useCommand(bridge.commands.getMod, params.id!); + + function installTo() { + setVisible(true); + } + + function download() { + setVisible(false); + tryResult(bridge.commands.downloadMod, cluster()!, params.id!).then((res) => { + console.log('finished'); + }).catch(err => console.error(err)); + } + + function onChange(selected: number) { + setCluster(clusters()![selected]![1]); + } + + onMount(() => { + tryResult(bridge.commands.getClusters).then((res) => { + const list: [string, string][] = res.map(cluster => [cluster.meta.name, cluster.uuid]); + setClusters(list); + setCluster(clusters()![0]![1]); + }); + }); return ( - - - {/* TODO */} -
uh oh this doesnt exist
-
- -
- {JSON.stringify(params)} + <> +
+ +
+
+

{pkg()?.title}

+
+

TO DO

- - +
+ + setVisible(false)} + />, +