From f48ce589ca05d739f275103fd5383244586ff64c Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Tue, 24 Mar 2026 19:04:09 -0400 Subject: [PATCH] v0.12.26 FCM feature changes --- backend/package.json | 2 +- backend/src/index.js | 22 +++++++++++++++++++--- backend/src/routes/push.js | 5 +++++ build.sh | 2 +- frontend/package.json | 2 +- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/backend/package.json b/backend/package.json index b674ec4..0467438 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-backend", - "version": "0.12.25", + "version": "0.12.26", "description": "RosterChirp backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/index.js b/backend/src/index.js index 8c07192..9da3a99 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -224,12 +224,14 @@ io.on('connection', async (socket) => { message.reactions = []; io.to(R('group', groupId)).emit('message:new', message); - // Push notifications for private groups + // Push notifications + const senderName = socket.user.display_name || socket.user.name || 'Someone'; + const msgBody = (content || (imageUrl ? '📷 Image' : '')).slice(0, 100); + if (group.type === 'private') { const members = await query(schema, 'SELECT user_id FROM group_members WHERE group_id = $1', [groupId] ); - const senderName = socket.user.display_name || socket.user.name || 'Someone'; for (const m of members) { if (m.user_id === userId) continue; const memberKey = `${schema}:${m.user_id}`; @@ -246,7 +248,21 @@ io.on('connection', async (socket) => { // after the PWA was backgrounded (OS kills WebSocket before ping timeout). sendPushToUser(schema, m.user_id, { title: senderName, - body: (content || (imageUrl ? '📷 Image' : '')).slice(0, 100), + body: msgBody, + url: '/', groupId, badge: 1, + }).catch(() => {}); + } + } else if (group.type === 'public') { + // Push to all users who have registered an FCM token — everyone is implicitly + // a member of every public group. Skip the sender. + const subUsers = await query(schema, + 'SELECT DISTINCT user_id FROM push_subscriptions WHERE fcm_token IS NOT NULL AND user_id != $1', + [userId] + ); + for (const sub of subUsers) { + sendPushToUser(schema, sub.user_id, { + title: `${senderName} in ${group.name}`, + body: msgBody, url: '/', groupId, badge: 1, }).catch(() => {}); } diff --git a/backend/src/routes/push.js b/backend/src/routes/push.js index abaa4b2..fb72911 100644 --- a/backend/src/routes/push.js +++ b/backend/src/routes/push.js @@ -36,6 +36,10 @@ async function sendPushToUser(schema, userId, payload) { 'SELECT * FROM push_subscriptions WHERE user_id = $1 AND fcm_token IS NOT NULL', [userId] ); + if (subs.length === 0) { + console.log(`[Push] No FCM token for user ${userId} (schema=${schema})`); + return; + } for (const sub of subs) { try { await messaging.send({ @@ -70,6 +74,7 @@ async function sendPushToUser(schema, userId, payload) { fcm_options: { link: payload.url || '/' }, }, }); + console.log(`[Push] Sent to user ${userId} device=${sub.device} schema=${schema}`); } catch (err) { // Remove stale tokens const stale = [ diff --git a/build.sh b/build.sh index 4f14809..771a673 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.12.25}" +VERSION="${1:-0.12.26}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="rosterchirp" diff --git a/frontend/package.json b/frontend/package.json index edd9fa5..d64261a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-frontend", - "version": "0.12.25", + "version": "0.12.26", "private": true, "scripts": { "dev": "vite",