mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-05 09:03:48 +08:00
e46dbec294
* Move EventSource to SharedWorker (#12095) Backport #12095 Move EventSource to use a SharedWorker. This prevents issues with HTTP/1.1 open browser connections from preventing gitea from opening multiple tabs. Also allow setting EVENT_SOURCE_UPDATE_TIME to disable EventSource updating Fix #11978 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: techknowlogick <techknowlogick@gitea.io> * Bugfix for shared event source For some reason our eslint configuration is not working correctly and a bug has become apparent when trying to backport this to 1.12. Signed-off-by: Andrew Thornton <art27@cantab.net> * Re-fix #12095 again Unfortunately some of the suggested changes to #12095 introduced bugs which due to caching behaviour of sharedworkers were not caught on simple tests. These are as follows: * Changing from simple for loop to use includes here: ```js register(port) { if (!this.clients.includes(port)) return; this.clients.push(port); port.postMessage({ type: 'status', message: `registered to ${this.url}`, }); } ``` The additional `!` prevents any clients from being added and should read: ```js if (this.clients.includes(port)) return; ``` * Dropping the use of jQuery `$(...)` selection and using DOM `querySelector` here: ```js async function receiveUpdateCount(event) { try { const data = JSON.parse(event.data); const notificationCount = document.querySelector('.notification_count'); if (data.Count > 0) { notificationCount.classList.remove('hidden'); } else { notificationCount.classList.add('hidden'); } notificationCount.text() = `${data.Count}`; await updateNotificationTable(); } catch (error) { console.error(error, event); } } ``` Requires that `notificationCount.text()` be changed to use `textContent` instead. Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
136 lines
3.3 KiB
JavaScript
136 lines
3.3 KiB
JavaScript
self.name = 'eventsource.sharedworker.js';
|
|
|
|
const sourcesByUrl = {};
|
|
const sourcesByPort = {};
|
|
|
|
class Source {
|
|
constructor(url) {
|
|
this.url = url;
|
|
this.eventSource = new EventSource(url);
|
|
this.listening = {};
|
|
this.clients = [];
|
|
this.listen('open');
|
|
this.listen('logout');
|
|
this.listen('notification-count');
|
|
this.listen('error');
|
|
}
|
|
|
|
register(port) {
|
|
if (this.clients.includes(port)) return;
|
|
|
|
this.clients.push(port);
|
|
|
|
port.postMessage({
|
|
type: 'status',
|
|
message: `registered to ${this.url}`,
|
|
});
|
|
}
|
|
|
|
deregister(port) {
|
|
const portIdx = this.clients.indexOf(port);
|
|
if (portIdx < 0) {
|
|
return this.clients.length;
|
|
}
|
|
this.clients.splice(portIdx, 1);
|
|
return this.clients.length;
|
|
}
|
|
|
|
close() {
|
|
if (!this.eventSource) return;
|
|
|
|
this.eventSource.close();
|
|
this.eventSource = null;
|
|
}
|
|
|
|
listen(eventType) {
|
|
if (this.listening[eventType]) return;
|
|
this.listening[eventType] = true;
|
|
const self = this;
|
|
this.eventSource.addEventListener(eventType, (event) => {
|
|
self.notifyClients({
|
|
type: eventType,
|
|
data: event.data
|
|
});
|
|
});
|
|
}
|
|
|
|
notifyClients(event) {
|
|
for (const client of this.clients) {
|
|
client.postMessage(event);
|
|
}
|
|
}
|
|
|
|
status(port) {
|
|
port.postMessage({
|
|
type: 'status',
|
|
message: `url: ${this.url} readyState: ${this.eventSource.readyState}`,
|
|
});
|
|
}
|
|
}
|
|
|
|
self.onconnect = (e) => {
|
|
for (const port of e.ports) {
|
|
port.addEventListener('message', (event) => {
|
|
if (event.data.type === 'start') {
|
|
const url = event.data.url;
|
|
if (sourcesByUrl[url]) {
|
|
// we have a Source registered to this url
|
|
const source = sourcesByUrl[url];
|
|
source.register(port);
|
|
sourcesByPort[port] = source;
|
|
return;
|
|
}
|
|
let source = sourcesByPort[port];
|
|
if (source) {
|
|
if (source.eventSource && source.url === url) return;
|
|
|
|
// How this has happened I don't understand...
|
|
// deregister from that source
|
|
const count = source.deregister(port);
|
|
// Clean-up
|
|
if (count === 0) {
|
|
source.close();
|
|
sourcesByUrl[source.url] = null;
|
|
}
|
|
}
|
|
// Create a new Source
|
|
source = new Source(url);
|
|
source.register(port);
|
|
sourcesByUrl[url] = source;
|
|
sourcesByPort[port] = source;
|
|
} else if (event.data.type === 'listen') {
|
|
const source = sourcesByPort[port];
|
|
source.listen(event.data.eventType);
|
|
} else if (event.data.type === 'close') {
|
|
const source = sourcesByPort[port];
|
|
|
|
if (!source) return;
|
|
|
|
const count = source.deregister(port);
|
|
if (count === 0) {
|
|
source.close();
|
|
sourcesByUrl[source.url] = null;
|
|
sourcesByPort[port] = null;
|
|
}
|
|
} else if (event.data.type === 'status') {
|
|
const source = sourcesByPort[port];
|
|
if (!source) {
|
|
port.postMessage({
|
|
type: 'status',
|
|
message: 'not connected',
|
|
});
|
|
return;
|
|
}
|
|
source.status(port);
|
|
} else {
|
|
// just send it back
|
|
port.postMessage({
|
|
type: 'error',
|
|
message: `received but don't know how to handle: ${event.data}`,
|
|
});
|
|
}
|
|
});
|
|
port.start();
|
|
}
|
|
};
|