diff --git a/apps/frontend/src/assets/logos/skyclient.png b/apps/frontend/src/assets/logos/skyclient.png deleted file mode 100644 index 1879366..0000000 Binary files a/apps/frontend/src/assets/logos/skyclient.png and /dev/null differ diff --git a/apps/frontend/src/ui/components/content/ProviderIcon.tsx b/apps/frontend/src/ui/components/content/ProviderIcon.tsx index b15f857..0530b33 100644 --- a/apps/frontend/src/ui/components/content/ProviderIcon.tsx +++ b/apps/frontend/src/ui/components/content/ProviderIcon.tsx @@ -1,7 +1,6 @@ import type { ManagedPackage, Providers } from '@onelauncher/client/bindings'; import CurseforgeIcon from '~assets/logos/curseforge.svg?component-solid'; import ModrinthImage from '~assets/logos/modrinth.svg?component-solid'; -import SkyClientImage from '~assets/logos/skyclient.png'; import { type Component, type JSX, Match, Show, splitProps, Switch } from 'solid-js'; export function getProviderLogoElement(provider: ManagedPackage | Providers): string | Component { @@ -11,7 +10,6 @@ export function getProviderLogoElement(provider: ManagedPackage | Providers): st const mapping: Record, string | Component> = { modrinth: ModrinthImage, curseforge: CurseforgeIcon, - skyclient: SkyClientImage, }; return mapping[providerName]; diff --git a/apps/frontend/src/ui/components/content/SearchResults.tsx b/apps/frontend/src/ui/components/content/SearchResults.tsx index 2ec4eb4..a8c5417 100644 --- a/apps/frontend/src/ui/components/content/SearchResults.tsx +++ b/apps/frontend/src/ui/components/content/SearchResults.tsx @@ -160,18 +160,16 @@ function PackageItem(props: SearchResult & { provider: Providers; row?: boolean

{props.description}

- +
+ + {abbreviateNumber(props.downloads)} +
+ + 0}>
- - {abbreviateNumber(props.downloads)} + + {abbreviateNumber(props.follows)}
- - 0}> -
- - {abbreviateNumber(props.follows)} -
-
diff --git a/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx b/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx index 6e07fbf..9f0a4ae 100644 --- a/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx +++ b/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx @@ -137,8 +137,8 @@ export default BrowserPackage; function BrowserSidebar(props: { package: ManagedPackage }) { const [authors] = useCommand(() => props.package, () => bridge.commands.getProviderAuthors(props.package.provider, props.package.author)); - const createdAt = createMemo(() => props.package.created ? new Date(props.package.created) : null); - const updatedAt = createMemo(() => props.package.updated ? new Date(props.package.updated) : null); + const createdAt = () => new Date(props.package.created); + const updatedAt = () => new Date(props.package.updated); const promptOpen = usePromptOpener(); return ( @@ -166,18 +166,16 @@ function BrowserSidebar(props: { package: ManagedPackage }) {

{props.package.description}

- +
+ + {abbreviateNumber(props.package.downloads)} +
+ + 0}>
- - {abbreviateNumber(props.package.downloads)} + + {abbreviateNumber(props.package.followers)}
- - 0}> -
- - {abbreviateNumber(props.package.followers)} -
-
@@ -237,29 +235,25 @@ function BrowserSidebar(props: { package: ManagedPackage }) { - - -
- - Created - - {formatAsRelative(createdAt()!.getTime(), 'en', 'long')} - -
-
-
+ +
+ + Created + + {formatAsRelative(createdAt().getTime(), 'en', 'long')} + +
+
- - -
- - Last Updated - - {formatAsRelative(updatedAt()!.getTime(), 'en', 'long')} - -
-
-
+ +
+ + Last Updated + + {formatAsRelative(updatedAt().getTime(), 'en', 'long')} + +
+
diff --git a/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx b/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx index e03fc5a..58add3b 100644 --- a/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx +++ b/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx @@ -7,7 +7,7 @@ import ProviderIcon from '~ui/components/content/ProviderIcon'; import useBrowser from '~ui/hooks/useBrowser'; import { PROVIDERS } from '~utils'; import { browserCategories } from '~utils/browser'; -import { createMemo, For, type JSX, type ParentProps, Show } from 'solid-js'; +import { For, type JSX, type ParentProps } from 'solid-js'; import BrowserMain from './BrowserMain'; import BrowserPackage from './BrowserPackage'; import BrowserSearch from './BrowserSearch'; @@ -87,29 +87,23 @@ function BrowserCategories() { browser.search(); }; - const categories = createMemo(() => { - return browserCategories.byPackageType(browser.packageType(), browser.searchQuery().provider); - }); - return (
- 0}> -
-
Categories
- - {category => ( -

toggleCategory(category.id)} - > - {category.display} -

- )} -
-
-
+
+
Categories
+ + {category => ( +

toggleCategory(category.id)} + > + {category.display} +

+ )} +
+
); diff --git a/apps/frontend/src/utils/browser.ts b/apps/frontend/src/utils/browser.ts index db4fbda..af4adf2 100644 --- a/apps/frontend/src/utils/browser.ts +++ b/apps/frontend/src/utils/browser.ts @@ -93,5 +93,4 @@ const modMapping: Record = { { display: 'Utility', id: 'utility' }, { display: 'World Generation', id: 'worldgen' }, ], - SkyClient: [], }; diff --git a/apps/frontend/src/utils/index.ts b/apps/frontend/src/utils/index.ts index f177513..186144d 100644 --- a/apps/frontend/src/utils/index.ts +++ b/apps/frontend/src/utils/index.ts @@ -174,14 +174,13 @@ export function getPackageUrl(pkg: ManagedPackage): string { return `https://www.curseforge.com/minecraft/${packageTypeMapping[pkg.package_type]}/${pkg.main}`; }, - SkyClient: () => 'TODO', }; return mapping[pkg.provider](); } export const LOADERS: Loader[] = ['vanilla', 'fabric', 'forge', 'neoforge', 'quilt'] as const; -export const PROVIDERS: Providers[] = ['Modrinth', 'Curseforge', 'SkyClient'] as const; +export const PROVIDERS: Providers[] = ['Modrinth', 'Curseforge'] as const; export const PACKAGE_TYPES: PackageType[] = ['mod', 'resourcepack', 'datapack', 'shaderpack'] as const; export const LAUNCHER_IMPORT_TYPES: ImportType[] = [ 'PrismLauncher', diff --git a/packages/client/src/bindings.ts b/packages/client/src/bindings.ts index 6ff12a3..c04e571 100644 --- a/packages/client/src/bindings.ts +++ b/packages/client/src/bindings.ts @@ -857,7 +857,7 @@ export interface ManagedDependency { version_id: string | null; package_id: stri /** * Universal metadata for any managed package from a Mod distribution platform. */ -export interface ManagedPackage { provider: Providers; id: string; package_type: PackageType; title: string; description: string; body: PackageBody; main: string; versions: string[]; game_versions: string[]; loaders: Loader[]; icon_url: string | null; created: string | null; updated: string | null; client: PackageSide; server: PackageSide; downloads: bigint; followers: number; categories: string[]; optional_categories: string[] | null; license: License | null; author: Author; is_archived: boolean }; +export interface ManagedPackage { provider: Providers; id: string; package_type: PackageType; title: string; description: string; body: PackageBody; main: string; versions: string[]; game_versions: string[]; loaders: Loader[]; icon_url: string | null; created: string; updated: string; client: PackageSide; server: PackageSide; downloads: bigint; followers: number; categories: string[]; optional_categories: string[] | null; license: License | null; author: Author; is_archived: boolean }; /** * Universal metadata for any managed user from a Mod distribution platform. */ @@ -989,12 +989,12 @@ export interface ProviderSearchResults { provider: Providers; results: SearchRes /** * Providers for content packages */ -export type Providers = 'Modrinth' | 'Curseforge' | 'SkyClient'; +export type Providers = 'Modrinth' | 'Curseforge'; /** * Global Minecraft resolution. */ export type Resolution = [number, number]; -export interface SearchResult { slug: string; title: string; description: string; categories?: string[]; client_side: PackageSide; server_side: PackageSide; project_type: PackageType; downloads: bigint; icon_url?: string; project_id: string; author: string; versions: string[]; follows: number; date_created: string | null; date_modified: string | null }; +export interface SearchResult { slug: string; title: string; description: string; categories?: string[]; client_side: PackageSide; server_side: PackageSide; project_type: PackageType; downloads: bigint; icon_url?: string; project_id: string; author: string; display_categories?: string[]; versions: string[]; follows: number; date_created: string; date_modified: string }; /** * A global settings state for the launcher. */ diff --git a/packages/core/src/api/package/content/curseforge.rs b/packages/core/src/api/package/content/curseforge.rs index cd55b21..ceba583 100644 --- a/packages/core/src/api/package/content/curseforge.rs +++ b/packages/core/src/api/package/content/curseforge.rs @@ -146,8 +146,8 @@ impl From for ManagedPackage { game_versions: vec![], loaders: vec![], icon_url: package.logo.and_then(|l| Some(l.url)), - created: Some(package.date_created), - updated: Some(package.date_modified), + created: package.date_created, + updated: package.date_modified, client: crate::store::PackageSide::Unknown, server: crate::store::PackageSide::Unknown, downloads: package.download_count, @@ -193,10 +193,11 @@ impl Into for CurseforgePackage { downloads: self.download_count, icon_url: self.logo.map_or(String::new(), |l| l.url), categories: vec![], // TODO + display_categories: vec![], versions: vec![], follows: self.thumbs_up_count, - date_created: Some(self.date_created), - date_modified: Some(self.date_modified), + date_created: self.date_created, + date_modified: self.date_modified, } } } diff --git a/packages/core/src/api/package/content/mod.rs b/packages/core/src/api/package/content/mod.rs index 8b50d97..7f8ef5d 100644 --- a/packages/core/src/api/package/content/mod.rs +++ b/packages/core/src/api/package/content/mod.rs @@ -17,7 +17,6 @@ use crate::{Result, State}; mod curseforge; mod modrinth; -mod skyclient; /// Providers for content packages #[cfg_attr(feature = "specta", derive(specta::Type))] @@ -25,7 +24,6 @@ mod skyclient; pub enum Providers { Modrinth, Curseforge, - SkyClient, } impl std::fmt::Display for Providers { @@ -41,7 +39,6 @@ impl Providers { match self { Self::Modrinth => "Modrinth", Self::Curseforge => "Curseforge", - Self::SkyClient => "SkyClient", } } @@ -51,12 +48,11 @@ impl Providers { match self { Self::Modrinth => "https://modrinth.com", Self::Curseforge => "https://curseforge.com", - Self::SkyClient => "https://skyclient.co", } } pub const fn get_providers() -> &'static [Providers] { - &[Self::Modrinth, Self::Curseforge, Self::SkyClient] + &[Self::Modrinth, Self::Curseforge] } #[allow(clippy::too_many_arguments)] @@ -102,37 +98,35 @@ impl Providers { ) .await } - Self::SkyClient => { - skyclient::search( - query, - limit, - offset, - game_versions, - loaders - ).await - }, } } pub async fn get(&self, slug_or_id: &str) -> Result { Ok(match self { Self::Modrinth => modrinth::get(slug_or_id).await?.into(), - Self::Curseforge => curseforge::get(slug_or_id.parse::().map_err(|err| anyhow::anyhow!(err))?).await?.into(), - Self::SkyClient => skyclient::get(slug_or_id).await?.into(), + Self::Curseforge => curseforge::get( + slug_or_id + .parse::() + .map_err(|err| anyhow::anyhow!(err))?, + ) + .await? + .into(), }) } pub async fn get_multiple(&self, slug_or_ids: &[String]) -> Result> { - if slug_or_ids.len() <= 0 { - return Ok(vec![]); - } - Ok(match self { - Self::Modrinth => modrinth::get_multiple(slug_or_ids) + Self::Modrinth => { + if slug_or_ids.len() <= 0 { + return Ok(vec![]); + } + + modrinth::get_multiple(slug_or_ids) .await? .into_iter() .map(Into::into) - .collect(), + .collect() + }, Self::Curseforge => { let parsed_ids = slug_or_ids .iter() @@ -151,11 +145,6 @@ impl Providers { .map(Into::into) .collect() }, - Self::SkyClient => skyclient::get_multiple(slug_or_ids) - .await? - .into_iter() - .map(Into::into) - .collect(), }) } @@ -177,8 +166,6 @@ impl Providers { let data = curseforge::get_all_versions(project_id, game_versions, loaders, page, page_size).await?; (data.0.into_iter().map(Into::into).collect(), data.1) } - - Self::SkyClient => todo!(), }) } @@ -194,7 +181,6 @@ impl Providers { .into_iter() .map(Into::into) .collect(), - Self::SkyClient => todo!(), }) } @@ -229,8 +215,7 @@ impl Providers { .await? .into_iter() .map(|(hash, version)| (hash, version.into())) - .collect(), - Self::SkyClient => todo!(), + .collect() }) } } diff --git a/packages/core/src/api/package/content/modrinth.rs b/packages/core/src/api/package/content/modrinth.rs index 19595e0..67de79b 100644 --- a/packages/core/src/api/package/content/modrinth.rs +++ b/packages/core/src/api/package/content/modrinth.rs @@ -75,8 +75,8 @@ impl From for ManagedPackage { game_versions: value.game_versions, loaders: value.loaders, icon_url: value.icon_url, - created: Some(value.published), - updated: Some(value.updated), + created: value.published, + updated: value.updated, client: value.client_side, server: value.server_side, downloads: value.downloads as u64, diff --git a/packages/core/src/api/package/content/skyclient.rs b/packages/core/src/api/package/content/skyclient.rs deleted file mode 100644 index adb5422..0000000 --- a/packages/core/src/api/package/content/skyclient.rs +++ /dev/null @@ -1,288 +0,0 @@ -use std::sync::Arc; - -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use tokio::sync::{OnceCell, RwLock}; - -use crate::{data::{Loader, ManagedPackage, ManagedUser, ManagedVersion}, store::{ProviderSearchResults, SearchResult}, utils::{http, pagination::Pagination}, Result, State}; - -async fn fetch(url: &str) -> Result { - let state = State::get().await?; - Ok(serde_json::from_slice( - &http::fetch( - format!("{}/{}", crate::constants::SKYCLIENT_BASE_URL, url).as_str(), - None, - &state.fetch_semaphore - ).await? - )?) -} - -static SKYCLIENTSTORE_STATIC: OnceCell> = OnceCell::const_new(); - -struct SkyClientStore { - pub mods: Option> -} - -impl SkyClientStore { - pub async fn get() -> Result>> { - Ok(Arc::new( - SKYCLIENTSTORE_STATIC - .get_or_try_init(Self::initialize) - .await? - .read() - .await, - )) - } - - #[tracing::instrument] - #[onelauncher_macros::memory] - async fn initialize() -> Result> { - let mods: Vec = fetch("/mods/mods.json").await?; - - Ok(RwLock::new(SkyClientStore { - mods: Some(mods) - })) - } -} - - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct SkyClientMod { - pub id: String, - #[serde(default)] - pub nicknames: Vec, - pub display: String, - pub creator: String, - pub discord_code: Option, - pub description: Option, - pub icon: Option, - #[serde(default)] - pub links: Vec, - pub versions: Vec, - #[serde(default)] - pub commands: Vec, - pub icon_scaling: Option, - pub oneconfig: Option, -} - -impl Into for SkyClientMod { - fn into(self) -> SearchResult { - SearchResult { - slug: self.id.clone(), - project_id: self.id, - author: self.creator, - categories: vec![], - title: self.display, - client_side: crate::store::PackageSide::Required, - server_side: crate::store::PackageSide::Unknown, - date_created: None, - date_modified: None, - description: self.description.unwrap_or_default(), - project_type: crate::data::PackageType::Mod, - downloads: 0, - icon_url: self.icon.map(|i| format!("{}/icons/{}", crate::constants::SKYCLIENT_BASE_URL, i)).unwrap_or_default(), - versions: self.versions.iter().map(|v| v.version.clone()).collect(), - follows: 0, - } - } -} - -impl Into for SkyClientMod { - fn into(self) -> ManagedPackage { - ManagedPackage { - id: self.id.clone(), - main: self.id, - title: self.display, - body: crate::store::PackageBody::Markdown(self.description.clone().unwrap_or_default()), - categories: vec![], - client: crate::store::PackageSide::Required, - server: crate::store::PackageSide::Unknown, - created: None, - updated: None, - description: self.description.unwrap_or_default(), - downloads: 0, - followers: 0, - game_versions: self.versions.iter().flat_map(|v| v.game_versions.clone()).collect(), - icon_url: self.icon.map(|i| format!("{}/icons/{}", crate::constants::SKYCLIENT_BASE_URL, i)), - is_archived: false, - license: None, - loaders: self.versions.iter().flat_map(|v| v.loaders.clone().into_iter().map(Loader::from_string)).collect(), - versions: self.versions.iter().map(|v| v.version.clone()).collect(), - optional_categories: None, - author: crate::store::Author::Users(vec![ - ManagedUser { - id: self.creator.clone(), - username: self.creator, - is_organization_user: false, - avatar_url: None, - bio: None, - role: None, - url: self.discord_code.map(|code| format!("https://discord.gg/{}", code)), - } - ]), - package_type: crate::data::PackageType::Mod, - provider: super::Providers::SkyClient, - } - } -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct SkyClientModLink { - pub icon: String, - pub text: String, - pub link: String, -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct SkyClientModVersion { - pub version: String, - pub game_versions: Vec, - pub loaders: Vec, - pub file: String, - pub url: String, - pub hash: String, - pub mod_id: Option, -} - -pub async fn get(id: &str) -> Result { - let store = SkyClientStore::get().await?; - - let mods = match &store.mods { - Some(mods) => mods, - None => return Err(anyhow::anyhow!("mod not found").into()) - }; - - for m in mods { - if m.id == id { - return Ok(m.clone()); - } - } - - Err(anyhow::anyhow!("mod not found").into()) -} - -pub async fn get_multiple(slug_or_ids: &[String]) -> Result> { - let store = SkyClientStore::get().await?; - - let mods = match &store.mods { - Some(mods) => mods, - None => return Ok(vec![]) - }; - - let mut results = vec![]; - - for id in slug_or_ids { - for m in mods { - if m.id == *id { - results.push(m.clone()); - } - } - } - - Ok(results) -} - -// pub async fn get_all_versions( -// project_id: &str, -// game_versions: Option>, -// loaders: Option>, -// page: Option, -// page_size: Option, -// ) -> Result<(Vec, Pagination)> { -// let store = SkyClientStore::get().await?; - -// let mods = match &store.mods { -// Some(mods) => mods, -// None => return Ok((vec![], Pagination::default())) -// }; - - -// let mut versions = vec![]; - -// for m in mods { -// if m.id == project_id { -// for v in m.versions { -// let mut can_add = true; - -// if let Some(game_versions) = &game_versions { -// can_add = v.game_versions.iter().any(|gv| game_versions.contains(gv)) -// } - -// if can_add { -// if let Some(loaders) = &loaders { -// can_add = v.loaders.iter().any(|l| loaders.contains(&Loader::from_string(l))) -// } -// } - -// if can_add { -// -// } -// } -// } -// } -// } - -pub async fn search( - query: Option, - limit: Option, - offset: Option, - game_versions: Option>, - // package_types: Option>, - loaders: Option>, -) -> Result { - let store = SkyClientStore::get().await?; - - let mut results = ProviderSearchResults { - provider: super::Providers::SkyClient, - results: vec![], - total: 0, - }; - - let mods = match &store.mods { - Some(mods) => mods, - None => return Ok(results), - }; - - let query = query.map(|q| q.trim().to_lowercase()); - let limit = limit.map(|l| l as usize); - let offset = offset.unwrap_or(0); - - for m in mods { - let mut can_add = true; - - if let Some(query) = &query { - if !query.is_empty() { - let display = m.display.to_lowercase(); - - can_add = display.contains(query) || m.id.contains(query) || m.nicknames.contains(query) - } - } - - if can_add { - if let Some(game_versions) = &game_versions { - if !game_versions.is_empty() { - can_add = m.versions.iter().any(|v| v.game_versions.iter().any(|gv| game_versions.contains(&gv))) - } - } - } - - if can_add { - if let Some(loaders) = &loaders { - if !loaders.is_empty() { - can_add = m.versions.iter().any(|v| v.loaders.iter().any(|l| loaders.contains(&Loader::from_string(l.to_owned())))) - } - } - } - - if can_add { - if limit.map(|l| results.results.len() < l).unwrap_or(true) && offset <= results.total { - let result: SearchResult = m.clone().into(); - results.results.push(result); - } - - results.total += 1; - } - - } - - Ok(results) -} \ No newline at end of file diff --git a/packages/core/src/constants.rs b/packages/core/src/constants.rs index 515cf5f..69dde14 100644 --- a/packages/core/src/constants.rs +++ b/packages/core/src/constants.rs @@ -48,8 +48,6 @@ pub const METADATA_API_URL: &str = "https://meta.polyfrost.org"; pub const FEATURED_PACKAGES_URL: &str = "https://polyfrost.org/meta/onelauncher/featured.json"; /// / API base url. pub const MCLOGS_API_URL: &str = "https://api.mclo.gs/1"; -/// https://skyclient.co/ metadata base url. -pub const SKYCLIENT_BASE_URL: &str = "https://raw.githubusercontent.com/SkyblockClient/SkyblockClient-REPO/refs/heads/main/v1"; // =========== Paths =========== /// The public `settings.json` file used to store the global [`Settings`] state. diff --git a/packages/core/src/store/clusters.rs b/packages/core/src/store/clusters.rs index 9f6fb9d..a87e0f8 100644 --- a/packages/core/src/store/clusters.rs +++ b/packages/core/src/store/clusters.rs @@ -376,12 +376,7 @@ impl Loader { } #[must_use] - pub fn from_string(val: String) -> Self { - Self::from_str(val.as_str()) - } - - #[must_use] - pub fn from_str(val: &str) -> Self { + pub fn from_string(val: &str) -> Self { match val { "forge" => Self::Forge, "fabric" => Self::Fabric, diff --git a/packages/core/src/store/package.rs b/packages/core/src/store/package.rs index 09f82f5..862f2b0 100644 --- a/packages/core/src/store/package.rs +++ b/packages/core/src/store/package.rs @@ -148,10 +148,12 @@ pub struct SearchResult { pub icon_url: String, pub project_id: String, pub author: String, + #[serde(default)] + pub display_categories: Vec, pub versions: Vec, pub follows: u32, - pub date_created: Option>, - pub date_modified: Option>, + pub date_created: DateTime, + pub date_modified: DateTime, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -717,8 +719,8 @@ pub struct ManagedPackage { pub loaders: Vec, pub icon_url: Option, - pub created: Option>, - pub updated: Option>, + pub created: DateTime, + pub updated: DateTime, pub client: PackageSide, pub server: PackageSide, pub downloads: u64,