FIX: fix bug not create avatar
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m33s

This commit is contained in:
2025-07-25 12:17:01 +07:00
parent 01bdafcf10
commit 31724fa88c
13 changed files with 44 additions and 44 deletions

View File

@@ -7,6 +7,7 @@ import useAvatarStore from "@/stores/avatarStore"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import { useFetchASData, useFetchAvatarData, useFetchConfigData, useFetchLightconeData, useFetchMOCData, useFetchMonsterData, useFetchPFData, useFetchRelicData } from "@/hooks" import { useFetchASData, useFetchAvatarData, useFetchConfigData, useFetchLightconeData, useFetchMOCData, useFetchMonsterData, useFetchPFData, useFetchRelicData } from "@/hooks"
export default function AvatarBar() { export default function AvatarBar() {
const [listElement, setListElement] = useState<Record<string, boolean>>({ "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false }) const [listElement, setListElement] = useState<Record<string, boolean>>({ "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false })
const [listPath, setListPath] = useState<Record<string, boolean>>({ "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false }) const [listPath, setListPath] = useState<Record<string, boolean>>({ "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false })
@@ -28,6 +29,7 @@ export default function AvatarBar() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [locale, listElement, listPath]) }, [locale, listElement, listPath])
return ( return (
<div className="grid grid-flow-row h-full auto-rows-max w-full"> <div className="grid grid-flow-row h-full auto-rows-max w-full">
<div className="h-full rounded-lg mx-2 py-2"> <div className="h-full rounded-lg mx-2 py-2">

View File

@@ -1,6 +1,7 @@
import { AvatarEnkaDetail, AvatarProfileStore, AvatarStore, CharacterDetail, EnkaResponse, FreeSRJson, RelicStore } from "@/types"; import { AvatarEnkaDetail, AvatarProfileStore, AvatarStore, CharacterDetail, FreeSRJson, RelicStore } from "@/types";
function safeNumber(val: any, fallback = 0): number { function safeNumber(val: string | number | null, fallback = 0): number {
if (!val) return fallback;
const num = Number(val); const num = Number(val);
return Number.isFinite(num) && num !== 0 ? num : fallback; return Number.isFinite(num) && num !== 0 ? num : fallback;
} }
@@ -14,7 +15,7 @@ export function converterToAvatarStore(data: Record<string, CharacterDetail>): {
avatar_id: Number(key), avatar_id: Number(key),
data: { data: {
rank: 0, rank: 0,
skills: Object.entries(value.SkillTrees).reduce((acc, [pointName, dataPointEntry]) => { skills: Object.values(value.SkillTrees).reduce((acc, dataPointEntry) => {
const firstEntry = Object.values(dataPointEntry)[0]; const firstEntry = Object.values(dataPointEntry)[0];
if (firstEntry) { if (firstEntry) {
acc[firstEntry.PointID] = firstEntry.MaxLevel; acc[firstEntry.PointID] = firstEntry.MaxLevel;

View File

@@ -1,15 +0,0 @@
import useUserDataStore from "@/stores/userDataStore";
import useAvatarStore from "@/stores/avatarStore";
import { CharacterDetail } from "@/types";
export function getAvatarNotExist(): Record<string, CharacterDetail> {
const { avatars } = useUserDataStore.getState()
const { mapAvatarInfo } = useAvatarStore.getState()
const listAvatarId = Object.keys(avatars)
const listAvatarNotExist = Object.keys(mapAvatarInfo).filter((avatarId) => !listAvatarId.includes(avatarId))
return listAvatarNotExist.reduce((acc, avatarId) => {
acc[avatarId] = mapAvatarInfo[avatarId]
return acc
}, {} as Record<string, CharacterDetail>)
}

View File

@@ -1,11 +1,10 @@
import useAvatarStore from "@/stores/avatarStore" import useAvatarStore from "@/stores/avatarStore"
import useUserDataStore from "@/stores/userDataStore"
export function getSkillTree(enhanced: string) { export function getSkillTree(enhanced: string) {
const { avatarSelected, mapAvatarInfo } = useAvatarStore.getState() const { avatarSelected, mapAvatarInfo } = useAvatarStore.getState()
if (!avatarSelected) return null; if (!avatarSelected) return null;
if (enhanced != "") return Object.entries(mapAvatarInfo[avatarSelected.id || ""]?.Enhanced[enhanced].SkillTrees || {}).reduce((acc, [pointName, dataPointEntry]) => { if (enhanced != "") return Object.values(mapAvatarInfo[avatarSelected.id || ""]?.Enhanced[enhanced].SkillTrees || {}).reduce((acc, dataPointEntry) => {
const firstEntry = Object.values(dataPointEntry)[0]; const firstEntry = Object.values(dataPointEntry)[0];
if (firstEntry) { if (firstEntry) {
acc[firstEntry.PointID] = firstEntry.MaxLevel; acc[firstEntry.PointID] = firstEntry.MaxLevel;
@@ -13,7 +12,7 @@ export function getSkillTree(enhanced: string) {
return acc; return acc;
}, {} as Record<string, number>) }, {} as Record<string, number>)
return Object.entries(mapAvatarInfo[avatarSelected.id || ""]?.SkillTrees).reduce((acc, [pointName, dataPointEntry]) => { return Object.values(mapAvatarInfo[avatarSelected.id || ""]?.SkillTrees).reduce((acc, dataPointEntry) => {
const firstEntry = Object.values(dataPointEntry)[0]; const firstEntry = Object.values(dataPointEntry)[0];
if (firstEntry) { if (firstEntry) {
acc[firstEntry.PointID] = firstEntry.MaxLevel; acc[firstEntry.PointID] = firstEntry.MaxLevel;

View File

@@ -1,7 +1,6 @@
export * from "./getName" export * from "./getName"
export * from "./replaceByParam" export * from "./replaceByParam"
export * from "./converterToAvatarStore" export * from "./converterToAvatarStore"
export * from "./getAvatarNotExist"
export * from "./calcData" export * from "./calcData"
export * from "./random" export * from "./random"
export * from "./json" export * from "./json"

View File

@@ -1,7 +1,7 @@
export function randomPartition(sum: number, parts: number): number[] { export function randomPartition(sum: number, parts: number): number[] {
let raw = Array.from({ length: parts }, () => Math.random()); const raw = Array.from({ length: parts }, () => Math.random());
let total = raw.reduce((a, b) => a + b, 0); const total = raw.reduce((a, b) => a + b, 0);
let result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1); const result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1);
let diff = sum - result.reduce((a, b) => a + b, 0); let diff = sum - result.reduce((a, b) => a + b, 0);
while (diff !== 0) { while (diff !== 0) {
for (let i = 0; i < result.length && diff !== 0; i++) { for (let i = 0; i < result.length && diff !== 0; i++) {

View File

@@ -6,12 +6,13 @@ import { toast } from 'react-toastify'
import useAvatarStore from '@/stores/avatarStore' import useAvatarStore from '@/stores/avatarStore'
import { listCurrentLanguageApi } from '@/lib/constant' import { listCurrentLanguageApi } from '@/lib/constant'
import useLocaleStore from '@/stores/localeStore' import useLocaleStore from '@/stores/localeStore'
import { converterToAvatarStore, getAvatarNotExist } from '@/helper'
import useUserDataStore from '@/stores/userDataStore' import useUserDataStore from '@/stores/userDataStore'
import { converterToAvatarStore } from '@/helper'
import { CharacterDetail } from '@/types'
export const useFetchAvatarData = () => { export const useFetchAvatarData = () => {
const { setAvatars, avatars } = useUserDataStore() const { setAvatars, avatars } = useUserDataStore()
const { setListAvatar, setAllMapAvatarInfo, setAvatarSelected } = useAvatarStore() const { setListAvatar, setAllMapAvatarInfo, mapAvatarInfo, setAvatarSelected } = useAvatarStore()
const { locale } = useLocaleStore() const { locale } = useLocaleStore()
const { data: dataAvatar, error: errorAvatar } = useQuery({ const { data: dataAvatar, error: errorAvatar } = useQuery({
queryKey: ['avatarData'], queryKey: ['avatarData'],
@@ -27,25 +28,37 @@ export const useFetchAvatarData = () => {
staleTime: 1000 * 60 * 5, staleTime: 1000 * 60 * 5,
}) })
useEffect(() => {
const listAvatarId = Object.keys(avatars)
const listAvatarNotExist = Object.keys(mapAvatarInfo).filter((avatarId) => !listAvatarId.includes(avatarId))
const avatarDiff = listAvatarNotExist.reduce((acc, avatarId) => {
acc[avatarId] = mapAvatarInfo[avatarId]
return acc
}, {} as Record<string, CharacterDetail>)
const avatarStore = converterToAvatarStore(avatarDiff)
if (Object.keys(avatarStore).length === 0) return
setAvatars({...avatarStore })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mapAvatarInfo])
const { data: dataAvatarInfo, error: errorAvatarInfo } = useQuery({ const { data: dataAvatarInfo, error: errorAvatarInfo } = useQuery({
queryKey: ['avatarInfoData', locale], queryKey: ['avatarInfoData', locale],
queryFn: () => queryFn: () =>
fetchCharactersByIdsNative( fetchCharactersByIdsNative(
dataAvatar!.map((item) => item.id), dataAvatar!.map((item) => item.id),
listCurrentLanguageApi[locale.toLowerCase()] listCurrentLanguageApi[locale.toLowerCase()]
), ),
staleTime: 1000 * 60 * 5, staleTime: 1000 * 60 * 5,
enabled: !!dataAvatar, enabled: !!dataAvatar,
}); });
useEffect(() => { useEffect(() => {
if (dataAvatar && !errorAvatar) { if (dataAvatar && !errorAvatar) {
setListAvatar(dataAvatar) setListAvatar(dataAvatar)
setAvatarSelected(dataAvatar[0]) setAvatarSelected(dataAvatar[0])
const avatarStore = converterToAvatarStore(getAvatarNotExist())
if (Object.keys(avatarStore).length > 0) {
setAvatars({ ...avatars, ...avatarStore })
}
} else if (errorAvatar) { } else if (errorAvatar) {
toast.error("Failed to load avatar data") toast.error("Failed to load avatar data")
} }
@@ -57,5 +70,5 @@ export const useFetchAvatarData = () => {
} else if (errorAvatarInfo) { } else if (errorAvatarInfo) {
toast.error("Failed to load avatar info data") toast.error("Failed to load avatar info data")
} }
}, [dataAvatarInfo, errorAvatarInfo, setAllMapAvatarInfo]) }, [dataAvatarInfo, errorAvatarInfo, setAllMapAvatarInfo, setAvatars])
} }

View File

@@ -8,7 +8,7 @@ interface AffixState {
setMapSubAffix: (newSubAffix: Record<string, Record<string, AffixDetail>>) => void; setMapSubAffix: (newSubAffix: Record<string, Record<string, AffixDetail>>) => void;
} }
const useAffixStore = create<AffixState>((set, get) => ({ const useAffixStore = create<AffixState>((set) => ({
mapMainAffix: {}, mapMainAffix: {},
mapSubAffix: {}, mapSubAffix: {},
setMapMainAffix: (newMainAffix: Record<string, Record<string, AffixDetail>>) => set({ mapMainAffix: newMainAffix }), setMapMainAffix: (newMainAffix: Record<string, Record<string, AffixDetail>>) => set({ mapMainAffix: newMainAffix }),

View File

@@ -1,4 +1,4 @@
import { AvatarDataStore, AvatarProfileCardType, CharacterBasic, CharacterInfoCardType, EnkaResponse, FilterAvatarType } from '@/types'; import { AvatarProfileCardType, CharacterBasic, FilterAvatarType } from '@/types';
import { create } from 'zustand' import { create } from 'zustand'
interface CopyProfileState { interface CopyProfileState {

View File

@@ -8,7 +8,7 @@ interface FreeSRState {
setFreeSRData: (newFreeSRData: FreeSRJson | null) => void; setFreeSRData: (newFreeSRData: FreeSRJson | null) => void;
} }
const useFreeSRStore = create<FreeSRState>((set, get) => ({ const useFreeSRStore = create<FreeSRState>((set) => ({
selectedCharacters: [], selectedCharacters: [],
freeSRData: null, freeSRData: null,
setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }),

View File

@@ -5,7 +5,7 @@ interface GlobalState {
setIsConnectPS: (newIsConnectPS: boolean) => void; setIsConnectPS: (newIsConnectPS: boolean) => void;
} }
const useGlobalStore = create<GlobalState>((set, get) => ({ const useGlobalStore = create<GlobalState>((set) => ({
isConnectPS: false, isConnectPS: false,
setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }), setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }),
})); }));

View File

@@ -1,4 +1,4 @@
import { AffixDetail, ASConfigMaze, AvatarConfigMaze, ConfigMaze, MOCConfigMaze, PFConfigMaze, StageConfigMaze } from '@/types'; import { ASConfigMaze, AvatarConfigMaze, ConfigMaze, MOCConfigMaze, PFConfigMaze, StageConfigMaze } from '@/types';
import { create } from 'zustand' import { create } from 'zustand'
interface MazeState { interface MazeState {

View File

@@ -1,6 +1,6 @@
import { ASConfigStore, AvatarStore, CEConfigStore, MOCConfigStore, PFConfigStore } from '@/types'; import { ASConfigStore, AvatarStore, CEConfigStore, MOCConfigStore, PFConfigStore } from '@/types';
import { create } from 'zustand' import { create } from 'zustand'
import { persist } from 'zustand/middleware'; import { createJSONStorage, persist } from 'zustand/middleware';
interface UserDataState { interface UserDataState {
@@ -71,6 +71,7 @@ const useUserDataStore = create<UserDataState>()(
}), }),
{ {
name: 'user-data-storage', name: 'user-data-storage',
storage: createJSONStorage(() => localStorage),
} }
) )
); );