From 225dcd718b8f479df7325d4dd479900a68ac317e Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Tue, 24 Mar 2026 18:13:15 -0400 Subject: [PATCH] v0.12.25 FCM bug fixes --- backend/package.json | 2 +- backend/src/routes/push.js | 4 ++-- build.sh | 2 +- frontend/package.json | 2 +- frontend/src/components/SettingsModal.jsx | 21 ++++++++++++++++-- frontend/src/pages/Chat.jsx | 27 ++++++++++++++++++----- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/backend/package.json b/backend/package.json index caf76a6..b674ec4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-backend", - "version": "0.12.24", + "version": "0.12.25", "description": "RosterChirp backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/routes/push.js b/backend/src/routes/push.js index f918718..abaa4b2 100644 --- a/backend/src/routes/push.js +++ b/backend/src/routes/push.js @@ -97,7 +97,7 @@ router.get('/firebase-config', (req, res) => { const appId = process.env.FIREBASE_APP_ID; const vapidKey = process.env.FIREBASE_VAPID_KEY; - if (!apiKey || !projectId || !messagingSenderId || !appId) { + if (!apiKey || !projectId || !messagingSenderId || !appId || !vapidKey) { return res.status(503).json({ error: 'FCM not configured' }); } res.json({ apiKey, projectId, messagingSenderId, appId, vapidKey }); @@ -220,7 +220,7 @@ router.get('/debug', authMiddleware, async (req, res) => { WHERE ps.fcm_token IS NOT NULL ORDER BY u.name, ps.device `); - const fcmConfigured = !!(process.env.FIREBASE_API_KEY && process.env.FIREBASE_SERVICE_ACCOUNT); + const fcmConfigured = !!(process.env.FIREBASE_API_KEY && process.env.FIREBASE_SERVICE_ACCOUNT && process.env.FIREBASE_VAPID_KEY); const firebaseAdminReady = !!getMessaging(); res.json({ subscriptions: subs, fcmConfigured, firebaseAdminReady }); } catch (e) { res.status(500).json({ error: e.message }); } diff --git a/build.sh b/build.sh index 46b9e9a..4f14809 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.12.24}" +VERSION="${1:-0.12.25}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="rosterchirp" diff --git a/frontend/package.json b/frontend/package.json index c583eec..edd9fa5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-frontend", - "version": "0.12.24", + "version": "0.12.25", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/SettingsModal.jsx b/frontend/src/components/SettingsModal.jsx index 218fef8..4970387 100644 --- a/frontend/src/components/SettingsModal.jsx +++ b/frontend/src/components/SettingsModal.jsx @@ -211,7 +211,8 @@ function PushDebugTab() { const [testing, setTesting] = useState(false); const permission = (typeof Notification !== 'undefined') ? Notification.permission : 'unsupported'; - const cachedToken = localStorage.getItem('rc_fcm_token'); + const [cachedToken, setCachedToken] = useState(localStorage.getItem('rc_fcm_token')); + const [lastError, setLastError] = useState(localStorage.getItem('rc_fcm_error')); const load = async () => { setLoading(true); @@ -245,9 +246,21 @@ function PushDebugTab() { const clearToken = () => { localStorage.removeItem('rc_fcm_token'); + localStorage.removeItem('rc_fcm_error'); + setCachedToken(null); + setLastError(null); toast('Cached token cleared — reload to re-register with server', 'info'); }; + const reregister = () => { + localStorage.removeItem('rc_fcm_token'); + localStorage.removeItem('rc_fcm_error'); + setCachedToken(null); + setLastError(null); + window.dispatchEvent(new CustomEvent('rosterchirp:push-init')); + toast('Re-registering push subscription…', 'info'); + }; + const box = { background: 'var(--surface-variant)', border: '1px solid var(--border)', borderRadius: 'var(--radius)', padding: '12px 14px', marginBottom: 14 }; const sectionLabel = { fontSize: 11, fontWeight: 600, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 8 }; @@ -263,8 +276,12 @@ function PushDebugTab() { {debugData && } {debugData && } + {lastError && } + +
+ +
- {/* Test push */} diff --git a/frontend/src/pages/Chat.jsx b/frontend/src/pages/Chat.jsx index 8008e73..6e0911b 100644 --- a/frontend/src/pages/Chat.jsx +++ b/frontend/src/pages/Chat.jsx @@ -132,12 +132,22 @@ export default function Chat() { // deliver. Passing serviceWorkerRegistration directly to getToken() is enough // for Firebase to return the existing valid token without needing a refresh. console.log('[Push] Requesting FCM token...'); - const fcmToken = await getToken(firebaseMessaging, { - vapidKey, - serviceWorkerRegistration: reg, - }); + let fcmToken; + try { + fcmToken = await getToken(firebaseMessaging, { + vapidKey, + serviceWorkerRegistration: reg, + }); + } catch (tokenErr) { + const msg = tokenErr.message || 'getToken() threw an error'; + console.warn('[Push] getToken() threw:', msg); + localStorage.setItem('rc_fcm_error', msg); + return; + } if (!fcmToken) { - console.warn('[Push] getToken() returned null — notification permission may not be granted at OS level, or VAPID key is wrong'); + const msg = 'getToken() returned null — check VAPID key and OS notification permission'; + console.warn('[Push]', msg); + localStorage.setItem('rc_fcm_error', msg); return; } console.log('[Push] FCM token obtained:', fcmToken.slice(0, 30) + '...'); @@ -147,6 +157,7 @@ export default function Chat() { const cachedToken = localStorage.getItem('rc_fcm_token'); if (cachedToken === fcmToken) { console.log('[Push] Token unchanged — skipping subscribe'); + localStorage.removeItem('rc_fcm_error'); return; } @@ -158,13 +169,17 @@ export default function Chat() { }); if (!subRes.ok) { const err = await subRes.json().catch(() => ({})); - console.warn('[Push] Subscribe failed:', err.error || subRes.status); + const msg = `Subscribe failed: ${err.error || subRes.status}`; + console.warn('[Push]', msg); + localStorage.setItem('rc_fcm_error', msg); } else { localStorage.setItem('rc_fcm_token', fcmToken); + localStorage.removeItem('rc_fcm_error'); console.log('[Push] FCM subscription registered successfully'); } } catch (e) { console.warn('[Push] FCM subscription failed:', e.message); + localStorage.setItem('rc_fcm_error', e.message); } };