import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App.tsx';
import { ErrorBoundary } from './components/ErrorBoundary';
import { ToastProvider } from './components/Toast';
import { ThemeProvider } from './contexts/ThemeContext';
import { API_ROOT, isDevelopment } from './config';
// Show update notification banner - Addresses ARCH-004
function showUpdateNotification(newWorker: ServiceWorker) {
// Create update notification banner
const banner = document.createElement('div');
banner.id = 'sw-update-banner';
banner.innerHTML = `
A new version of the app is ready. Update now for the latest features and improvements.
`;
document.body.appendChild(banner);
// Update button click handler
document.getElementById('sw-update-btn')?.addEventListener('click', () => {
newWorker.postMessage({ type: 'SKIP_WAITING' });
window.location.reload();
});
// Dismiss button click handler
document.getElementById('sw-dismiss-btn')?.addEventListener('click', () => {
banner.remove();
});
}
const applyPwaSettings = async () => {
try {
// Use driver-specific PWA settings endpoint
const res = await fetch(`${API_ROOT}/api/driver/settings/pwa`, { credentials: 'include' });
if (!res.ok) return;
const payload = await res.json();
const settings = payload.data || payload;
if (!settings?.enabled) return;
const normalizeUrl = (value?: string) => {
if (!value) return '';
if (value.startsWith('http')) return value;
return value.startsWith('/') ? value : `/${value}`;
};
const manifest = {
name: settings.name,
short_name: settings.shortName,
description: settings.description,
start_url: settings.startUrl || '/',
scope: settings.scope || '/',
display: settings.display || 'standalone',
theme_color: settings.themeColor,
background_color: settings.backgroundColor,
icons: [
settings.icon192 ? { src: normalizeUrl(settings.icon192), sizes: '192x192', type: 'image/png' } : null,
settings.icon512 ? { src: normalizeUrl(settings.icon512), sizes: '512x512', type: 'image/png' } : null
].filter(Boolean)
};
const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/manifest+json' });
const manifestUrl = URL.createObjectURL(manifestBlob);
let manifestLink = document.querySelector("link[rel='manifest']") as HTMLLinkElement | null;
if (!manifestLink) {
manifestLink = document.createElement('link');
manifestLink.rel = 'manifest';
document.head.appendChild(manifestLink);
} else if (manifestLink.href.startsWith('blob:')) {
// Cleanup old blob URL to prevent memory leak - Addresses BUG-018 equivalent
URL.revokeObjectURL(manifestLink.href);
}
manifestLink.href = manifestUrl;
let themeMeta = document.querySelector("meta[name='theme-color']") as HTMLMetaElement | null;
if (!themeMeta) {
themeMeta = document.createElement('meta');
themeMeta.name = 'theme-color';
document.head.appendChild(themeMeta);
}
themeMeta.content = settings.themeColor || '#1e293b';
// Service worker registration with update handling - Addresses ARCH-004
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
// Check for updates periodically
setInterval(() => {
registration.update();
}, 60 * 60 * 1000); // Check every hour
// Check for updates
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
if (newWorker) {
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New content available, show update notification
showUpdateNotification(newWorker);
}
});
}
});
})
.catch((error) => {
if (isDevelopment) {
console.error('Failed to register service worker', error);
}
});
}
} catch (error) {
if (isDevelopment) {
console.error('Failed to apply PWA settings', error);
}
}
};
applyPwaSettings();
createRoot(document.getElementById('root')!).render(
{/* BrowserRouter enables URL-based navigation - Addresses ARCH-001 */}
,
);