v0.12.40 iso notificastion bug fix
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rosterchirp-backend",
|
"name": "rosterchirp-backend",
|
||||||
"version": "0.12.39",
|
"version": "0.12.40",
|
||||||
"description": "RosterChirp backend server",
|
"description": "RosterChirp backend server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -13,7 +13,7 @@
|
|||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
VERSION="${1:-0.12.39}"
|
VERSION="${1:-0.12.40}"
|
||||||
ACTION="${2:-}"
|
ACTION="${2:-}"
|
||||||
REGISTRY="${REGISTRY:-}"
|
REGISTRY="${REGISTRY:-}"
|
||||||
IMAGE_NAME="rosterchirp"
|
IMAGE_NAME="rosterchirp"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rosterchirp-frontend",
|
"name": "rosterchirp-frontend",
|
||||||
"version": "0.12.39",
|
"version": "0.12.40",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ const FIREBASE_CONFIG = {
|
|||||||
appId: "1:126479377334:web:280abdd135cf7e0c50d717"
|
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;
|
let messaging = null;
|
||||||
if (FIREBASE_CONFIG.apiKey !== '__FIREBASE_API_KEY__') {
|
if (!isIOS && FIREBASE_CONFIG.apiKey !== '__FIREBASE_API_KEY__') {
|
||||||
firebase.initializeApp(FIREBASE_CONFIG);
|
firebase.initializeApp(FIREBASE_CONFIG);
|
||||||
messaging = firebase.messaging();
|
messaging = firebase.messaging();
|
||||||
console.log('[SW] Firebase initialised');
|
console.log('[SW] Firebase initialised');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { useSocket } from '../contexts/SocketContext.jsx';
|
import { useSocket } from '../contexts/SocketContext.jsx';
|
||||||
import { useAuth } from '../contexts/AuthContext.jsx';
|
import { useAuth } from '../contexts/AuthContext.jsx';
|
||||||
import { useToast } from '../contexts/ToastContext.jsx';
|
import { useToast } from '../contexts/ToastContext.jsx';
|
||||||
@@ -35,6 +35,9 @@ export default function Chat() {
|
|||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
const [groups, setGroups] = useState({ publicGroups: [], privateGroups: [] });
|
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 [onlineUserIds, setOnlineUserIds] = useState(new Set());
|
||||||
const [activeGroupId, setActiveGroupId] = useState(null);
|
const [activeGroupId, setActiveGroupId] = useState(null);
|
||||||
const [chatHasText, setChatHasText] = useState(false);
|
const [chatHasText, setChatHasText] = useState(false);
|
||||||
@@ -74,6 +77,9 @@ export default function Chat() {
|
|||||||
|
|
||||||
useEffect(() => { loadGroups(); }, [loadGroups]);
|
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
|
// Load feature flags + current user's group memberships on mount
|
||||||
const loadFeatures = useCallback(() => {
|
const loadFeatures = useCallback(() => {
|
||||||
api.getSettings().then(({ settings }) => {
|
api.getSettings().then(({ settings }) => {
|
||||||
@@ -424,14 +430,40 @@ export default function Chat() {
|
|||||||
socket.on('group:updated', handleGroupUpdated);
|
socket.on('group:updated', handleGroupUpdated);
|
||||||
socket.on('session:displaced', handleSessionDisplaced);
|
socket.on('session:displaced', handleSessionDisplaced);
|
||||||
|
|
||||||
// Bug B fix: on reconnect, reload groups to catch any messages missed while offline
|
// On reconnect or visibility restore: reload groups AND badge any groups that
|
||||||
const handleReconnect = () => { loadGroups(); };
|
// 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);
|
socket.on('connect', handleReconnect);
|
||||||
|
|
||||||
// Bug B fix: also reload on visibility restore if socket is already connected
|
|
||||||
const handleVisibility = () => {
|
const handleVisibility = () => {
|
||||||
if (document.visibilityState === 'visible' && socket.connected) {
|
if (document.visibilityState === 'visible' && socket.connected) {
|
||||||
loadGroups();
|
checkForMissedMessages();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener('visibilitychange', handleVisibility);
|
document.addEventListener('visibilitychange', handleVisibility);
|
||||||
|
|||||||
Reference in New Issue
Block a user