/** * Centralized timer service to ensure consistent timing across the application */ // Store the hunt timer information in localStorage for persistence export const setHuntTimer = (startTime, endTime = null, isActive = true) => { if (!startTime) { console.error("Cannot set timer without a start time"); return false; } try { const timerData = { startTime, endTime, isActive, lastUpdated: new Date().toISOString(), }; localStorage.setItem("huntTimerData", JSON.stringify(timerData)); // Also save to sessionStorage as a fallback // This can survive page refreshes even if localStorage is cleared sessionStorage.setItem("huntTimerData", JSON.stringify(timerData)); return true; } catch (error) { console.error("Error setting hunt timer data:", error); return false; } }; // Get the current hunt timer data with improved error handling export const getHuntTimer = () => { try { // First try to get timer data from localStorage let timerData = localStorage.getItem("huntTimerData"); // If not in localStorage, try sessionStorage as fallback if (!timerData) { timerData = sessionStorage.getItem("huntTimerData"); } if (!timerData) { // Try to migrate from old format if exists const legacyStartTime = localStorage.getItem("teamHuntStartTime"); if (legacyStartTime) { console.log("Using legacy team hunt start time"); const migratedData = { startTime: legacyStartTime, endTime: null, isActive: true, lastUpdated: new Date().toISOString(), }; // Save the migrated data for future use localStorage.setItem("huntTimerData", JSON.stringify(migratedData)); sessionStorage.setItem("huntTimerData", JSON.stringify(migratedData)); return migratedData; } return null; } // Parse the timer data const parsedData = JSON.parse(timerData); // Validate the data has required fields if (!parsedData || !parsedData.startTime) { console.log("Invalid hunt timer data", parsedData); return null; } return parsedData; } catch (error) { console.error("Error parsing hunt timer data:", error); // Try to recover by clearing corrupt data try { localStorage.removeItem("huntTimerData"); sessionStorage.removeItem("huntTimerData"); } catch (e) { // Ignore errors when trying to clean up } return null; } }; // Clear the hunt timer completely export const clearHuntTimer = () => { try { localStorage.removeItem("huntTimerData"); sessionStorage.removeItem("huntTimerData"); localStorage.removeItem("teamHuntStartTime"); // Remove legacy timer as well return true; } catch (error) { console.error("Error clearing hunt timer data:", error); return false; } }; // Calculate and format elapsed time from a given start time with high accuracy export const formatElapsedTime = (startTimeStr) => { if (!startTimeStr) return "00:00:00"; try { const startTime = new Date(startTimeStr).getTime(); // Validate that we have a valid timestamp if (isNaN(startTime)) { console.error("Invalid start time:", startTimeStr); return "00:00:00"; } const now = Date.now(); const elapsed = Math.floor((now - startTime) / 1000); // Handle negative time (shouldn't happen in normal usage) if (elapsed < 0) { console.warn("Negative elapsed time detected:", elapsed); return "00:00:00"; } 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"); return `${hours}:${minutes}:${seconds}`; } catch (error) { console.error("Error formatting elapsed time:", error); return "00:00:00"; } }; // Function to fetch current timer state from server and update local storage export const syncTimerWithServer = async () => { try { const response = await fetch("/api/hunt/timer", { credentials: "include", headers: { "Cache-Control": "no-cache, no-store, must-revalidate", Pragma: "no-cache", }, }); if (!response.ok) { console.error("Server responded with status:", response.status); return { success: false, error: `Server error: ${response.status}` }; } 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) { 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 { success: true, data: { startTime: data.startTime, endTime: data.endTime, active: true, }, }; } else { console.log("Timer sync: Clearing timer as server reports inactive"); clearHuntTimer(); return { success: true, data: null }; } } catch (error) { console.error("Error syncing timer with server:", error); return { success: false, error: error.message }; } }; // Hook function to use in components to get formatted elapsed time export const useElapsedTime = (startTime, isActive = true) => { if (!startTime || !isActive) return "00:00:00"; return formatElapsedTime(startTime); };