Skip to content

Commit b10c416

Browse files
zeripathsilverwind
andauthored
Use AJAX for notifications table (#10961)
* Use AJAX for notifications table Signed-off-by: Andrew Thornton <[email protected]> * move to separate js Signed-off-by: Andrew Thornton <[email protected]> * placate golangci-lint Signed-off-by: Andrew Thornton <[email protected]> * Add autoupdating notification count Signed-off-by: Andrew Thornton <[email protected]> * Fix wipeall Signed-off-by: Andrew Thornton <[email protected]> * placate tests Signed-off-by: Andrew Thornton <[email protected]> * Try hidden Signed-off-by: Andrew Thornton <[email protected]> * Try hide and hidden Signed-off-by: Andrew Thornton <[email protected]> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <[email protected]> * string comparison! Signed-off-by: Andrew Thornton <[email protected]> * as per @silverwind Signed-off-by: Andrew Thornton <[email protected]> * add configurability as per @6543 Signed-off-by: Andrew Thornton <[email protected]> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <[email protected]> * Use CSRF header not query Signed-off-by: Andrew Thornton <[email protected]> * Further JS improvements Fix @Etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <[email protected]> Signed-off-by: Andrew Thornton <[email protected]> * Simplify the notification count fns Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: silverwind <[email protected]>
1 parent e74c4e1 commit b10c416

File tree

12 files changed

+331
-140
lines changed

12 files changed

+331
-140
lines changed

.eslintrc

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ rules:
5555
no-param-reassign: [0]
5656
no-plusplus: [0]
5757
no-restricted-syntax: [0]
58+
no-return-await: [0]
5859
no-shadow: [0]
5960
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
6061
no-use-before-define: [0]

custom/conf/app.ini.sample

+8
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ AUTHOR = Gitea - Git with a cup of tea
200200
DESCRIPTION = Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go
201201
KEYWORDS = go,git,self-hosted,gitea
202202

203+
[ui.notification]
204+
; Control how often notification is queried to update the notification
205+
; The timeout will increase to MAX_TIMEOUT in TIMEOUT_STEPs if the notification count is unchanged
206+
; Set MIN_TIMEOUT to 0 to turn off
207+
MIN_TIMEOUT = 10s
208+
MAX_TIMEOUT = 60s
209+
TIMEOUT_STEP = 10s
210+
203211
[markdown]
204212
; Render soft line breaks as hard line breaks, which means a single newline character between
205213
; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not

docs/content/doc/advanced/config-cheat-sheet.en-us.md

+7
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
140140
- `NOTICE_PAGING_NUM`: **25**: Number of notices that are shown in one page.
141141
- `ORG_PAGING_NUM`: **50**: Number of organizations that are shown in one page.
142142

143+
### UI - Notification (`ui.notification`)
144+
145+
- `MIN_TIMEOUT`: **10s**: These options control how often notification is queried to update the notification count. On page load the notification count will be checked after `MIN_TIMEOUT`. The timeout will increase to `MAX_TIMEOUT` by `TIMEOUT_STEP` if the notification count is unchanged. Set MIN_TIMEOUT to 0 to turn off.
146+
- `MAX_TIMEOUT`: **60s**.
147+
- `TIMEOUT_STEP`: **10s**.
148+
149+
143150
## Markdown (`markdown`)
144151

145152
- `ENABLE_HARD_LINE_BREAK`: **true**: Render soft line breaks as hard line breaks, which

modules/setting/setting.go

+15
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ var (
181181
SearchRepoDescription bool
182182
UseServiceWorker bool
183183

184+
Notification struct {
185+
MinTimeout time.Duration
186+
TimeoutStep time.Duration
187+
MaxTimeout time.Duration
188+
} `ini:"ui.notification"`
189+
184190
Admin struct {
185191
UserPagingNum int
186192
RepoPagingNum int
@@ -209,6 +215,15 @@ var (
209215
DefaultTheme: `gitea`,
210216
Themes: []string{`gitea`, `arc-green`},
211217
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
218+
Notification: struct {
219+
MinTimeout time.Duration
220+
TimeoutStep time.Duration
221+
MaxTimeout time.Duration
222+
}{
223+
MinTimeout: 10 * time.Second,
224+
TimeoutStep: 10 * time.Second,
225+
MaxTimeout: 60 * time.Second,
226+
},
212227
Admin: struct {
213228
UserPagingNum int
214229
RepoPagingNum int

modules/templates/helper.go

+7
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,13 @@ func NewFuncMap() []template.FuncMap {
278278
return ""
279279
}
280280
},
281+
"NotificationSettings": func() map[string]int {
282+
return map[string]int{
283+
"MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
284+
"TimeoutStep": int(setting.UI.Notification.TimeoutStep / time.Millisecond),
285+
"MaxTimeout": int(setting.UI.Notification.MaxTimeout / time.Millisecond),
286+
}
287+
},
281288
"contain": func(s []int64, id int64) bool {
282289
for i := 0; i < len(s); i++ {
283290
if s[i] == id {

routers/user/notification.go

+36-17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package user
77
import (
88
"errors"
99
"fmt"
10+
"net/http"
1011
"strconv"
1112
"strings"
1213

@@ -17,7 +18,8 @@ import (
1718
)
1819

1920
const (
20-
tplNotification base.TplName = "user/notification/notification"
21+
tplNotification base.TplName = "user/notification/notification"
22+
tplNotificationDiv base.TplName = "user/notification/notification_div"
2123
)
2224

2325
// GetNotificationCount is the middleware that sets the notification count in the context
@@ -30,17 +32,31 @@ func GetNotificationCount(c *context.Context) {
3032
return
3133
}
3234

33-
count, err := models.GetNotificationCount(c.User, models.NotificationStatusUnread)
34-
if err != nil {
35-
c.ServerError("GetNotificationCount", err)
36-
return
37-
}
35+
c.Data["NotificationUnreadCount"] = func() int64 {
36+
count, err := models.GetNotificationCount(c.User, models.NotificationStatusUnread)
37+
if err != nil {
38+
c.ServerError("GetNotificationCount", err)
39+
return -1
40+
}
3841

39-
c.Data["NotificationUnreadCount"] = count
42+
return count
43+
}
4044
}
4145

4246
// Notifications is the notifications page
4347
func Notifications(c *context.Context) {
48+
getNotifications(c)
49+
if c.Written() {
50+
return
51+
}
52+
if c.QueryBool("div-only") {
53+
c.HTML(http.StatusOK, tplNotificationDiv)
54+
return
55+
}
56+
c.HTML(http.StatusOK, tplNotification)
57+
}
58+
59+
func getNotifications(c *context.Context) {
4460
var (
4561
keyword = strings.Trim(c.Query("q"), " ")
4662
status models.NotificationStatus
@@ -115,19 +131,13 @@ func Notifications(c *context.Context) {
115131
c.Flash.Error(fmt.Sprintf("ERROR: %d notifications were removed due to missing parts - check the logs", failCount))
116132
}
117133

118-
title := c.Tr("notifications")
119-
if status == models.NotificationStatusUnread && total > 0 {
120-
title = fmt.Sprintf("(%d) %s", total, title)
121-
}
122-
c.Data["Title"] = title
134+
c.Data["Title"] = c.Tr("notifications")
123135
c.Data["Keyword"] = keyword
124136
c.Data["Status"] = status
125137
c.Data["Notifications"] = notifications
126138

127139
pager.SetDefaultParams(c)
128140
c.Data["Page"] = pager
129-
130-
c.HTML(200, tplNotification)
131141
}
132142

133143
// NotificationStatusPost is a route for changing the status of a notification
@@ -155,8 +165,17 @@ func NotificationStatusPost(c *context.Context) {
155165
return
156166
}
157167

158-
url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.Query("page"))
159-
c.Redirect(url, 303)
168+
if !c.QueryBool("noredirect") {
169+
url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.Query("page"))
170+
c.Redirect(url, http.StatusSeeOther)
171+
}
172+
173+
getNotifications(c)
174+
if c.Written() {
175+
return
176+
}
177+
178+
c.HTML(http.StatusOK, tplNotificationDiv)
160179
}
161180

162181
// NotificationPurgePost is a route for 'purging' the list of notifications - marking all unread as read
@@ -168,5 +187,5 @@ func NotificationPurgePost(c *context.Context) {
168187
}
169188

170189
url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
171-
c.Redirect(url, 303)
190+
c.Redirect(url, http.StatusSeeOther)
172191
}

templates/base/head.tmpl

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@
9494
U2F: {{if .RequireU2F}}true{{else}}false{{end}},
9595
Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}},
9696
heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}},
97+
NotificationSettings: {
98+
MinTimeout: {{NotificationSettings.MinTimeout}},
99+
TimeoutStep: {{NotificationSettings.TimeoutStep}},
100+
MaxTimeout: {{NotificationSettings.MaxTimeout}},
101+
},
97102
};
98103
</script>
99104
<link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png">

templates/base/head_navbar.tmpl

+5-6
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,11 @@
4646
<span class="text">
4747
<span class="fitted">{{svg "octicon-bell" 16}}</span>
4848
<span class="sr-mobile-only">{{.i18n.Tr "notifications"}}</span>
49-
50-
{{if .NotificationUnreadCount}}
51-
<span class="ui red label">
52-
{{.NotificationUnreadCount}}
53-
</span>
54-
{{end}}
49+
{{$notificationUnreadCount := 0}}
50+
{{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}}
51+
<span class="ui red label {{if not $notificationUnreadCount}}hidden{{end}} notification_count">
52+
{{$notificationUnreadCount}}
53+
</span>
5554
</span>
5655
</a>
5756

+1-117
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,3 @@
11
{{template "base/head" .}}
2-
3-
<div class="user notification">
4-
<div class="ui container">
5-
<h1 class="ui dividing header">{{.i18n.Tr "notification.notifications"}}</h1>
6-
7-
<div class="ui top attached tabular menu">
8-
<a href="{{AppSubUrl}}/notifications?q=unread" class="{{if eq .Status 1}}active{{end}} item">
9-
{{.i18n.Tr "notification.unread"}}
10-
{{if .NotificationUnreadCount}}
11-
<div class="ui label">{{.NotificationUnreadCount}}</div>
12-
{{end}}
13-
</a>
14-
<a href="{{AppSubUrl}}/notifications?q=read" class="{{if eq .Status 2}}active{{end}} item">
15-
{{.i18n.Tr "notification.read"}}
16-
</a>
17-
{{if and (eq .Status 1) (.NotificationUnreadCount)}}
18-
<form action="{{AppSubUrl}}/notifications/purge" method="POST" style="margin-left: auto;">
19-
{{$.CsrfTokenHtml}}
20-
<button class="ui mini button primary" title='{{$.i18n.Tr "notification.mark_all_as_read"}}'>
21-
{{svg "octicon-checklist" 16}}
22-
</button>
23-
</form>
24-
{{end}}
25-
</div>
26-
<div class="ui bottom attached active tab segment">
27-
{{if eq (len .Notifications) 0}}
28-
{{if eq .Status 1}}
29-
{{.i18n.Tr "notification.no_unread"}}
30-
{{else}}
31-
{{.i18n.Tr "notification.no_read"}}
32-
{{end}}
33-
{{else}}
34-
<table class="ui unstackable striped very compact small selectable table">
35-
<tbody>
36-
{{range $notification := .Notifications}}
37-
{{$issue := $notification.Issue}}
38-
{{$repo := $notification.Repository}}
39-
{{$repoOwner := $repo.MustOwner}}
40-
41-
<tr data-href="{{$notification.HTMLURL}}">
42-
<td class="collapsing">
43-
{{if eq $notification.Status 3}}
44-
<span class="blue">{{svg "octicon-pin" 16}}</span>
45-
{{else if $issue.IsPull}}
46-
{{if $issue.IsClosed}}
47-
{{if $issue.GetPullRequest.HasMerged}}
48-
<span class="purple">{{svg "octicon-git-merge" 16}}</span>
49-
{{else}}
50-
<span class="red">{{svg "octicon-git-pull-request" 16}}</span>
51-
{{end}}
52-
{{else}}
53-
<span class="green">{{svg "octicon-git-pull-request" 16}}</span>
54-
{{end}}
55-
{{else}}
56-
{{if $issue.IsClosed}}
57-
<span class="red">{{svg "octicon-issue-closed" 16}}</span>
58-
{{else}}
59-
<span class="green">{{svg "octicon-issue-opened" 16}}</span>
60-
{{end}}
61-
{{end}}
62-
</td>
63-
<td class="eleven wide">
64-
<a class="item" href="{{$notification.HTMLURL}}">
65-
#{{$issue.Index}} - {{$issue.Title}}
66-
</a>
67-
</td>
68-
<td>
69-
<a class="item" href="{{AppSubUrl}}/{{$repoOwner.Name}}/{{$repo.Name}}">
70-
{{$repoOwner.Name}}/{{$repo.Name}}
71-
</a>
72-
</td>
73-
<td class="collapsing">
74-
{{if ne $notification.Status 3}}
75-
<form action="{{AppSubUrl}}/notifications/status" method="POST">
76-
{{$.CsrfTokenHtml}}
77-
<input type="hidden" name="notification_id" value="{{$notification.ID}}" />
78-
<input type="hidden" name="status" value="pinned" />
79-
<button class="ui mini button" title='{{$.i18n.Tr "notification.pin"}}'>
80-
{{svg "octicon-pin" 16}}
81-
</button>
82-
</form>
83-
{{end}}
84-
</td>
85-
<td class="collapsing">
86-
{{if or (eq $notification.Status 1) (eq $notification.Status 3)}}
87-
<form action="{{AppSubUrl}}/notifications/status" method="POST">
88-
{{$.CsrfTokenHtml}}
89-
<input type="hidden" name="notification_id" value="{{$notification.ID}}" />
90-
<input type="hidden" name="status" value="read" />
91-
<input type="hidden" name="page" value="{{$.Page.Paginater.Current}}" />
92-
<button class="ui mini button" title='{{$.i18n.Tr "notification.mark_as_read"}}'>
93-
{{svg "octicon-check" 16}}
94-
</button>
95-
</form>
96-
{{else if eq $notification.Status 2}}
97-
<form action="{{AppSubUrl}}/notifications/status" method="POST">
98-
{{$.CsrfTokenHtml}}
99-
<input type="hidden" name="notification_id" value="{{$notification.ID}}" />
100-
<input type="hidden" name="status" value="unread" />
101-
<input type="hidden" name="page" value="{{$.Page.Paginater.Current}}" />
102-
<button class="ui mini button" title='{{$.i18n.Tr "notification.mark_as_unread"}}'>
103-
{{svg "octicon-bell" 16}}
104-
</button>
105-
</form>
106-
{{end}}
107-
</td>
108-
</tr>
109-
{{end}}
110-
</tbody>
111-
</table>
112-
{{end}}
113-
</div>
114-
115-
{{template "base/paginate" .}}
116-
</div>
117-
</div>
118-
2+
{{template "user/notification/notification_div" .}}
1193
{{template "base/footer" .}}

0 commit comments

Comments
 (0)