All checks were successful
Build and Push Docker Image / build (push) Successful in 11m12s
Version guard: - Replace API route with prebuild-generated public/build-id.json - Captures build ID on first load, only notifies on mismatch - Fixes false positive refresh prompts from env mismatch Members table (applicants): - Show project name + round badge instead of round name + state - Red badge for rejected, gray for withdrawn, green for passed, outline for active rounds - Include projectName in applicantRoundInfo from backend Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useRef } from 'react'
|
|
import { toast } from 'sonner'
|
|
|
|
// Capture the build ID when this module first loads (from the current deployment's JS bundle).
|
|
// On subsequent fetches of /build-id.json, if the value differs, a new deploy happened.
|
|
let initialBuildId: string | null = null
|
|
|
|
export function VersionGuard() {
|
|
const notified = useRef(false)
|
|
|
|
useEffect(() => {
|
|
async function checkVersion() {
|
|
if (notified.current) return
|
|
try {
|
|
const res = await fetch('/build-id.json?t=' + Date.now())
|
|
if (!res.ok) return
|
|
const { buildId } = await res.json()
|
|
if (!buildId) return
|
|
|
|
// First load — capture the build ID
|
|
if (initialBuildId === null) {
|
|
initialBuildId = buildId
|
|
return
|
|
}
|
|
|
|
// Subsequent checks — compare
|
|
if (buildId !== initialBuildId) {
|
|
notified.current = true
|
|
toast('A new version is available', {
|
|
description: 'Refresh to get the latest updates.',
|
|
duration: Infinity,
|
|
action: {
|
|
label: 'Refresh',
|
|
onClick: () => window.location.reload(),
|
|
},
|
|
})
|
|
}
|
|
} catch {
|
|
// Network error — ignore
|
|
}
|
|
}
|
|
|
|
// Initial check (captures build ID)
|
|
checkVersion()
|
|
|
|
// Check on tab focus (covers users returning to stale tabs)
|
|
window.addEventListener('focus', checkVersion)
|
|
|
|
// Also check every 5 minutes for long-lived tabs
|
|
const interval = setInterval(checkVersion, 5 * 60 * 1000)
|
|
|
|
return () => {
|
|
window.removeEventListener('focus', checkVersion)
|
|
clearInterval(interval)
|
|
}
|
|
}, [])
|
|
|
|
return null
|
|
}
|