From 6e2cb45553c2b7683fd52b5cd38b8d3626469deb Mon Sep 17 00:00:00 2001 From: Hikari Date: Sun, 8 Mar 2026 19:18:46 -0700 Subject: [PATCH] fix: delay boss lore toasts until battle animation reveals result Co-Authored-By: Claude Sonnet 4.6 --- apps/web/src/components/game/battleModal.tsx | 9 ++++- apps/web/src/context/gameContext.tsx | 42 ++++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/game/battleModal.tsx b/apps/web/src/components/game/battleModal.tsx index e1f177c..169a03f 100644 --- a/apps/web/src/components/game/battleModal.tsx +++ b/apps/web/src/components/game/battleModal.tsx @@ -58,7 +58,12 @@ const BattleModal = ({ onDismiss, }: BattleModalProperties): JSX.Element => { const { result, bossName } = battle; - const { enableNotifications, enableSounds, formatNumber } = useGame(); + const { + enableNotifications, + enableSounds, + flushBossLoreToasts, + formatNumber, + } = useGame(); const [ phase, setPhase ] = useState<"animating" | "result">("animating"); @@ -111,6 +116,7 @@ const BattleModal = ({ const revealTimeout = setTimeout(() => { setPhase("result"); + flushBossLoreToasts(); if (result.won) { if (enableSounds) { playSound("bossVictory"); @@ -132,6 +138,7 @@ const BattleModal = ({ bossStartPercent, enableNotifications, enableSounds, + flushBossLoreToasts, partyEndPercent, result.won, ]); diff --git a/apps/web/src/context/gameContext.tsx b/apps/web/src/context/gameContext.tsx index 618669a..eb72f0a 100644 --- a/apps/web/src/context/gameContext.tsx +++ b/apps/web/src/context/gameContext.tsx @@ -455,6 +455,11 @@ interface GameContextValue { */ dismissCodexEntry: (id: string)=> void; + /** + * Flush pending boss lore codex toasts — call after the battle animation reveals the result. + */ + flushBossLoreToasts: ()=> void; + /** * Perform a transcendence — nuclear reset, earning echoes. */ @@ -613,6 +618,7 @@ export const GameProvider = ({ Array >([]); const codexProcessedReference = useRef>(new Set()); + const pendingBossCodexIdsReference = useRef>([]); const [ unlockedStoryChapterIds, setUnlockedStoryChapterIds ] = useState< Array >([]); @@ -880,12 +886,30 @@ export const GameProvider = ({ }; }); if (!isFirstRun) { - setUnlockedCodexEntryIds((previous) => { - return [ ...previous, ...addedIds ]; + const bossIds = addedIds.filter((id) => { + return id.startsWith("boss_"); }); + const otherIds = addedIds.filter((id) => { + return !id.startsWith("boss_"); + }); + if (bossIds.length > 0) { + if (battleResult === null) { + otherIds.push(...bossIds); + } else { + pendingBossCodexIdsReference.current = [ + ...pendingBossCodexIdsReference.current, + ...bossIds, + ]; + } + } + if (otherIds.length > 0) { + setUnlockedCodexEntryIds((previous) => { + return [ ...previous, ...otherIds ]; + }); + } } } - }, [ state ]); + }, [ battleResult, state ]); // Detect newly unlocked story chapters useEffect(() => { @@ -1836,6 +1860,16 @@ export const GameProvider = ({ }); }, []); + const flushBossLoreToasts = useCallback(() => { + const pending = pendingBossCodexIdsReference.current; + if (pending.length > 0) { + pendingBossCodexIdsReference.current = []; + setUnlockedCodexEntryIds((previous) => { + return [ ...previous, ...pending ]; + }); + } + }, []); + const dismissStoryChapter = useCallback((id: string) => { setUnlockedStoryChapterIds((previous) => { return previous.filter((chapter) => { @@ -1935,6 +1969,7 @@ export const GameProvider = ({ equipItem, error, failedQuestToasts, + flushBossLoreToasts, forceSync, formatNumber, handleClick, @@ -2001,6 +2036,7 @@ export const GameProvider = ({ enableSounds, equipItem, error, + flushBossLoreToasts, forceSync, handleClick, isLoading,