This commit is contained in:
2026-01-10 09:22:28 +07:00
parent 5c452c5c74
commit cf0df928b1

View File

@@ -75,24 +75,26 @@ export function DashboardPage() {
// Check if we should track time: any tracked game is running + active assignment exists // Check if we should track time: any tracked game is running + active assignment exists
const isTrackingAssignment = !!(currentGame && currentAssignment && currentAssignment.status === 'active') const isTrackingAssignment = !!(currentGame && currentAssignment && currentAssignment.status === 'active')
// Sync time to server // Track base minutes at session start to avoid re-adding on each sync
const baseMinutesRef = useRef<number>(0)
// Sync time to server - use refs to avoid dependency issues
const doSyncTime = useCallback(async () => { const doSyncTime = useCallback(async () => {
if (!currentAssignment || !isTrackingAssignment) { const assignment = useMarathonStore.getState().currentAssignment
if (!assignment || assignment.status !== 'active' || !sessionStartRef.current) {
return return
} }
// Calculate total minutes: previous tracked + current session // Calculate session duration only
const sessionDuration = sessionStartRef.current const sessionMinutes = Math.floor((Date.now() - sessionStartRef.current) / 60000)
? Math.floor((Date.now() - sessionStartRef.current) / 60000) const totalMinutes = baseMinutesRef.current + sessionMinutes
: 0
const totalMinutes = currentAssignment.tracked_time_minutes + sessionDuration
if (totalMinutes !== lastSyncedMinutesRef.current && totalMinutes > 0) { if (totalMinutes !== lastSyncedMinutesRef.current && totalMinutes > 0) {
console.log(`[Sync] Syncing ${totalMinutes} minutes for assignment ${currentAssignment.id}`) console.log(`[Sync] Syncing ${totalMinutes} minutes for assignment ${assignment.id} (base: ${baseMinutesRef.current}, session: ${sessionMinutes})`)
await syncTime(totalMinutes) await syncTime(totalMinutes)
lastSyncedMinutesRef.current = totalMinutes lastSyncedMinutesRef.current = totalMinutes
} }
}, [currentAssignment, isTrackingAssignment, syncTime]) }, [syncTime])
useEffect(() => { useEffect(() => {
loadTrackedGames() loadTrackedGames()
@@ -134,16 +136,17 @@ export function DashboardPage() {
useEffect(() => { useEffect(() => {
let localTimerInterval: NodeJS.Timeout | null = null let localTimerInterval: NodeJS.Timeout | null = null
if (isTrackingAssignment) { if (isTrackingAssignment && currentAssignment) {
// Start session if not already started // Start session if not already started
if (!sessionStartRef.current) { if (!sessionStartRef.current) {
sessionStartRef.current = Date.now() sessionStartRef.current = Date.now()
// Store base minutes at session start
baseMinutesRef.current = currentAssignment.tracked_time_minutes || 0
lastSyncedMinutesRef.current = baseMinutesRef.current
console.log(`[Sync] Session started, base minutes: ${baseMinutesRef.current}`)
} }
// Sync immediately when game starts // Setup periodic sync every 60 seconds (don't sync immediately to avoid loops)
doSyncTime()
// Setup periodic sync every 60 seconds
syncIntervalRef.current = setInterval(() => { syncIntervalRef.current = setInterval(() => {
doSyncTime() doSyncTime()
}, 60000) }, 60000)
@@ -157,12 +160,15 @@ export function DashboardPage() {
} else { } else {
// Do final sync when game stops // Do final sync when game stops
if (syncIntervalRef.current) { if (sessionStartRef.current) {
doSyncTime() doSyncTime()
}
if (syncIntervalRef.current) {
clearInterval(syncIntervalRef.current) clearInterval(syncIntervalRef.current)
syncIntervalRef.current = null syncIntervalRef.current = null
sessionStartRef.current = null
} }
sessionStartRef.current = null
baseMinutesRef.current = 0
setLocalSessionSeconds(0) setLocalSessionSeconds(0)
} }
@@ -175,7 +181,9 @@ export function DashboardPage() {
clearInterval(localTimerInterval) clearInterval(localTimerInterval)
} }
} }
}, [isTrackingAssignment, doSyncTime]) // Note: doSyncTime is intentionally excluded to avoid infinite loops
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isTrackingAssignment])
// Toggle monitoring // Toggle monitoring
const toggleMonitoring = async () => { const toggleMonitoring = async () => {
@@ -222,9 +230,11 @@ export function DashboardPage() {
// Playthrough assignment // Playthrough assignment
if (assignment.is_playthrough && assignment.playthrough_info) { if (assignment.is_playthrough && assignment.playthrough_info) {
// Use localSessionSeconds for live display (updates every second) // When actively tracking: use baseMinutesRef (set at session start) + current session
// Otherwise: use tracked_time_minutes from assignment
const baseMinutes = isTrackingAssignment ? baseMinutesRef.current : assignment.tracked_time_minutes
const sessionSeconds = isTrackingAssignment ? localSessionSeconds : 0 const sessionSeconds = isTrackingAssignment ? localSessionSeconds : 0
const totalSeconds = (assignment.tracked_time_minutes * 60) + sessionSeconds const totalSeconds = (baseMinutes * 60) + sessionSeconds
const totalMinutes = Math.floor(totalSeconds / 60) const totalMinutes = Math.floor(totalSeconds / 60)
const trackedHours = totalMinutes / 60 const trackedHours = totalMinutes / 60
const estimatedPoints = Math.floor(trackedHours * 30) const estimatedPoints = Math.floor(trackedHours * 30)