캐릭터 코디/기본 정보 자동 새로고침

보스 수익 계산기/심볼 계산기에서 저장된 캐릭터의 character_image, level,
직업 정보를 페이지 로드마다 /api/character/search로 재조회해 반영.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-04-16 13:48:03 +09:00
parent 3829ada3cf
commit 791f4f8e35
3 changed files with 61 additions and 1 deletions

View file

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { useQuery } from '@tanstack/react-query' import { useQuery, useQueries } from '@tanstack/react-query'
import { api } from '../../api/client' import { api } from '../../api/client'
import { useLayout } from '../../components/Layout' import { useLayout } from '../../components/Layout'
import CharacterPanel from './user/CharacterPanel' import CharacterPanel from './user/CharacterPanel'
@ -44,6 +44,34 @@ export default function BossCrystal() {
queryFn: () => api('/api/boss-crystal/bosses').catch(() => []), 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) => { const handleAddCharacter = (char) => {
setCharacters((prev) => [...prev, char]) setCharacters((prev) => [...prev, char])
setSelectedChar(char.character_name) setSelectedChar(char.character_name)

View file

@ -377,12 +377,40 @@ export default function Symbol() {
const removeCharacter = useSymbolStore((s) => s.removeCharacter) const removeCharacter = useSymbolStore((s) => s.removeCharacter)
const selectCharacter = useSymbolStore((s) => s.selectCharacter) const selectCharacter = useSymbolStore((s) => s.selectCharacter)
const syncCharacterSymbols = useSymbolStore((s) => s.syncCharacterSymbols) const syncCharacterSymbols = useSymbolStore((s) => s.syncCharacterSymbols)
const updateCharacter = useSymbolStore((s) => s.updateCharacter)
const storedTab = useSymbolStore((s) => s.selectedTabs?.[selectedCharId]) const storedTab = useSymbolStore((s) => s.selectedTabs?.[selectedCharId])
const setTabStore = useSymbolStore((s) => s.setTab) const setTabStore = useSymbolStore((s) => s.setTab)
const tab = storedTab || tabs[0]?.key || null const tab = storedTab || tabs[0]?.key || null
const setTab = (t) => { if (selectedCharId) setTabStore(selectedCharId, t) } 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 ( ) // fetch ( )
const symbolQueries = useQueries({ const symbolQueries = useQueries({
queries: characters.map((c) => ({ queries: characters.map((c) => ({

View file

@ -54,6 +54,10 @@ export const useSymbolStore = create(persist(
selectCharacter: (id) => set({ selectedCharId: id }), 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], getSymbolState: (charId, symbolId) => get().progress?.[charId]?.[symbolId],
updateSymbol: (charId, symbolId, patch) => set((s) => { updateSymbol: (charId, symbolId, patch) => set((s) => {