From d2ed487079179c4d7115be48cb729da87f0fd436 Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Sun, 22 Mar 2026 21:39:09 -0400 Subject: [PATCH] v0.11.26 FCM bugs fixes --- backend/package.json | 2 +- backend/src/index.js | 19 ++++++++++++------- build.sh | 2 +- frontend/package.json | 2 +- frontend/src/contexts/SocketContext.jsx | 13 ++++++++----- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/backend/package.json b/backend/package.json index d2b0668..05e8e44 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-backend", - "version": "0.11.25", + "version": "0.11.26", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/index.js b/backend/src/index.js index e587519..8c07192 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -233,17 +233,22 @@ io.on('connection', async (socket) => { for (const m of members) { if (m.user_id === userId) continue; const memberKey = `${schema}:${m.user_id}`; - if (!onlineUsers.has(memberKey)) { - sendPushToUser(schema, m.user_id, { - title: senderName, - body: (content || (imageUrl ? '📷 Image' : '')).slice(0, 100), - url: '/', groupId, badge: 1, - }).catch(() => {}); - } else { + if (onlineUsers.has(memberKey)) { + // In-app notification for connected sockets for (const sid of onlineUsers.get(memberKey)) { io.to(sid).emit('notification:new', { type: 'private_message', groupId, fromUser: socket.user }); } } + // Always send push — when the app is in the foreground FCM delivers + // silently (no system notification); when backgrounded or offline the + // service worker shows the system notification. This covers the common + // Android case where the socket appears online but is silently dead + // 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), + url: '/', groupId, badge: 1, + }).catch(() => {}); } } diff --git a/build.sh b/build.sh index 77c25f2..df769a0 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.12.1}" +VERSION="${1:-0.11.26}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="rosterchirp" diff --git a/frontend/package.json b/frontend/package.json index f5b8a33..bf26aab 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-frontend", - "version": "0.11.25", + "version": "0.11.26", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/contexts/SocketContext.jsx b/frontend/src/contexts/SocketContext.jsx index 3f24a5c..91e2cd6 100644 --- a/frontend/src/contexts/SocketContext.jsx +++ b/frontend/src/contexts/SocketContext.jsx @@ -47,12 +47,15 @@ export function SocketProvider({ children }) { window.dispatchEvent(new CustomEvent('rosterchirp:session-displaced')); }); - // Bug B fix: when app returns to foreground, force socket reconnect if disconnected + // When app returns to foreground, force a full disconnect+reconnect. + // The underlying WebSocket is often silently dead after Android background + // suspension while socket.io-client still reports connected (stale state + // until the ping/pong timeout fires ~45s later). Always force a fresh + // connection so the "offline" indicator clears immediately on focus. const handleVisibilityChange = () => { - if (document.visibilityState === 'visible') { - if (socketRef.current && !socketRef.current.connected) { - socketRef.current.connect(); - } + if (document.visibilityState === 'visible' && socketRef.current) { + socketRef.current.disconnect(); + socketRef.current.connect(); } }; document.addEventListener('visibilitychange', handleVisibilityChange);