added versioning
This commit is contained in:
1 parent
5b2da87f05
commit
c82b112624
8 files changed
+3888
-107
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useRegisterSW } from 'virtual:pwa-register/react';
|
||||
import { Button } from './ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from './ui/dialog';
|
||||
|
||||
export const ReloadPrompt: React.FC = () => {
|
||||
// Skip SW in dev mode
|
||||
if (import.meta.env.DEV) {
|
||||
useEffect(() => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
||||
registrations.forEach((registration) => registration.unregister());
|
||||
});
|
||||
}
|
||||
// Clear workbox caches
|
||||
if (typeof caches !== 'undefined') {
|
||||
caches.keys().then((keys) => {
|
||||
keys.forEach((key) => {
|
||||
if (key.toLowerCase().includes('workbox') || key.toLowerCase().includes('cognitive-clarity')) {
|
||||
caches.delete(key);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
offlineReady: [offlineReady, setOfflineReady],
|
||||
needRefresh: [needRefresh, setNeedRefresh],
|
||||
updateServiceWorker,
|
||||
} = useRegisterSW({
|
||||
onRegisteredSW(swUrl, r) {
|
||||
if (r) {
|
||||
// Check for updates every 60 minutes
|
||||
setInterval(() => {
|
||||
console.log('[PWA] Periodic update check...');
|
||||
r.update();
|
||||
}, 60 * 60 * 1000);
|
||||
|
||||
// Check for updates when coming back online
|
||||
const handleOnline = () => {
|
||||
console.log('[PWA] Back online, checking for updates...');
|
||||
r.update();
|
||||
};
|
||||
window.addEventListener('online', handleOnline);
|
||||
}
|
||||
|
||||
// Auto-reload when a new service worker takes control
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
console.log('[PWA] Controller changed, reloading...');
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
setOfflineReady(false);
|
||||
setNeedRefresh(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={needRefresh || offlineReady} onOpenChange={close}>
|
||||
<DialogContent className="sm:max-w-md bg-slate-900 border-slate-800 text-slate-100">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{needRefresh ? 'Update Available' : 'Offline Ready'}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-slate-400">
|
||||
{needRefresh
|
||||
? 'A new version of Cognitive Clarity is available. Click reload to update immediately.'
|
||||
: 'The application is ready to work offline.'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter className="sm:justify-start gap-2">
|
||||
{needRefresh && (
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => updateServiceWorker(true)}
|
||||
className="bg-blue-600 hover:bg-blue-500 text-white"
|
||||
>
|
||||
Reload and Update
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={close}
|
||||
className="bg-slate-800 hover:bg-slate-700 text-slate-300 border-none"
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Badge } from './ui/badge';
|
||||
|
||||
interface VersionBadgeProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const VersionBadge: React.FC<VersionBadgeProps> = ({ className = '' }) => {
|
||||
const version = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'dev';
|
||||
const buildTime = typeof __BUILD_TIMESTAMP__ !== 'undefined' ? __BUILD_TIMESTAMP__ : '';
|
||||
|
||||
// Format ISO string to a more readable short date/time
|
||||
const formattedTime = buildTime
|
||||
? new Date(buildTime).toLocaleString(undefined, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
: '';
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col items-end text-[10px] text-slate-500 ${className}`}>
|
||||
<Badge variant="outline" className="text-[10px] h-5 py-0 px-2 font-mono border-slate-800 text-slate-500">
|
||||
v{version}
|
||||
</Badge>
|
||||
{formattedTime && (
|
||||
<span className="mt-1 opacity-50 uppercase tracking-tighter">
|
||||
Built: {formattedTime}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAppVersion = () => ({
|
||||
version: typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'dev',
|
||||
buildTimestamp: typeof __BUILD_TIMESTAMP__ !== 'undefined' ? __BUILD_TIMESTAMP__ : '',
|
||||
});
|
||||
Reference in new issue
Block a user