From 4e912a61c8083498427eba930a0a99eba91be0ed Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 25 Apr 2022 20:45:22 +0000 Subject: [PATCH] Improve Stopwatch behavior (#18930) - Don't send empty stopwatch over and over again, only send once. - Stop interval to update stopwatch's timer when there is no more stopwatch. --- models/issue_stopwatch.go | 32 ++++++++++++++++++++++++++++ modules/eventsource/manager_run.go | 27 +++++++++++++++++++++++ routers/web/events/events.go | 33 ----------------------------- routers/web/repo/issue_stopwatch.go | 14 ++++++++++++ web_src/js/features/stopwatch.js | 4 ++++ 5 files changed, 77 insertions(+), 33 deletions(-) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index 80dd44642e..81459ba446 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -66,6 +66,38 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex return } +// UserIDCount is a simple coalition of UserID and Count +type UserStopwatch struct { + UserID int64 + StopWatches []*Stopwatch +} + +// GetUIDsAndNotificationCounts between the two provided times +func GetUIDsAndStopwatch() ([]*UserStopwatch, error) { + sws := []*Stopwatch{} + if err := db.GetEngine(db.DefaultContext).Find(&sws); err != nil { + return nil, err + } + if len(sws) == 0 { + return []*UserStopwatch{}, nil + } + + lastUserID := int64(-1) + res := []*UserStopwatch{} + for _, sw := range sws { + if lastUserID == sw.UserID { + lastUserStopwatch := res[len(res)-1] + lastUserStopwatch.StopWatches = append(lastUserStopwatch.StopWatches, sw) + } else { + res = append(res, &UserStopwatch{ + UserID: sw.UserID, + StopWatches: []*Stopwatch{sw}, + }) + } + } + return res, nil +} + // GetUserStopwatches return list of all stopwatches of a user func GetUserStopwatches(userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) { sws := make([]*Stopwatch, 0, 8) diff --git a/modules/eventsource/manager_run.go b/modules/eventsource/manager_run.go index 9af5c9e78a..127979ad63 100644 --- a/modules/eventsource/manager_run.go +++ b/modules/eventsource/manager_run.go @@ -9,7 +9,9 @@ import ( "time" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/graceful" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" @@ -80,6 +82,31 @@ loop: }) } then = now + + if setting.Service.EnableTimetracking { + usersStopwatches, err := models.GetUIDsAndStopwatch() + if err != nil { + log.Error("Unable to get GetUIDsAndStopwatch: %v", err) + return + } + + for _, userStopwatches := range usersStopwatches { + apiSWs, err := convert.ToStopWatches(userStopwatches.StopWatches) + 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 + } + m.SendMessage(userStopwatches.UserID, &Event{ + Name: "stopwatches", + Data: string(dataBs), + }) + } + } } } m.UnregisterAll() diff --git a/routers/web/events/events.go b/routers/web/events/events.go index 02d20550af..d8c6f38d02 100644 --- a/routers/web/events/events.go +++ b/routers/web/events/events.go @@ -8,15 +8,10 @@ import ( "net/http" "time" - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/db" "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/json" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/web/auth" ) @@ -71,8 +66,6 @@ func Events(ctx *context.Context) { timer := time.NewTicker(30 * time.Second) - stopwatchTimer := time.NewTicker(setting.UI.Notification.MinTimeout) - loop: for { select { @@ -93,32 +86,6 @@ loop: case <-shutdownCtx.Done(): go unregister() break loop - case <-stopwatchTimer.C: - sws, err := models.GetUserStopwatches(ctx.Doer.ID, db.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.Doer.Name, err) - go unregister() - break loop - } - ctx.Resp.Flush() case event, ok := <-messageChan: if !ok { break loop diff --git a/routers/web/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go index 7ef80e7725..83e4ecedbf 100644 --- a/routers/web/repo/issue_stopwatch.go +++ b/routers/web/repo/issue_stopwatch.go @@ -9,7 +9,9 @@ import ( "strings" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/eventsource" ) // IssueStopwatch creates or stops a stopwatch for the given issue. @@ -59,6 +61,18 @@ func CancelStopwatch(c *context.Context) { return } + stopwatches, err := models.GetUserStopwatches(c.Doer.ID, db.ListOptions{}) + if err != nil { + c.ServerError("GetUserStopwatches", err) + return + } + if len(stopwatches) == 0 { + eventsource.GetManager().SendMessage(c.Doer.ID, &eventsource.Event{ + Name: "stopwatches", + Data: "{}", + }) + } + url := issue.HTMLURL() c.Redirect(url, http.StatusSeeOther) } diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js index f86a801038..c47ba22124 100644 --- a/web_src/js/features/stopwatch.js +++ b/web_src/js/features/stopwatch.js @@ -127,6 +127,10 @@ function updateStopwatchData(data) { const watch = data[0]; const btnEl = $('.active-stopwatch-trigger'); if (!watch) { + if (updateTimeInterval) { + clearInterval(updateTimeInterval); + updateTimeInterval = null; + } btnEl.addClass('hidden'); } else { const {repo_owner_name, repo_name, issue_index, seconds} = watch;