// Load Firebase SDK immediately const script1 = document.createElement('script'); script1.src = 'https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js'; script1.onload = () => { const script2 = document.createElement('script'); script2.src = 'https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js'; script2.onload = () => { // Initialize Firebase immediately initializeFirebase(); console.log('Firebase SDK and initialization complete'); // Now that Firebase is ready, set up the app setupApp(); }; document.head.appendChild(script2); }; document.head.appendChild(script1); // Global variables let currentUser = null; let fcmToken = null; let messaging = null; let swRegistration = null; let initPromise = null; let foregroundHandlerSetup = false; const VAPID_KEY = 'BE6hPKkbf-h0lUQ1tYo249pBOdZFFcWQn9suwg3NDwSE8C_hv8hk1dUY9zxHBQEChO_IAqyFZplF_SUb5c4Ofrw'; // Simple user authentication const users = { 'pwau1': { password: 'test123', name: 'Desktop User' }, 'pwau2': { password: 'test123', name: 'Mobile User 1' }, 'pwau3': { password: 'test123', name: 'Mobile User 2' } }; // Initialize Firebase — returns a promise that resolves when messaging is ready function initializeFirebase() { if (initPromise) return initPromise; const firebaseConfig = { apiKey: "AIzaSyAw1v4COZ68Po8CuwVKrQq0ygf7zFd2QCA", authDomain: "fcmtest-push.firebaseapp.com", projectId: "fcmtest-push", storageBucket: "fcmtest-push.firebasestorage.app", messagingSenderId: "439263996034", appId: "1:439263996034:web:9b3d52af2c402e65fdec9b" }; if (firebase.apps.length === 0) { firebase.initializeApp(firebaseConfig); console.log('Firebase app initialized'); } initPromise = navigator.serviceWorker.register('/sw.js') .then((registration) => { console.log('Service Worker registered:', registration); swRegistration = registration; messaging = firebase.messaging(); console.log('Firebase messaging initialized successfully'); }) .catch((error) => { console.error('Service Worker registration failed:', error); initPromise = null; throw error; }); return initPromise; } // Show user info panel and hide login form function showUserInfo() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('userInfo').style.display = 'block'; document.getElementById('currentUser').textContent = users[currentUser]?.name || currentUser; } // Setup app after Firebase is ready function setupApp() { // Set up event listeners document.getElementById('loginForm').addEventListener('submit', login); document.getElementById('sendNotificationBtn').addEventListener('click', sendNotification); document.getElementById('logoutBtn').addEventListener('click', logout); // Restore session and re-register FCM token if notifications were already granted const savedUser = localStorage.getItem('currentUser'); if (savedUser) { currentUser = savedUser; showUserInfo(); if (Notification.permission === 'granted') { initializeFirebase() .then(() => messaging.getToken({ vapidKey: VAPID_KEY, serviceWorkerRegistration: swRegistration })) .then(token => { if (token) return registerToken(currentUser, token); }) .catch(err => console.error('Token refresh on session restore failed:', err)); } } } // Request notification permission and get FCM token async function requestNotificationPermission() { try { console.log('Requesting notification permission...'); const permission = await Notification.requestPermission(); console.log('Permission result:', permission); if (permission === 'granted') { console.log('Notification permission granted.'); showStatus('Getting FCM token...', 'info'); try { const token = await messaging.getToken({ vapidKey: VAPID_KEY, serviceWorkerRegistration: swRegistration }); console.log('FCM Token generated:', token); if (!token) { throw new Error('getToken() returned empty — check VAPID key and service worker'); } fcmToken = token; // Send token to server await registerToken(currentUser, token); showStatus('Notifications enabled successfully!', 'success'); } catch (tokenError) { console.error('Error getting FCM token:', tokenError); showStatus('Failed to get FCM token: ' + tokenError.message, 'error'); } } else { console.log('Notification permission denied.'); showStatus('Notification permission denied.', 'error'); } } catch (error) { console.error('Error requesting notification permission:', error); showStatus('Failed to enable notifications: ' + error.message, 'error'); } } // Register FCM token with server async function registerToken(username, token) { try { console.log('Attempting to register token:', { username, token: token.substring(0, 20) + '...' }); const response = await fetch('/register-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, token }) }); console.log('Registration response status:', response.status); if (!response.ok) { const errorText = await response.text(); throw new Error(`Server returned ${response.status}: ${errorText}`); } const result = await response.json(); console.log('Token registered successfully:', result); showStatus(`Token registered for ${username}`, 'success'); } catch (error) { console.error('Error registering token:', error); showStatus('Failed to register token with server: ' + error.message, 'error'); } } // Handle foreground messages (guard against duplicate registration) function handleForegroundMessages() { if (foregroundHandlerSetup) return; foregroundHandlerSetup = true; messaging.onMessage(function(payload) { console.log('Received foreground message: ', payload); // Show notification in foreground const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, icon: '/icon-192.png', badge: '/icon-192.png' }; new Notification(notificationTitle, notificationOptions); showStatus(`New notification: ${payload.notification.body}`, 'info'); }); } // Login function async function login(event) { if (event) event.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; if (!users[username] || users[username].password !== password) { showStatus('Invalid username or password', 'error'); return; } currentUser = username; localStorage.setItem('currentUser', username); showUserInfo(); showStatus(`Logged in as ${users[username].name}`, 'success'); // Initialize Firebase and request notifications if (typeof firebase !== 'undefined') { await initializeFirebase(); await requestNotificationPermission(); handleForegroundMessages(); } else { showStatus('Firebase not loaded. Please check your connection.', 'error'); } } // Logout function function logout() { currentUser = null; fcmToken = null; localStorage.removeItem('currentUser'); document.getElementById('loginForm').style.display = 'block'; document.getElementById('userInfo').style.display = 'none'; document.getElementById('username').value = ''; document.getElementById('password').value = ''; showStatus('Logged out successfully.', 'info'); } // Send notification function async function sendNotification() { if (!currentUser) { showStatus('Please login first.', 'error'); return; } try { // First check registered users const usersResponse = await fetch('/users'); const users = await usersResponse.json(); console.log('Registered users:', users); const response = await fetch('/send-notification', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fromUser: currentUser, title: 'Test Notification', body: `Notification sent from ${currentUser} at ${new Date().toLocaleTimeString()}` }) }); if (!response.ok) { throw new Error('Failed to send notification'); } const result = await response.json(); console.log('Send result:', result); if (result.recipients === 0) { showStatus('No other users have registered tokens. Open the app on other devices and enable notifications.', 'error'); } else { showStatus(`Notification sent to ${result.recipients} user(s)!`, 'success'); } } catch (error) { console.error('Error sending notification:', error); showStatus('Failed to send notification.', 'error'); } } // Show status message function showStatus(message, type) { const statusEl = document.getElementById('status'); statusEl.textContent = message; statusEl.className = `status ${type}`; statusEl.style.display = 'block'; setTimeout(() => { statusEl.style.display = 'none'; }, 5000); } // Register service worker and handle PWA installation if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/sw.js') .then(function(registration) { console.log('ServiceWorker registration successful with scope: ', registration.scope); // Handle PWA installation let deferredPrompt; window.addEventListener('beforeinstallprompt', (e) => { console.log('beforeinstallprompt fired'); e.preventDefault(); deferredPrompt = e; // Show install button or banner showInstallButton(); }); function showInstallButton() { const installBtn = document.createElement('button'); installBtn.textContent = 'Install App'; installBtn.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: #2196F3; color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer; z-index: 1000; font-size: 14px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); `; installBtn.addEventListener('click', async () => { if (deferredPrompt) { deferredPrompt.prompt(); const { outcome } = await deferredPrompt.userChoice; console.log(`User response to the install prompt: ${outcome}`); deferredPrompt = null; installBtn.remove(); } }); document.body.appendChild(installBtn); } }) .catch(function(error) { console.log('ServiceWorker registration failed: ', error); }); }); }