Skip to content

Commit b81d422

Browse files
authored
fix(resource): manually sort quilt and optifine version list API response by semver and custom handler (#1586)
* fix(resource): manually sort quilt and optifine version list API response by semver and custom handler * refactor(frontend): unify shaderpack loader card style, the same as mod loader page
1 parent 2d4ed1a commit b81d422

3 files changed

Lines changed: 108 additions & 38 deletions

File tree

src-tauri/src/resource/helpers/loader_meta/optifine.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ use crate::resource::models::{OptiFineResourceInfo, ResourceError, ResourceType,
44
use tauri::Manager;
55
use tauri_plugin_http::reqwest;
66

7+
fn get_optifine_sort_key(info: &OptiFineResourceInfo) -> (u32, u32, u32) {
8+
let Some((_, suffix)) = info.filename.rsplit_once("_HD_U_") else {
9+
return (0, 0, 0);
10+
};
11+
let suffix = suffix.trim_end_matches(".jar");
12+
let (version, pre) = match suffix.split_once("_pre") {
13+
Some((version, pre)) => (version, pre.parse().unwrap_or(0)),
14+
None => (suffix, u32::MAX),
15+
};
16+
let mut chars = version.chars();
17+
let prefix = chars
18+
.next()
19+
.map(|ch| ch.to_ascii_uppercase() as u32)
20+
.unwrap_or(0);
21+
let series = chars.as_str().parse().unwrap_or(0);
22+
(prefix, series, pre)
23+
}
24+
725
async fn get_optifine_meta_by_game_version_bmcl(
826
app: &tauri::AppHandle,
927
game_version: &str,
@@ -14,10 +32,16 @@ async fn get_optifine_meta_by_game_version_bmcl(
1432
match client.get(url).send().await {
1533
Ok(response) => {
1634
if response.status().is_success() {
17-
response
35+
let mut manifest = response
1836
.json::<Vec<OptiFineResourceInfo>>()
1937
.await
20-
.map_err(|_| ResourceError::ParseError.into())
38+
.map_err(|_| ResourceError::ParseError)?;
39+
manifest.sort_by(|a, b| {
40+
get_optifine_sort_key(b)
41+
.cmp(&get_optifine_sort_key(a))
42+
.then_with(|| b.filename.cmp(&a.filename))
43+
});
44+
Ok(manifest)
2145
} else {
2246
Err(ResourceError::NetworkError.into())
2347
}

src-tauri/src/resource/helpers/loader_meta/quilt.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::error::SJMCLResult;
22
use crate::instance::models::misc::ModLoaderType;
33
use crate::resource::helpers::misc::get_download_api;
44
use crate::resource::models::{ModLoaderResourceInfo, ResourceError, ResourceType, SourceType};
5+
use semver::Version;
56
use serde::{Deserialize, Serialize};
67
use serde_json::Value;
78
use tauri::{AppHandle, Manager};
@@ -35,16 +36,34 @@ pub async fn get_quilt_meta_by_game_version(
3536
match client.get(url).send().await {
3637
Ok(response) => {
3738
if response.status().is_success() {
38-
if let Ok(manifest) = response.json::<Vec<QuiltMetaItem>>().await {
39+
// API response may not be in current order, sort by semver here.
40+
if let Ok(mut manifest) = response.json::<Vec<QuiltMetaItem>>().await {
41+
manifest.sort_by(|a, b| {
42+
match (
43+
Version::parse(&a.loader.version),
44+
Version::parse(&b.loader.version),
45+
) {
46+
(Ok(left), Ok(right)) => right.cmp(&left),
47+
(Ok(_), Err(_)) => std::cmp::Ordering::Less,
48+
(Err(_), Ok(_)) => std::cmp::Ordering::Greater,
49+
(Err(_), Err(_)) => b.loader.version.cmp(&a.loader.version),
50+
}
51+
});
3952
return Ok(
4053
manifest
4154
.into_iter()
42-
.map(|info| ModLoaderResourceInfo {
43-
loader_type: ModLoaderType::Quilt,
44-
version: info.loader.version,
45-
description: String::new(),
46-
stable: true,
47-
branch: None,
55+
.map(|info| {
56+
let version = info.loader.version;
57+
let stable = !version.contains("beta")
58+
&& !version.contains("alpha")
59+
&& !version.contains("rc");
60+
ModLoaderResourceInfo {
61+
loader_type: ModLoaderType::Quilt,
62+
version,
63+
description: String::new(),
64+
stable,
65+
branch: None,
66+
}
4867
})
4968
.collect(),
5069
);

src/pages/instances/details/[id]/shaderpacks.tsx

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
import { Center, HStack, useDisclosure } from "@chakra-ui/react";
1+
import {
2+
Center,
3+
Flex,
4+
HStack,
5+
Icon,
6+
IconButton,
7+
Image,
8+
Text,
9+
VStack,
10+
useDisclosure,
11+
} from "@chakra-ui/react";
212
import { revealItemInDir } from "@tauri-apps/plugin-opener";
313
import { useCallback, useEffect, useState } from "react";
414
import { useTranslation } from "react-i18next";
5-
import { LuHaze } from "react-icons/lu";
15+
import { LuChevronRight, LuHaze } from "react-icons/lu";
616
import { BeatLoader } from "react-spinners";
717
import { CommonIconButton } from "@/components/common/common-icon-button";
818
import CountTag from "@/components/common/count-tag";
919
import Empty from "@/components/common/empty";
1020
import { OptionItem, OptionItemGroup } from "@/components/common/option-item";
1121
import { Section } from "@/components/common/section";
12-
import SelectableCard, {
13-
SelectableCardProps,
14-
} from "@/components/common/selectable-card";
22+
import { WrapCardGroup } from "@/components/common/wrap-card";
1523
import { ChangeLoaderModal } from "@/components/modals/change-loader-modal";
1624
import { useFileDnD } from "@/components/special/file-dnd-overlay";
1725
import { useLauncherConfig } from "@/contexts/config";
@@ -149,21 +157,50 @@ const InstanceShaderPacksPage = () => {
149157
},
150158
];
151159

152-
const selectableCardItems: SelectableCardProps[] = [
160+
const shaderLoaderCardItems = [
153161
{
154-
title: "OptiFine",
155-
iconSrc: "/images/icons/OptiFine.png",
156-
description:
157-
summary?.optifine?.status === "Installed"
158-
? summary?.optifine?.version
159-
: t("InstanceShaderPacksPage.shaderLoaderList.notInstalled"),
160-
displayMode: "entry",
162+
cardContent: (
163+
<Flex justify="space-between" align="center">
164+
<HStack spacing={2}>
165+
<Image
166+
src="/images/icons/OptiFine.png"
167+
alt="OptiFine"
168+
boxSize="28px"
169+
borderRadius="4px"
170+
/>
171+
<VStack spacing={0} alignItems="start">
172+
<Text
173+
fontSize="xs-sm"
174+
fontWeight={
175+
summary?.optifine?.status === "Installed" ? "bold" : "normal"
176+
}
177+
color={
178+
summary?.optifine?.status === "Installed"
179+
? `${config.appearance.theme.primaryColor}.600`
180+
: "inherit"
181+
}
182+
>
183+
OptiFine
184+
</Text>
185+
<Text fontSize="xs" className="secondary-text">
186+
{summary?.optifine?.status === "Installed"
187+
? summary?.optifine?.version
188+
: t("InstanceShaderPacksPage.shaderLoaderList.notInstalled")}
189+
</Text>
190+
</VStack>
191+
</HStack>
192+
<HStack spacing={0}>
193+
<IconButton
194+
aria-label="select"
195+
icon={<Icon as={LuChevronRight} boxSize={3.5} />}
196+
variant="ghost"
197+
size="xs"
198+
onClick={onChangeLoaderModalOpen}
199+
/>
200+
</HStack>
201+
</Flex>
202+
),
161203
isSelected: summary?.optifine?.status === "Installed",
162-
onSelect: () => {
163-
onChangeLoaderModalOpen();
164-
},
165-
isDisabled: false,
166-
isChevronShown: true,
167204
},
168205
];
169206

@@ -180,17 +217,7 @@ const InstanceShaderPacksPage = () => {
180217
);
181218
}}
182219
>
183-
<HStack spacing={3.5} w="100%">
184-
{selectableCardItems.map((item, index) => (
185-
<SelectableCard
186-
key={index}
187-
{...item}
188-
flex={1}
189-
minH="max-content"
190-
h="100%"
191-
/>
192-
))}
193-
</HStack>
220+
<WrapCardGroup items={shaderLoaderCardItems} />
194221
</Section>
195222
<Section
196223
title={t("InstanceShaderPacksPage.shaderPackList.title")}

0 commit comments

Comments
 (0)