From 092299891f07326f65b27e050c7aa8a9595b3445 Mon Sep 17 00:00:00 2001 From: zeripath Date: Fri, 19 Feb 2021 10:05:35 +0000 Subject: [PATCH] Move the stopwatches to the eventsource stream (#14588) Move the stopwatches to the eventsource stream Use the /user/events eventsource to update the stopwatches instead of polling /api/v1/user/stopwatches if the eventsource is enabled. Signed-off-by: Andrew Thornton --- routers/events/events.go | 32 +++++++++++ .../js/features/eventsource.sharedworker.js | 1 + web_src/js/features/stopwatch.js | 57 ++++++++++++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/routers/events/events.go b/routers/events/events.go index a1131f29e3..2b78d8603b 100644 --- a/routers/events/events.go +++ b/routers/events/events.go @@ -5,13 +5,17 @@ package events import ( + "encoding/json" "net/http" "time" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/user" ) @@ -55,6 +59,8 @@ func Events(ctx *context.Context) { timer := time.NewTicker(30 * time.Second) + stopwatchTimer := time.NewTicker(setting.UI.Notification.MinTimeout) + loop: for { select { @@ -75,6 +81,32 @@ loop: case <-shutdownCtx.Done(): go unregister() break loop + case <-stopwatchTimer.C: + sws, err := models.GetUserStopwatches(ctx.User.ID, models.ListOptions{}) + if err != nil { + log.Error("Unable to GetUserStopwatches: %v", err) + continue + } + apiSWs, err := convert.ToStopWatches(sws) + if err != nil { + log.Error("Unable to APIFormat stopwatches: %v", err) + continue + } + dataBs, err := json.Marshal(apiSWs) + if err != nil { + log.Error("Unable to marshal stopwatches: %v", err) + continue + } + _, err = (&eventsource.Event{ + Name: "stopwatches", + Data: string(dataBs), + }).WriteTo(ctx.Resp) + if err != nil { + log.Error("Unable to write to EventStream for user %s: %v", ctx.User.Name, err) + go unregister() + break loop + } + ctx.Resp.Flush() case event, ok := <-messageChan: if !ok { break loop diff --git a/web_src/js/features/eventsource.sharedworker.js b/web_src/js/features/eventsource.sharedworker.js index bb4f628f6c..a94551e826 100644 --- a/web_src/js/features/eventsource.sharedworker.js +++ b/web_src/js/features/eventsource.sharedworker.js @@ -12,6 +12,7 @@ class Source { this.listen('open'); this.listen('logout'); this.listen('notification-count'); + this.listen('stopwatches'); this.listen('error'); } diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js index d500fb5f0f..433f042a1f 100644 --- a/web_src/js/features/stopwatch.js +++ b/web_src/js/features/stopwatch.js @@ -17,7 +17,58 @@ export async function initStopwatch() { $(this).parent().trigger('submit'); }); - if (!stopwatchEl || NotificationSettings.MinTimeout <= 0) { + if (!stopwatchEl) { + return; + } + + if (NotificationSettings.EventSourceUpdateTime > 0 && !!window.EventSource && window.SharedWorker) { + // Try to connect to the event source via the shared worker first + const worker = new SharedWorker(`${__webpack_public_path__}js/eventsource.sharedworker.js`, 'notification-worker'); + worker.addEventListener('error', (event) => { + console.error(event); + }); + worker.port.onmessageerror = () => { + console.error('Unable to deserialize message'); + }; + worker.port.postMessage({ + type: 'start', + url: `${window.location.origin}${AppSubUrl}/user/events`, + }); + worker.port.addEventListener('message', (event) => { + if (!event.data || !event.data.type) { + console.error(event); + return; + } + if (event.data.type === 'stopwatches') { + updateStopwatchData(JSON.parse(event.data.data)); + } else if (event.data.type === 'error') { + console.error(event.data); + } else if (event.data.type === 'logout') { + if (event.data !== 'here') { + return; + } + worker.port.postMessage({ + type: 'close', + }); + worker.port.close(); + window.location.href = AppSubUrl; + } + }); + worker.port.addEventListener('error', (e) => { + console.error(e); + }); + worker.port.start(); + window.addEventListener('beforeunload', () => { + worker.port.postMessage({ + type: 'close', + }); + worker.port.close(); + }); + + return; + } + + if (NotificationSettings.MinTimeout <= 0) { return; } @@ -59,6 +110,10 @@ async function updateStopwatch() { updateTimeInterval = null; } + return updateStopwatchData(data); +} + +async function updateStopwatchData(data) { const watch = data[0]; const btnEl = $('.active-stopwatch-trigger'); if (!watch) {