mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	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.
This commit is contained in:
		@@ -66,6 +66,38 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex
 | 
				
			|||||||
	return
 | 
						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
 | 
					// GetUserStopwatches return list of all stopwatches of a user
 | 
				
			||||||
func GetUserStopwatches(userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) {
 | 
					func GetUserStopwatches(userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) {
 | 
				
			||||||
	sws := make([]*Stopwatch, 0, 8)
 | 
						sws := make([]*Stopwatch, 0, 8)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,9 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/convert"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
						"code.gitea.io/gitea/modules/graceful"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/process"
 | 
						"code.gitea.io/gitea/modules/process"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -80,6 +82,31 @@ loop:
 | 
				
			|||||||
				})
 | 
									})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			then = now
 | 
								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()
 | 
						m.UnregisterAll()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,15 +8,10 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/convert"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/eventsource"
 | 
						"code.gitea.io/gitea/modules/eventsource"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
						"code.gitea.io/gitea/modules/graceful"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/routers/web/auth"
 | 
						"code.gitea.io/gitea/routers/web/auth"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,8 +66,6 @@ func Events(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	timer := time.NewTicker(30 * time.Second)
 | 
						timer := time.NewTicker(30 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stopwatchTimer := time.NewTicker(setting.UI.Notification.MinTimeout)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
loop:
 | 
					loop:
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
@@ -93,32 +86,6 @@ loop:
 | 
				
			|||||||
		case <-shutdownCtx.Done():
 | 
							case <-shutdownCtx.Done():
 | 
				
			||||||
			go unregister()
 | 
								go unregister()
 | 
				
			||||||
			break loop
 | 
								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:
 | 
							case event, ok := <-messageChan:
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				break loop
 | 
									break loop
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,9 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/eventsource"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueStopwatch creates or stops a stopwatch for the given issue.
 | 
					// IssueStopwatch creates or stops a stopwatch for the given issue.
 | 
				
			||||||
@@ -59,6 +61,18 @@ func CancelStopwatch(c *context.Context) {
 | 
				
			|||||||
		return
 | 
							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()
 | 
						url := issue.HTMLURL()
 | 
				
			||||||
	c.Redirect(url, http.StatusSeeOther)
 | 
						c.Redirect(url, http.StatusSeeOther)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,6 +127,10 @@ function updateStopwatchData(data) {
 | 
				
			|||||||
  const watch = data[0];
 | 
					  const watch = data[0];
 | 
				
			||||||
  const btnEl = $('.active-stopwatch-trigger');
 | 
					  const btnEl = $('.active-stopwatch-trigger');
 | 
				
			||||||
  if (!watch) {
 | 
					  if (!watch) {
 | 
				
			||||||
 | 
					    if (updateTimeInterval) {
 | 
				
			||||||
 | 
					      clearInterval(updateTimeInterval);
 | 
				
			||||||
 | 
					      updateTimeInterval = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    btnEl.addClass('hidden');
 | 
					    btnEl.addClass('hidden');
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    const {repo_owner_name, repo_name, issue_index, seconds} = watch;
 | 
					    const {repo_owner_name, repo_name, issue_index, seconds} = watch;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user