mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
4.1 KiB
146 lines
4.1 KiB
class Source { |
|
url: string; |
|
eventSource: EventSource | null; |
|
listening: Record<string, boolean>; |
|
clients: Array<MessagePort>; |
|
|
|
constructor(url: string) { |
|
this.url = url; |
|
this.eventSource = new EventSource(url); |
|
this.listening = {}; |
|
this.clients = []; |
|
this.listen('open'); |
|
this.listen('close'); |
|
this.listen('logout'); |
|
this.listen('notification-count'); |
|
this.listen('stopwatches'); |
|
this.listen('error'); |
|
} |
|
|
|
register(port: MessagePort) { |
|
if (this.clients.includes(port)) return; |
|
|
|
this.clients.push(port); |
|
|
|
port.postMessage({ |
|
type: 'status', |
|
message: `registered to ${this.url}`, |
|
}); |
|
} |
|
|
|
deregister(port: MessagePort) { |
|
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: string) { |
|
if (this.listening[eventType]) return; |
|
this.listening[eventType] = true; |
|
this.eventSource?.addEventListener(eventType, (event) => { |
|
this.notifyClients({ |
|
type: eventType, |
|
data: event.data, |
|
}); |
|
}); |
|
} |
|
|
|
notifyClients(event: {type: string, data: any}) { |
|
for (const client of this.clients) { |
|
client.postMessage(event); |
|
} |
|
} |
|
|
|
status(port: MessagePort) { |
|
port.postMessage({ |
|
type: 'status', |
|
message: `url: ${this.url} readyState: ${this.eventSource?.readyState}`, |
|
}); |
|
} |
|
} |
|
|
|
const sourcesByUrl = new Map<string, Source | null>(); |
|
const sourcesByPort = new Map<MessagePort, Source | null>(); |
|
|
|
// @ts-expect-error: typescript bug? |
|
self.addEventListener('connect', (e: MessageEvent) => { |
|
for (const port of e.ports) { |
|
port.addEventListener('message', (event) => { |
|
if (!self.EventSource) { |
|
// some browsers (like PaleMoon, Firefox<53) don't support EventSource in SharedWorkerGlobalScope. |
|
// this event handler needs EventSource when doing "new Source(url)", so just post a message back to the caller, |
|
// in case the caller would like to use a fallback method to do its work. |
|
port.postMessage({type: 'no-event-source'}); |
|
return; |
|
} |
|
if (event.data.type === 'start') { |
|
const url = event.data.url; |
|
let source = sourcesByUrl.get(url); |
|
if (source) { |
|
// we have a Source registered to this url |
|
source.register(port); |
|
sourcesByPort.set(port, source); |
|
return; |
|
} |
|
source = sourcesByPort.get(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.set(source.url, null); |
|
} |
|
} |
|
// Create a new Source |
|
source = new Source(url); |
|
source.register(port); |
|
sourcesByUrl.set(url, source); |
|
sourcesByPort.set(port, source); |
|
} else if (event.data.type === 'listen') { |
|
const source = sourcesByPort.get(port)!; |
|
source.listen(event.data.eventType); |
|
} else if (event.data.type === 'close') { |
|
const source = sourcesByPort.get(port); |
|
if (!source) return; |
|
|
|
const count = source.deregister(port); |
|
if (count === 0) { |
|
source.close(); |
|
sourcesByUrl.set(source.url, null); |
|
sourcesByPort.set(port, null); |
|
} |
|
} else if (event.data.type === 'status') { |
|
const source = sourcesByPort.get(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(); |
|
} |
|
});
|
|
|