diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 13cf1b8f..0c4b7437 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -303,11 +303,8 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'core':'{"removeUser":["uuid"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"changeCape":["access_token","cape_uuid"],"getScreenshots":["id"],"setClusterStage":["id","stage"],"getRunningProcesses":[],"convertUsernameUUID":["username_uuid"],"getClusterById":["id"],"getRunningProcessesByClusterId":["cluster_id"],"getLoadersForVersion":["mc_version"],"getPackageBody":["provider","body"],"open":["input"],"getMultiplePackages":["provider","slugs"],"isClusterRunning":["cluster_id"],"removeCape":["access_token"],"getGlobalProfile":[],"setDiscordRPCMessage":["message"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"fetchLoggedInProfile":["access_token"],"readSettings":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"killProcess":["pid"],"refreshAccounts":[],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getLogs":["id"],"updateClusterProfile":["name","profile"],"searchPackages":["provider","query"],"updateClusterById":["id","request"],"getWorlds":["id"],"getProfileOrDefault":["name"],"refreshAccount":["uuid"],"getUsers":[],"getLogByName":["id","name"],"removeCluster":["id"],"launchCluster":["id","uuid","search_for_java"],"openMsaLogin":[],"getPackage":["provider","slug"],"writeSettings":["setting"],"getClusters":[],"getUsersFromAuthor":["provider","author"],"getGameVersions":[],"getUser":["uuid"],"getLinkedPackages":["cluster_id"],"removePackage":["cluster_id","package_hash"],"createCluster":["options"],"getDefaultUser":["fallback"],"setDefaultUser":["uuid"],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"createSettingsProfile":["name"],"changeSkin":["access_token","skin_url","skin_variant"]}', 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'debug':'{"getLocale":[],"getPlatform":[],"isInDev":[],"getGitCommitHash":[],"getOsVersion":[],"getFamily":[],"getBuildTimestamp":[],"getPackageVersion":[],"openDevTools":[],"getType":[],"getArch":[]}', 'oneclient':'{"getVersions":[],"installUpdate":[],"getClustersGroupedByMajor":[],"extractBundleOverrides":["bundle_path","cluster_id"],"checkForUpdate":[],"getBundlesFor":["cluster_id"]}' } -export type Router = { 'events': { ingress: (event: IngressPayload) => Promise, -message: (event: MessagePayload) => Promise, -process: (event: ProcessPayload) => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, +const ARGS_MAP = { 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'debug':'{"getLocale":[],"getPlatform":[],"isInDev":[],"getGitCommitHash":[],"getOsVersion":[],"getFamily":[],"getBuildTimestamp":[],"getPackageVersion":[],"openDevTools":[],"getType":[],"getArch":[]}', 'core':'{"removeUser":["uuid"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"changeCape":["access_token","cape_uuid"],"getScreenshots":["id"],"setClusterStage":["id","stage"],"getRunningProcesses":[],"convertUsernameUUID":["username_uuid"],"getClusterById":["id"],"getRunningProcessesByClusterId":["cluster_id"],"getLoadersForVersion":["mc_version"],"getPackageBody":["provider","body"],"open":["input"],"getMultiplePackages":["provider","slugs"],"isClusterRunning":["cluster_id"],"removeCape":["access_token"],"getGlobalProfile":[],"setDiscordRPCMessage":["message"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"fetchLoggedInProfile":["access_token"],"readSettings":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"killProcess":["pid"],"refreshAccounts":[],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getLogs":["id"],"updateClusterProfile":["name","profile"],"searchPackages":["provider","query"],"updateClusterById":["id","request"],"getWorlds":["id"],"getProfileOrDefault":["name"],"refreshAccount":["uuid"],"getUsers":[],"getLogByName":["id","name"],"removeCluster":["id"],"launchCluster":["id","uuid","search_for_java"],"openMsaLogin":[],"getPackage":["provider","slug"],"writeSettings":["setting"],"getClusters":[],"getUsersFromAuthor":["provider","author"],"getGameVersions":[],"getUser":["uuid"],"getLinkedPackages":["cluster_id"],"removePackage":["cluster_id","package_hash"],"createCluster":["options"],"getDefaultUser":["fallback"],"setDefaultUser":["uuid"],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"createSettingsProfile":["name"],"changeSkin":["access_token","skin_url","skin_variant"]}', 'oneclient':'{"getVersions":[],"installUpdate":[],"getClustersGroupedByMajor":[],"extractBundleOverrides":["bundle_path","cluster_id"],"checkForUpdate":[],"getBundlesFor":["cluster_id"]}' } +export type Router = { 'folders': { fromCluster: (folderName: string) => Promise, openCluster: (folderName: string) => Promise }, 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, @@ -360,12 +357,6 @@ removeCape: (accessToken: string) => Promise, convertUsernameUUID: (usernameUuid: string) => Promise, setDiscordRPCMessage: (message: string) => Promise, open: (input: string) => Promise }, -'oneclient': { getClustersGroupedByMajor: () => Promise>, -getBundlesFor: (clusterId: number) => Promise, -getVersions: () => Promise, -extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise, -checkForUpdate: () => Promise, -installUpdate: () => Promise }, 'debug': { openDevTools: () => Promise, isInDev: () => Promise, getArch: () => Promise, @@ -376,7 +367,16 @@ getPlatform: () => Promise, getOsVersion: () => Promise, getGitCommitHash: () => Promise, getBuildTimestamp: () => Promise, -getPackageVersion: () => Promise } }; +getPackageVersion: () => Promise }, +'events': { ingress: (event: IngressPayload) => Promise, +message: (event: MessagePayload) => Promise, +process: (event: ProcessPayload) => Promise }, +'oneclient': { getClustersGroupedByMajor: () => Promise>, +getBundlesFor: (clusterId: number) => Promise, +getVersions: () => Promise, +extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise, +checkForUpdate: () => Promise, +installUpdate: () => Promise } }; export type { InferCommandOutput } diff --git a/apps/oneclient/frontend/src/components/DownloadMods.tsx b/apps/oneclient/frontend/src/components/DownloadMods.tsx index a9663a0b..b50b2b1e 100644 --- a/apps/oneclient/frontend/src/components/DownloadMods.tsx +++ b/apps/oneclient/frontend/src/components/DownloadMods.tsx @@ -1,11 +1,11 @@ -import type { ExternalPackage, ManagedVersionDependency, ModpackArchive, ModpackFile, Provider } from '@/bindings.gen'; +import type { ExternalPackage, ManagedVersionDependency, ModpackFile, Provider } from '@/bindings.gen'; import { getModMetaDataName, Overlay } from '@/components'; import { useSettings } from '@/hooks/useSettings'; import { bindings } from '@/main'; import { useCommandMut } from '@onelauncher/common'; import { Button } from '@onelauncher/common/components'; import { useNavigate } from '@tanstack/react-router'; -import { useEffect, useImperativeHandle, useRef, useState } from 'react'; +import { useEffect, useImperativeHandle, useState } from 'react'; import { DialogTrigger } from 'react-aria-components'; export interface DownloadModsRef { @@ -36,7 +36,7 @@ export function isManagedMod(mod: ModData): mod is ManagedModData { return mod.managed === true; } -export function DownloadMods({ modsPerCluster, bundlesPerCluster, ref }: { modsPerCluster: Record>; bundlesPerCluster?: Record>; ref: React.Ref }) { +export function DownloadMods({ modsPerCluster, ref }: { modsPerCluster: Record>; ref: React.Ref }) { const navigate = useNavigate(); const [isOpen, setOpen] = useState(false); const [mods, setMods] = useState([]); @@ -89,7 +89,7 @@ export function DownloadMods({ modsPerCluster, bundlesPerCluster, ref }: { modsP - + ); @@ -106,12 +106,10 @@ function downloadModsParallel(items: ModDataArray, limit: number, fn: (mod: ModD return Promise.all(workers); } -function DownloadingMods({ mods, setOpen, nextPath, bundlesPerCluster }: { mods: ModDataArray; setOpen: React.Dispatch>; nextPath: string; bundlesPerCluster?: Record> }) { +function DownloadingMods({ mods, setOpen, nextPath }: { mods: ModDataArray; setOpen: React.Dispatch>; nextPath: string }) { const navigate = useNavigate(); const [downloadedMods, setDownloadedMods] = useState(0); const [modName, setModName] = useState(null); - const [allDone, setAllDone] = useState(false); - const hasStarted = useRef(false); const download = useCommandMut(async (mod: ModData) => { if (isManagedMod(mod)) { if (mod.dependencies.length > 0) @@ -135,72 +133,37 @@ function DownloadingMods({ mods, setOpen, nextPath, bundlesPerCluster }: { mods: let useParallelModDownloading = setting('parallel_mod_downloading'); useEffect(() => { - if (hasStarted.current) - return; - hasStarted.current = true; - const downloadAll = async () => { - try { - if (useParallelModDownloading) - await downloadModsParallel(mods, 10, async (mod) => { - setModName(mod.name); - try { - await download.mutateAsync(mod); - } - catch (e) { - console.warn(`Failed to download mod ${mod.name}:`, e); - } - finally { - setDownloadedMods(prev => prev + 1); - } - }); else - for (const mod of mods) { - setModName(mod.name); - try { - await download.mutateAsync(mod); - } - catch (e) { - console.warn(`Failed to download mod ${mod.name}:`, e); - } - finally { - setDownloadedMods(prev => prev + 1); - } + if (useParallelModDownloading) + await downloadModsParallel(mods, 10, async (mod) => { + setModName(mod.name); + try { + await download.mutateAsync(mod); } - - // Extract overrides from enabled bundles after all mods are downloaded - if (bundlesPerCluster) { - setModName('Extracting overrides...'); - for (const [clusterId, bundles] of Object.entries(bundlesPerCluster)) { - for (const bundle of bundles) { - if (bundle.manifest.enabled) { - try { - await bindings.oneclient.extractBundleOverrides(bundle.path, Number(clusterId)); - } - catch (e) { - console.error(`Failed to extract overrides for bundle ${bundle.manifest.name}:`, e); - } - } - } + finally { + setDownloadedMods(prev => prev + 1); + } + }); else + for (const mod of mods) { + setModName(mod.name); + try { + await download.mutateAsync(mod); + } + finally { + setDownloadedMods(prev => prev + 1); } } - } - catch (e) { - console.error('Error during mod download:', e); - } - finally { - setAllDone(true); - } }; downloadAll(); }, [mods]); useEffect(() => { - if (allDone) { + if (downloadedMods >= mods.length) { setOpen(false); navigate({ to: nextPath }); } - }, [allDone, navigate, nextPath, setOpen]); + }, [downloadedMods, mods, navigate, nextPath, setOpen]); return ( diff --git a/apps/oneclient/frontend/src/routes/onboarding/preferences/versionCategory.tsx b/apps/oneclient/frontend/src/routes/onboarding/preferences/versionCategory.tsx index 075a60de..8a852a7f 100644 --- a/apps/oneclient/frontend/src/routes/onboarding/preferences/versionCategory.tsx +++ b/apps/oneclient/frontend/src/routes/onboarding/preferences/versionCategory.tsx @@ -9,7 +9,7 @@ import { Button } from '@onelauncher/common/components'; import { createFileRoute } from '@tanstack/react-router'; import { DotsVerticalIcon } from '@untitled-theme/icons-react'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; -import { useMemo, useRef, useState } from 'react'; +import { useImperativeHandle, useMemo, useRef, useState } from 'react'; import { Button as AriaButton } from 'react-aria-components'; import { twMerge } from 'tailwind-merge'; @@ -62,13 +62,38 @@ function RouteComponent() { ); const bundlesPerCluster = useMemo(() => { - return clusters.reduce((acc, cluster, i) => { - acc[cluster.id] = bundleQueries[i]; - return acc; - }, {} as Record>); - }, [clusters, bundleQueries]); + const result: Record> = {}; + clusters.forEach((cluster, i) => { + const selected = selectedClusters.some(sc => sc.mc_version === cluster.mc_version && sc.mc_loader === cluster.mc_loader); + if (selected) + result[cluster.id] = bundleQueries[i]; + }); + return result; + }, [clusters, bundleQueries, selectedClusters]); const downloadModsRef = useRef(null); + const wrappedRef = useRef(null); + + useImperativeHandle(wrappedRef, () => ({ + async openDownloadDialog(nextPath?: string) { + // Extract overrides from all enabled bundles before downloading mods + for (const [clusterId, bundles] of Object.entries(bundlesPerCluster)) { + for (const bundle of bundles) { + if (bundle.manifest.enabled) { + try { + await bindings.oneclient.extractBundleOverrides(bundle.path, Number(clusterId)); + } + catch (e) { + console.error(`Failed to extract overrides for bundle ${bundle.manifest.name}:`, e); + } + } + } + } + + // Then delegate to the actual mod download dialog + downloadModsRef.current?.openDownloadDialog(nextPath); + }, + }), [bundlesPerCluster]); return ( <> @@ -89,13 +114,13 @@ function RouteComponent() {
- +
- + ); }