fixed elapsed timer on clue page cannot fix it on admin page. I am done. now to move on to the styling to match UI

This commit is contained in:
bobbert committed 2025-03-18 23:09:57 +00:00
1 parent 4cd449c0cf
commit 8fd2cabbe0
10 files changed
+226 -136

No files matched your search

+65 -42
View File
@@ -7,6 +7,7 @@ import {
clearHuntTimer, clearHuntTimer,
getHuntTimer, getHuntTimer,
formatElapsedTime, formatElapsedTime,
syncTimerWithServer,
} from "@/utils/timerService"; } from "@/utils/timerService";
export default function AdminDashboard() { export default function AdminDashboard() {
@@ -14,7 +15,6 @@ export default function AdminDashboard() {
const [timerActive, setTimerActive] = useState(false); const [timerActive, setTimerActive] = useState(false);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [elapsedTime, setElapsedTime] = useState(0); // in seconds
const [timerDisplay, setTimerDisplay] = useState("--:--:--"); const [timerDisplay, setTimerDisplay] = useState("--:--:--");
const [presetHours, setPresetHours] = useState(""); const [presetHours, setPresetHours] = useState("");
const [presetMinutes, setPresetMinutes] = useState(""); const [presetMinutes, setPresetMinutes] = useState("");
@@ -22,6 +22,7 @@ export default function AdminDashboard() {
const [timerEndTime, setTimerEndTime] = useState(null); const [timerEndTime, setTimerEndTime] = useState(null);
const timerRef = useRef(null); const timerRef = useRef(null);
const timerIntervalRef = useRef(null); const timerIntervalRef = useRef(null);
const timerStartTimeRef = useRef(null); // New ref to store the start time
const router = useRouter(); const router = useRouter();
// Load timer status on page load // Load timer status on page load
@@ -33,8 +34,12 @@ export default function AdminDashboard() {
// First, check if we already have timer data in localStorage // First, check if we already have timer data in localStorage
const existingTimer = getHuntTimer(); const existingTimer = getHuntTimer();
if (existingTimer && existingTimer.startTime) { if (existingTimer && existingTimer.startTime) {
// Store the start time in the ref to prevent it from being lost on re-renders
timerStartTimeRef.current = existingTimer.startTime;
// If we have timer data, immediately set the display // If we have timer data, immediately set the display
setTimerActive(existingTimer.isActive); setTimerActive(existingTimer.isActive !== false);
setTimerDisplay(formatElapsedTime(existingTimer.startTime));
if (existingTimer.endTime) { if (existingTimer.endTime) {
setTimerEndTime(new Date(existingTimer.endTime).getTime()); setTimerEndTime(new Date(existingTimer.endTime).getTime());
} }
@@ -61,10 +66,15 @@ export default function AdminDashboard() {
if (isActive && data.startTime) { if (isActive && data.startTime) {
setTimerActive(true); setTimerActive(true);
// Store the start time in the ref for persistence across renders
timerStartTimeRef.current = data.startTime;
// Store the start time in the centralized service // Store the start time in the centralized service
setHuntTimer(data.startTime, data.endTime); setHuntTimer(data.startTime, data.endTime, true); // Explicitly set isActive=true
// Update the display immediately with the correct time
setTimerDisplay(formatElapsedTime(data.startTime));
// If there's an end time, set it
if (data.endTime) { if (data.endTime) {
setTimerEndTime(new Date(data.endTime).getTime()); setTimerEndTime(new Date(data.endTime).getTime());
} else { } else {
@@ -72,10 +82,12 @@ export default function AdminDashboard() {
} }
} else { } else {
// Only clear local timer if server explicitly says timer is not active // Only clear local timer if server explicitly says timer is not active
// This prevents clearing a valid timer if the server response is ambiguous
if (data.active === false && data.timerActive === false) { if (data.active === false && data.timerActive === false) {
clearHuntTimer(); clearHuntTimer();
timerStartTimeRef.current = null; // Clear the start time ref
setTimerActive(false); setTimerActive(false);
setTimerDisplay("--:--:--");
setTimerEndTime(null);
} }
} }
@@ -111,48 +123,53 @@ export default function AdminDashboard() {
} }
}, [session, status, router]); }, [session, status, router]);
// Create a separate effect for updating the timer display // Create a separate effect for updating the timer display - but only have one instance
useEffect(() => { useEffect(() => {
let timerId; // Clear any existing interval to avoid duplicates
if (timerIntervalRef.current) {
clearInterval(timerIntervalRef.current);
timerIntervalRef.current = null;
}
// Initialize timer const updateTimer = () => {
async function initializeTimer() { // Use the ref to access the persisted start time
// Set up timer updates const startTime = timerStartTimeRef.current;
const updateTimer = () => {
const timerData = getHuntTimer();
if (timerData && timerData.startTime) {
setTimerDisplay(formatElapsedTime(timerData.startTime));
// If there's an end time, check if we've reached it if (timerActive && startTime) {
if (timerEndTime && Date.now() >= timerEndTime) { // Format time using the start time from our ref, which persists across renders
stopTimer(); setTimerDisplay(formatElapsedTime(startTime));
}
} else { // Check if timer has ended
setTimerDisplay("--:--:--"); if (timerEndTime && Date.now() >= timerEndTime) {
stopTimer();
} }
}; } else if (timerActive) {
// If timer is active but no data, try to restore
// Initial update syncTimerWithServer().then((result) => {
updateTimer(); if (result.success && result.data) {
timerStartTimeRef.current = result.data.startTime;
// Update every second }
timerId = setInterval(updateTimer, 1000); });
} } else {
setTimerDisplay("--:--:--");
// Only initialize timer if the timer is active
if (timerActive) {
initializeTimer();
} else {
setTimerDisplay("--:--:--");
}
// Clean up interval on unmount
return () => {
if (timerId) {
clearInterval(timerId);
} }
}; };
}, [timerActive, timerEndTime]);
// Initial update
updateTimer();
// Set up interval for timer updates
if (timerActive) {
timerIntervalRef.current = setInterval(updateTimer, 1000);
}
return () => {
if (timerIntervalRef.current) {
clearInterval(timerIntervalRef.current);
timerIntervalRef.current = null;
}
};
}, [timerActive, timerEndTime]); // Include timerEndTime as a dependency
// Add a function to fetch timer status directly from the server // Add a function to fetch timer status directly from the server
const fetchTimerStatusFromServer = async () => { const fetchTimerStatusFromServer = async () => {
@@ -177,8 +194,8 @@ export default function AdminDashboard() {
if (isActive && data.startTime) { if (isActive && data.startTime) {
setTimerActive(true); setTimerActive(true);
timerStartTimeRef.current = data.startTime; // Update the ref
setHuntTimer(data.startTime, data.endTime); setHuntTimer(data.startTime, data.endTime);
if (data.endTime) { if (data.endTime) {
setTimerEndTime(new Date(data.endTime).getTime()); setTimerEndTime(new Date(data.endTime).getTime());
} else { } else {
@@ -189,6 +206,7 @@ export default function AdminDashboard() {
} else if (data.active === false && data.timerActive === false) { } else if (data.active === false && data.timerActive === false) {
// Only clear timer if server explicitly says timer is inactive // Only clear timer if server explicitly says timer is inactive
setTimerActive(false); setTimerActive(false);
timerStartTimeRef.current = null; // Clear the ref
clearHuntTimer(); clearHuntTimer();
setTimerDisplay("--:--:--"); setTimerDisplay("--:--:--");
} }
@@ -250,6 +268,9 @@ export default function AdminDashboard() {
if (data.success) { if (data.success) {
setTimerActive(true); setTimerActive(true);
// Store the start time in the ref
timerStartTimeRef.current = data.startTime;
if (endTime) { if (endTime) {
setTimerEndTime(new Date(endTime).getTime()); setTimerEndTime(new Date(endTime).getTime());
} else { } else {
@@ -293,6 +314,7 @@ export default function AdminDashboard() {
if (data.success) { if (data.success) {
setTimerActive(false); setTimerActive(false);
timerStartTimeRef.current = null; // Clear the ref
setTimerEndTime(null); setTimerEndTime(null);
setTimerDisplay("--:--:--"); setTimerDisplay("--:--:--");
@@ -465,6 +487,7 @@ export default function AdminDashboard() {
padding: 1rem; padding: 1rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
align-items: center; align-items: center;
padding-bottom: 70px; /* Add space for the navbar */ padding-bottom: 70px; /* Add space for the navbar */
} }
+81 -27
View File
@@ -47,6 +47,7 @@ export default function CluePage({ params }) {
const notificationTimeoutRef = useRef(null); const notificationTimeoutRef = useRef(null);
const navErrorTimeoutRef = useRef(null); const navErrorTimeoutRef = useRef(null);
const timerIdRef = useRef(null); const timerIdRef = useRef(null);
const timerInitializedRef = useRef(false); // Add this ref to track if timer is already initialized
// Check if user is admin // Check if user is admin
const isAdmin = session?.user?.role === "admin"; const isAdmin = session?.user?.role === "admin";
@@ -194,7 +195,7 @@ export default function CluePage({ params }) {
}; };
}, [router, previousHuntState]); }, [router, previousHuntState]);
// Function to ensure we have timer data from the server // Function to ensure we have timer data from the server - only if needed
async function fetchTeamHuntStartTime() { async function fetchTeamHuntStartTime() {
try { try {
// First try to get any existing timer data // First try to get any existing timer data
@@ -204,6 +205,7 @@ export default function CluePage({ params }) {
} }
// If no timer data exists, sync with server // If no timer data exists, sync with server
console.log("No timer data found, syncing with server...");
const result = await syncTimerWithServer(); const result = await syncTimerWithServer();
return result.success; return result.success;
} catch (err) { } catch (err) {
@@ -212,44 +214,53 @@ export default function CluePage({ params }) {
} }
} }
// Updated timer effect that follows the same pattern as the map page // Updated timer effect with protection against multiple initializations
useEffect(() => { useEffect(() => {
// Prevent multiple initializations of the timer
if (timerInitializedRef.current) {
return;
}
// Initialize timer // Initialize timer
async function initializeTimer() { async function initializeTimer() {
// First ensure we have timer data // First ensure we have timer data
await fetchTeamHuntStartTime(); await fetchTeamHuntStartTime();
// Set up timer updates // Set up timer updates if not already set
const updateTimer = () => { if (!timerIdRef.current) {
const timerData = getHuntTimer(); const updateTimer = () => {
if (timerData && timerData.startTime) { const timerData = getHuntTimer();
setElapsedTime(formatElapsedTime(timerData.startTime)); if (timerData && timerData.startTime) {
setElapsedTime(formatElapsedTime(timerData.startTime));
// Check if timer has ended (if there's an endTime) // Check if timer has ended (if there's an endTime)
if (timerData.endTime) { if (timerData.endTime) {
const now = Date.now(); const now = Date.now();
const endTime = new Date(timerData.endTime).getTime(); const endTime = new Date(timerData.endTime).getTime();
// If current time is past the end time, hunt has ended // If current time is past the end time, hunt has ended
if (now >= endTime) { if (now >= endTime) {
setHuntActive(false); setHuntActive(false);
clearInterval(timerIdRef.current); clearInterval(timerIdRef.current);
timerIdRef.current = null;
// Redirect to leaderboard with huntEnded flag // Redirect to leaderboard with huntEnded flag
console.log( console.log(
"Hunt end time reached! Redirecting to leaderboard..." "Hunt end time reached! Redirecting to leaderboard..."
); );
router.push("/leaderboard?huntEnded=true"); router.push("/leaderboard?huntEnded=true");
}
} }
} }
} };
};
// Initial update // Initial update
updateTimer(); updateTimer();
// Update every second // Update every second
timerIdRef.current = setInterval(updateTimer, 1000); timerIdRef.current = setInterval(updateTimer, 1000);
timerInitializedRef.current = true;
}
} }
initializeTimer(); initializeTimer();
@@ -259,9 +270,52 @@ export default function CluePage({ params }) {
if (timerIdRef.current) { if (timerIdRef.current) {
clearInterval(timerIdRef.current); clearInterval(timerIdRef.current);
timerIdRef.current = null; timerIdRef.current = null;
timerInitializedRef.current = false;
} }
}; };
}, [router]); }, [router]); // Only depend on router
// Make sure hunt status check doesn't interfere with the timer
useEffect(() => {
// This function should NOT reset the timer or recreate it
async function checkHuntStatus() {
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();
// Only update hunt state, don't touch the timer
const newHuntState = !!data.active;
// If hunt was active but is now inactive, redirect to leaderboard
if (previousHuntState === true && newHuntState === false) {
console.log("Hunt has ended! Redirecting to leaderboard...");
router.push("/leaderboard?huntEnded=true");
return;
}
// Update the previous hunt state for next comparison
setPreviousHuntState(newHuntState);
}
} catch (err) {
console.error("Failed to check hunt status:", err);
}
}
const intervalId = setInterval(checkHuntStatus, 5000);
return () => {
clearInterval(intervalId);
};
}, [router, previousHuntState]);
// Fetch team data on component mount // Fetch team data on component mount
useEffect(() => { useEffect(() => {
+20 -3
View File
@@ -27,6 +27,8 @@ export default function MapPage() {
// Add local state for hunt active status // Add local state for hunt active status
const [huntActive, setHuntActive] = useState(null); const [huntActive, setHuntActive] = useState(null);
// Add previous hunt state to detect when hunt ends
const [previousHuntState, setPreviousHuntState] = useState(null);
// Use useCallback for functions called in useEffect to avoid dependency issues // Use useCallback for functions called in useEffect to avoid dependency issues
const softRedirect = useCallback( const softRedirect = useCallback(
@@ -293,6 +295,18 @@ export default function MapPage() {
} }
}, [status, fetchTeamData]); }, [status, fetchTeamData]);
// Add this useEffect to detect when hunt ends
useEffect(() => {
// If previous state was active but current state is not active, the hunt just ended
if (previousHuntState === true && huntActive === false) {
console.log("Hunt has ended! Redirecting to leaderboard...");
router.push("/leaderboard?huntEnded=true");
}
// Update previous state after checking
setPreviousHuntState(huntActive);
}, [huntActive, previousHuntState, router]);
if (status === "loading" || loading) { if (status === "loading" || loading) {
return ( return (
<div className={styles["loading-container"]}> <div className={styles["loading-container"]}>
@@ -302,8 +316,11 @@ export default function MapPage() {
); );
} }
// If hunt status is known to be inactive, show message // Remove or modify this conditional block since we're now handling hunt ending differently
if (huntActive === false) { // Instead of showing an error message, we'll redirect in the useEffect
if (huntActive === false && previousHuntState === null) {
// Only show this if we just landed on the page and hunt is inactive
// (not when the hunt just ended)
return ( return (
<div className={styles["map-error"]}> <div className={styles["map-error"]}>
<h2>Hunt Not Active</h2> <h2>Hunt Not Active</h2>
@@ -371,7 +388,7 @@ export default function MapPage() {
{/* Map Banner with team info and timer */} {/* Map Banner with team info and timer */}
<div className={styles["map-banner"]}> <div className={styles["map-banner"]}>
<div className={styles["team-name"]}> <div className={styles["team-name"]}>
{teamData?.team?.name || "Loading..."} Team Name: {teamData?.team?.name || "Loading..."}
</div> </div>
<div className={styles["elapsed-time"]}> <div className={styles["elapsed-time"]}>
<span className={styles["time-value"]}>{elapsedTime}</span> <span className={styles["time-value"]}>{elapsedTime}</span>
+2 -13
View File
@@ -1,7 +1,7 @@
import { query, queryOne, run } from "@/lib/db"; import { query, queryOne, run } from "@/lib/db";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
export const dynamic = "force-dynamic"; // Disable caching export const dynamic = "force-dynamic";
export async function GET(request) { export async function GET(request) {
try { try {
@@ -17,7 +17,6 @@ export async function GET(request) {
return Response.json({ error: "Unauthorized" }, { status: 401 }); return Response.json({ error: "Unauthorized" }, { status: 401 });
} }
// Check if user has a team
if (!token.teamId) { if (!token.teamId) {
console.log("API: User without team tried to access clues"); console.log("API: User without team tried to access clues");
return Response.json( return Response.json(
@@ -26,9 +25,7 @@ export async function GET(request) {
); );
} }
// First, ensure the team_clues table has the 'skipped' column
try { try {
// Check if team_clues table exists, create it if it doesn't
await run(` await run(`
CREATE TABLE IF NOT EXISTS team_clues ( CREATE TABLE IF NOT EXISTS team_clues (
team_id INTEGER NOT NULL, team_id INTEGER NOT NULL,
@@ -40,7 +37,6 @@ export async function GET(request) {
) )
`); `);
// Check if skipped column exists
const tableInfo = await query("PRAGMA table_info(team_clues)"); const tableInfo = await query("PRAGMA table_info(team_clues)");
const hasSkippedColumn = tableInfo.some((col) => col.name === "skipped"); const hasSkippedColumn = tableInfo.some((col) => col.name === "skipped");
@@ -52,18 +48,14 @@ export async function GET(request) {
} }
} catch (schemaError) { } catch (schemaError) {
console.error("Error ensuring database schema:", schemaError); console.error("Error ensuring database schema:", schemaError);
// Continue execution - if this fails, the main query might still work
// with existing columns
} }
// Get all clues with explicit ordering
const clues = await query(` const clues = await query(`
SELECT id, title, description, location SELECT id, title, description, location
FROM clues FROM clues
ORDER BY id ASC ORDER BY id ASC
`); `);
// For each clue, check if the team has found it
const cluesWithStatus = await Promise.all( const cluesWithStatus = await Promise.all(
clues.map(async (clue) => { clues.map(async (clue) => {
let clueStatus; let clueStatus;
@@ -74,7 +66,6 @@ export async function GET(request) {
); );
} catch (error) { } catch (error) {
console.error(`Error querying status for clue ${clue.id}:`, error); console.error(`Error querying status for clue ${clue.id}:`, error);
// Fallback if query fails (e.g., skipped column still doesn't exist)
clueStatus = await queryOne( clueStatus = await queryOne(
"SELECT found_at FROM team_clues WHERE team_id = ? AND clue_id = ?", "SELECT found_at FROM team_clues WHERE team_id = ? AND clue_id = ?",
[token.teamId, clue.id] [token.teamId, clue.id]
@@ -85,19 +76,17 @@ export async function GET(request) {
...clue, ...clue,
found: !!clueStatus && !(clueStatus.skipped === 1), found: !!clueStatus && !(clueStatus.skipped === 1),
skipped: !!clueStatus && clueStatus.skipped === 1, skipped: !!clueStatus && clueStatus.skipped === 1,
available: false, // Will be determined after processing all clues available: false,
}; };
}) })
); );
// Determine which clues should be available
let firstUnavailableFound = false; let firstUnavailableFound = false;
const finalClues = cluesWithStatus.map((clue, index, arr) => { const finalClues = cluesWithStatus.map((clue, index, arr) => {
if (clue.found || clue.skipped) { if (clue.found || clue.skipped) {
return { ...clue, available: true }; return { ...clue, available: true };
} }
// If we haven't found an unavailable clue yet, this one is available
if (!firstUnavailableFound) { if (!firstUnavailableFound) {
firstUnavailableFound = true; firstUnavailableFound = true;
return { ...clue, available: true }; return { ...clue, available: true };
-9
View File
@@ -3,7 +3,6 @@ import { queryOne, run } from "@/lib/db";
export async function POST(request) { export async function POST(request) {
try { try {
// Verify user authentication
const token = await getToken({ const token = await getToken({
req: request, req: request,
secret: secret:
@@ -15,20 +14,16 @@ export async function POST(request) {
return Response.json({ error: "Unauthorized" }, { status: 401 }); return Response.json({ error: "Unauthorized" }, { status: 401 });
} }
// Get user ID from token
const userId = token.id; const userId = token.id;
// Get team name from request body
const { name } = await request.json(); const { name } = await request.json();
if (!name || !name.trim()) { if (!name || !name.trim()) {
return Response.json({ error: "Team name is required" }, { status: 400 }); return Response.json({ error: "Team name is required" }, { status: 400 });
} }
// Generate a unique team code
const code = generateTeamCode(); const code = generateTeamCode();
// Create teams table if it doesn't exist
await run(` await run(`
CREATE TABLE IF NOT EXISTS teams ( CREATE TABLE IF NOT EXISTS teams (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -38,7 +33,6 @@ export async function POST(request) {
) )
`); `);
// Check if team name already exists
const existingTeam = await queryOne("SELECT id FROM teams WHERE name = ?", [ const existingTeam = await queryOne("SELECT id FROM teams WHERE name = ?", [
name, name,
]); ]);
@@ -50,7 +44,6 @@ export async function POST(request) {
); );
} }
// Insert team
const result = await run("INSERT INTO teams (name, code) VALUES (?, ?)", [ const result = await run("INSERT INTO teams (name, code) VALUES (?, ?)", [
name, name,
code, code,
@@ -61,10 +54,8 @@ export async function POST(request) {
throw new Error("Failed to create team - no ID returned"); throw new Error("Failed to create team - no ID returned");
} }
// Update the user's team_id
await run("UPDATE users SET team_id = ? WHERE id = ?", [teamId, userId]); await run("UPDATE users SET team_id = ? WHERE id = ?", [teamId, userId]);
// Return success with team info
return Response.json({ return Response.json({
success: true, success: true,
teamId, teamId,
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -9,7 +9,7 @@ export default function CreateTeamForm({ onSubmit }) {
e.preventDefault(); e.preventDefault();
if (!name.trim()) { if (!name.trim()) {
return; // Don't submit if name is empty return;
} }
setSubmitting(true); setSubmitting(true);
+32 -32
View File
@@ -1,38 +1,38 @@
'use client'; "use client";
import { useSession } from 'next-auth/react'; import { useSession } from "next-auth/react";
import { useState, useEffect } from 'react'; import { useState, useEffect } from "react";
export default function TeamHeader() { export default function TeamHeader() {
const { data: session } = useSession(); const { data: session } = useSession();
const [teamName, setTeamName] = useState(null); const [teamName, setTeamName] = useState(null);
useEffect(() => { useEffect(() => {
// Only fetch team info if user is logged in and not an admin // Only fetch team info if user is logged in and not an admin
if (session?.user?.teamId && session?.user?.role !== 'admin') { if (session?.user?.teamId && session?.user?.role !== "admin") {
fetchTeamInfo(); fetchTeamInfo();
}
}, [session]);
const fetchTeamInfo = async () => {
try {
const response = await fetch(`/api/teams/${session.user.teamId}`);
if (response.ok) {
const data = await response.json();
setTeamName(data.name);
}
} catch (error) {
console.error('Failed to fetch team info:', error);
}
};
// Don't render anything if no team name or user is admin
if (!teamName || session?.user?.role === 'admin') {
return null;
} }
}, [session]);
return ( const fetchTeamInfo = async () => {
<div className="team-header"> try {
<div className="team-name">{teamName}</div> const response = await fetch(`/api/teams/${session.user.teamId}`);
</div> if (response.ok) {
); const data = await response.json();
setTeamName(data.name);
}
} catch (error) {
console.error("Failed to fetch team info:", error);
}
};
// Don't render anything if no team name or user is admin
if (!teamName || session?.user?.role === "admin") {
return null;
}
return (
<div className="team-header">
<div className="team-name">Team Name: {teamName}</div>
</div>
);
} }
-7
View File
@@ -1,20 +1,16 @@
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
// Get JWT secret from environment or use fallback
const JWT_SECRET = const JWT_SECRET =
process.env.NEXTAUTH_SECRET || process.env.NEXTAUTH_SECRET ||
process.env.JWT_SECRET || process.env.JWT_SECRET ||
"your-fallback-secret-should-be-at-least-32-chars"; "your-fallback-secret-should-be-at-least-32-chars";
// Create a new JWT token
export function createToken(payload) { export function createToken(payload) {
// Check if payload already has an expiration date set
const options = payload.exp ? {} : { expiresIn: "30d" }; const options = payload.exp ? {} : { expiresIn: "30d" };
return jwt.sign(payload, JWT_SECRET, options); return jwt.sign(payload, JWT_SECRET, options);
} }
// Verify a JWT token
export function verifyToken(token) { export function verifyToken(token) {
try { try {
if (!token) { if (!token) {
@@ -26,13 +22,11 @@ export function verifyToken(token) {
const decoded = jwt.verify(token, JWT_SECRET); const decoded = jwt.verify(token, JWT_SECRET);
// Validate that we have the minimum required fields
if (!decoded.sub && !decoded.id) { if (!decoded.sub && !decoded.id) {
console.error("Token missing required user identifier"); console.error("Token missing required user identifier");
return null; return null;
} }
// Log successful token verification with user ID
console.log( console.log(
"Token verified successfully for user:", "Token verified successfully for user:",
decoded.sub || decoded.id decoded.sub || decoded.id
@@ -40,7 +34,6 @@ export function verifyToken(token) {
return decoded; return decoded;
} catch (error) { } catch (error) {
// Provide detailed error logs
if (error.name === "TokenExpiredError") { if (error.name === "TokenExpiredError") {
console.error("Token expired at:", error.expiredAt); console.error("Token expired at:", error.expiredAt);
} else if (error.name === "JsonWebTokenError") { } else if (error.name === "JsonWebTokenError") {
+25 -2
View File
@@ -154,18 +154,41 @@ export const syncTimerWithServer = async () => {
} }
const data = await response.json(); const data = await response.json();
console.log("Timer sync: Server returned data:", data);
// Don't change timer if we already have it and server confirms it's active
const existingTimer = getHuntTimer();
if (existingTimer && existingTimer.startTime && data.active) {
console.log("Timer sync: Using existing timer data, confirmed by server");
return {
success: true,
data: {
startTime: existingTimer.startTime,
endTime: existingTimer.endTime || data.endTime,
active: true,
},
};
}
if (data.active && data.startTime) { if (data.active && data.startTime) {
setHuntTimer(data.startTime, data.endTime); console.log(
"Timer sync: Setting timer with startTime:",
new Date(data.startTime).toISOString()
);
// Ensure isActive is explicitly true
setHuntTimer(data.startTime, data.endTime, true);
return { return {
success: true, success: true,
data: { data: {
startTime: data.startTime, startTime: data.startTime,
endTime: data.endTime, endTime: data.endTime,
active: data.active, active: true,
}, },
}; };
} else { } else {
console.log("Timer sync: Clearing timer as server reports inactive");
clearHuntTimer(); clearHuntTimer();
return { success: true, data: null }; return { success: true, data: null };
} }