refactor: adapt frontend to backend changes

This commit is contained in:
Szedann 2025-08-09 23:12:54 +02:00
parent 37b85adefa
commit 4fda6e44b2
6 changed files with 57 additions and 36 deletions

View file

@ -5,9 +5,9 @@ import { createTauRPCProxy as createProxy, type InferCommandOutput } from 'taurp
type TAURI_CHANNEL<T> = (response: T) => void
export type ClusterError = { type: "InvalidVersion"; data: string } | { type: "MissingJavaVersion"; data: string } | { type: "ClusterDownloading"; data: string } | { type: "ClusterAlreadyRunning"; data: string }
export type ClusterError = { type: "InvalidVersion"; data: string } | { type: "MismatchedVersion"; data: string } | { type: "MismatchedLoader"; data: string } | { type: "MissingJavaVersion"; data: string } | { type: "ClusterDownloading"; data: string } | { type: "ClusterAlreadyRunning"; data: string }
export type ClusterModel = { id: bigint; folder_name: string; stage: ClusterStage; created_at: string; group_id: bigint | null; name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; last_played: string | null; overall_played: bigint | null; icon_url: Icon | null; setting_profile_name: string | null; linked_pack_id: string | null; linked_pack_version: string | null }
export type ClusterModel = { id: bigint; folder_name: string; stage: ClusterStage; created_at: string; group_id: bigint | null; name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; last_played: string | null; overall_played: bigint | null; icon_url: Icon | null; setting_profile_name: string | null; linked_modpack_hash: string | null }
export type ClusterStage = "notready" | "downloading" | "repairing" | "ready"
@ -25,11 +25,11 @@ export type DirectoryError = { type: "BaseDir"; data: string }
export type DiscordError = { type: "MissingClientId"; data: string } | { type: "ConnectError"; data: string }
export type Filters = { game_versions: string[] | null; loaders: GameLoader[] | null; categories: string[] | null; package_types: PackageType[] | null }
export type Filters = { game_versions: string[] | null; loaders: GameLoader[] | null; categories: PackageCategories | null; package_type: PackageType | null }
export type GameLoader = "vanilla" | "forge" | "neoforge" | "quilt" | "fabric" | "legacyfabric"
export type IOError = { type: "InvalidAbsolutePath"; data: string } | { type: "IOErrorWrapper"; data: string } | { type: "IOError"; data: string } | { type: "DeserializeError"; data: string } | { type: "AsyncZipError"; data: string }
export type IOError = { type: "InvalidAbsolutePath"; data: string } | { type: "FileNotFoundInZip"; data: string } | { type: "IOErrorWrapper"; data: string } | { type: "IOError"; data: string } | { type: "DeserializeError"; data: string } | { type: "AsyncZipError"; data: string }
export type Icon = string
@ -45,7 +45,14 @@ export type JavaError = { type: "ParseVersion"; data: string } | { type: "Execut
export type LauncherError = { type: "DirError"; data: DirectoryError } | { type: "IOError"; data: IOError } | { type: "IngressError"; data: IngressError } | { type: "JavaError"; data: JavaError } | { type: "CryptoError"; data: CryptoError } | { type: "DiscordError"; data: DiscordError } | { type: "MetadataError"; data: MetadataError } | { type: "ClusterError"; data: ClusterError } | { type: "MinecraftAuthError"; data: MinecraftAuthError } | { type: "ProcessError"; data: ProcessError } | { type: "PackageError"; data: PackageError } | { type: "DaoError"; data: DaoError } | { type: "SerdeError"; data: string } | { type: "AnyhowError"; data: string } | { type: "DbError"; data: string } | { type: "ReqwestError"; data: string } | { type: "InterpulseError"; data: string } | { type: "RegexError"; data: string } | { type: "SemaphoreError"; data: string } | { type: "UrlError"; data: string } | { type: "TauriError"; data: string }
export type ManagedPackage = { id: string; slug: string; provider: Provider; package_type: PackageType; name: string; short_desc: string; body: string; version_ids: string[]; mc_versions: string[]; loaders: GameLoader[]; icon_url: string | null; created: string; updated: string; client: PackageSide; server: PackageSide; categories: string[]; license: PackageLicense | null; author: PackageAuthor; links: PackageLinks; status: PackageStatus; downloads: bigint; gallery: PackageGallery[] }
export type ManagedPackage = { id: string; slug: string; provider: Provider; package_type: PackageType; name: string; short_desc: string; body: ManagedPackageBody;
/**
* Won't have all versions for some providers, like CurseForge.
* Try making a request to get the versions if needed
*/
version_ids: string[]; mc_versions: string[]; loaders: GameLoader[]; icon_url: string | null; created: string; updated: string; client: PackageSide; server: PackageSide; categories: PackageCategories; license: PackageLicense | null; author: PackageAuthor; links: PackageLinks; status: PackageStatus; downloads: bigint; gallery: PackageGallery[] }
export type ManagedPackageBody = { Url: string } | { Raw: string }
export type ManagedUser = { id: string; username: string; url?: string | null; avatar_url?: string | null; bio?: string | null; is_organization_user?: boolean; role?: string | null }
@ -90,6 +97,8 @@ expires: string }
export type PackageAuthor = { Team: { team_id: string; org_id: string | null } } | { Users: ManagedUser[] }
export type PackageCategories = { Mod: PackageModCategory[] } | { ResourcePack: PackageResourcePackCategory[] } | { Shader: PackageShaderCategory[] } | { DataPack: PackageModCategory[] } | { ModPack: PackageModPackCategory[] }
export type PackageDependencyType = "required" | "optional" | "embedded" | "incompatible"
/**
@ -99,7 +108,7 @@ export type PackageDonationPlatform = "patreon" | "buymeacoffee" | "paypal" | "g
export type PackageDonationUrl = { id: PackageDonationPlatform; url: string }
export type PackageError = { type: "NoPrimaryFile"; data: string } | { type: "IsModPack"; data: string } | { type: "Incompatible"; data: IncompatiblePackageType }
export type PackageError = { type: "NoPrimaryFile"; data: string } | { type: "IsNotModPack"; data: string } | { type: "Incompatible"; data: IncompatiblePackageType } | { type: "MissingApiKey"; data: string } | { type: "UnsupportedBodyType"; data: string } | { type: "UnsupportedAuthorType"; data: string } | { type: "UnsupportedModpackFormat"; data: string }
export type PackageGallery = { url: string; thumbnail_url: string; title: string | null; description: string | null; featured: boolean | null }
@ -113,10 +122,18 @@ export type PackageLicense = { id: string; name: string; url: string | null }
*/
export type PackageLinks = { website: string | null; issues: string | null; source: string | null; wiki: string | null; donation: PackageDonationUrl[] | null; discord: string | null }
export type PackageModCategory = "Adventure" | "Library" | "Equipment" | "Patches" | "Cosmetic" | "Food" | "Magic" | "Information" | "Misc" | "Performance" | "Redstone" | "ServerUtil" | "Storage" | "Technology" | "Farming" | "Automation" | "Transport" | "Utility" | "QoL" | "WorldGen" | "Mobs" | "Economy" | "Social"
export type PackageModPackCategory = "Technology" | "Quests" | "Optimization" | "Multiplayer" | "Magic" | "LightWeight" | "Combat" | "Challenging" | "Adventure"
export type PackageModel = { hash: string; file_name: string; version_id: string; published_at: string; display_name: string; display_version: string; package_type: PackageType; provider: Provider; package_id: string; mc_versions: DbVec<string>; mc_loader: DbVec<GameLoader>; icon: Icon | null }
export type PackageReleaseType = "release" | "beta" | "alpha"
export type PackageResourcePackCategory = "X8" | "X16" | "X32" | "X48" | "X64" | "X128" | "X256" | "X512" | "VanillaLike" | "Utility" | "Tweaks" | "Themed" | "Simplistic" | "Realistic" | "Modded" | "Decoration" | "Cursed" | "Combat" | "Audio" | "Blocks" | "CoreShaders" | "Gui" | "Fonts" | "Equipment" | "Environment" | "Entities" | "Items" | "Locale" | "Models"
export type PackageShaderCategory = "VanillaLike" | "SemiRealistic" | "Realistic" | "Fantasy" | "Cursed" | "Cartoon" | "Bloom" | "Atmosphere" | "Reflections" | "Shadows" | "PBR" | "PathTracing" | "Foliage" | "ColoredLightning" | "Potato" | "Low" | "Medium" | "High" | "Ultra"
export type PackageSide = "unknown" | "required" | "optional" | "unsupported"
export type PackageStatus = "active" | "abandoned"
@ -139,7 +156,7 @@ export type Resolution = { width: number; height: number }
export type SearchQuery = { query: string | null; offset: bigint | null; limit: bigint | null; sort: Sort | null; filters: Filters | null }
export type SearchResult = { project_id: string; project_type: string; slug: string; author: string; title: string; description: string; categories: string[]; display_categories: string[]; versions: string[]; downloads: bigint; icon_url: string; date_created: string; date_modified: string; latest_version: string; license: string; client_side: PackageSide; server_side: PackageSide;
export type SearchResult = { project_id: string; package_type: PackageType; slug: string; author: string; title: string; description: string; categories: PackageCategories; mc_versions: string[]; downloads: bigint; icon_url: string; date_created: string; date_modified: string; latest_version: string; license: string | null; client_side: PackageSide; server_side: PackageSide;
/**
* List of URLs to images
*/
@ -149,7 +166,7 @@ export type SettingProfileModel = { name: string; java_id: bigint | null; res: R
export type Settings = { global_game_settings: SettingProfileModel; allow_parallel_running_clusters: boolean; enable_gamemode: boolean; discord_enabled: boolean; max_concurrent_requests: bigint; settings_version: number; native_window_frame: boolean }
export type SettingsOsExtra = { enable_gamemode: boolean | null }
export type SettingsOsExtra = Record<string, never>
export type Sort = "Relevance" | "Downloads" | "Newest" | "Updated"
@ -212,11 +229,8 @@ export type VersionType =
*/
"old_beta"
const ARGS_MAP = { 'core':'{"getUser":["uuid"],"getLoadersForVersion":["mc_version"],"getPackage":["provider","slug"],"getMultiplePackages":["provider","slugs"],"updateClusterProfile":["name","profile"],"getGlobalProfile":[],"openMsaLogin":[],"writeSettings":["setting"],"getProfileOrDefault":["name"],"removeUser":["uuid"],"getClusterById":["id"],"getGameVersions":[],"getUsers":[],"getPackageVersions":["provider","slug","mc_versions","loaders","offset","limit"],"getPackageUser":["provider","slug"],"getClusters":[],"updateClusterById":["id","request"],"getUsersFromAuthor":["provider","author"],"launchCluster":["id","uuid"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"createCluster":["options"],"setDefaultUser":["uuid"],"getWorlds":["id"],"removeCluster":["id"],"getScreenshots":["id"],"getDefaultUser":["fallback"],"readSettings":[],"searchPackages":["provider","query"]}', 'onelauncher':'{"set_window_style":["decorations"],"open_dev_tools":[],"return_error":[]}', 'events':'{"process":["event"],"message":["event"],"ingress":["event"]}' }
export type Router = { 'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'onelauncher': { return_error: () => Promise<null>,
const ARGS_MAP = { 'onelauncher':'{"open_dev_tools":[],"set_window_style":["decorations"],"return_error":[]}', 'core':'{"getClusters":[],"searchPackages":["provider","query"],"getLoadersForVersion":["mc_version"],"getUsers":[],"getScreenshots":["id"],"getMultiplePackages":["provider","slugs"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getUsersFromAuthor":["provider","author"],"updateClusterProfile":["name","profile"],"updateClusterById":["id","request"],"getPackageBody":["provider","body"],"getDefaultUser":["fallback"],"removeCluster":["id"],"removeUser":["uuid"],"createCluster":["options"],"openMsaLogin":[],"readSettings":[],"getGameVersions":[],"launchCluster":["id","uuid"],"writeSettings":["setting"],"getProfileOrDefault":["name"],"getPackage":["provider","slug"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getClusterById":["id"],"getWorlds":["id"],"getGlobalProfile":[],"getUser":["uuid"],"setDefaultUser":["uuid"]}', 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}' }
export type Router = { 'onelauncher': { return_error: () => Promise<null>,
open_dev_tools: () => Promise<void>,
set_window_style: (decorations: boolean) => Promise<void> },
'core': { getClusters: () => Promise<ClusterModel[]>,
@ -242,11 +256,14 @@ readSettings: () => Promise<Settings>,
writeSettings: (setting: Settings) => Promise<null>,
searchPackages: (provider: Provider, query: SearchQuery) => Promise<Paginated<SearchResult>>,
getPackage: (provider: Provider, slug: string) => Promise<ManagedPackage>,
getPackageBody: (provider: Provider, body: ManagedPackageBody) => Promise<string>,
getMultiplePackages: (provider: Provider, slugs: string[]) => Promise<ManagedPackage[]>,
getPackageVersions: (provider: Provider, slug: string, mcVersions: string[] | null, loaders: GameLoader[] | null, offset: bigint, limit: bigint) => Promise<Paginated<ManagedVersion>>,
getPackageUser: (provider: Provider, slug: string) => Promise<ManagedUser>,
getPackageVersions: (provider: Provider, slug: string, mcVersion: string | null, loader: GameLoader | null, offset: bigint, limit: bigint) => Promise<Paginated<ManagedVersion>>,
downloadPackage: (provider: Provider, packageId: string, versionId: string, clusterId: bigint, skipCompatibility: boolean | null) => Promise<PackageModel>,
getUsersFromAuthor: (provider: Provider, author: PackageAuthor) => Promise<ManagedUser[]> } };
getUsersFromAuthor: (provider: Provider, author: PackageAuthor) => Promise<ManagedUser[]> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> } };
export type { InferCommandOutput }

View file

@ -32,9 +32,9 @@ export function PackageItem({ provider, ...item }: SearchResult & { provider: Pr
return (
<Link
className="h-full min-w-50 overflow-hidden rounded-lg bg-component-bg hover:bg-component-bg-hover grid grid-rows-[7rem_auto] max-h-74 min-h-74"
params={{ provider, slug: item.slug }}
params={{ provider, id: item.project_id }}
tabIndex={0}
to="/app/browser/package/$provider/$slug"
to="/app/browser/package/$provider/$id"
>
<div
className="relative flex items-center justify-center overflow-hidden w-full h-28"

View file

@ -83,14 +83,14 @@ export function getCategories(categories: PackageCategories) {
export function useBrowserSearch(provider: Provider, query: SearchQuery, options?: Omit<UndefinedInitialDataOptions<Paginated<SearchResult>>, 'queryKey' | 'queryFn'> | undefined) {
const validFilters = useMemo(() => Object.values(query.filters ?? {}).filter(a => a).length > 0, [query.filters]);
return useCommand('searchPackages', () => bindings.core.searchPackages(provider, validFilters ? query : { ...query, filters: null }), options);
return useCommand(`search.${provider}`, () => bindings.core.searchPackages(provider, validFilters ? query : { ...query, filters: null }), options);
}
export function usePackageData(provider: Provider, slug: string, options?: Omit<UndefinedInitialDataOptions<ManagedPackage>, 'queryKey' | 'queryFn'> | undefined, key: false | BindingCommands | (string & {}) = `getPackage.${provider}.${slug}`) {
return useCommand(key, () => bindings.core.getPackage(provider, slug), options);
}
export function usePackageVersions(provider: Provider, slug: string, { mc_version, loader, offset, limit, ...options }: { mc_version?: string | null; loader?: GameLoader | null; offset?: number; limit: number } & Omit<UndefinedInitialDataOptions<Paginated<ManagedVersion>>, 'queryKey' | 'queryFn'>, key: false | BindingCommands | (string & {}) = `getPackageVersions.${provider}.${slug}.${mc_version}.${loader}`) {
export function usePackageVersions(provider: Provider, slug: string, { mc_version, loader, offset, limit, ...options }: { mc_version?: string | null; loader?: GameLoader | null; offset?: number; limit: number } & Omit<UndefinedInitialDataOptions<Paginated<ManagedVersion>>, 'queryKey' | 'queryFn'>, key: false | BindingCommands | (string & {}) = `getPackageVersions.${provider}.${slug}.${mc_version}.${loader}.${offset}`) {
return useCommand(key, () => bindings.core.getPackageVersions(provider, slug, mc_version ?? null, loader ?? null, (offset ?? 0) as unknown as bigint, limit as unknown as bigint), options);
}

View file

@ -5,7 +5,7 @@ import { useBrowserContext, useDownloadPackage, usePackageData, usePackageVersio
import { ChooseClusterModal, useClusters } from '@/hooks/useCluster';
import usePagination from '@/hooks/usePagination';
import { bindings } from '@/main';
import { abbreviateNumber, formatAsRelative, PROVIDERS, upperFirst } from '@/utils';
import { abbreviateNumber, formatAsRelative, includes, PROVIDERS, upperFirst } from '@/utils';
import { useCommand } from '@onelauncher/common';
import { Button, Show, Tooltip } from '@onelauncher/common/components';
import { createFileRoute, Link } from '@tanstack/react-router';
@ -18,14 +18,10 @@ import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import { twMerge } from 'tailwind-merge';
export const Route = createFileRoute('/app/browser/package/$provider/$slug')({
export const Route = createFileRoute('/app/browser/package/$provider/$id')({
component: RouteComponent,
});
function includes<T, TArray extends T>(list: { includes: (arg0: TArray) => boolean }, element: T): element is TArray {
return list.includes(element as unknown as TArray);
}
function CustomA({ href, children, includeIcon, className, ...rest }: { href: string; children: any; includeIcon?: boolean } & HTMLProps<HTMLAnchorElement>) {
return (
<a
@ -52,10 +48,11 @@ const PackageContext = createContext<PackageContextType>({
});
function RouteComponent() {
const { provider, slug } = Route.useParams();
const { provider, id } = Route.useParams();
if (!includes(PROVIDERS, provider))
throw new Error('Invalid provider');
const packageData = usePackageData(provider, slug, {});
const packageData = usePackageData(provider, id, {});
const { data: packageBody } = useCommand('getPackageBody', () => bindings.core.getPackageBody(provider, packageData.data?.body ?? { Raw: 'No Package Data' }));
const packageContextValue = useMemo(() => ({
pkg: packageData.data,
}), [packageData.data]);
@ -81,9 +78,7 @@ function RouteComponent() {
rehypePlugins={[rehypeRaw]}
remarkPlugins={[remarkGfm]}
>
{'Raw' in (packageData.data?.body ?? { Raw: '' })
? (packageData.data?.body ?? { Raw: '' }).Raw
: packageData.data?.body.Url}
{packageBody}
</Markdown>
</div>
</TabPanel>
@ -249,13 +244,13 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {
function InstallButton() {
const triggerRef = useRef<HTMLDivElement>(null);
const { provider, slug } = Route.useParams();
const { provider, id } = Route.useParams();
if (!includes(PROVIDERS, provider))
throw new Error('invalid provider');
const [open, setOpen] = useState(false);
const clusters = useClusters();
const browserContext = useBrowserContext();
const { data: versions, isLoading: versionsLoading } = usePackageVersions(provider, slug, {
const { data: versions, isLoading: versionsLoading } = usePackageVersions(provider, id, {
mc_version: browserContext.cluster ? browserContext.cluster.mc_version : null,
loader: browserContext.cluster ? browserContext.cluster.mc_loader : null,
limit: 1,
@ -408,11 +403,12 @@ function colorForType(type: string) {
}
function Versions() {
const { provider, slug } = Route.useParams();
const { provider, id } = Route.useParams();
if (!includes(PROVIDERS, provider))
throw new Error('invalid provider');
const [offset, setOffset] = useState(0);
const { data: versions } = usePackageVersions(provider, slug, {
const { data: versions } = usePackageVersions(provider, id, {
limit: 20,
offset,
});

View file

@ -342,6 +342,7 @@ struct ModrinthSearchResult {
pub title: String,
pub description: String,
pub categories: Vec<String>,
#[serde(alias = "versions")]
pub mc_versions: Vec<String>,
pub downloads: usize,
pub icon_url: String,

View file

@ -1,5 +1,5 @@
use crate::api::packages::data::{
ManagedPackage, ManagedUser, ManagedVersion, PackageAuthor, SearchQuery, SearchResult,
ManagedPackage, ManagedPackageBody, ManagedUser, ManagedVersion, PackageAuthor, SearchQuery, SearchResult
};
use crate::api::packages::provider::ProviderExt;
use crate::store::{Settings, State};
@ -108,6 +108,9 @@ pub trait TauriLauncherApi {
#[taurpc(alias = "getPackage")]
async fn get_package(provider: Provider, slug: String) -> LauncherResult<ManagedPackage>;
#[taurpc(alias = "getPackageBody")]
async fn get_package_body(provider: Provider, body: ManagedPackageBody) -> LauncherResult<String>;
#[taurpc(alias = "getMultiplePackages")]
async fn get_multiple_packages(
provider: Provider,
@ -473,6 +476,10 @@ impl TauriLauncherApi for TauriLauncherApiImpl {
provider.get(&slug).await
}
async fn get_package_body(self, provider: Provider, body: ManagedPackageBody) -> LauncherResult<String> {
provider.get_body(&body).await
}
async fn get_multiple_packages(
self,
provider: Provider,