Files

126 lines
4.1 KiB
TypeScript
Raw Permalink Normal View History

import {GET} from '../modules/fetch.ts';
import {toggleElem, createElementFromHTML} from '../utils/dom.ts';
import {UserEventsSharedWorker} from '../modules/worker.ts';
2020-04-24 04:57:38 +01:00
const {appSubUrl, notificationSettings} = window.config;
let notificationSequenceNumber = 0;
async function receiveUpdateCount(event: MessageEvent<{type: string, data: string}>) {
2020-07-03 10:55:36 +01:00
try {
const data = JSON.parse(event.data.data);
for (const count of document.querySelectorAll('.notification_count')) {
2024-03-24 19:23:38 +01:00
count.classList.toggle('tw-hidden', data.Count === 0);
count.textContent = `${data.Count}`;
2020-07-03 10:55:36 +01:00
}
await updateNotificationTable();
} catch (error) {
console.error(error, event);
}
}
export function initNotificationCount() {
2024-06-10 12:12:31 +02:00
if (!document.querySelector('.notification_count')) return;
2020-04-24 04:57:38 +01:00
let usingPeriodicPoller = false;
const startPeriodicPoller = (timeout: number, lastCount?: number) => {
if (timeout <= 0 || !Number.isFinite(timeout)) return;
usingPeriodicPoller = true;
2024-06-10 12:12:31 +02:00
lastCount = lastCount ?? getCurrentCount();
setTimeout(async () => {
await updateNotificationCountWithCallback(startPeriodicPoller, timeout, lastCount);
}, timeout);
};
if (notificationSettings.EventSourceUpdateTime > 0 && window.EventSource && window.SharedWorker) {
2020-07-03 10:55:36 +01:00
// Try to connect to the event source via the shared worker first
const worker = new UserEventsSharedWorker('notification-worker');
worker.addMessageEventListener((event: MessageEvent) => {
if (event.data.type === 'no-event-source') {
if (!usingPeriodicPoller) startPeriodicPoller(notificationSettings.MinTimeout);
} else if (event.data.type === 'notification-count') {
receiveUpdateCount(event); // no await
2020-12-27 15:24:27 +01:00
}
});
worker.startPort();
2020-12-27 15:24:27 +01:00
return;
2020-04-24 04:57:38 +01:00
}
2020-05-07 22:49:00 +01:00
startPeriodicPoller(notificationSettings.MinTimeout);
2020-04-24 04:57:38 +01:00
}
2024-06-10 12:12:31 +02:00
function getCurrentCount() {
2025-12-03 03:13:16 +01:00
return Number(document.querySelector('.notification_count')!.textContent ?? '0');
2024-06-10 12:12:31 +02:00
}
async function updateNotificationCountWithCallback(callback: (timeout: number, newCount: number) => void, timeout: number, lastCount: number) {
2024-06-10 12:12:31 +02:00
const currentCount = getCurrentCount();
2020-04-24 04:57:38 +01:00
if (lastCount !== currentCount) {
callback(notificationSettings.MinTimeout, currentCount);
2020-04-24 04:57:38 +01:00
return;
}
const newCount = await updateNotificationCount();
let needsUpdate = false;
if (lastCount !== newCount) {
needsUpdate = true;
timeout = notificationSettings.MinTimeout;
} else if (timeout < notificationSettings.MaxTimeout) {
timeout += notificationSettings.TimeoutStep;
2020-04-24 04:57:38 +01:00
}
callback(timeout, newCount);
2020-05-07 22:49:00 +01:00
if (needsUpdate) {
await updateNotificationTable();
}
}
2020-04-24 04:57:38 +01:00
2020-05-07 22:49:00 +01:00
async function updateNotificationTable() {
let notificationDiv = document.querySelector('#notification_div');
2024-03-16 15:25:27 +02:00
if (notificationDiv) {
try {
const params = new URLSearchParams(window.location.search);
params.set('div-only', 'true');
params.set('sequence-number', String(++notificationSequenceNumber));
const response = await GET(`${appSubUrl}/notifications?${params.toString()}`);
if (!response.ok) {
throw new Error('Failed to fetch notification table');
2020-04-24 04:57:38 +01:00
}
const data = await response.text();
2025-01-06 17:38:42 +08:00
const el = createElementFromHTML(data);
2025-12-03 03:13:16 +01:00
if (parseInt(el.getAttribute('data-sequence-number')!) === notificationSequenceNumber) {
2024-03-16 15:25:27 +02:00
notificationDiv.outerHTML = data;
2025-12-03 03:13:16 +01:00
notificationDiv = document.querySelector('#notification_div')!;
window.htmx.process(notificationDiv); // when using htmx, we must always remember to process the new content changed by us
}
} catch (error) {
console.error(error);
}
2020-04-24 04:57:38 +01:00
}
}
async function updateNotificationCount(): Promise<number> {
try {
const response = await GET(`${appSubUrl}/notifications/new`);
2020-04-24 04:57:38 +01:00
if (!response.ok) {
throw new Error('Failed to fetch notification count');
}
2020-04-24 04:57:38 +01:00
const data = await response.json();
2020-04-24 04:57:38 +01:00
toggleElem('.notification_count', data.new !== 0);
2020-04-24 04:57:38 +01:00
for (const el of document.querySelectorAll('.notification_count')) {
el.textContent = `${data.new}`;
}
2020-04-24 04:57:38 +01:00
return data.new as number;
} catch (error) {
console.error(error);
return 0;
}
2020-04-24 04:57:38 +01:00
}