fix: force sync before boss fight and surface challenge errors to UI
CI / Lint, Build & Test (pull_request) Successful in 1m32s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m36s

Closes #50
This commit is contained in:
2026-03-18 11:19:37 -07:00
committed by Naomi Carrigan
parent a20cf3ef87
commit 498c97582d
2 changed files with 31 additions and 2 deletions
@@ -235,6 +235,7 @@ const BossPanel = (): JSX.Element => {
toggleAutoBoss, toggleAutoBoss,
autoBossLastResult, autoBossLastResult,
autoBossError, autoBossError,
bossError,
} = useGame(); } = useGame();
const [ challengingBossId, setChallengingBossId ] = useState<string | null>( const [ challengingBossId, setChallengingBossId ] = useState<string | null>(
null, null,
@@ -362,6 +363,13 @@ const BossPanel = (): JSX.Element => {
</div> </div>
</div> </div>
{bossError === null
? null
: <p className="auto-boss-error">
{"⚠️ "}
{bossError}
</p>
}
{autoBossError === null {autoBossError === null
? null ? null
: <p className="auto-boss-error"> : <p className="auto-boss-error">
+23 -2
View File
@@ -557,6 +557,12 @@ interface GameContextValue {
* when no error). Cleared automatically when the player re-enables auto-boss. * when no error). Cleared automatically when the player re-enables auto-boss.
*/ */
autoBossError: string | null; autoBossError: string | null;
/**
* Error message from the most recent manual boss challenge (null when no
* error). Cleared automatically when a new challenge is initiated.
*/
bossError: string | null;
} }
export interface BattleResult { export interface BattleResult {
@@ -606,6 +612,7 @@ export const GameProvider = ({
at: number; at: number;
} | null>(null); } | null>(null);
const [ autoBossError, setAutoBossError ] = useState<string | null>(null); const [ autoBossError, setAutoBossError ] = useState<string | null>(null);
const [ bossError, setBossError ] = useState<string | null>(null);
const syncErrorTimerReference = useRef<ReturnType<typeof setTimeout> | null>( const syncErrorTimerReference = useRef<ReturnType<typeof setTimeout> | null>(
null, null,
); );
@@ -1867,6 +1874,14 @@ export const GameProvider = ({
return; return;
} }
setBossError(null);
/*
* Flush any pending state (e.g. newly equipped items) to the server before
* the fight so the server-side calculation uses the player's live stats.
*/
await forceSync();
try { try {
const result = await challengeBossApi({ bossId }); const result = await challengeBossApi({ bossId });
setState((previous) => { setState((previous) => {
@@ -1878,9 +1893,13 @@ export const GameProvider = ({
setBattleResult({ bossName: boss.name, result: result }); setBattleResult({ bossName: boss.name, result: result });
} catch (error_: unknown) { } catch (error_: unknown) {
logError("challenge_boss", error_); logError("challenge_boss", error_);
// Silently ignore — server errors shouldn't crash the UI setBossError(
error_ instanceof Error
? error_.message
: "Failed to challenge boss",
);
} }
}, []); }, [ forceSync ]);
const dismissOfflineGold = useCallback(() => { const dismissOfflineGold = useCallback(() => {
setOfflineGold(0); setOfflineGold(0);
@@ -2023,6 +2042,7 @@ export const GameProvider = ({
autoBossError, autoBossError,
autoBossLastResult, autoBossLastResult,
battleResult, battleResult,
bossError,
buyAdventurer, buyAdventurer,
buyEchoUpgrade, buyEchoUpgrade,
buyEquipment, buyEquipment,
@@ -2091,6 +2111,7 @@ export const GameProvider = ({
autoBossError, autoBossError,
autoBossLastResult, autoBossLastResult,
battleResult, battleResult,
bossError,
completedQuestToasts, completedQuestToasts,
failedQuestToasts, failedQuestToasts,
formatNumber, formatNumber,