phanpy/src/components/background-service.jsx

155 lines
4.8 KiB
React
Raw Normal View History

2024-08-13 10:26:23 +03:00
import { t, Trans } from '@lingui/macro';
import { memo } from 'preact/compat';
2023-09-02 13:19:09 +03:00
import { useEffect, useRef, useState } from 'preact/hooks';
import { useHotkeys } from 'react-hotkeys-hook';
2023-09-02 13:19:09 +03:00
import { api } from '../utils/api';
import showToast from '../utils/show-toast';
2023-09-02 13:19:09 +03:00
import states, { saveStatus } from '../utils/states';
import useInterval from '../utils/useInterval';
import usePageVisibility from '../utils/usePageVisibility';
const STREAMING_TIMEOUT = 1000 * 3; // 3 seconds
2024-08-04 05:09:33 +03:00
const POLL_INTERVAL = 20_000; // 20 seconds
export default memo(function BackgroundService({ isLoggedIn }) {
2023-09-02 13:19:09 +03:00
// Notifications service
// - WebSocket to receive notifications when page is visible
const [visible, setVisible] = useState(true);
const visibleTimeout = useRef();
usePageVisibility((visible) => {
clearTimeout(visibleTimeout.current);
if (visible) {
setVisible(true);
} else {
visibleTimeout.current = setTimeout(() => {
setVisible(false);
}, POLL_INTERVAL);
}
});
2023-11-01 16:31:43 +03:00
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 streamTimeout;
2023-11-01 16:31:43 +03:00
let pollNotifications;
if (isLoggedIn && visible) {
const { masto, streaming, instance } = api();
(async () => {
// 1. Get the latest notification
2023-11-01 16:31:43 +03:00
await checkLatestNotification(masto, instance);
2023-09-02 13:19:09 +03:00
2023-11-01 16:31:43 +03:00
let hasStreaming = false;
// 2. Start streaming
if (streaming) {
streamTimeout = setTimeout(() => {
(async () => {
try {
hasStreaming = true;
sub = streaming.user.notification.subscribe();
console.log('🎏 Streaming notification', sub);
for await (const entry of sub) {
if (!sub) break;
if (!visible) break;
console.log('🔔🔔 Notification entry', entry);
if (entry.event === 'notification') {
console.log('🔔🔔 Notification', entry);
saveStatus(entry.payload, instance, {
skipThreading: true,
});
}
states.notificationsShowNew = true;
}
2023-12-12 03:34:06 +03:00
console.log('💥 Streaming notification loop STOPPED');
} catch (e) {
hasStreaming = false;
console.error(e);
2023-11-01 16:31:43 +03:00
}
if (!hasStreaming) {
console.log('🎏 Streaming failed, fallback to polling');
pollNotifications = setInterval(() => {
checkLatestNotification(masto, instance, true);
}, POLL_INTERVAL);
}
})();
}, STREAMING_TIMEOUT);
2023-11-01 16:31:43 +03:00
}
})();
2023-09-02 13:19:09 +03:00
}
return () => {
sub?.unsubscribe?.();
sub = null;
clearTimeout(streamTimeout);
2023-11-01 16:31:43 +03:00
clearInterval(pollNotifications);
2023-09-02 13:19:09 +03:00
};
}, [visible, isLoggedIn]);
// Check for updates service
const lastCheckDate = useRef();
const checkForUpdates = () => {
lastCheckDate.current = Date.now();
console.log('✨ Check app update');
fetch('./version.json')
.then((r) => r.json())
.then((info) => {
if (info) states.appVersion = info;
})
.catch((e) => {
console.error(e);
});
};
useInterval(checkForUpdates, visible && 1000 * 60 * 30); // 30 minutes
usePageVisibility((visible) => {
if (visible) {
if (!lastCheckDate.current) {
checkForUpdates();
} else {
const diff = Date.now() - lastCheckDate.current;
if (diff > 1000 * 60 * 60) {
// 1 hour
checkForUpdates();
}
}
}
});
// Global keyboard shortcuts "service"
useHotkeys('shift+alt+k', () => {
const currentCloakMode = states.settings.cloakMode;
states.settings.cloakMode = !currentCloakMode;
showToast({
2024-08-13 10:26:23 +03:00
text: currentCloakMode ? t`Cloak mode disabled` : t`Cloak mode enabled`,
});
});
2023-09-02 13:19:09 +03:00
return null;
});