diff --git a/routers/common/pagetmpl.go b/routers/common/pagetmpl.go new file mode 100644 index 0000000000..52c9fceba3 --- /dev/null +++ b/routers/common/pagetmpl.go @@ -0,0 +1,75 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package common + +import ( + goctx "context" + "errors" + + activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" +) + +// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering +type StopwatchTmplInfo struct { + IssueLink string + RepoSlug string + IssueIndex int64 + Seconds int64 +} + +func getActiveStopwatch(goCtx goctx.Context) *StopwatchTmplInfo { + ctx := context.GetWebContext(goCtx) + if ctx.Doer == nil { + return nil + } + + _, sw, issue, err := issues_model.HasUserStopwatch(ctx, ctx.Doer.ID) + if err != nil { + if !errors.Is(err, goctx.Canceled) { + log.Error("Unable to HasUserStopwatch for user:%-v: %v", ctx.Doer, err) + } + return nil + } + + if sw == nil || sw.ID == 0 { + return nil + } + + return &StopwatchTmplInfo{ + issue.Link(), + issue.Repo.FullName(), + issue.Index, + sw.Seconds() + 1, // ensure time is never zero in ui + } +} + +func notificationUnreadCount(goCtx goctx.Context) int64 { + ctx := context.GetWebContext(goCtx) + if ctx.Doer == nil { + return 0 + } + count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) + if err != nil { + if !errors.Is(err, goctx.Canceled) { + log.Error("Unable to find notification for user:%-v: %v", ctx.Doer, err) + } + return 0 + } + return count +} + +func PageTmplFunctions(ctx *context.Context) { + if ctx.IsSigned { + // defer the function call to the last moment when the tmpl renders + ctx.Data["NotificationUnreadCount"] = notificationUnreadCount + ctx.Data["GetActiveStopwatch"] = getActiveStopwatch + } +} diff --git a/routers/web/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go index 73e279e0a6..5a8d203771 100644 --- a/routers/web/repo/issue_stopwatch.go +++ b/routers/web/repo/issue_stopwatch.go @@ -4,8 +4,6 @@ package repo import ( - "strings" - "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/eventsource" @@ -72,39 +70,3 @@ func CancelStopwatch(c *context.Context) { c.JSONRedirect("") } - -// GetActiveStopwatch is the middleware that sets .ActiveStopwatch on context -func GetActiveStopwatch(ctx *context.Context) { - if strings.HasPrefix(ctx.Req.URL.Path, "/api") { - return - } - - if !ctx.IsSigned { - return - } - - _, sw, issue, err := issues_model.HasUserStopwatch(ctx, ctx.Doer.ID) - if err != nil { - ctx.ServerError("HasUserStopwatch", err) - return - } - - if sw == nil || sw.ID == 0 { - return - } - - ctx.Data["ActiveStopwatch"] = StopwatchTmplInfo{ - issue.Link(), - issue.Repo.FullName(), - issue.Index, - sw.Seconds() + 1, // ensure time is never zero in ui - } -} - -// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering -type StopwatchTmplInfo struct { - IssueLink string - RepoSlug string - IssueIndex int64 - Seconds int64 -} diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 1c91ff6364..f0c4390852 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -4,7 +4,6 @@ package user import ( - goctx "context" "errors" "fmt" "net/http" @@ -35,32 +34,6 @@ const ( tplNotificationSubscriptions templates.TplName = "user/notification/notification_subscriptions" ) -// GetNotificationCount is the middleware that sets the notification count in the context -func GetNotificationCount(ctx *context.Context) { - if strings.HasPrefix(ctx.Req.URL.Path, "/api") { - return - } - - if !ctx.IsSigned { - return - } - - ctx.Data["NotificationUnreadCount"] = func() int64 { - count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ - UserID: ctx.Doer.ID, - Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, - }) - if err != nil { - if err != goctx.Canceled { - log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err) - } - return -1 - } - - return count - } -} - // Notifications is the notifications page func Notifications(ctx *context.Context) { getNotifications(ctx) diff --git a/routers/web/web.go b/routers/web/web.go index 463f486250..4d635f04f0 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -280,10 +280,8 @@ func Routes() *web.Router { routes.Get("/api/swagger", append(mid, misc.Swagger)...) // Render V1 by default } - // TODO: These really seem like things that could be folded into Contexter or as helper functions - mid = append(mid, user.GetNotificationCount) - mid = append(mid, repo.GetActiveStopwatch) mid = append(mid, goGet) + mid = append(mid, common.PageTmplFunctions) others := web.NewRouter() others.Use(mid...) diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index eab6c00840..35e14d38d3 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -1,8 +1,11 @@ {{$notificationUnreadCount := 0}} {{if and .IsSigned .NotificationUnreadCount}} - {{$notificationUnreadCount = call .NotificationUnreadCount}} + {{$notificationUnreadCount = call .NotificationUnreadCount ctx}} +{{end}} +{{$activeStopwatch := NIL}} +{{if and .IsSigned EnableTimetracking .GetActiveStopwatch}} + {{$activeStopwatch = call .GetActiveStopwatch ctx}} {{end}} - <nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}"> <div class="navbar-left"> <!-- the logo --> @@ -12,8 +15,8 @@ <!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column --> <div class="ui secondary menu navbar-mobile-right only-mobile"> - {{if and .IsSigned EnableTimetracking .ActiveStopwatch}} - <a id="mobile-stopwatch-icon" class="active-stopwatch item" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}"> + {{if $activeStopwatch}} + <a id="mobile-stopwatch-icon" class="active-stopwatch item" href="{{$activeStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{$activeStopwatch.Seconds}}"> <div class="tw-relative"> {{svg "octicon-stopwatch"}} <span class="header-stopwatch-dot"></span> @@ -82,8 +85,8 @@ </div><!-- end content avatar menu --> </div><!-- end dropdown avatar menu --> {{else if .IsSigned}} - {{if and EnableTimetracking .ActiveStopwatch}} - <a class="item not-mobile active-stopwatch" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}"> + {{if $activeStopwatch}} + <a class="item not-mobile active-stopwatch" href="{{$activeStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{$activeStopwatch.Seconds}}"> <div class="tw-relative"> {{svg "octicon-stopwatch"}} <span class="header-stopwatch-dot"></span> @@ -186,15 +189,15 @@ {{end}} </div><!-- end full right menu --> - {{if and .IsSigned EnableTimetracking .ActiveStopwatch}} + {{if $activeStopwatch}} <div class="active-stopwatch-popup tippy-target"> <div class="tw-flex tw-items-center tw-gap-2 tw-p-3"> - <a class="stopwatch-link tw-flex tw-items-center tw-gap-2 muted" href="{{.ActiveStopwatch.IssueLink}}"> + <a class="stopwatch-link tw-flex tw-items-center tw-gap-2 muted" href="{{$activeStopwatch.IssueLink}}"> {{svg "octicon-issue-opened" 16}} - <span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span> + <span class="stopwatch-issue">{{$activeStopwatch.RepoSlug}}#{{$activeStopwatch.IssueIndex}}</span> </a> <div class="tw-flex tw-gap-1"> - <form class="stopwatch-commit form-fetch-action" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/toggle"> + <form class="stopwatch-commit form-fetch-action" method="post" action="{{$activeStopwatch.IssueLink}}/times/stopwatch/toggle"> {{.CsrfTokenHtml}} <button type="submit" @@ -202,7 +205,7 @@ data-tooltip-content="{{ctx.Locale.Tr "repo.issues.stop_tracking"}}" >{{svg "octicon-square-fill"}}</button> </form> - <form class="stopwatch-cancel form-fetch-action" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/cancel"> + <form class="stopwatch-cancel form-fetch-action" method="post" action="{{$activeStopwatch.IssueLink}}/times/stopwatch/cancel"> {{.CsrfTokenHtml}} <button type="submit" diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index 0d2371a358..9af2cd53b3 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -1,6 +1,6 @@ <div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}"> <div class="ui container"> - {{$notificationUnreadCount := call .NotificationUnreadCount}} + {{$notificationUnreadCount := call .NotificationUnreadCount ctx}} <div class="tw-flex tw-items-center tw-justify-between tw-mb-[--page-spacing]"> <div class="small-menu-items ui compact tiny menu"> <a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">