From 402dbf4c562370d2da0b45779ee7a1bca32082dd Mon Sep 17 00:00:00 2001 From: Lina Date: Wed, 11 Feb 2026 12:17:11 -0800 Subject: [PATCH] update switch track timing logic --- .../SwitchRace/SwitchTrackScreen.jsx | 77 +++++++++++++++---- client/src/context/reducer.js | 3 +- client/src/utils/loadGameState.js | 4 +- server/controllers/handleLoadGameState.js | 2 +- server/controllers/handleRaceStart.js | 7 ++ server/controllers/handleSwitchTrack.js | 1 - 6 files changed, 73 insertions(+), 21 deletions(-) diff --git a/client/src/components/SwitchRace/SwitchTrackScreen.jsx b/client/src/components/SwitchRace/SwitchTrackScreen.jsx index 5607eb0..44e8177 100644 --- a/client/src/components/SwitchRace/SwitchTrackScreen.jsx +++ b/client/src/components/SwitchRace/SwitchTrackScreen.jsx @@ -1,7 +1,7 @@ import { useState, useContext, useEffect } from "react"; // components -import { BackButton, Footer } from "@components"; +import { BackButton, ConfirmationModal, Footer } from "@components"; // context import { GlobalDispatchContext, GlobalStateContext } from "@context/GlobalContext"; @@ -12,22 +12,36 @@ import { backendAPI, getErrorMessage } from "@utils"; export const SwitchTrackScreen = () => { const dispatch = useContext(GlobalDispatchContext); - const { tracks, trackLastSwitchedDate } = useContext(GlobalStateContext); + const { tracks, lastRaceStartedDate, isAdmin } = useContext(GlobalStateContext); const [selectedTrack, setSelectedTrack] = useState(null); - const [areAllButtonsDisabled, setAreAllButtonsDisabled] = useState(true); + const [areAllButtonsDisabled, setAreAllButtonsDisabled] = useState(false); + const [showRaceWarning, setShowRaceWarning] = useState(false); + const [showConfirmationModal, setShowConfirmationModal] = useState(false); useEffect(() => { - if (trackLastSwitchedDate) { - const lastSwitch = trackLastSwitchedDate; - const now = new Date().getTime(); - const diffMs = now - lastSwitch; - const diffMinutes = diffMs / (100 * 60); - setAreAllButtonsDisabled(diffMinutes < 30); + if (lastRaceStartedDate) { + const now = Date.now(); + const diffMinutes = (now - lastRaceStartedDate) / (1000 * 60); + const isRecentRace = diffMinutes < 5; + + if (isRecentRace) { + if (isAdmin) { + setAreAllButtonsDisabled(false); + setShowRaceWarning(true); + } else { + setAreAllButtonsDisabled(true); + setShowRaceWarning(false); + } + } else { + setAreAllButtonsDisabled(false); + setShowRaceWarning(false); + } } else { setAreAllButtonsDisabled(false); + setShowRaceWarning(false); } - }, [trackLastSwitchedDate]); + }, [lastRaceStartedDate, isAdmin]); const updateTrack = async () => { setAreAllButtonsDisabled(true); @@ -35,7 +49,7 @@ export const SwitchTrackScreen = () => { await backendAPI .post("/race/switch-track", { selectedTrack }) .then((response) => { - const { leaderboard, numberOfCheckpoints, trackLastSwitchedDate } = response.data.sceneData; + const { leaderboard, numberOfCheckpoints } = response.data.sceneData; dispatch({ type: SET_SCENE_DATA, @@ -43,7 +57,6 @@ export const SwitchTrackScreen = () => { leaderboard, numberOfCheckpoints, tracks, - trackLastSwitchedDate, }, }); }) @@ -56,6 +69,14 @@ export const SwitchTrackScreen = () => { }); }; + const handleUpdateTrackClick = () => { + if (showRaceWarning) { + setShowConfirmationModal(true); + } else { + updateTrack(); + } + }; + return ( <> dispatch({ type: SCREEN_MANAGER.SHOW_HOME_SCREEN })} /> @@ -83,10 +104,36 @@ export const SwitchTrackScreen = () => {
- + {areAllButtonsDisabled && !isAdmin ? ( +
+ A race was recently started. Please try again in a few minutes. + +
+ ) : ( + + )}
+ + {showConfirmationModal && ( + setShowConfirmationModal(false)} + /> + )} ); }; diff --git a/client/src/context/reducer.js b/client/src/context/reducer.js index f402e30..e225fa8 100644 --- a/client/src/context/reducer.js +++ b/client/src/context/reducer.js @@ -100,7 +100,7 @@ const globalReducer = (state, action) => { tracks: payload.tracks, visitorInventory: payload.visitorInventory, badges: payload.badges, - trackLastSwitchedDate: payload.trackLastSwitchedDate, + lastRaceStartedDate: payload.lastRaceStartedDate, error: "", }; case SET_VISITOR_INVENTORY: @@ -114,7 +114,6 @@ const globalReducer = (state, action) => { ...state, leaderboard: payload.leaderboard, numberOfCheckpoints: payload.numberOfCheckpoints, - trackLastSwitchedDate: payload.trackLastSwitchedDate, error: "", }; case SET_LEADERBOARD: diff --git a/client/src/utils/loadGameState.js b/client/src/utils/loadGameState.js index f63df0c..2127347 100644 --- a/client/src/utils/loadGameState.js +++ b/client/src/utils/loadGameState.js @@ -17,7 +17,7 @@ export const loadGameState = async (dispatch) => { tracks, visitorInventory, badges, - trackLastSwitchedDate, + lastRaceStartedDate, } = result.data; await dispatch({ @@ -33,7 +33,7 @@ export const loadGameState = async (dispatch) => { tracks, visitorInventory, badges, - trackLastSwitchedDate, + lastRaceStartedDate, }, }); diff --git a/server/controllers/handleLoadGameState.js b/server/controllers/handleLoadGameState.js index 417dc83..295f27e 100644 --- a/server/controllers/handleLoadGameState.js +++ b/server/controllers/handleLoadGameState.js @@ -84,7 +84,7 @@ export const handleLoadGameState = async (req, res) => { tracks: parseEnvJson(process.env.TRACKS) || TRACKS, visitorInventory, badges, - trackLastSwitchedDate: sceneData.trackLastSwitchedDate || null, + lastRaceStartedDate: sceneData.lastRaceStartedDate || null, }); } catch (error) { return errorHandler({ diff --git a/server/controllers/handleRaceStart.js b/server/controllers/handleRaceStart.js index 3e77c98..1d07f82 100644 --- a/server/controllers/handleRaceStart.js +++ b/server/controllers/handleRaceStart.js @@ -63,6 +63,13 @@ export const handleRaceStart = async (req, res) => { }); if (updateVisitorResult instanceof Error) throw updateVisitorResult; + // Update world data object with last race started timestamp + await world.updateDataObject( + { + [`${sceneDropId}.lastRaceStartedDate`]: startTimestamp, + }, + ); + addNewRowToGoogleSheets({ identityId, displayName, diff --git a/server/controllers/handleSwitchTrack.js b/server/controllers/handleSwitchTrack.js index a82a964..bd282de 100644 --- a/server/controllers/handleSwitchTrack.js +++ b/server/controllers/handleSwitchTrack.js @@ -70,7 +70,6 @@ export const handleSwitchTrack = async (req, res) => { numberOfCheckpoints: numberOfCheckpoints?.length, leaderboard: {}, position, - trackLastSwitchedDate: new Date().getTime(), }; await world.updateDataObject(