Skip to content
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

Use AJAX for notifications table #10961

Merged
merged 19 commits into from
Apr 24, 2020
Merged
Show file tree
Hide file tree
Changes from 16 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
8 changes: 8 additions & 0 deletions custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ AUTHOR = Gitea - Git with a cup of tea
DESCRIPTION = Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go
KEYWORDS = go,git,self-hosted,gitea

[ui.notification]
; Control how often notification is queried to update the notification
; The timeout will increase to MAX_TIMEOUT in TIMEOUT_STEPs if the notification count is unchanged
; Set MIN_TIMEOUT to 0 to turn off
MIN_TIMEOUT = 10s
MAX_TIMEOUT = 60s
TIMEOUT_STEP = 10s

[markdown]
; Render soft line breaks as hard line breaks, which means a single newline character between
; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not
Expand Down
7 changes: 7 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `NOTICE_PAGING_NUM`: **25**: Number of notices that are shown in one page.
- `ORG_PAGING_NUM`: **50**: Number of organizations that are shown in one page.

### UI - Notification (`ui.notification`)

- `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.
- `MAX_TIMEOUT`: **60s**.
- `TIMEOUT_STEP`: **10s**.


## Markdown (`markdown`)

- `ENABLE_HARD_LINE_BREAK`: **true**: Render soft line breaks as hard line breaks, which
Expand Down
15 changes: 15 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ var (
SearchRepoDescription bool
UseServiceWorker bool

Notification struct {
MinTimeout time.Duration
TimeoutStep time.Duration
MaxTimeout time.Duration
} `ini:"ui.notification"`

Admin struct {
UserPagingNum int
RepoPagingNum int
Expand Down Expand Up @@ -208,6 +214,15 @@ var (
DefaultTheme: `gitea`,
Themes: []string{`gitea`, `arc-green`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
Notification: struct {
MinTimeout time.Duration
TimeoutStep time.Duration
MaxTimeout time.Duration
}{
MinTimeout: 10 * time.Second,
TimeoutStep: 10 * time.Second,
MaxTimeout: 60 * time.Second,
},
Admin: struct {
UserPagingNum int
RepoPagingNum int
Expand Down
7 changes: 7 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@ func NewFuncMap() []template.FuncMap {
return ""
}
},
"NotificationSettings": func() map[string]int {
return map[string]int{
"MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
"TimeoutStep": int(setting.UI.Notification.TimeoutStep / time.Millisecond),
"MaxTimeout": int(setting.UI.Notification.MaxTimeout / time.Millisecond),
}
},
"contain": func(s []int64, id int64) bool {
for i := 0; i < len(s); i++ {
if s[i] == id {
Expand Down
49 changes: 32 additions & 17 deletions routers/user/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package user
import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"

Expand All @@ -17,7 +18,8 @@ import (
)

const (
tplNotification base.TplName = "user/notification/notification"
tplNotification base.TplName = "user/notification/notification"
tplNotificationDiv base.TplName = "user/notification/notification_div"
)

// GetNotificationCount is the middleware that sets the notification count in the context
Expand All @@ -30,17 +32,27 @@ func GetNotificationCount(c *context.Context) {
return
}

count, err := models.GetNotificationCount(c.User, models.NotificationStatusUnread)
if err != nil {
c.ServerError("GetNotificationCount", err)
return
}
c.Data["NotificationUnreadCount"] = func() int64 {
count, err := models.GetNotificationCount(c.User, models.NotificationStatusUnread)
if err != nil {
c.ServerError("GetNotificationCount", err)
return -1
}

c.Data["NotificationUnreadCount"] = count
return count
}
}

// Notifications is the notifications page
func Notifications(c *context.Context) {
getNotifications(c)
if c.Written() {
return
}
c.HTML(http.StatusOK, tplNotification)
}

func getNotifications(c *context.Context) {
var (
keyword = strings.Trim(c.Query("q"), " ")
status models.NotificationStatus
Expand Down Expand Up @@ -115,19 +127,13 @@ func Notifications(c *context.Context) {
c.Flash.Error(fmt.Sprintf("ERROR: %d notifications were removed due to missing parts - check the logs", failCount))
}

title := c.Tr("notifications")
if status == models.NotificationStatusUnread && total > 0 {
title = fmt.Sprintf("(%d) %s", total, title)
}
c.Data["Title"] = title
c.Data["Title"] = c.Tr("notifications")
c.Data["Keyword"] = keyword
c.Data["Status"] = status
c.Data["Notifications"] = notifications

pager.SetDefaultParams(c)
c.Data["Page"] = pager

c.HTML(200, tplNotification)
}

// NotificationStatusPost is a route for changing the status of a notification
Expand Down Expand Up @@ -155,8 +161,17 @@ func NotificationStatusPost(c *context.Context) {
return
}

url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.Query("page"))
c.Redirect(url, 303)
if !c.QueryBool("noredirect") {
url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.Query("page"))
c.Redirect(url, http.StatusSeeOther)
}

getNotifications(c)
if c.Written() {
return
}

c.HTML(http.StatusOK, tplNotificationDiv)
}

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

url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
c.Redirect(url, 303)
c.Redirect(url, http.StatusSeeOther)
}
5 changes: 5 additions & 0 deletions templates/base/head.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@
U2F: {{if .RequireU2F}}true{{else}}false{{end}},
Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}},
heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}},
NotificationSettings: {
MinTimeout: {{NotificationSettings.MinTimeout}},
TimeoutStep: {{NotificationSettings.TimeoutStep}},
MaxTimeout: {{NotificationSettings.MaxTimeout}},
},
};
</script>
<link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png">
Expand Down
11 changes: 5 additions & 6 deletions templates/base/head_navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,11 @@
<span class="text">
<span class="fitted">{{svg "octicon-bell" 16}}</span>
<span class="sr-mobile-only">{{.i18n.Tr "notifications"}}</span>

{{if .NotificationUnreadCount}}
<span class="ui red label">
{{.NotificationUnreadCount}}
</span>
{{end}}
{{$notificationUnreadCount := 0}}
{{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}}
<span class="ui red label {{if not $notificationUnreadCount}}hidden{{end}} notification_count">
{{$notificationUnreadCount}}
</span>
</span>
</a>

Expand Down
105 changes: 9 additions & 96 deletions templates/user/notification/notification.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,113 +5,26 @@
<h1 class="ui dividing header">{{.i18n.Tr "notification.notifications"}}</h1>

<div class="ui top attached tabular menu">
{{ $notificationUnreadCount := call .NotificationUnreadCount}}
<a href="{{AppSubUrl}}/notifications?q=unread" class="{{if eq .Status 1}}active{{end}} item">
{{.i18n.Tr "notification.unread"}}
{{if .NotificationUnreadCount}}
<div class="ui label">{{.NotificationUnreadCount}}</div>
{{end}}
<div class="ui label {{if not $notificationUnreadCount}}hidden{{end}} notification_count">{{$notificationUnreadCount}}</div>
</a>
<a href="{{AppSubUrl}}/notifications?q=read" class="{{if eq .Status 2}}active{{end}} item">
{{.i18n.Tr "notification.read"}}
</a>
{{if and (eq .Status 1) (.NotificationUnreadCount)}}
{{if and (eq .Status 1)}}
<form action="{{AppSubUrl}}/notifications/purge" method="POST" style="margin-left: auto;">
{{$.CsrfTokenHtml}}
<button class="ui mini button primary" title='{{$.i18n.Tr "notification.mark_all_as_read"}}'>
{{svg "octicon-checklist" 16}}
</button>
<div class="{{if not $notificationUnreadCount}}hide{{end}} notification_dependent">
zeripath marked this conversation as resolved.
Show resolved Hide resolved
<button class="ui mini button primary" title='{{$.i18n.Tr "notification.mark_all_as_read"}}'>
{{svg "octicon-checklist" 16}}
</button>
</div>
</form>
{{end}}
</div>
<div class="ui bottom attached active tab segment">
{{if eq (len .Notifications) 0}}
{{if eq .Status 1}}
{{.i18n.Tr "notification.no_unread"}}
{{else}}
{{.i18n.Tr "notification.no_read"}}
{{end}}
{{else}}
<table class="ui unstackable striped very compact small selectable table">
<tbody>
{{range $notification := .Notifications}}
{{$issue := $notification.Issue}}
{{$repo := $notification.Repository}}
{{$repoOwner := $repo.MustOwner}}

<tr data-href="{{$notification.HTMLURL}}">
<td class="collapsing">
{{if eq $notification.Status 3}}
<span class="blue">{{svg "octicon-pin" 16}}</span>
{{else if $issue.IsPull}}
{{if $issue.IsClosed}}
{{if $issue.GetPullRequest.HasMerged}}
<span class="purple">{{svg "octicon-git-merge" 16}}</span>
{{else}}
<span class="red">{{svg "octicon-git-pull-request" 16}}</span>
{{end}}
{{else}}
<span class="green">{{svg "octicon-git-pull-request" 16}}</span>
{{end}}
{{else}}
{{if $issue.IsClosed}}
<span class="red">{{svg "octicon-issue-closed" 16}}</span>
{{else}}
<span class="green">{{svg "octicon-issue-opened" 16}}</span>
{{end}}
{{end}}
</td>
<td class="eleven wide">
<a class="item" href="{{$notification.HTMLURL}}">
#{{$issue.Index}} - {{$issue.Title}}
</a>
</td>
<td>
<a class="item" href="{{AppSubUrl}}/{{$repoOwner.Name}}/{{$repo.Name}}">
{{$repoOwner.Name}}/{{$repo.Name}}
</a>
</td>
<td class="collapsing">
{{if ne $notification.Status 3}}
<form action="{{AppSubUrl}}/notifications/status" method="POST">
{{$.CsrfTokenHtml}}
<input type="hidden" name="notification_id" value="{{$notification.ID}}" />
<input type="hidden" name="status" value="pinned" />
<button class="ui mini button" title='{{$.i18n.Tr "notification.pin"}}'>
{{svg "octicon-pin" 16}}
</button>
</form>
{{end}}
</td>
<td class="collapsing">
{{if or (eq $notification.Status 1) (eq $notification.Status 3)}}
<form action="{{AppSubUrl}}/notifications/status" method="POST">
{{$.CsrfTokenHtml}}
<input type="hidden" name="notification_id" value="{{$notification.ID}}" />
<input type="hidden" name="status" value="read" />
<input type="hidden" name="page" value="{{$.Page.Paginater.Current}}" />
<button class="ui mini button" title='{{$.i18n.Tr "notification.mark_as_read"}}'>
{{svg "octicon-check" 16}}
</button>
</form>
{{else if eq $notification.Status 2}}
<form action="{{AppSubUrl}}/notifications/status" method="POST">
{{$.CsrfTokenHtml}}
<input type="hidden" name="notification_id" value="{{$notification.ID}}" />
<input type="hidden" name="status" value="unread" />
<input type="hidden" name="page" value="{{$.Page.Paginater.Current}}" />
<button class="ui mini button" title='{{$.i18n.Tr "notification.mark_as_unread"}}'>
{{svg "octicon-bell" 16}}
</button>
</form>
{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>

{{template "user/notification/notification_div" .}}
{{template "base/paginate" .}}
</div>
</div>
Expand Down
Loading