Skip to content

Commit 0c69a51

Browse files
committed
Merge branch 'fix/log' of https://github.com/UNIkeEN/SJMCL into fix/log
2 parents 735f58f + b81d422 commit 0c69a51

15 files changed

Lines changed: 288 additions & 145 deletions

File tree

package-lock.json

Lines changed: 15 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@tauri-apps/plugin-os": "^2.3.0",
4545
"@tauri-apps/plugin-process": "^2.3.0",
4646
"@tauri-apps/plugin-window-state": "^2.3.0",
47+
"fuse.js": "^7.3.0",
4748
"i18next": "^24.0.0",
4849
"lodash": "^4.18.1",
4950
"masonic": "^4.0.1",
@@ -57,8 +58,7 @@
5758
"react-spinners": "^0.14.1",
5859
"react-virtualized": "^9.22.6",
5960
"remark-gfm": "^4.0.1",
60-
"skinview3d": "^3.1.0",
61-
"string-similarity": "^4.0.4"
61+
"skinview3d": "^3.1.0"
6262
},
6363
"devDependencies": {
6464
"@next/bundle-analyzer": "^16.2.4",
@@ -70,7 +70,6 @@
7070
"@types/react": "^18",
7171
"@types/react-dom": "^18",
7272
"@types/react-virtualized": "^9.22.0",
73-
"@types/string-similarity": "^4.0.2",
7473
"chalk": "^4.1.2",
7574
"eslint": "^9.39.4",
7675
"eslint-config-next": "16.2.4",

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/components/home-button-group.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,10 @@ const HomeButtonGroup = () => {
209209
label={t("LaunchPage.button.instanceSettings")}
210210
tooltipPlacement="top"
211211
onClick={() =>
212-
router.push(
213-
`/instances/details/${encodeURIComponent(
214-
selectedInstance.id
215-
)}/settings`
216-
)
212+
router.push({
213+
pathname: "/instances/details/[id]/settings",
214+
query: { id: selectedInstance.id },
215+
})
217216
}
218217
/>
219218
)}

src/components/instance-menu.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ export const InstanceMenu: React.FC<InstanceMenuProps> = ({
4747
icon: LuLayoutList,
4848
label: t("InstanceMenu.label.details"),
4949
onClick: () => {
50-
router.push(`/instances/details/${encodeURIComponent(instance.id)}`);
50+
router.push({
51+
pathname: "/instances/details/[id]",
52+
query: { id: instance.id },
53+
});
5154
},
5255
},
5356
{

src/components/instance-widgets.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ export const InstanceScreenshotsWidget = () => {
229229
onClick={() => {
230230
router.push(
231231
{
232-
pathname: `/instances/details/${encodeURIComponent(instanceId || "")}/screenshots`,
232+
pathname: "/instances/details/[id]/screenshots",
233+
query: { id: instanceId || "" },
233234
},
234235
undefined,
235236
{ shallow: true }
@@ -314,9 +315,10 @@ export const InstanceModsWidget = () => {
314315
justifyContent="flex-start"
315316
colorScheme={primaryColor}
316317
onClick={() => {
317-
router.push(
318-
`/instances/details/${encodeURIComponent(instanceId || "")}/mods`
319-
);
318+
router.push({
319+
pathname: "/instances/details/[id]/mods",
320+
query: { id: instanceId || "" },
321+
});
320322
}}
321323
>
322324
<HStack spacing={1.5}>
@@ -461,9 +463,10 @@ export const InstanceMoreWidget = () => {
461463
size="lg"
462464
colorScheme={primaryColor}
463465
onClick={() =>
464-
router.push(
465-
`/instances/details/${encodeURIComponent(instanceId || "")}/${key}`
466-
)
466+
router.push({
467+
pathname: `/instances/details/[id]/${key}`,
468+
query: { id: instanceId || "" },
469+
})
467470
}
468471
>
469472
<VStack spacing={1} align="center">
@@ -484,9 +487,10 @@ export const InstanceMoreWidget = () => {
484487
size="lg"
485488
colorScheme={primaryColor}
486489
onClick={() =>
487-
router.push(
488-
`/instances/details/${encodeURIComponent(instanceId || "")}/${key}`
489-
)
490+
router.push({
491+
pathname: `/instances/details/[id]/${key}`,
492+
query: { id: instanceId || "" },
493+
})
490494
}
491495
aria-label={t(`InstanceDetailsLayout.instanceTabList.${key}`)}
492496
/>

src/components/modals/edit-game-directory-modal.tsx

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import {
2323
ModalOverlay,
2424
ModalProps,
2525
Stack,
26+
Tag,
27+
TagLabel,
2628
useDisclosure,
2729
} from "@chakra-ui/react";
2830
import { open } from "@tauri-apps/plugin-dialog";
@@ -33,6 +35,7 @@ import { useGlobalData } from "@/contexts/global-data";
3335
import { useRoutingHistory } from "@/contexts/routing-history";
3436
import { useToast } from "@/contexts/toast";
3537
import { ConfigService } from "@/services/config";
38+
import { getGameDirName, isSpecialGameDir } from "@/utils/instance";
3639
import { isPathSanitized } from "@/utils/string";
3740

3841
interface ActionSelectDialogProps extends Omit<ModalProps, "children"> {
@@ -245,12 +248,14 @@ const EditGameDirectoryModal: React.FC<EditGameDirectoryModalProps> = ({
245248
if (currentPath === _dirPath) {
246249
// only update dir name, instance not changed
247250
replaceHistory(
248-
`/instances/details/${currentName}:`,
249-
`/instances/details/${dirName}:`
251+
`/instances/details/${encodeURIComponent(`${currentName}:`)}`,
252+
`/instances/details/${encodeURIComponent(`${dirName}:`)}`
250253
);
251254
} else {
252255
// update dir path, instance may change, remove all route history
253-
removeHistory(`/instances/details/${currentName}:`);
256+
removeHistory(
257+
`/instances/details/${encodeURIComponent(`${currentName}:`)}`
258+
);
254259
}
255260
const encodedCurrentName = encodeURIComponent(currentName);
256261
const encodedDirName = encodeURIComponent(dirName);
@@ -308,30 +313,46 @@ const EditGameDirectoryModal: React.FC<EditGameDirectoryModalProps> = ({
308313
}
309314
>
310315
<FormLabel>{t("EditGameDirectoryModal.label.dirName")}</FormLabel>
311-
<Input
312-
placeholder={t("EditGameDirectoryModal.placeholder.dirName")}
313-
value={dirName}
314-
onChange={(e) => setDirName(e.target.value)}
315-
onBlur={() => {
316-
setIsDirNameEmpty(dirName.length === 0);
317-
setIsDirNameTooLong(dirName.length > 20);
318-
setIsDirNameExist(
319-
config.localGameDirectories
320-
.map((dir) => dir.name)
321-
.includes(dirName) && dirName !== currentName
322-
);
323-
setIsDirNameInvalid(/[\/:\\?#&%]/.test(dirName));
324-
}}
325-
onFocus={() => {
326-
setIsDirNameEmpty(false);
327-
setIsDirNameTooLong(false);
328-
setIsDirNameExist(false);
329-
setIsDirNameInvalid(false);
330-
}}
331-
required
332-
ref={initialRef}
333-
focusBorderColor={`${primaryColor}.500`}
334-
/>
316+
<InputGroup>
317+
<Input
318+
placeholder={t("EditGameDirectoryModal.placeholder.dirName")}
319+
value={dirName}
320+
pr={
321+
isSpecialGameDir(dirName)
322+
? `${getGameDirName(dirName).length}ch`
323+
: undefined
324+
}
325+
onChange={(e) => setDirName(e.target.value)}
326+
onBlur={() => {
327+
setIsDirNameEmpty(dirName.length === 0);
328+
setIsDirNameTooLong(dirName.length > 20);
329+
setIsDirNameExist(
330+
config.localGameDirectories
331+
.map((dir) => dir.name)
332+
.includes(dirName) && dirName !== currentName
333+
);
334+
setIsDirNameInvalid(/[\/:\\?#&%]/.test(dirName));
335+
}}
336+
onFocus={() => {
337+
setIsDirNameEmpty(false);
338+
setIsDirNameTooLong(false);
339+
setIsDirNameExist(false);
340+
setIsDirNameInvalid(false);
341+
}}
342+
required
343+
ref={initialRef}
344+
focusBorderColor={`${primaryColor}.500`}
345+
/>
346+
{isSpecialGameDir(dirName) && (
347+
<InputRightElement width="auto" pr={2}>
348+
<Tag title={getGameDirName(dirName)}>
349+
<TagLabel overflow="hidden" textOverflow="ellipsis">
350+
{getGameDirName(dirName)}
351+
</TagLabel>
352+
</Tag>
353+
</InputRightElement>
354+
)}
355+
</InputGroup>
335356
{isDirNameTooLong && (
336357
<FormErrorMessage>
337358
{t("EditGameDirectoryModal.errorMessage.dirName.tooLong")}

0 commit comments

Comments
 (0)