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
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> |
|
) |
|
} |
|
|
|
|