Files
peworkshop/utils/timerService.js

206 lines
5.9 KiB
JavaScript

/**
* 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);
};