mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 09:31:53 +01:00 
			
		
		
		
	Prepare common tmpl functions in a middleware (#33957)
Fix the TODO in `routers/web/web.go`, and avoid the unnecessary `GetActiveStopwatch` SQL query in non-related route handlers.
This commit is contained in:
		
							parent
							
								
									32258e0f22
								
							
						
					
					
						commit
						41c946a66f
					
				
							
								
								
									
										75
									
								
								routers/common/pagetmpl.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								routers/common/pagetmpl.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| 	} | ||||
| } | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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...) | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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"> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user