2 changed files with 146 additions and 0 deletions
@ -0,0 +1,144 @@
@@ -0,0 +1,144 @@
|
||||
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> |
||||
) |
||||
} |
||||
|
||||
Loading…
Reference in new issue