From aa84ea53335444e45bf2e24b09016e122d6fd48a Mon Sep 17 00:00:00 2001 From: lgou2w Date: Sat, 13 Jul 2024 12:42:13 +0800 Subject: [PATCH 1/7] fix: gacha record response error code problem --- src-tauri/src/gacha/utilities.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/gacha/utilities.rs b/src-tauri/src/gacha/utilities.rs index 92ce907..d5d3f80 100644 --- a/src-tauri/src/gacha/utilities.rs +++ b/src-tauri/src/gacha/utilities.rs @@ -309,13 +309,18 @@ async fn request_gacha_url( ) -> Result> { let response: GachaResponse = reqwest.get(url).send().await?.json().await?; if response.retcode != 0 { - match response.retcode { - -101 => Err(Error::TimeoutdGachaUrl), - -110 => Err(Error::VisitTooFrequentlyGachaUrl), - _ => Err(Error::GachaRecordRetcode { - retcode: response.retcode, + let retcode = response.retcode; + let message = &response.message; + + if retcode == -101 || message.contains("authkey") || message.contains("auth key") { + Err(Error::TimeoutdGachaUrl) + } else if retcode == -110 || message.contains("visit too frequently") { + Err(Error::VisitTooFrequentlyGachaUrl) + } else { + Err(Error::GachaRecordRetcode { + retcode, message: response.message, - }), + }) } } else { Ok(response) From 4ce46bff4f7f67177110404fa91fe817faa16dd2 Mon Sep 17 00:00:00 2001 From: lgou2w Date: Sat, 13 Jul 2024 14:57:31 +0800 Subject: [PATCH 2/7] fix: icons for other languages can now be loaded --- src/components/gacha/GachaItemView.tsx | 51 ++++++++-------- .../gacha/ItemTypeCategoryMappings.ts | 60 +++++++++++++++++++ .../gacha/analysis/GachaAnalysisHistory.tsx | 5 +- .../gacha/overview/GachaOverviewGrid.tsx | 5 +- 4 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 src/components/gacha/ItemTypeCategoryMappings.ts diff --git a/src/components/gacha/GachaItemView.tsx b/src/components/gacha/GachaItemView.tsx index 02cf69b..04988e4 100644 --- a/src/components/gacha/GachaItemView.tsx +++ b/src/components/gacha/GachaItemView.tsx @@ -6,13 +6,15 @@ import Typography from '@mui/material/Typography' import GenshinUIRarity3Background from '@/assets/images/genshin/UI_Rarity_3_Background.png' import GenshinUIRarity4Background from '@/assets/images/genshin/UI_Rarity_4_Background.png' import GenshinUIRarity5Background from '@/assets/images/genshin/UI_Rarity_5_Background.png' +import { ItemTypeCategoryMappings } from './ItemTypeCategoryMappings' import { lookupAssetIcon } from './icons' import dayjs from '@/utilities/dayjs' export interface GachaItemViewProps { facet: AccountFacet - name: string - id: string + lang: string + itemName: string + itemId: undefined | null | string itemType: string rank: 3 | 4 | 5 | '3' | '4' | '5' size: number @@ -22,19 +24,12 @@ export interface GachaItemViewProps { } export default function GachaItemView (props: GachaItemViewProps) { - const { facet, name, id, itemType, rank, size, usedPity, restricted, time } = props - - const category = ItemTypeCategoryMappings[itemType] - const icon = lookupAssetIcon(facet, category, id) - - let src = icon?.[1] - if (!src) { - src = getRemoteResourceSrc(facet, category, id) - } + const { facet, lang, itemName, itemId, itemType, rank, size, usedPity, restricted, time } = props + const iconSrc = resolveItemIconSrc(facet, lang, itemName, itemId, itemType) const title = !time - ? name - : name + '\n' + dayjs(time).format('LLLL') + ? itemName + : itemName + '\n' + dayjs(time).format('LLLL') return ( - {name} + {itemName} {usedPity && {usedPity}} {restricted && 限定} ) } -const ItemTypeCategoryMappings: Record = { - 角色: 'character', - 武器: 'weapon', - 光锥: 'weapon', - 代理人: 'character', - 音擎: 'weapon', - 邦布: 'bangboo' -} +function resolveItemIconSrc (facet: AccountFacet, lang: string, itemName: string, itemId: string | undefined | null, itemType: string): string | undefined { + const category = ItemTypeCategoryMappings[facet][lang]?.[itemType] + if (!category) { + console.error('Unable to resolve item icon category:', facet, lang, itemType, itemName, itemId) + return undefined + } + + // HACK: GenshinImpact embedded icons are only available for zh-cn language, go to Remote Loading + if (facet === AccountFacet.Genshin && lang !== 'zh-cn') { + return `https://hoyo-gacha.lgou2w.com/static/${facet}/${lang}/${category}/cutted/${itemName}.png` + } -function getRemoteResourceSrc (facet: AccountFacet, category: string, itemIdOrName: string) { - return `https://hoyo-gacha.lgou2w.com/static/${facet}/${category}/cutted/${itemIdOrName}.png` + const embedded = lookupAssetIcon(facet, category, itemId || itemName) + if (embedded) { + return embedded[1] + } else { + return `https://hoyo-gacha.lgou2w.com/static/${facet}/${category}/cutted/${itemId}.png` + } } const GachaItemViewCls = 'gacha-item-view' diff --git a/src/components/gacha/ItemTypeCategoryMappings.ts b/src/components/gacha/ItemTypeCategoryMappings.ts new file mode 100644 index 0000000..befae6b --- /dev/null +++ b/src/components/gacha/ItemTypeCategoryMappings.ts @@ -0,0 +1,60 @@ +/* eslint-disable object-shorthand */ +/* eslint-disable quote-props */ + +import { AccountFacet } from '@/interfaces/account' + +export const Character = 'character' +export const Weapon = 'weapon' +export const Bangboo = 'bangboo' + +export type Category = typeof Character | typeof Weapon | typeof Bangboo + +export const ItemTypeCategoryMappings: Record>> = { + [AccountFacet.Genshin]: { + 'de-de': { 'Figur': Character, 'Waffe': Weapon }, + 'en-us': { 'Character': Character, 'Weapon': Weapon }, + 'es-es': { 'Personaje': Character, 'Arma': Weapon }, + 'fr-fr': { 'Personnages': Character, 'Arme': Weapon }, + 'id-id': { 'Karakter': Character, 'Senjata': Weapon }, + 'it-it': { 'Personaggio': Character, 'Armi': Weapon }, + 'ja-jp': { 'キャラクター': Character, '武器': Weapon }, + 'ko-kr': { '캐릭터': Character, '무기': Weapon }, + 'pt-pt': { 'Personagem': Character, 'Arma': Weapon }, + 'ru-ru': { 'Персонаж': Character, 'Оружие': Weapon }, + 'th-th': { 'ตัวละคร': Character, 'อาวุธ': Weapon }, + 'tr-tr': { 'Karakter': Character, 'Silahlar': Weapon }, + 'vi-vn': { 'Nhân vật': Character, 'Vũ Khí': Weapon }, + 'zh-cn': { '角色': Character, '武器': Weapon }, + 'zh-tw': { '角色': Character, '武器': Weapon } + }, + [AccountFacet.StarRail]: { + 'de-de': { 'Figuren': Character, 'Lichtkegel': Weapon }, + 'en-us': { 'Character': Character, 'Light Cone': Weapon }, + 'es-es': { 'Personajes': Character, 'Conos de luz': Weapon }, + 'fr-fr': { 'Personnages': Character, 'Cônes de lumière': Weapon }, + 'id-id': { 'Karakter': Character, 'Light Cone': Weapon }, + 'ja-jp': { 'キャラクター': Character, '光円錐': Weapon }, + 'ko-kr': { '캐릭터': Character, '광추': Weapon }, + 'pt-pt': { 'Personagens': Character, 'Cones de Luz': Weapon }, + 'ru-ru': { 'Персонажи': Character, 'Световые конусы': Weapon }, + 'th-th': { 'ตัวละคร': Character, 'Light Cone': Weapon }, + 'vi-vn': { 'Nhân Vật': Character, 'Nón Ánh Sáng': Weapon }, + 'zh-cn': { '角色': Character, '光锥': Weapon }, + 'zh-tw': { '角色': Character, '光錐': Weapon } + }, + [AccountFacet.ZenlessZoneZero]: { + 'de-de': { 'Agenten': Character, 'W-Motoren': Weapon, 'Bangboos': Bangboo }, + 'en-us': { 'Agents': Character, 'W-Engines': Weapon, 'Bangboo': Bangboo }, + 'es-es': { 'Agentes': Character, 'Amplificadores': Weapon, 'Bangbús': Bangboo }, + 'fr-fr': { 'Agents': Character, 'Moteurs-amplis': Weapon, 'Bangbous': Bangboo }, + 'id-id': { 'Agen': Character, 'W-Engine': Weapon, 'Bangboo': Bangboo }, + 'ja-jp': { 'エージェント': Character, '音動機': Weapon, 'ボンプ': Bangboo }, + 'ko-kr': { '에이전트': Character, 'W-엔진': Weapon, '「Bangboo」': Bangboo }, + 'pt-pt': { 'Agentes': Character, 'Motores-W': Weapon, 'Bangboos': Bangboo }, + 'ru-ru': { 'Агенты': Character, 'Амплификаторы': Weapon, 'Банбу': Bangboo }, + 'th-th': { 'Agent': Character, 'W-Engine': Weapon, 'Bangboo': Bangboo }, + 'vi-vn': { 'Người Đại Diện': Character, 'W-Engine': Weapon, 'Bangboo': Bangboo }, + 'zh-cn': { '代理人': Character, '音擎': Weapon, '邦布': Bangboo }, + 'zh-tw': { '代理人': Character, '音擎': Weapon, '邦布': Bangboo } + } +} diff --git a/src/components/gacha/analysis/GachaAnalysisHistory.tsx b/src/components/gacha/analysis/GachaAnalysisHistory.tsx index 0e81425..8db9949 100644 --- a/src/components/gacha/analysis/GachaAnalysisHistory.tsx +++ b/src/components/gacha/analysis/GachaAnalysisHistory.tsx @@ -64,8 +64,9 @@ function GachaAnalysisHistoryList ({ facet, value }: { Date: Sat, 13 Jul 2024 15:55:20 +0800 Subject: [PATCH 3/7] chore: item id or name of gacha item view --- src/components/gacha/GachaItemView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/gacha/GachaItemView.tsx b/src/components/gacha/GachaItemView.tsx index 04988e4..9c979cc 100644 --- a/src/components/gacha/GachaItemView.tsx +++ b/src/components/gacha/GachaItemView.tsx @@ -62,7 +62,7 @@ function resolveItemIconSrc (facet: AccountFacet, lang: string, itemName: string if (embedded) { return embedded[1] } else { - return `https://hoyo-gacha.lgou2w.com/static/${facet}/${category}/cutted/${itemId}.png` + return `https://hoyo-gacha.lgou2w.com/static/${facet}/${category}/cutted/${itemId || itemName}.png` } } From 803826053198738ce209fb6dc19e38a1a3afa756 Mon Sep 17 00:00:00 2001 From: lgou2w Date: Sat, 13 Jul 2024 16:11:22 +0800 Subject: [PATCH 4/7] fix: return user error type if game data folder does not exist --- src-tauri/src/gacha/utilities.rs | 4 ++++ src/components/account/AccountMenuDialog.tsx | 4 ++-- src/components/gacha/GachaLayout.tsx | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/gacha/utilities.rs b/src-tauri/src/gacha/utilities.rs index d5d3f80..92aa927 100644 --- a/src-tauri/src/gacha/utilities.rs +++ b/src-tauri/src/gacha/utilities.rs @@ -143,6 +143,10 @@ pub(super) fn lookup_valid_cache_data_dir>(game_data_dir: P) -> R // Read webCaches directory let web_caches_dir = game_data_dir.as_ref().join("webCaches"); + if !web_caches_dir.exists() { + return Err(Error::WebCaches); + } + for entry in read_dir(&web_caches_dir)? { let entry = entry?; let entry_path = entry.path(); diff --git a/src/components/account/AccountMenuDialog.tsx b/src/components/account/AccountMenuDialog.tsx index 08f8043..6942881 100644 --- a/src/components/account/AccountMenuDialog.tsx +++ b/src/components/account/AccountMenuDialog.tsx @@ -281,13 +281,13 @@ function AccountMenuDialogForm (props: AccountMenuDialogFormProps) { name="gameDataDir" label="游戏数据文件夹" type="text" placeholder={'例如:' + FacetGameDataDirExamples[facet]} variant="filled" size="small" margin="dense" - fullWidth required + fullWidth required multiline disabled={busy} error={!!errors.gameDataDir} helperText={errors.gameDataDir?.message} InputProps={{ ...register('gameDataDir', { - required: '请选择游戏数据目录!' + required: '请选择游戏数据文件夹!' }), readOnly: true, startAdornment: ( diff --git a/src/components/gacha/GachaLayout.tsx b/src/components/gacha/GachaLayout.tsx index 54d64b9..d3e3d35 100644 --- a/src/components/gacha/GachaLayout.tsx +++ b/src/components/gacha/GachaLayout.tsx @@ -121,7 +121,7 @@ export default function GachaLayout () { const KnownErrorIdentifiers: Record = { INTERNAL_CRATE: '内部错误:', - WEB_CACHES: '读取硬盘缓存失败:未找到正确目录!请尝试在游戏内打开抽卡历史记录界面!', + WEB_CACHES: '读取硬盘缓存失败:未找到有效文件夹!请检查账号的游戏数据文件夹!', ILLEGAL_GACHA_URL: '无效的抽卡链接!', VACANT_GACHA_URL: '未找到有效的抽卡链接。请尝试在游戏内打开抽卡历史记录界面!', TIMEOUTD_GACHA_URL: '抽卡链接已经过期失效。请重新在游戏内打开抽卡历史记录界面!', From 8326dd888a613dcc334f317773738b857dd59cc3 Mon Sep 17 00:00:00 2001 From: lgou2w Date: Sat, 13 Jul 2024 16:46:07 +0800 Subject: [PATCH 5/7] fix: improve some error messages --- src-tauri/src/error.rs | 7 ++++--- src-tauri/src/gacha/srgf.rs | 6 +++--- src-tauri/src/gacha/uigf.rs | 6 +++--- src/components/gacha/GachaLayout.tsx | 27 +++++++++++++++------------ 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs index 69d0478..d136aaa 100644 --- a/src-tauri/src/error.rs +++ b/src-tauri/src/error.rs @@ -13,9 +13,6 @@ pub enum Error { #[error(transparent)] Db(#[from] sea_orm::error::DbErr), - #[error(transparent)] - SerdeJson(#[from] serde_json::Error), - #[error(transparent)] Tauri(#[from] tauri::Error), @@ -54,6 +51,9 @@ pub enum Error { GachaRecordFetcherChannelJoin, // UIGF & SRGF + #[error("{0}")] + UIGFOrSRGFSerdeJson(serde_json::Error), + #[error("UIGF or SRGF Mismatched UID: expected {expected:?}, actual {actual:?}")] UIGFOrSRGFMismatchedUID { expected: String, actual: String }, @@ -95,6 +95,7 @@ impl_error_identifiers! { GachaRecordRetcode => GACHA_RECORD_RETCODE, GachaRecordFetcherChannelSend => GACHA_RECORD_FETCHER_CHANNEL_SEND, GachaRecordFetcherChannelJoin => GACHA_RECORD_FETCHER_CHANNEL_JOIN, + UIGFOrSRGFSerdeJson => UIGF_OR_SRGF_SERDE_JSON, UIGFOrSRGFMismatchedUID => UIGF_OR_SRGF_MISMATCHED_UID, UIGFOrSRGFInvalidField => UIGF_OR_SRGF_INVALID_FIELD, AccountAlreadyExists => ACCOUNT_ALREADY_EXISTS, diff --git a/src-tauri/src/gacha/srgf.rs b/src-tauri/src/gacha/srgf.rs index 40f87c7..9d01448 100644 --- a/src-tauri/src/gacha/srgf.rs +++ b/src-tauri/src/gacha/srgf.rs @@ -66,14 +66,14 @@ impl SRGF { } pub fn from_reader(reader: impl Read) -> Result { - Ok(serde_json::from_reader(reader)?) + serde_json::from_reader(reader).map_err(Error::UIGFOrSRGFSerdeJson) } pub fn to_writer(&self, writer: impl Write, pretty: bool) -> Result<()> { if pretty { - Ok(serde_json::to_writer_pretty(writer, self)?) + serde_json::to_writer_pretty(writer, self).map_err(Error::UIGFOrSRGFSerdeJson) } else { - Ok(serde_json::to_writer(writer, self)?) + serde_json::to_writer(writer, self).map_err(Error::UIGFOrSRGFSerdeJson) } } } diff --git a/src-tauri/src/gacha/uigf.rs b/src-tauri/src/gacha/uigf.rs index 20a1826..ae82f7e 100644 --- a/src-tauri/src/gacha/uigf.rs +++ b/src-tauri/src/gacha/uigf.rs @@ -68,14 +68,14 @@ impl UIGF { } pub fn from_reader(reader: impl Read) -> Result { - Ok(serde_json::from_reader(reader)?) + serde_json::from_reader(reader).map_err(Error::UIGFOrSRGFSerdeJson) } pub fn to_writer(&self, writer: impl Write, pretty: bool) -> Result<()> { if pretty { - Ok(serde_json::to_writer_pretty(writer, self)?) + serde_json::to_writer_pretty(writer, self).map_err(Error::UIGFOrSRGFSerdeJson) } else { - Ok(serde_json::to_writer(writer, self)?) + serde_json::to_writer(writer, self).map_err(Error::UIGFOrSRGFSerdeJson) } } } diff --git a/src/components/gacha/GachaLayout.tsx b/src/components/gacha/GachaLayout.tsx index d3e3d35..d3707b9 100644 --- a/src/components/gacha/GachaLayout.tsx +++ b/src/components/gacha/GachaLayout.tsx @@ -68,17 +68,19 @@ export default function GachaLayout () { if (error && (error instanceof Error || typeof error === 'object')) { const msg = (error as { message: string }).message const identifier = (error as { identifier?: string }).identifier - let knownMessage = identifier ? KnownErrorIdentifiers[identifier] : undefined - if (knownMessage) { + const knownMessage = identifier ? KnownErrorIdentifiers[identifier] : undefined + const appendMessage = Array.isArray(knownMessage) + let pretty = appendMessage ? knownMessage[0] : knownMessage + if (pretty && appendMessage) { let index: number if ((index = msg.indexOf(':')) !== -1) { - knownMessage += msg.substring(index + 1) - } - if (identifier === 'INTERNAL_CRATE') { - knownMessage += msg + pretty += msg.substring(index) + } else if (appendMessage) { + pretty += msg } } - message = knownMessage || msg + + message = pretty || msg } else if (error) { message = String(error) } @@ -119,13 +121,14 @@ export default function GachaLayout () { ) } -const KnownErrorIdentifiers: Record = { - INTERNAL_CRATE: '内部错误:', - WEB_CACHES: '读取硬盘缓存失败:未找到有效文件夹!请检查账号的游戏数据文件夹!', +const KnownErrorIdentifiers: Record = { + INTERNAL_CRATE: ['内部错误'], + WEB_CACHES: '读取硬盘缓存失败:未找到有效文件夹!请检查账号的游戏数据文件夹是否正确!', ILLEGAL_GACHA_URL: '无效的抽卡链接!', VACANT_GACHA_URL: '未找到有效的抽卡链接。请尝试在游戏内打开抽卡历史记录界面!', TIMEOUTD_GACHA_URL: '抽卡链接已经过期失效。请重新在游戏内打开抽卡历史记录界面!', VISIT_TOO_FREQUENTLY_GACHA_URL: '请求获取抽卡记录 API 速率过快!请稍等几秒后再次重试!', - UIGF_OR_SRGF_MISMATCHED_UID: '待导入的 UIGF 或 SRGF 数据 UID 与当前账号不匹配!', - UIGF_OR_SRGF_INVALID_FIELD: '待导入的 UIGF 或 SRGF 数据中存在无效的字段!' + UIGF_OR_SRGF_SERDE_JSON: ['待导入的 UIGF 或 SRGF 数据文件格式不正确'], + UIGF_OR_SRGF_MISMATCHED_UID: ['待导入的 UIGF 或 SRGF 数据 UID 与当前账号不匹配'], + UIGF_OR_SRGF_INVALID_FIELD: ['待导入的 UIGF 或 SRGF 数据中存在无效的字段'] } From b04466930ab9571c3a647a75287e8efb38d60d2d Mon Sep 17 00:00:00 2001 From: lgou2w Date: Sat, 13 Jul 2024 17:32:43 +0800 Subject: [PATCH 6/7] chore: update version check --- src-tauri/src/commands.rs | 9 +++++++-- src/components/common/VersionChecker.tsx | 25 ++++++++++++++++++++---- src/components/setting/SettingAbout.tsx | 2 +- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 5704d39..27494ce 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,9 +1,12 @@ -use crate::constants; -use crate::error::Result; +use std::time::Duration; + use reqwest::Client as Reqwest; use serde::{Deserialize, Serialize}; use tauri::{Invoke, Runtime}; +use crate::constants; +use crate::error::Result; + pub fn get_handlers() -> Box) + Send + Sync> { Box::new(tauri::generate_handler![ get_current_exe_dir, @@ -67,6 +70,7 @@ async fn get_latest_version() -> Result { Reqwest::builder() .build()? .get("https://hoyo-gacha.lgou2w.com/release/latest") + .timeout(Duration::from_secs(15)) .send() .await? .json::() @@ -93,6 +97,7 @@ async fn update_app(latest_version: LatestVersion) -> Result<()> { .build()? .get("https://hoyo-gacha.lgou2w.com/release/download") .query(&[("id", latest_version.id.to_string())]) + .timeout(Duration::from_secs(15)) .send() .await? .error_for_status()?; diff --git a/src/components/common/VersionChecker.tsx b/src/components/common/VersionChecker.tsx index 86fca6c..ac28e1b 100644 --- a/src/components/common/VersionChecker.tsx +++ b/src/components/common/VersionChecker.tsx @@ -47,14 +47,31 @@ export default function VersionChecker () { }, [latestVersion.data, setBusy]) if (!version.data) { - return 版本更新不可用 + return ( + 版本更新不可用 + ) } - if (latestVersion.isLoading) return 加载中... - if (latestVersion.isError) return 检查最新版本失败 + if (latestVersion.isLoading) { + return ( + 检查更新中... + ) + } + + if (latestVersion.isError) { + return ( + + 检查最新版本失败:{latestVersion.error.message} + + ) + } const needUpdate = isNeedUpdate(version.data, latestVersion.data) - if (!needUpdate) return 已是最新版本 + if (!needUpdate) { + return ( + 已是最新版本 + ) + } return (