issues regarding the map and hunt stopping have been fixed. leaderboard page needs to be integrated and a clues should be added to be properly processed. after that the css for the application should be developed.

This commit is contained in:
bobbert committed 2025-03-15 01:10:05 +00:00
1 parent c79a9636a7
commit e7ad53643c
12 files changed
+855 -187

No files matched your search

+203 -55
View File
@@ -3,9 +3,7 @@ import { useState, useEffect, useRef } from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import styles from "./map.module.css";
import { useHuntStatusCheck } from "@/hooks/useHuntStatusCheck";
export default function MapPage() {
const { data: session, status } = useSession();
@@ -17,14 +15,63 @@ export default function MapPage() {
const mapContainerRef = useRef(null);
const router = useRouter();
// Use the custom hook to check if hunt is still active
const huntActive = useHuntStatusCheck();
// Add local state for hunt active status
const [huntActive, setHuntActive] = useState(null);
// Debug log for hunt status
// Add function to fetch team hunt start time
const fetchTeamHuntStartTime = async () => {
try {
// First check localStorage for cached start time
const cachedStartTime = localStorage.getItem("teamHuntStartTime");
// If we have a cached time, use it
if (cachedStartTime) {
return cachedStartTime;
}
// Otherwise fetch from the server
if (session?.user?.teamId) {
const response = await fetch(
`/api/teams/${session.user.teamId}/hunt-session`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache, no-store, must-revalidate",
},
credentials: "include",
}
);
if (response.ok) {
const data = await response.json();
if (data.startTime) {
// Cache the start time for future use
localStorage.setItem("teamHuntStartTime", data.startTime);
return data.startTime;
}
}
}
// Fallback to current time if we couldn't get a start time
return new Date().toISOString();
} catch (error) {
console.error("Failed to fetch team hunt start time:", error);
return new Date().toISOString();
}
};
// Initial hunt status check
useEffect(() => {
console.log("Hunt active status in map page:", huntActive);
}, [huntActive]);
// Check if hunt is active
checkHuntAndTimer();
// Set up periodic checks
const interval = setInterval(checkHuntAndTimer, 3000);
return () => clearInterval(interval);
}, []);
// Handle authentication and hunt status
useEffect(() => {
// Only redirect if explicitly not authenticated
if (status === "unauthenticated") {
@@ -45,31 +92,89 @@ export default function MapPage() {
}
}, [status, huntActive, router]);
// Update elapsed time every second
// Function to check hunt status and timer
async function checkHuntAndTimer() {
try {
const response = await fetch("/api/hunt-status", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache, no-store, must-revalidate",
},
credentials: "include",
});
if (response.ok) {
const data = await response.json();
console.log("Hunt status check from map:", data);
// If hunt is not active, check if it has ended
if (!data.active) {
console.log("Hunt not active, checking if it ended...");
// Check if we had an active hunt before (using localStorage)
const wasActive = localStorage.getItem("teamHuntStartTime");
if (wasActive) {
// Hunt was active before but not now - it ended
console.log("Hunt has ended, redirecting to leaderboard");
localStorage.removeItem("teamHuntStartTime"); // Clean up
window.location.href = "/leaderboard?huntEnded=true";
return;
}
// Otherwise just show the waiting page
setHuntActive(false);
router.push("/waiting");
return;
}
// If timer endTime exists and has passed, redirect to leaderboard
if (data.endTime && new Date(data.endTime) <= new Date()) {
console.log("Timer expired, redirecting to leaderboard");
// Clean up the storage
localStorage.removeItem("teamHuntStartTime");
router.push("/leaderboard?huntEnded=true");
return;
}
// Otherwise, hunt is active and timer is running
setHuntActive(true);
}
} catch (err) {
console.error("Failed to check hunt status:", err);
}
}
// Use team hunt start time for elapsed time calculation
useEffect(() => {
if (!teamData?.team) return;
const startTime = new Date(teamData?.huntStartTime || Date.now()).getTime();
let startTimePromise = fetchTeamHuntStartTime();
startTimePromise.then((startTimeStr) => {
// Parse the ISO string to milliseconds
const startTime = new Date(startTimeStr).getTime();
const timer = setInterval(() => {
const now = Date.now();
const elapsed = Math.floor((now - startTime) / 1000);
const timer = setInterval(() => {
const now = Date.now();
const elapsed = Math.floor((now - startTime) / 1000);
const hours = Math.floor(elapsed / 3600)
.toString()
.padStart(2, "0");
const minutes = Math.floor((elapsed % 3600) / 60)
.toString()
.padStart(2, "0");
const seconds = Math.floor(elapsed % 60)
.toString()
.padStart(2, "0");
const hours = Math.floor(elapsed / 3600)
.toString()
.padStart(2, "0");
const minutes = Math.floor((elapsed % 3600) / 60)
.toString()
.padStart(2, "0");
const seconds = Math.floor(elapsed % 60)
.toString()
.padStart(2, "0");
setElapsedTime(`${hours}:${minutes}:${seconds}`);
}, 1000);
setElapsedTime(`${hours}:${minutes}:${seconds}`);
}, 1000);
return () => clearInterval(timer);
}, [teamData]);
return () => clearInterval(timer);
});
}, [teamData, session]);
const fetchTeamData = async () => {
try {
@@ -119,6 +224,56 @@ export default function MapPage() {
}
};
// Check hunt status every second to ensure immediate updates
useEffect(() => {
const checkHuntStatus = async () => {
try {
const timestamp = Date.now(); // Add cache-busting timestamp
const response = await fetch(`/api/hunt-status?t=${timestamp}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store, no-cache, must-revalidate",
Pragma: "no-cache",
},
credentials: "include",
});
if (!response.ok) {
console.error("Failed to fetch hunt status:", response.statusText);
return;
}
const data = await response.json();
console.log("Map page hunt status check:", data);
// If hunt is not active, force redirect to waiting page
if (!data.active) {
console.log("Hunt not active, redirecting to waiting page");
window.location.href = "/waiting";
return;
}
// If timer endTime exists and has passed, redirect to leaderboard
if (data.endTime && new Date(data.endTime) <= new Date()) {
console.log("Timer expired, redirecting to leaderboard");
window.location.href = "/leaderboard?huntEnded=true";
return;
}
// Otherwise, hunt is active and timer is running
setHuntActive(true);
} catch (err) {
console.error("Failed to check hunt status:", err);
}
};
// Check immediately and every second
checkHuntStatus();
const intervalId = setInterval(checkHuntStatus, 1000);
return () => clearInterval(intervalId);
}, []);
if (status === "loading" || loading) {
return (
<div className={styles["loading-container"]}>
@@ -174,42 +329,20 @@ export default function MapPage() {
<div className={styles["team-code"]}>{teamData?.team?.code || ""}</div>
</div>
{/* Main map content area */}
{/* Main map content area - showing map as background */}
<div className={styles["map-container"]} ref={mapContainerRef}>
<div className={styles["treasure-map"]}>
{/* Use Image component with fill layout for better SVG handling */}
<Image
src="/images/treasure-map.svg"
alt="Treasure Map"
className={styles["map-svg"]}
width={1000}
height={1000}
priority
onError={() => {
// Fallback to a static image if SVG fails to load
setMapError(true);
}}
/>
{/* Using background-image for the map SVG */}
<div
className={styles["map-background"]}
role="img"
aria-label="Treasure Map"
></div>
</div>
</div>
{/* Navigation bar - now sticky */}
{/* Navigation bar - Map as first button */}
<nav className={styles["nav-bar"]}>
<Link
href={teamData?.teamId ? `/${teamData.teamId}` : "/"}
className={styles["nav-item"]}
>
<div className={styles["nav-icon"]}>🏠</div>
<div className={styles["nav-label"]}>Home</div>
</Link>
<Link href="/leaderboard" className={styles["nav-item"]}>
<div className={styles["nav-icon"]}>📊</div>
<div className={styles["nav-label"]}>Stats</div>
</Link>
<Link href="/team-selection" className={styles["nav-item"]}>
<div className={styles["nav-icon"]}>👥</div>
<div className={styles["nav-label"]}>Team</div>
</Link>
<Link
href="/map"
className={`${styles["nav-item"]} ${styles["active"]}`}
@@ -217,6 +350,21 @@ export default function MapPage() {
<div className={styles["nav-icon"]}>🗺️</div>
<div className={styles["nav-label"]}>Map</div>
</Link>
<Link href="/leaderboard" className={styles["nav-item"]}>
<div className={styles["nav-icon"]}>📊</div>
<div className={styles["nav-label"]}>Stats</div>
</Link>
<Link
href={teamData?.team ? `/${teamData.team.id}` : "/team-selection"}
className={styles["nav-item"]}
>
<div className={styles["nav-icon"]}>👥</div>
<div className={styles["nav-label"]}>Team</div>
</Link>
<Link href="/team-selection" className={styles["nav-item"]}>
<div className={styles["nav-icon"]}>🔙</div>
<div className={styles["nav-label"]}>Back</div>
</Link>
</nav>
</div>
);