Skip to content

Prepare common tmpl functions in a middleware #33957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions routers/common/pagetmpl.go
Original file line number Diff line number Diff line change
@@ -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
}
}
38 changes: 0 additions & 38 deletions routers/web/repo/issue_stopwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
27 changes: 0 additions & 27 deletions routers/web/user/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package user

import (
goctx "context"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 1 addition & 3 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down
25 changes: 14 additions & 11 deletions templates/base/head_navbar.tmpl
Original file line number Diff line number Diff line change
@@ -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 -->
Expand All @@ -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>
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -186,23 +189,23 @@
{{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"
class="ui button mini compact basic icon tw-mr-0"
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"
Expand Down
2 changes: 1 addition & 1 deletion templates/user/notification/notification_div.tmpl
Original file line number Diff line number Diff line change
@@ -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">
Expand Down