v0.12.40 iso notificastion bug fix

This commit is contained in:
2026-03-29 23:21:35 -04:00
parent d03baec163
commit ff6743c9b1
5 changed files with 46 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "rosterchirp-backend",
"version": "0.12.39",
"version": "0.12.40",
"description": "RosterChirp backend server",
"main": "src/index.js",
"scripts": {

View File

@@ -13,7 +13,7 @@
# ─────────────────────────────────────────────────────────────
set -euo pipefail
VERSION="${1:-0.12.39}"
VERSION="${1:-0.12.40}"
ACTION="${2:-}"
REGISTRY="${REGISTRY:-}"
IMAGE_NAME="rosterchirp"

View File

@@ -1,6 +1,6 @@
{
"name": "rosterchirp-frontend",
"version": "0.12.39",
"version": "0.12.40",
"private": true,
"scripts": {
"dev": "vite",

View File

@@ -13,9 +13,13 @@ const FIREBASE_CONFIG = {
appId: "1:126479377334:web:280abdd135cf7e0c50d717"
};
// Initialise Firebase synchronously so the push listener is ready immediately
// Initialise Firebase synchronously so the push listener is ready immediately.
// Skip on iOS — iOS PWAs use standard W3C WebPush (VAPID), not FCM. Initialising
// firebase.messaging() on iOS registers a second internal push listener alongside
// the custom one below, causing every notification to appear twice.
const isIOS = /iPhone|iPad|iPod/.test(self.navigator?.userAgent || '');
let messaging = null;
if (FIREBASE_CONFIG.apiKey !== '__FIREBASE_API_KEY__') {
if (!isIOS && FIREBASE_CONFIG.apiKey !== '__FIREBASE_API_KEY__') {
firebase.initializeApp(FIREBASE_CONFIG);
messaging = firebase.messaging();
console.log('[SW] Firebase initialised');

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import { useSocket } from '../contexts/SocketContext.jsx';
import { useAuth } from '../contexts/AuthContext.jsx';
import { useToast } from '../contexts/ToastContext.jsx';
@@ -35,6 +35,9 @@ export default function Chat() {
const toast = useToast();
const [groups, setGroups] = useState({ publicGroups: [], privateGroups: [] });
// Ref so visibility/reconnect handlers always see the latest groups without
// being dependencies of the socket effect (which would cause excessive re-runs)
const groupsRef = useRef({ publicGroups: [], privateGroups: [] });
const [onlineUserIds, setOnlineUserIds] = useState(new Set());
const [activeGroupId, setActiveGroupId] = useState(null);
const [chatHasText, setChatHasText] = useState(false);
@@ -74,6 +77,9 @@ export default function Chat() {
useEffect(() => { loadGroups(); }, [loadGroups]);
// Keep groupsRef in sync so visibility/reconnect handlers can read current groups
useEffect(() => { groupsRef.current = groups; }, [groups]);
// Load feature flags + current user's group memberships on mount
const loadFeatures = useCallback(() => {
api.getSettings().then(({ settings }) => {
@@ -424,14 +430,40 @@ export default function Chat() {
socket.on('group:updated', handleGroupUpdated);
socket.on('session:displaced', handleSessionDisplaced);
// Bug B fix: on reconnect, reload groups to catch any messages missed while offline
const handleReconnect = () => { loadGroups(); };
// On reconnect or visibility restore: reload groups AND badge any groups that
// received messages while the iOS PWA was backgrounded (socket was dead, so
// message:new events were never received — only push notifications arrived).
const checkForMissedMessages = () => {
api.getGroups().then(newGroups => {
const prev = groupsRef.current;
setGroups(newGroups);
const allPrev = [...prev.publicGroups, ...prev.privateGroups];
const allNew = [...newGroups.publicGroups, ...newGroups.privateGroups];
setUnreadGroups(prevUnread => {
const next = new Map(prevUnread);
for (const ng of allNew) {
if (ng.id === activeGroupId) continue; // currently open — no badge
if (ng.last_message_user_id === user?.id) continue; // own message
const pg = allPrev.find(g => g.id === ng.id);
const isNewer = ng.last_message_at && (
!pg?.last_message_at ||
new Date(ng.last_message_at) > new Date(pg.last_message_at)
);
if (isNewer && !next.has(ng.id)) {
next.set(ng.id, 1);
}
}
return next;
});
}).catch(() => {});
};
const handleReconnect = () => { checkForMissedMessages(); };
socket.on('connect', handleReconnect);
// Bug B fix: also reload on visibility restore if socket is already connected
const handleVisibility = () => {
if (document.visibilityState === 'visible' && socket.connected) {
loadGroups();
checkForMissedMessages();
}
};
document.addEventListener('visibilitychange', handleVisibility);