Files
rosterchirp-dev/fcm-app/public/app.js
2026-03-23 19:34:13 -04:00

335 lines
11 KiB
JavaScript

// 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);
});
});
}