v0.6.5 various bug fixes

This commit is contained in:
2026-03-10 19:02:14 -04:00
parent 2d21aac35f
commit daaf4a4805
13 changed files with 166 additions and 75 deletions

View File

@@ -63,39 +63,54 @@ export default function Chat() {
useEffect(() => { loadGroups(); }, [loadGroups]);
// Register push subscription
// Register / refresh push subscription
useEffect(() => {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return;
(async () => {
const registerPush = async () => {
try {
const permission = Notification.permission;
if (permission === 'denied') return;
const reg = await navigator.serviceWorker.ready;
const { publicKey } = await fetch('/api/push/vapid-public').then(r => r.json());
const existing = await reg.pushManager.getSubscription();
if (existing) {
// Re-register to keep subscription fresh
await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('tc_token') || sessionStorage.getItem('tc_token')}` },
body: JSON.stringify(existing.toJSON())
const token = localStorage.getItem('tc_token') || sessionStorage.getItem('tc_token');
let sub = await reg.pushManager.getSubscription();
if (!sub) {
// First time or subscription was lost — request permission then subscribe
const granted = permission === 'granted'
? 'granted'
: await Notification.requestPermission();
if (granted !== 'granted') return;
sub = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicKey),
});
return;
}
const permission = await Notification.requestPermission();
if (permission !== 'granted') return;
const sub = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicKey)
});
// Always re-register subscription with the server (keeps it fresh on mobile)
await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('tc_token') || sessionStorage.getItem('tc_token')}` },
body: JSON.stringify(sub.toJSON())
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
body: JSON.stringify(sub.toJSON()),
});
console.log('[Push] Subscribed');
console.log('[Push] Subscription registered');
} catch (e) {
console.warn('[Push] Subscription failed:', e.message);
}
})();
};
registerPush();
// Bug A fix: re-register push subscription when app returns to foreground
// Mobile browsers can drop push subscriptions when the app is backgrounded
const handleVisibility = () => {
if (document.visibilityState === 'visible') registerPush();
};
document.addEventListener('visibilitychange', handleVisibility);
return () => document.removeEventListener('visibilitychange', handleVisibility);
}, []);
// Socket message events to update group previews
@@ -113,10 +128,13 @@ export default function Chat() {
privateGroups: prev.privateGroups.map(updateGroup),
};
});
// Don't badge: message is from this user, or group is currently open
// Don't badge own messages
if (msg.user_id === user?.id) return;
// Bug C fix: count unread even in the active group when window is hidden/minimized
const groupIsActive = msg.group_id === activeGroupId;
const windowHidden = document.visibilityState === 'hidden';
setUnreadGroups(prev => {
if (msg.group_id === activeGroupId) return prev;
if (groupIsActive && !windowHidden) return prev; // visible & active: no badge
const next = new Map(prev);
next.set(msg.group_id, (next.get(msg.group_id) || 0) + 1);
return next;
@@ -173,12 +191,26 @@ export default function Chat() {
socket.on('group:deleted', handleGroupDeleted);
socket.on('group:updated', handleGroupUpdated);
// Bug B fix: on reconnect, reload groups to catch any messages missed while offline
const handleReconnect = () => { loadGroups(); };
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();
}
};
document.addEventListener('visibilitychange', handleVisibility);
return () => {
socket.off('message:new', handleNewMsg);
socket.off('notification:new', handleNotification);
socket.off('group:new', handleGroupNew);
socket.off('group:deleted', handleGroupDeleted);
socket.off('group:updated', handleGroupUpdated);
socket.off('connect', handleReconnect);
document.removeEventListener('visibilitychange', handleVisibility);
};
}, [socket, toast, activeGroupId, user, isMobile, loadGroups]);