diff --git a/app/(main)/clue/[clueId]/page.js b/app/(main)/clue/[clueId]/page.js index dbb2119..eb9b5b8 100644 --- a/app/(main)/clue/[clueId]/page.js +++ b/app/(main)/clue/[clueId]/page.js @@ -2,7 +2,11 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { useSession } from "next-auth/react"; -import { formatElapsedTime, getHuntTimer } from "@/utils/timerService"; +import { + formatElapsedTime, + getHuntTimer, + syncTimerWithServer, +} from "@/utils/timerService"; import Link from "next/link"; import dynamic from "next/dynamic"; import { use } from "react"; @@ -31,6 +35,8 @@ export default function CluePage({ params }) { const [clueFound, setClueFound] = useState(false); const [allClues, setAllClues] = useState([]); const [nextClueId, setNextClueId] = useState(null); + const [huntActive, setHuntActive] = useState(true); // Track hunt active status + const [previousHuntState, setPreviousHuntState] = useState(true); // To detect changes const router = useRouter(); const { data: session, status } = useSession(); @@ -129,6 +135,53 @@ export default function CluePage({ params }) { }; }, [clueId, router, status, lastRefresh, loading, isAdmin]); + // Add a new effect to check hunt status periodically + useEffect(() => { + // Check hunt status every 5 seconds + const checkHuntStatus = async () => { + try { + // First check locally if timer indicates hunt has ended + const timerData = getHuntTimer(); + let isActive = timerData?.isActive ?? true; + + // Only make API call if local data suggests the hunt is active + // This reduces unnecessary API calls + if (isActive) { + // Also sync with server occasionally + const result = await syncTimerWithServer(); + + if (result.success) { + isActive = result.data?.active ?? false; + } + } + + // Update hunt active state + setHuntActive(isActive); + + // If hunt was active before but is not active now, it just ended + if (previousHuntState && !isActive) { + console.log("Hunt has ended! Redirecting to leaderboard..."); + router.push("/leaderboard?huntEnded=true"); + } + + // Update previous state for next comparison + setPreviousHuntState(isActive); + } catch (err) { + console.error("Error checking hunt status:", err); + } + }; + + // Check immediately on mount and after any hunt state change + checkHuntStatus(); + + // Also set up interval for periodic checks + const intervalId = setInterval(checkHuntStatus, 5000); + + return () => { + clearInterval(intervalId); + }; + }, [router, previousHuntState]); + // Separate effect to handle timer updates useEffect(() => { let timerId; @@ -143,12 +196,28 @@ export default function CluePage({ params }) { // Update timer every second timerId = setInterval(() => { setElapsedTime(formatElapsedTime(timerData.startTime)); + + // Check if timer has ended (if there's an endTime) + if (timerData.endTime) { + const now = Date.now(); + const endTime = new Date(timerData.endTime).getTime(); + + // If current time is past the end time, hunt has ended + if (now >= endTime) { + setHuntActive(false); + clearInterval(timerId); + + // Redirect to leaderboard with huntEnded flag + console.log("Hunt end time reached! Redirecting to leaderboard..."); + router.push("/leaderboard?huntEnded=true"); + } + } }, 1000); return () => { if (timerId) clearInterval(timerId); }; - }, []); + }, [router]); // Handle QR scan result const handleScan = (data) => { @@ -264,6 +333,20 @@ export default function CluePage({ params }) { ); } + if (!huntActive) { + // Display temporary message before redirecting + return ( +
+
+
🏁
+

Hunt Has Ended!

+

The treasure hunt has concluded. Redirecting to results page...

+
+
+
+ ); + } + if (error) { return (
@@ -389,6 +472,30 @@ export default function CluePage({ params }) { position: relative; } + .hunt-ended { + text-align: center; + padding: 30px; + background-color: white; + border-radius: 10px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + margin: auto; + max-width: 500px; + } + + .hunt-ended-icon { + font-size: 48px; + margin-bottom: 20px; + } + + .hunt-ended h2 { + color: #68543c; + margin-bottom: 15px; + } + + .hunt-ended p { + margin-bottom: 20px; + } + .clue-content-wrapper { flex: 1; display: flex; diff --git a/clue_hunt.db b/clue_hunt.db index 9adb727..19a4a3f 100644 Binary files a/clue_hunt.db and b/clue_hunt.db differ