diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 4b01aefa..cd5340aa 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -236,7 +236,7 @@ export type SettingProfileModel = { name: string; java_id: number | null; res: R export type Settings = { global_game_settings: SettingProfileModel; allow_parallel_running_clusters: boolean; enable_gamemode: boolean; discord_enabled: boolean; seen_onboarding: boolean; mod_list_use_grid: boolean; parallel_mod_downloading: boolean; max_concurrent_requests: number; settings_version: number; native_window_frame: boolean; show_tanstack_dev_tools: boolean } -export type SettingsOsExtra = { enable_gamemode: boolean | null } +export type SettingsOsExtra = Record export type SkinVariant = "classic" | "slim" @@ -303,10 +303,8 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'oneclient':'{"installUpdate":[],"checkForUpdate":[],"getBundlesFor":["cluster_id"],"getVersions":[],"getClustersGroupedByMajor":[]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'debug':'{"isInDev":[],"getPlatform":[],"getArch":[],"getFamily":[],"getLocale":[],"openDevTools":[],"getOsVersion":[],"getBuildTimestamp":[],"getGitCommitHash":[],"getPackageVersion":[],"getType":[]}', 'core':'{"getLinkedPackages":["cluster_id"],"setDefaultUser":["uuid"],"refreshAccounts":[],"getUsers":[],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getRunningProcessesByClusterId":["cluster_id"],"getUser":["uuid"],"readSettings":[],"getClusters":[],"getLoadersForVersion":["mc_version"],"removePackage":["cluster_id","package_hash"],"fetchMinecraftProfile":["uuid"],"fetchLoggedInProfile":["access_token"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeSkin":["access_token","skin_url","skin_variant"],"removeCape":["access_token"],"getPackageBody":["provider","body"],"setDiscordRPCMessage":["message"],"setClusterStage":["id","stage"],"writeSettings":["setting"],"open":["input"],"getLogByName":["id","name"],"getRunningProcesses":[],"searchPackages":["provider","query"],"createSettingsProfile":["name"],"getLogs":["id"],"getProfileOrDefault":["name"],"openMsaLogin":[],"convertUsernameUUID":["username_uuid"],"getScreenshots":["id"],"createCluster":["options"],"removeCluster":["id"],"isClusterRunning":["cluster_id"],"getDefaultUser":["fallback"],"getPackage":["provider","slug"],"installModpack":["modpack","cluster_id"],"launchCluster":["id","uuid","search_for_java"],"getGlobalProfile":[],"refreshAccount":["uuid"],"changeCape":["access_token","cape_uuid"],"getUsersFromAuthor":["provider","author"],"killProcess":["pid"],"getClusterById":["id"],"getWorlds":["id"],"updateClusterProfile":["name","profile"],"getGameVersions":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"updateClusterById":["id","request"],"getMultiplePackages":["provider","slugs"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"removeUser":["uuid"]}' } -export type Router = { 'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise }, -'debug': { openDevTools: () => Promise, +const ARGS_MAP = { 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'oneclient':'{"getBundlesFor":["cluster_id"],"checkForUpdate":[],"installUpdate":[],"getVersions":[],"getClustersGroupedByMajor":[]}', 'debug':'{"getLocale":[],"getType":[],"getArch":[],"getFamily":[],"getOsVersion":[],"getPackageVersion":[],"getPlatform":[],"getBuildTimestamp":[],"isInDev":[],"getGitCommitHash":[],"openDevTools":[]}', 'core':'{"getWorlds":["id"],"setClusterStage":["id","stage"],"getDefaultUser":["fallback"],"setDefaultUser":["uuid"],"getClusterById":["id"],"getLogByName":["id","name"],"removePackage":["cluster_id","package_hash"],"getRunningProcessesByClusterId":["cluster_id"],"getPackage":["provider","slug"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"isClusterRunning":["cluster_id"],"getUser":["uuid"],"setDiscordRPCMessage":["message"],"getRunningProcesses":[],"killProcess":["pid"],"getLoadersForVersion":["mc_version"],"searchPackages":["provider","query"],"updateClusterById":["id","request"],"getClusters":[],"getGameVersions":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"launchCluster":["id","uuid","search_for_java"],"installModpack":["modpack","cluster_id"],"changeSkin":["access_token","skin_url","skin_variant"],"readSettings":[],"refreshAccount":["uuid"],"getMultiplePackages":["provider","slugs"],"openMsaLogin":[],"updateClusterProfile":["name","profile"],"open":["input"],"getUsers":[],"getScreenshots":["id"],"getProfileOrDefault":["name"],"removeUser":["uuid"],"getGlobalProfile":[],"createCluster":["options"],"getUsersFromAuthor":["provider","author"],"fetchMinecraftProfile":["uuid"],"removeCape":["access_token"],"convertUsernameUUID":["username_uuid"],"createSettingsProfile":["name"],"getLogs":["id"],"getPackageBody":["provider","body"],"refreshAccounts":[],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getLinkedPackages":["cluster_id"],"changeCape":["access_token","cape_uuid"],"writeSettings":["setting"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"fetchLoggedInProfile":["access_token"],"removeCluster":["id"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' } +export type Router = { 'debug': { openDevTools: () => Promise, isInDev: () => Promise, getArch: () => Promise, getFamily: () => Promise, @@ -317,6 +315,8 @@ getOsVersion: () => Promise, getGitCommitHash: () => Promise, getBuildTimestamp: () => Promise, getPackageVersion: () => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise }, 'oneclient': { getClustersGroupedByMajor: () => Promise>, getBundlesFor: (clusterId: number) => Promise, getVersions: () => Promise, diff --git a/apps/oneclient/frontend/src/routes/app/clusters.tsx b/apps/oneclient/frontend/src/routes/app/clusters.tsx index d85922b8..24e5ac1a 100644 --- a/apps/oneclient/frontend/src/routes/app/clusters.tsx +++ b/apps/oneclient/frontend/src/routes/app/clusters.tsx @@ -48,21 +48,32 @@ function RouteComponent() { return []; const versions = list.map(c => parseMcVersion(c.mc_version)?.minor).filter((v): v is number => v !== undefined); - const sorted = Array.from(new Set(versions)).sort((a, b) => a - b); + return Array.from(new Set(versions)).sort((a, b) => a - b); + }, [clusters, majorVersion]); - // attempt to load the minor version from localStorage - const storedMinor = loadMinorVersion(majorVersion) || sorted[0]; - setActiveMinorVersion(storedMinor); - - return sorted; - }, [clusters, loadMinorVersion, majorVersion]); - - const modLoader = useMemo(() => { - if (!activeMinorVersion) + // Derive effective minor version without side effects in useMemo + const effectiveMinorVersion = useMemo(() => { + if (minorVersions.length === 0) return undefined; - return modLoaders[`${majorVersion}.${activeMinorVersion}`]; - }, [modLoaders, majorVersion, activeMinorVersion]); + // If user has explicitly selected a minor version valid for this major, use it + if (activeMinorVersion !== undefined && minorVersions.includes(activeMinorVersion)) + return activeMinorVersion; + + // Otherwise fall back to stored version or first available + const storedMinor = loadMinorVersion(majorVersion); + if (storedMinor !== undefined && minorVersions.includes(storedMinor)) + return storedMinor; + + return minorVersions[0]; + }, [minorVersions, activeMinorVersion, loadMinorVersion, majorVersion]); + + const modLoader = useMemo(() => { + if (!effectiveMinorVersion) + return undefined; + + return modLoaders[`${majorVersion}.${effectiveMinorVersion}`]; + }, [modLoaders, majorVersion, effectiveMinorVersion]); const loaders = useMemo(() => { const list = clusters[majorVersion]; @@ -79,15 +90,16 @@ function RouteComponent() { const cluster = useMemo(() => { const list = clusters[majorVersion]; - if (!activeMinorVersion) + if (effectiveMinorVersion === undefined) return list?.[0]; return list?.find((c) => { - const version = c.mc_version.endsWith(activeMinorVersion.toString()); + const parsed = parseMcVersion(c.mc_version); + const versionMatch = parsed?.minor === effectiveMinorVersion; const loaderCheck = modLoader ? c.mc_loader === modLoader : true; - return version && loaderCheck; + return versionMatch && loaderCheck; }); - }, [clusters, majorVersion, activeMinorVersion, modLoader]); + }, [clusters, majorVersion, effectiveMinorVersion, modLoader]); const versionInfo = useMemo(() => getVersionInfoOrDefault(cluster?.mc_version), [cluster]); @@ -165,7 +177,7 @@ function RouteComponent() { setActiveMinorVersion(Number(selected)); setMinorVersion(majorVersion, Number(selected)); }} - selectedKey={activeMinorVersion} + selectedKey={effectiveMinorVersion} > {minorVersions.map((minorVersion) => { const fullVer = `${versionInfo.prettyName}.${minorVersion}`; @@ -189,12 +201,12 @@ function RouteComponent() { { - if (!activeMinorVersion) - return; + if (!effectiveMinorVersion) + return; - setModLoader(`${majorVersion}.${activeMinorVersion}`, selected as GameLoader); - }} - selectedKey={activeMinorVersion ? (modLoaders[`${majorVersion}.${activeMinorVersion}`] ?? loaders[0]) : loaders[0]} + setModLoader(`${majorVersion}.${effectiveMinorVersion}`, selected as GameLoader); + }} + selectedKey={effectiveMinorVersion ? (modLoaders[`${majorVersion}.${effectiveMinorVersion}`] ?? loaders[0]) : loaders[0]} > {loaders.map(loader => (