mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 11:54:59 -04:00
fix(#98): rearm capped race hub refresh
This commit is contained in:
@@ -145,11 +145,13 @@ export function formatSessionScheduleTime(value: string): string {
|
||||
})
|
||||
}
|
||||
|
||||
export const MAX_BROWSER_TIMEOUT = 2_147_483_647
|
||||
|
||||
export function refreshDeadlineDelay(refreshAt: string | undefined, now = Date.now()): number | null {
|
||||
if (!refreshAt) return null
|
||||
const deadline = Date.parse(refreshAt)
|
||||
if (Number.isNaN(deadline)) return null
|
||||
return Math.min(Math.max(0, deadline - now), 2_147_483_647)
|
||||
return Math.min(Math.max(0, deadline - now), MAX_BROWSER_TIMEOUT)
|
||||
}
|
||||
|
||||
export type FocusMeetingKind = 'current' | 'next' | 'recent' | 'fallback'
|
||||
|
||||
@@ -17,6 +17,7 @@ import { SourceBadge } from '../components/SourceBadge'
|
||||
import { countryAccent, countryDecal, formatGpDateRange } from '../lib/gpIdentity'
|
||||
import { formatCoverageHint, sessionTypeAbbrev } from '../lib/coverage'
|
||||
import {
|
||||
MAX_BROWSER_TIMEOUT,
|
||||
formatCountdown,
|
||||
formatSessionScheduleTime,
|
||||
refreshDeadlineDelay,
|
||||
@@ -33,6 +34,7 @@ export function RaceHubPage({ sessionKey }: Props) {
|
||||
const [activeTab, setActiveTab] = useState<Tab>('overview')
|
||||
const [switcherOpen, setSwitcherOpen] = useState(false)
|
||||
const [now, setNow] = useState(() => Date.now())
|
||||
const [refreshGeneration, setRefreshGeneration] = useState(0)
|
||||
|
||||
// The server owns bare Race Hub selection so every open tab crosses the
|
||||
// one-hour handoff at the same instant.
|
||||
@@ -57,9 +59,14 @@ export function RaceHubPage({ sessionKey }: Props) {
|
||||
if (sessionKey !== 0) return
|
||||
const delay = refreshDeadlineDelay(context?.race_hub_refresh_at)
|
||||
if (delay == null) return
|
||||
const timer = window.setTimeout(() => { void refetchContext() }, delay)
|
||||
const rearmAfterRefetch = delay === MAX_BROWSER_TIMEOUT
|
||||
const timer = window.setTimeout(() => {
|
||||
void refetchContext().finally(() => {
|
||||
if (rearmAfterRefetch) setRefreshGeneration((generation) => generation + 1)
|
||||
})
|
||||
}, delay)
|
||||
return () => window.clearTimeout(timer)
|
||||
}, [sessionKey, context?.race_hub_refresh_at, refetchContext])
|
||||
}, [sessionKey, context?.race_hub_refresh_at, refetchContext, refreshGeneration])
|
||||
|
||||
useEffect(() => {
|
||||
if (!preSession) return
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
createRoute,
|
||||
} from '@tanstack/react-router'
|
||||
import { RaceHubPage } from '../pages/RaceHubPage'
|
||||
import { MAX_BROWSER_TIMEOUT } from '../lib/schedule'
|
||||
import type { ContextAvailability, DatasetInfo, Meeting, RaceHub, Session, Weekend, WeekendContext } from '../types'
|
||||
|
||||
vi.mock('../api', () => ({
|
||||
@@ -375,6 +376,22 @@ describe('RaceHubPage', () => {
|
||||
expect(mockFetchRaceHub).not.toHaveBeenCalledWith(9473)
|
||||
})
|
||||
|
||||
it('re-arms a bare route refresh after a capped browser timer', async () => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true })
|
||||
mockFetchWeekendContext.mockResolvedValue({
|
||||
...analysisContext,
|
||||
race_hub_refresh_at: new Date(Date.now() + MAX_BROWSER_TIMEOUT + 1_000).toISOString(),
|
||||
})
|
||||
|
||||
renderRaceHub(0)
|
||||
await screen.findByTestId('race-hub')
|
||||
await act(async () => { await vi.advanceTimersByTimeAsync(MAX_BROWSER_TIMEOUT) })
|
||||
|
||||
await waitFor(() => expect(mockFetchWeekendContext).toHaveBeenCalledTimes(2))
|
||||
await act(async () => { await vi.advanceTimersByTimeAsync(1_000) })
|
||||
await waitFor(() => expect(mockFetchWeekendContext).toHaveBeenCalledTimes(3))
|
||||
})
|
||||
|
||||
it('keeps an explicit session URL stable across the canonical refresh boundary', async () => {
|
||||
vi.useFakeTimers()
|
||||
renderRaceHub(9472)
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
focusMeetingKind,
|
||||
focusMeetingLabel,
|
||||
formatCountdown,
|
||||
MAX_BROWSER_TIMEOUT,
|
||||
nextUpcomingMeeting,
|
||||
pickFocusMeeting,
|
||||
refreshDeadlineDelay,
|
||||
@@ -84,4 +85,10 @@ describe('schedule helpers', () => {
|
||||
expect(refreshDeadlineDelay('2025-05-25T13:00:00Z', Date.parse('2025-05-25T12:59:30Z'))).toBe(30_000)
|
||||
expect(refreshDeadlineDelay(undefined)).toBeNull()
|
||||
})
|
||||
|
||||
it('caps a refresh deadline beyond the browser timer maximum', () => {
|
||||
const now = Date.parse('2025-05-25T12:00:00Z')
|
||||
const deadline = new Date(now + MAX_BROWSER_TIMEOUT + 1_000).toISOString()
|
||||
expect(refreshDeadlineDelay(deadline, now)).toBe(MAX_BROWSER_TIMEOUT)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user