diff --git a/src/components/MissionTab.jsx b/src/components/MissionTab.jsx index 8b62296..a9152fc 100644 --- a/src/components/MissionTab.jsx +++ b/src/components/MissionTab.jsx @@ -177,9 +177,8 @@ export default function MissionTab({ authedPlayer }) { // ─── Data loading ─────────────────────────────────────────────── - const loadMissions = useCallback(async function loadMissions() { - setLoading(true); - setError(''); + const loadMissions = useCallback(async function loadMissions({ silent = false } = {}) { + if (!silent) { setLoading(true); setError(''); } try { if (isGM) { const [allRes, activeRes] = await Promise.all([ @@ -188,19 +187,18 @@ export default function MissionTab({ authedPlayer }) { ]); const all = Array.isArray(allRes.data) ? allRes.data.map(normalizeMission) : []; const active = normalizeMission(activeRes.data); - setMissions(all); - setActiveMission(active); - setSelectedMissionId(active?.id ? String(active.id) : (all[0]?.id ? String(all[0].id) : '')); + setMissions(prev => JSON.stringify(prev) !== JSON.stringify(all) ? all : prev); + setActiveMission(prev => JSON.stringify(prev) !== JSON.stringify(active) ? active : prev); + if (!silent) setSelectedMissionId(active?.id ? String(active.id) : (all[0]?.id ? String(all[0].id) : '')); } else { const activeRes = await axios.get('/api/missions/active/player'); - setMissions([]); - setActiveMission(normalizeMission(activeRes.data)); - setSelectedMissionId(''); + const active = normalizeMission(activeRes.data); + setActiveMission(prev => JSON.stringify(prev) !== JSON.stringify(active) ? active : prev); } } catch (e) { - setError(`Failed to load missions: ${e.response?.data?.error || e.message}`); + if (!silent) setError(`Failed to load missions: ${e.response?.data?.error || e.message}`); } finally { - setLoading(false); + if (!silent) setLoading(false); } }, [isGM]); @@ -208,7 +206,8 @@ export default function MissionTab({ authedPlayer }) { const loadRollFeed = useCallback(async function loadRollFeed() { try { const res = await axios.get('/api/missions/active/rolls/feed?limit=50'); - setRollFeed(Array.isArray(res.data) ? res.data : []); + const next = Array.isArray(res.data) ? res.data : []; + setRollFeed(prev => JSON.stringify(prev) !== JSON.stringify(next) ? next : prev); } catch (e) { // non-blocking } @@ -236,7 +235,7 @@ export default function MissionTab({ authedPlayer }) { useEffect(() => { if (isGM) return undefined; const timer = setInterval(() => { - loadMissions(); + loadMissions({ silent: true }); loadRollFeed(); }, 4000); return () => clearInterval(timer); @@ -291,6 +290,7 @@ export default function MissionTab({ authedPlayer }) { objectives: r.objectives ?? legacy, complications: r.complications ?? false, // never auto-revealed checks: r.checks || {}, + combat: r.combat ?? false, }; }, [currentScene]); @@ -407,7 +407,7 @@ export default function MissionTab({ authedPlayer }) { i === index ? { ...scene, ...updates } : scene ); await saveProgress(index, nextScenes); - await loadMissions(); + await loadMissions({ silent: true }); } async function updateCombatState(updates) { @@ -1208,10 +1208,10 @@ export default function MissionTab({ authedPlayer }) { {/* Combat Setup */}
Combat briefing is classified.
Awaiting GM to initiate combat.
Combat briefing er klassificeret.
Afventer GM-ordre.