From d672648a0578006ab8d4fad996a9dc584282b3c0 Mon Sep 17 00:00:00 2001 From: nessi Date: Sun, 2 Aug 2026 11:38:08 +0200 Subject: [PATCH] Prevent winner selection from being overwritten by polling updates using dirty flag Added `winnerSelectionDirtyRef` to track when user has manually changed winner selection. Modified `loadGameMeta` to skip updating `winnerUserId` state when dirty flag is set, preventing polling from overwriting user's unsaved selection. Introduced `selectWinner` helper that sets dirty flag on manual selection and clears flag after successful save. Reset dirty flag in all state cleanup handlers (goHome, logout, le --- frontend/src/App.jsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index c9d2384..b84148b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -60,6 +60,7 @@ export default function App() { // Winner selection (host only) const [winnerUserId, setWinnerUserId] = useState(""); + const winnerSelectionDirtyRef = useRef(false); // Modals const [helpOpen, setHelpOpen] = useState(false); @@ -149,7 +150,9 @@ export default function App() { const meta = await api(`/games/${gameId}`); setGameMeta(meta); - setWinnerUserId(meta?.winner_user_id || ""); + if (!winnerSelectionDirtyRef.current) { + setWinnerUserId(meta?.winner_user_id || ""); + } const chips = meta?.started ? await api(`/games/${gameId}/chips`) : []; setGameChips(chips || []); @@ -226,6 +229,7 @@ export default function App() { lastWinnerIdRef.current = null; gameStartBaselineRef.current = false; lastGameStartedRef.current = false; + winnerSelectionDirtyRef.current = false; setCelebrateOpen(false); setCelebrateName(""); setStartCelebrateOpen(false); @@ -370,6 +374,7 @@ export default function App() { setMembers([]); setGameChips([]); setWinnerUserId(""); + winnerSelectionDirtyRef.current = false; // reset winner celebration on logout winnerBaselineRef.current = false; @@ -478,6 +483,7 @@ export default function App() { setMembers([]); setGameChips([]); setWinnerUserId(""); + winnerSelectionDirtyRef.current = false; setPulseId(null); // auch Chip-Modal-State resetten @@ -535,6 +541,7 @@ export default function App() { setMembers([]); setGameChips([]); setWinnerUserId(""); + winnerSelectionDirtyRef.current = false; setChipOpen(false); setChipEntry(null); showSnack(language === "en" ? "Game cancelled." : "Spiel abgebrochen."); @@ -547,6 +554,7 @@ export default function App() { setMembers([]); setGameChips([]); setWinnerUserId(""); + winnerSelectionDirtyRef.current = false; setChipOpen(false); setChipEntry(null); setCelebrateOpen(false); @@ -561,9 +569,15 @@ export default function App() { method: "PATCH", body: JSON.stringify({ winner_user_id: winnerUserId || null }), }); + winnerSelectionDirtyRef.current = false; await loadGameMeta(); }; + const selectWinner = (value) => { + winnerSelectionDirtyRef.current = true; + setWinnerUserId(value); + }; + // ===== Sheet actions ===== const cycleStatus = async (entry) => { if (gameMeta?.winner_user_id) return; @@ -806,7 +820,7 @@ export default function App() { isHost={isHost} members={members} winnerUserId={winnerUserId} - setWinnerUserId={setWinnerUserId} + setWinnerUserId={selectWinner} onSave={saveWinner} /> )}