import React from 'react'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App'; import './index.css'; // Initialize lightweight presence tracking (RTDB per-session) - called after Firebase is ready import { initPresence } from './utils/presence.ts'; import { CacheManager } from './utils/cacheManager.ts'; import { ConsoleFilter } from './utils/consoleFilter.ts'; import { ProductionConsoleGuard } from './utils/productionConsoleGuard.ts'; import { EnvironmentSecurity } from './utils/environmentSecurity.ts'; import { validateSecurity } from './utils/securityValidator.ts'; import { VersionManager } from './utils/versionManager.ts'; // Mobile viewport height fix for 100vh issues on mobile browsers if (typeof window !== 'undefined') { const setVh = () => { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); }; setVh(); window.addEventListener('resize', setVh); } // Enable production security measures ProductionConsoleGuard.enableProductionMode(); EnvironmentSecurity.validateProductionSecurity(); // Validate security setup (development only) if (import.meta.env.DEV) { validateSecurity(); } // Setup console filtering to reduce verbose logging and prevent info leakage ConsoleFilter.setupFilter({ enableWorkboxLogs: false, // Disabled to prevent service worker info leakage enableServiceWorkerLogs: false, // Disabled for security enablePWALogs: false, // Disabled to prevent app structure info leakage enableDevInfoLogs: false, // Disabled to clean up development console enableReactRouterWarnings: false // Disabled to reduce future flag warnings }); // Additional global console override for security and missed contexts if (typeof window !== 'undefined') { const originalLog = window.console.log; const originalInfo = window.console.info; const originalWarn = window.console.warn; const originalDebug = window.console.debug; const shouldSuppressLog = (message: unknown) => { if (typeof message !== 'string') return false; // In production, suppress all non-essential logs if (!import.meta.env.DEV) { return true; // Suppress all logs in production for security } // In development, suppress service worker and framework logs return message.toLowerCase().startsWith('workbox ') || message.includes('Router is responding to') || message.includes('Using NetworkFirst') || message.includes('Using StaleWhileRevalidate') || message.includes('Using CacheFirst') || message.includes('Precaching') || message.includes('files are already cached') || message.includes('Service Worker for notifications loaded') || message.includes('Firebase Auth') || message.includes('Firebase config') || message.includes('Firebase Auth instance'); }; window.console.log = (...args) => { if (!shouldSuppressLog(args[0])) { originalLog.apply(console, args); } }; window.console.info = (...args) => { if (!shouldSuppressLog(args[0])) { originalInfo.apply(console, args); } }; window.console.warn = (...args) => { if (!shouldSuppressLog(args[0])) { originalWarn.apply(console, args); } }; window.console.debug = (...args) => { if (!shouldSuppressLog(args[0])) { originalDebug.apply(console, args); } }; } // Removed cookie-clear function to avoid logging users out during updates /* // Function to show a subtle update notification (only once per version) // Currently not used but kept for potential future implementation const showUpdateNotification = () => { // Check if notification already shown for this version const curr = (typeof __APP_VERSION__ !== 'undefined') ? __APP_VERSION__ : ''; const notificationShown = localStorage.getItem('notification-shown'); if (notificationShown === curr) { return; // Already shown for this version } // Check if notification already exists const existingNotification = document.getElementById('app-update-notification'); if (existingNotification) { return; // Notification already visible } const notification = document.createElement('div'); notification.id = 'app-update-notification'; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); color: white; padding: 8px; border-radius: 50%; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); z-index: 10000; cursor: pointer; transition: all 0.3s ease; animation: flashUpdate 1.5s infinite, slideInUpdate 0.3s ease-out; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; font-size: 16px; `; notification.innerHTML = '๐'; notification.title = "App Updated - Click to refresh for latest features"; // Add animation styles const style = document.createElement('style'); style.textContent = ` @keyframes slideInUpdate { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes flashUpdate { 0% { opacity: 1; transform: scale(1); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); } 25% { opacity: 0.6; transform: scale(1.15); box-shadow: 0 6px 20px rgba(59, 130, 246, 0.6); } 50% { opacity: 1; transform: scale(1); box-shadow: 0 8px 24px rgba(59, 130, 246, 0.8); } 75% { opacity: 0.8; transform: scale(0.95); box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4); } 100% { opacity: 1; transform: scale(1); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); } } #app-update-notification:hover { transform: scale(1.2) !important; box-shadow: 0 8px 24px rgba(59, 130, 246, 0.6); animation: none !important; background: linear-gradient(135deg, #1d4ed8 0%, #3b82f6 100%); } `; document.head.appendChild(style); // Click to refresh and clear cookies/cache notification.addEventListener('click', () => { // Clear all data before refresh clearAllCookies(); CacheManager.clearAllCaches(); // Mark notification as shown for this version localStorage.setItem('notification-shown', curr); // Remove the notification icon immediately if (notification.parentNode) { notification.parentNode.removeChild(notification); } // Reload the page window.location.reload(); }); document.body.appendChild(notification); // Mark notification as shown for this version localStorage.setItem('notification-shown', curr); }; */ // Versioned cache/cookie purge: only clear when app version changes const VERSION_STORAGE_KEY = 'app-version'; const RELOAD_ONCE_KEY = 'app-version-reloaded'; const hasNewVersionAndMark = (): boolean => { try { const prev = localStorage.getItem(VERSION_STORAGE_KEY); const curr = (typeof __APP_VERSION__ !== 'undefined') ? __APP_VERSION__ : ''; const reloaded = localStorage.getItem(RELOAD_ONCE_KEY); if (!prev || prev !== curr) { localStorage.setItem(VERSION_STORAGE_KEY, curr); localStorage.removeItem(RELOAD_ONCE_KEY); // Reset reload marker for new version localStorage.removeItem('notification-shown'); // Reset notification marker for new version return true; } // Only reload once per version if (!reloaded) { localStorage.setItem(RELOAD_ONCE_KEY, curr); return true; } return false; } catch { // If storage fails, default to clearing once return true; } }; // Register service worker for PWA functionality if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { // Background cache clearing: only clear when a new version is detected if (hasNewVersionAndMark()) { try { // Clear caches in background without reload (preserve auth/session) await CacheManager.clearAllCaches(); // Do not clear cookies to avoid logging out users // Show a subtle update notification (non-blocking) - DISABLED: Now shown on login page // if (!import.meta.env.DEV) { // showUpdateNotification(); // } } catch (cacheError) { console.warn('โ ๏ธ Cache/cookie clearing failed:', cacheError); } } let registration; // Only register service workers in production to avoid dev caching issues if (!import.meta.env.DEV) { // In production, register the generated PWA service worker registration = await navigator.serviceWorker.register('/sw.js', { scope: '/' }); } // Listen for service worker updates (gentle approach) registration.addEventListener('updatefound', () => { const newWorker = registration.installing; if (newWorker) { newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // New SW installed; show notification instead of forcing reload - DISABLED: Now shown on login page // if (!import.meta.env.DEV) { // showUpdateNotification(); // } } }); } }); // When the new SW takes control, show update notification navigator.serviceWorker.addEventListener('controllerchange', () => { if (!import.meta.env.DEV) { // Clear any existing update notifications first const existing = document.getElementById('app-update-notification'); if (existing) { existing.remove(); } // Only show if not already shown for this version - DISABLED: Now shown on login page // const curr = (typeof __APP_VERSION__ !== 'undefined') ? __APP_VERSION__ : ''; // const notificationShown = localStorage.getItem('notification-shown'); // if (notificationShown !== curr) { // showUpdateNotification(); // } } }); // Enable cache in both modes (reduced logging) // Log cache status after a short delay to allow initialization (only in dev if needed) if (import.meta.env.DEV) { setTimeout(() => { // Only log cache status if explicitly needed // CacheManager.logCacheStatus(); }, 2000); } } catch (error) { console.error('โ Service worker registration failed:', error); } }); } // Remove development debug functions for production // Cache debug functions are available in development only // Add console log to confirm service worker is enabled // Service Worker registration ENABLED // Edge Browser Compatibility Check const isEdge = navigator.userAgent.includes('Edg'); const isEdgeLegacy = navigator.userAgent.includes('Edge/'); if (isEdge || isEdgeLegacy) { // Microsoft Edge detected - applying compatibility mode // Add Edge-specific polyfills or fixes if needed interface WindowWithWebkit extends Window { webkitIndexedDB?: IDBFactory; } if (!window.indexedDB && (window as WindowWithWebkit).webkitIndexedDB) { (window as typeof window & { indexedDB: IDBFactory }).indexedDB = (window as WindowWithWebkit).webkitIndexedDB!; } // Edge sometimes has issues with Firebase Auth persistence try { localStorage.setItem('edgeCompatMode', 'true'); } catch { console.warn('Edge: LocalStorage not available'); } } // PWA Password Reset Message Listener // Listen for messages from the password reset handler (Firebase auth domain) window.addEventListener('message', (event) => { // Verify origin for security if (event.origin !== 'https://system.firebaseapp.com') { return; } if (event.data && event.data.type === 'PASSWORD_RESET_SUCCESS') { // Received password reset success message from auth handler // Show success notification const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #38a169 0%, #2f855a 100%); color: white; padding: 16px 24px; border-radius: 12px; box-shadow: 0 8px 24px rgba(56, 161, 105, 0.3); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-weight: 600; max-width: 300px; animation: slideIn 0.3s ease-out; `; notification.innerHTML = `
Getting the latest features ready