chore: update dependencies and improve type safety in Enka components
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m2s

- Updated @tanstack/react-query to version 5.102.8
- Updated next-intl to version 4.14.2
- Updated zod to version 4.5.4
- Updated baseline-browser-mapping to version 2.11.21
- Updated daisyui to version 5.7.28
- Enhanced type definitions in enka.ts by making several properties optional
- Improved data handling in enka.tsx to prevent runtime errors when accessing potentially undefined properties
- Added error logging for data validation failures and fetch errors
This commit is contained in:
2026-09-05 23:28:29 +07:00
parent d76b0396d3
commit b3020f8736
5 changed files with 170 additions and 160 deletions
+9 -7
View File
@@ -39,7 +39,7 @@ const toCharacterInfoCard = (character: AvatarEnkaDetailInput): CharacterInfoCar
rank: character.equipment?.rank ?? 0,
item_id: character.equipment?.tid ?? 0,
},
relics: character.relicList.map((relic) => ({
relics: (character.relicList ?? []).map((relic) => ({
level: relic.level,
relic_id: relic.tid,
relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10),
@@ -84,7 +84,7 @@ export default function EnkaImport() {
const [isLoading, setIsLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState("");
const validCharacters = enkaData?.detailInfo.avatarDetailList.filter((character) => mapAvatar?.[character.avatarId]) ?? [];
const validCharacters = enkaData?.detailInfo?.avatarDetailList?.filter((character) => mapAvatar?.[character.avatarId]) ?? [];
const handlerFetchData = async () => {
const cleanUid = uidInput.trim();
@@ -105,6 +105,7 @@ export default function EnkaImport() {
const parsed = enkaResponseSchema.safeParse(data);
if (!parsed.success) {
console.error("Enka data validation failed:", parsed.error);
setEnkaData(null);
setSelectedCharacters([]);
setErrorMsg(transI18n("failedToFetchEnkaData"));
@@ -112,12 +113,13 @@ export default function EnkaImport() {
}
setEnkaData(parsed.data as EnkaResponse);
const validChars = parsed.data.detailInfo.avatarDetailList
const validChars = (parsed.data.detailInfo?.avatarDetailList ?? [])
.filter((character) => mapAvatar?.[character.avatarId])
.map(toCharacterInfoCard);
setSelectedCharacters(validChars);
setErrorMsg("");
} catch {
} catch (error) {
console.error("Enka fetch failed:", error);
setEnkaData(null);
setSelectedCharacters([]);
setErrorMsg(transI18n("failedToFetchEnkaData"));
@@ -167,7 +169,7 @@ export default function EnkaImport() {
return;
}
const filterData = parsed.data.detailInfo.avatarDetailList.filter((character) =>
const filterData = (parsed.data.detailInfo?.avatarDetailList ?? []).filter((character) =>
mapAvatar?.[character.avatarId] && selectedCharacters.some((selected) => selected.avatar_id === character.avatarId)
);
@@ -175,7 +177,7 @@ export default function EnkaImport() {
const currentAvatar = listAvatars[character.avatarId.toString()];
if (currentAvatar && Object.keys(currentAvatar).length !== 0) {
const newProfileList = [...(currentAvatar.profileList || [])];
const newProfile = converterOneEnkaDataToAvatarStore(character, newProfileList.length);
const newProfile = converterOneEnkaDataToAvatarStore(character as unknown as AvatarEnkaDetailInput, newProfileList.length);
if (newProfile) {
newProfileList.push(newProfile);
}
@@ -185,7 +187,7 @@ export default function EnkaImport() {
promotion: character.promotion ?? 0,
data: {
rank: character.rank ?? 0,
skills: character.skillTreeList.reduce((acc, skill) => {
skills: (character.skillTreeList ?? []).reduce((acc, skill) => {
acc[skill.pointId] = skill.level;
return acc;
}, {} as Record<string, number>),
+38 -37
View File
@@ -1,20 +1,20 @@
interface PrivacySettingInfo {
displayCollection: boolean
displayRecord: boolean
displayRecordTeam: boolean
displayOnlineStatus: boolean
displayDiary: boolean
displayCollection?: boolean
displayRecord?: boolean
displayRecordTeam?: boolean
displayOnlineStatus?: boolean
displayDiary?: boolean
}
interface RecordInfo {
achievementCount: number
bookCount: number
avatarCount: number
equipmentCount: number
musicCount: number
relicCount: number
challengeInfo: unknown;
maxRogueChallengeScore: number
achievementCount?: number
bookCount?: number
avatarCount?: number
equipmentCount?: number
musicCount?: number
relicCount?: number
challengeInfo?: unknown
maxRogueChallengeScore?: number
}
@@ -30,18 +30,18 @@ interface FlatProp {
}
interface RelicFlat {
props: FlatProp[]
setName: string | number
setID: number
props?: FlatProp[]
setName?: string | number
setID?: number
}
interface Relic {
mainAffixId: number
subAffixList: SubAffix[]
subAffixList?: SubAffix[]
tid: number
type: number
level: number
_flat: RelicFlat
_flat?: RelicFlat
}
interface SkillTree {
@@ -50,43 +50,44 @@ interface SkillTree {
}
interface EquipmentFlat {
props: FlatProp[]
name: string | number
props?: FlatProp[]
name?: string | number
}
interface Equipment {
rank: number
rank?: number
tid: number
promotion: number
promotion?: number
level: number
_flat: EquipmentFlat
_flat?: EquipmentFlat
}
export interface AvatarEnkaDetail {
relicList: Relic[]
relicList?: Relic[]
level: number
promotion: number
promotion?: number
rank?: number
skillTreeList: SkillTree[]
equipment: Equipment
skillTreeList?: SkillTree[]
equipment?: Equipment
avatarId: number
_assist?: boolean
pos?: number
}
interface DetailInfo {
worldLevel: number
privacySettingInfo: PrivacySettingInfo
headIcon: number
signature: string
avatarDetailList: AvatarEnkaDetail[]
platform: string
recordInfo: RecordInfo
worldLevel?: number
privacySettingInfo?: PrivacySettingInfo
headIcon?: number
signature?: string
avatarDetailList?: AvatarEnkaDetail[]
platform?: string
recordInfo?: RecordInfo
uid: number
level: number
nickname: string
isDisplayAvatar: boolean
friendCount: number
personalCardId: number
isDisplayAvatar?: boolean
friendCount?: number
personalCardId?: number
}
export interface EnkaResponse {
+39 -38
View File
@@ -2,22 +2,22 @@
import { z } from "zod";
const privacySettingInfoSchema = z.object({
displayCollection: z.boolean(),
displayRecord: z.boolean(),
displayRecordTeam: z.boolean(),
displayOnlineStatus: z.boolean(),
displayDiary: z.boolean()
displayCollection: z.boolean().optional(),
displayRecord: z.boolean().optional(),
displayRecordTeam: z.boolean().optional(),
displayOnlineStatus: z.boolean().optional(),
displayDiary: z.boolean().optional()
});
const recordInfoSchema = z.object({
achievementCount: z.number(),
bookCount: z.number(),
avatarCount: z.number(),
equipmentCount: z.number(),
musicCount: z.number(),
relicCount: z.number(),
challengeInfo: z.unknown(),
maxRogueChallengeScore: z.number()
achievementCount: z.number().optional(),
bookCount: z.number().optional(),
avatarCount: z.number().optional(),
equipmentCount: z.number().optional(),
musicCount: z.number().optional(),
relicCount: z.number().optional(),
challengeInfo: z.unknown().optional(),
maxRogueChallengeScore: z.number().optional()
});
const subAffixSchema = z.object({
@@ -32,18 +32,18 @@ const flatPropSchema = z.object({
});
const relicFlatSchema = z.object({
props: z.array(flatPropSchema),
setName: z.union([z.string(), z.number()]),
setID: z.number()
props: z.array(flatPropSchema).optional(),
setName: z.union([z.string(), z.number()]).optional(),
setID: z.number().optional()
});
const relicSchema = z.object({
mainAffixId: z.number(),
subAffixList: z.array(subAffixSchema),
subAffixList: z.array(subAffixSchema).optional(),
tid: z.number(),
type: z.number(),
level: z.number(),
_flat: relicFlatSchema
_flat: relicFlatSchema.optional()
});
const skillTreeSchema = z.object({
@@ -52,43 +52,44 @@ const skillTreeSchema = z.object({
});
const equipmentFlatSchema = z.object({
props: z.array(flatPropSchema),
name: z.union([z.string(), z.number()])
props: z.array(flatPropSchema).optional(),
name: z.union([z.string(), z.number()]).optional()
});
const equipmentSchema = z.object({
rank: z.number(),
rank: z.number().optional(),
tid: z.number(),
promotion: z.number(),
promotion: z.number().optional(),
level: z.number(),
_flat: equipmentFlatSchema
_flat: equipmentFlatSchema.optional()
});
export const avatarEnkaDetailSchema = z.object({
relicList: z.array(relicSchema),
relicList: z.array(relicSchema).optional(),
level: z.number(),
promotion: z.number(),
promotion: z.number().optional(),
rank: z.number().optional(),
skillTreeList: z.array(skillTreeSchema),
equipment: equipmentSchema,
skillTreeList: z.array(skillTreeSchema).optional(),
equipment: equipmentSchema.optional(),
avatarId: z.number(),
_assist: z.boolean().optional()
_assist: z.boolean().optional(),
pos: z.number().optional()
});
const detailInfoSchema = z.object({
worldLevel: z.number(),
privacySettingInfo: privacySettingInfoSchema,
headIcon: z.number(),
signature: z.string(),
avatarDetailList: z.array(avatarEnkaDetailSchema),
platform: z.string(),
recordInfo: recordInfoSchema,
worldLevel: z.number().optional(),
privacySettingInfo: privacySettingInfoSchema.optional(),
headIcon: z.number().optional(),
signature: z.string().optional(),
avatarDetailList: z.array(avatarEnkaDetailSchema).optional(),
platform: z.string().optional(),
recordInfo: recordInfoSchema.optional(),
uid: z.number(),
level: z.number(),
nickname: z.string(),
isDisplayAvatar: z.boolean(),
friendCount: z.number(),
personalCardId: z.number()
isDisplayAvatar: z.boolean().optional(),
friendCount: z.number().optional(),
personalCardId: z.number().optional()
});
export const enkaResponseSchema = z.object({