You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

144 lines
4.4 KiB

import { Button } from '@/components/ui/button'
import { RefreshCw, X } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function VersionUpdateBanner() {
const { t } = useTranslation()
const [updateAvailable, setUpdateAvailable] = useState(false)
const [isDismissed, setIsDismissed] = useState(false)
const [isUpdating, setIsUpdating] = useState(false)
useEffect(() => {
if (typeof window === 'undefined' || !('serviceWorker' in navigator)) {
return
}
let registration: ServiceWorkerRegistration | null = null
const checkForUpdates = async () => {
try {
registration = await navigator.serviceWorker.ready
if (!registration) return
// Check if there's a waiting service worker (new version ready)
if (registration.waiting) {
// There's already a new version waiting
setUpdateAvailable(true)
}
// Listen for updates
const handleUpdateFound = () => {
const newWorker = registration?.installing
if (!newWorker) return
const handleStateChange = () => {
if (newWorker.state === 'installed') {
// New version installed
if (navigator.serviceWorker.controller) {
// There's a new version ready (not the first install)
setUpdateAvailable(true)
}
}
}
newWorker.addEventListener('statechange', handleStateChange)
}
registration.addEventListener('updatefound', handleUpdateFound)
// Check for updates periodically
const checkInterval = setInterval(() => {
if (registration) {
registration.update()
}
}, 60000) // Check every minute
// Initial update check
registration.update()
return () => {
clearInterval(checkInterval)
if (registration) {
registration.removeEventListener('updatefound', handleUpdateFound as EventListener)
}
}
} catch (error) {
console.error('Error checking for updates:', error)
}
}
checkForUpdates()
}, [])
const handleUpdate = () => {
setIsUpdating(true)
// Reload the page to activate the new service worker
window.location.reload()
}
const handleDismiss = () => {
setIsDismissed(true)
// Store dismissal in localStorage to avoid showing it again this session
sessionStorage.setItem('versionUpdateDismissed', 'true')
}
// Check if user already dismissed this session
useEffect(() => {
const dismissed = sessionStorage.getItem('versionUpdateDismissed')
if (dismissed === 'true') {
setIsDismissed(true)
}
}, [])
if (!updateAvailable || isDismissed) {
return null
}
return (
<div className="bg-blue-50 dark:bg-blue-900/20 border-b border-blue-200 dark:border-blue-800 px-4 py-3">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3 flex-1">
<RefreshCw className="h-5 w-5 text-blue-600 dark:text-blue-400 shrink-0" />
<div className="flex-1">
<p className="text-sm font-medium text-blue-800 dark:text-blue-200">
{t('A new version is available')}
</p>
<p className="text-xs text-blue-600 dark:text-blue-300">
{t('Click update to get the latest features and improvements')}
</p>
</div>
</div>
<div className="flex items-center gap-2 shrink-0">
<Button
size="sm"
onClick={handleUpdate}
disabled={isUpdating}
className="bg-blue-600 hover:bg-blue-700 text-white"
>
{isUpdating ? (
<>
<RefreshCw className="h-4 w-4 mr-2 animate-spin" />
{t('Updating...')}
</>
) : (
<>
<RefreshCw className="h-4 w-4 mr-2" />
{t('Update')}
</>
)}
</Button>
<Button
variant="ghost"
size="icon"
onClick={handleDismiss}
className="h-8 w-8 text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200"
>
<X className="h-4 w-4" />
</Button>
</div>
</div>
</div>
)
}