diff --git a/backend/src/routes/push.js b/backend/src/routes/push.js index b90443e..61521ff 100644 --- a/backend/src/routes/push.js +++ b/backend/src/routes/push.js @@ -112,4 +112,47 @@ router.post('/unsubscribe', authMiddleware, async (req, res) => { } catch (e) { res.status(500).json({ error: e.message }); } }); +// Send a test push to the requesting user's own device β for diagnosing FCM setup +router.post('/test', authMiddleware, async (req, res) => { + try { + const subs = await query(req.schema, + 'SELECT * FROM push_subscriptions WHERE user_id = $1 AND fcm_token IS NOT NULL', + [req.user.id] + ); + if (subs.length === 0) { + return res.status(404).json({ + error: 'No push subscription found for your account. Grant notification permission and reload the app first.', + }); + } + + const messaging = getMessaging(); + if (!messaging) { + return res.status(503).json({ error: 'Firebase Admin not initialised on server β check FIREBASE_SERVICE_ACCOUNT in .env' }); + } + + const results = []; + for (const sub of subs) { + try { + await messaging.send({ + token: sub.fcm_token, + data: { + title: 'RosterChirp Test', + body: 'Push notifications are working! π', + url: '/', + groupId: '', + }, + android: { priority: 'high' }, + webpush: { headers: { Urgency: 'high' } }, + }); + results.push({ device: sub.device, status: 'sent' }); + console.log(`[Push] Test notification sent to user ${req.user.id} device=${sub.device}`); + } catch (err) { + results.push({ device: sub.device, status: 'failed', error: err.message, code: err.code }); + console.error(`[Push] Test notification failed for user ${req.user.id} device=${sub.device}:`, err.message); + } + } + res.json({ results }); + } catch (e) { res.status(500).json({ error: e.message }); } +}); + module.exports = { router, sendPushToUser }; diff --git a/frontend/src/components/ProfileModal.jsx b/frontend/src/components/ProfileModal.jsx index 0c33d2f..a3618f4 100644 --- a/frontend/src/components/ProfileModal.jsx +++ b/frontend/src/components/ProfileModal.jsx @@ -16,7 +16,9 @@ export default function ProfileModal({ onClose }) { const [newPw, setNewPw] = useState(''); const [confirmPw, setConfirmPw] = useState(''); const [loading, setLoading] = useState(false); - const [tab, setTab] = useState('profile'); // 'profile' | 'password' + const [tab, setTab] = useState('profile'); // 'profile' | 'password' | 'notifications' + const [pushTesting, setPushTesting] = useState(false); + const [pushResult, setPushResult] = useState(null); const [hideAdminTag, setHideAdminTag] = useState(!!user?.hide_admin_tag); const [allowDm, setAllowDm] = useState(user?.allow_dm !== 0); @@ -101,6 +103,7 @@ export default function ProfileModal({ onClose }) {
Tap Send Test Notification to trigger a push to this device. The notification will arrive shortly if everything is configured correctly.
+If it doesn't arrive, check:
+ β’ Notification permission granted (browser prompt)
+ β’ Android Settings β Apps β RosterChirp β Notifications β Enabled
+ β’ App is backgrounded when the test fires
+