diff --git a/src/components/background-service.jsx b/src/components/background-service.jsx index 96a6bb28..0a9665c2 100644 --- a/src/components/background-service.jsx +++ b/src/components/background-service.jsx @@ -13,55 +13,80 @@ export default memo(function BackgroundService({ isLoggedIn }) { // - WebSocket to receive notifications when page is visible const [visible, setVisible] = useState(true); usePageVisibility(setVisible); + const checkLatestNotification = async (masto, instance, skipCheckMarkers) => { + if (states.notificationsLast) { + const notificationsIterator = masto.v1.notifications.list({ + limit: 1, + sinceId: states.notificationsLast.id, + }); + const { value: notifications } = await notificationsIterator.next(); + if (notifications?.length) { + if (skipCheckMarkers) { + states.notificationsShowNew = true; + } else { + let lastReadId; + try { + const markers = await masto.v1.markers.fetch({ + timeline: 'notifications', + }); + lastReadId = markers?.notifications?.lastReadId; + } catch (e) {} + if (lastReadId) { + states.notificationsShowNew = notifications[0].id !== lastReadId; + } else { + states.notificationsShowNew = true; + } + } + } + } + }; + useEffect(() => { let sub; + let pollNotifications; if (isLoggedIn && visible) { const { masto, streaming, instance } = api(); (async () => { // 1. Get the latest notification - if (states.notificationsLast) { - const notificationsIterator = masto.v1.notifications.list({ - limit: 1, - since_id: states.notificationsLast.id, - }); - const { value: notifications } = await notificationsIterator.next(); - if (notifications?.length) { - let lastReadId; - try { - const markers = await masto.v1.markers.fetch({ - timeline: 'notifications', - }); - lastReadId = markers?.notifications?.lastReadId; - } catch (e) {} - if (lastReadId) { - states.notificationsShowNew = notifications[0].id !== lastReadId; - } else { + await checkLatestNotification(masto, instance); + + let hasStreaming = false; + // 2. Start streaming + if (streaming) { + try { + hasStreaming = true; + sub = streaming.user.notification.subscribe(); + console.log('🎏 Streaming notification', sub); + for await (const entry of sub) { + if (!sub) break; + console.log('🔔🔔 Notification entry', entry); + if (entry.event === 'notification') { + console.log('🔔🔔 Notification', entry); + saveStatus(entry.payload, instance, { + skipThreading: true, + }); + } states.notificationsShowNew = true; } + } catch (e) { + hasStreaming = false; + console.error(e); } } - // 2. Start streaming - if (streaming) { - sub = streaming.user.notification.subscribe(); - console.log('🎏 Streaming notification', sub); - for await (const entry of sub) { - if (!sub) break; - console.log('🔔🔔 Notification entry', entry); - if (entry.event === 'notification') { - console.log('🔔🔔 Notification', entry); - saveStatus(entry.payload, instance, { - skipThreading: true, - }); - } - states.notificationsShowNew = true; - } + if (!hasStreaming) { + console.log('🎏 Streaming failed, fallback to polling'); + // Fallback to polling every minute + pollNotifications = setInterval(() => { + checkLatestNotification(masto, instance, true); + }, 1000 * 60); } })(); } return () => { sub?.unsubscribe?.(); sub = null; + clearInterval(pollNotifications); }; }, [visible, isLoggedIn]);