diff --git a/frontend/src/features/boss-crystal/BossCrystal.jsx b/frontend/src/features/boss-crystal/BossCrystal.jsx index cd6d7b7..4aed18e 100644 --- a/frontend/src/features/boss-crystal/BossCrystal.jsx +++ b/frontend/src/features/boss-crystal/BossCrystal.jsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { useQuery } from '@tanstack/react-query' +import { useQuery, useQueries } from '@tanstack/react-query' import { api } from '../../api/client' import { useLayout } from '../../components/Layout' import CharacterPanel from './user/CharacterPanel' @@ -44,6 +44,34 @@ export default function BossCrystal() { queryFn: () => api('/api/boss-crystal/bosses').catch(() => []), }) + // 저장된 캐릭터의 기본 정보(코디 이미지 포함) 새로고침 + const charRefreshQueries = useQueries({ + queries: characters.map((c) => ({ + queryKey: ['character', 'basic', c.character_name], + queryFn: () => api(`/api/character/search?name=${encodeURIComponent(c.character_name)}`), + enabled: !!c.character_name, + refetchOnMount: 'always', + staleTime: 0, + retry: false, + })), + }) + + useEffect(() => { + if (!charRefreshQueries.length) return + let changed = false + const next = characters.map((c, i) => { + const d = charRefreshQueries[i]?.data + if (!d) return c + if (d.character_image !== c.character_image || d.character_level !== c.character_level || d.job_name !== c.job_name) { + changed = true + return { ...c, ...d } + } + return c + }) + if (changed) setCharacters(next) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [charRefreshQueries.map((q) => q.dataUpdatedAt).join(',')]) + const handleAddCharacter = (char) => { setCharacters((prev) => [...prev, char]) setSelectedChar(char.character_name) diff --git a/frontend/src/features/symbol/Symbol.jsx b/frontend/src/features/symbol/Symbol.jsx index a08a412..c3c0f2c 100644 --- a/frontend/src/features/symbol/Symbol.jsx +++ b/frontend/src/features/symbol/Symbol.jsx @@ -377,12 +377,40 @@ export default function Symbol() { const removeCharacter = useSymbolStore((s) => s.removeCharacter) const selectCharacter = useSymbolStore((s) => s.selectCharacter) const syncCharacterSymbols = useSymbolStore((s) => s.syncCharacterSymbols) + const updateCharacter = useSymbolStore((s) => s.updateCharacter) const storedTab = useSymbolStore((s) => s.selectedTabs?.[selectedCharId]) const setTabStore = useSymbolStore((s) => s.setTab) const tab = storedTab || tabs[0]?.key || null const setTab = (t) => { if (selectedCharId) setTabStore(selectedCharId, t) } + // 각 캐릭터 기본정보(코디 이미지) 새로고침 + const basicQueries = useQueries({ + queries: characters.map((c) => ({ + queryKey: ['character', 'basic', c.character_name], + queryFn: () => api(`/api/character/search?name=${encodeURIComponent(c.character_name)}`), + enabled: !!c.character_name, + refetchOnMount: 'always', + staleTime: 0, + retry: false, + })), + }) + useEffect(() => { + characters.forEach((c, idx) => { + const d = basicQueries[idx]?.data + if (!d) return + if (d.character_image !== c.character_image || d.character_level !== c.character_level || d.job_name !== c.job_name) { + updateCharacter(c.id, { + character_image: d.character_image, + character_level: d.character_level, + job_name: d.job_name, + world_name: d.world_name, + }) + } + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [basicQueries.map((q) => q.dataUpdatedAt).join(',')]) + // 각 캐릭터의 장착 심볼 fetch (새로고침마다 갱신) const symbolQueries = useQueries({ queries: characters.map((c) => ({ diff --git a/frontend/src/features/symbol/store.js b/frontend/src/features/symbol/store.js index 26504d6..6fa2eb7 100644 --- a/frontend/src/features/symbol/store.js +++ b/frontend/src/features/symbol/store.js @@ -54,6 +54,10 @@ export const useSymbolStore = create(persist( selectCharacter: (id) => set({ selectedCharId: id }), + updateCharacter: (id, patch) => set((s) => ({ + characters: s.characters.map((c) => (c.id === id ? { ...c, ...patch } : c)), + })), + getSymbolState: (charId, symbolId) => get().progress?.[charId]?.[symbolId], updateSymbol: (charId, symbolId, patch) => set((s) => {