Skip to content

Commit 138cc99

Browse files
committed
refactor
1 parent 2e4add4 commit 138cc99

File tree

3 files changed

+106
-137
lines changed

3 files changed

+106
-137
lines changed

routers/web/user/notification.go

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package user
55

66
import (
7-
"errors"
87
"fmt"
98
"net/http"
109
"net/url"
@@ -34,58 +33,42 @@ const (
3433
tplNotificationSubscriptions templates.TplName = "user/notification/notification_subscriptions"
3534
)
3635

37-
// Notifications is the notifications page
36+
// Notifications is the notification list page
3837
func Notifications(ctx *context.Context) {
39-
getNotifications(ctx)
38+
prepareUserNotificationsData(ctx)
4039
if ctx.Written() {
4140
return
4241
}
4342
if ctx.FormBool("div-only") {
44-
ctx.Data["SequenceNumber"] = ctx.FormString("sequence-number")
4543
ctx.HTML(http.StatusOK, tplNotificationDiv)
4644
return
4745
}
4846
ctx.HTML(http.StatusOK, tplNotification)
4947
}
5048

51-
func getNotifications(ctx *context.Context) {
52-
var (
53-
keyword = ctx.FormTrim("q")
54-
status activities_model.NotificationStatus
55-
page = ctx.FormInt("page")
56-
perPage = ctx.FormInt("perPage")
57-
)
58-
if page < 1 {
59-
page = 1
60-
}
61-
if perPage < 1 {
62-
perPage = 20
63-
}
64-
65-
switch keyword {
66-
case "read":
67-
status = activities_model.NotificationStatusRead
68-
default:
69-
status = activities_model.NotificationStatusUnread
70-
}
49+
func prepareUserNotificationsData(ctx *context.Context) {
50+
pageType := ctx.FormString("type", ctx.FormString("q")) // "q" is the legacy query parameter for "page type"
51+
page := min(1, ctx.FormInt("page"))
52+
perPage := util.IfZero(ctx.FormInt("perPage"), 20)
53+
queryStatus := util.Iif(pageType == "read", activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread)
7154

7255
total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
7356
UserID: ctx.Doer.ID,
74-
Status: []activities_model.NotificationStatus{status},
57+
Status: []activities_model.NotificationStatus{queryStatus},
7558
})
7659
if err != nil {
7760
ctx.ServerError("ErrGetNotificationCount", err)
7861
return
7962
}
8063

81-
// redirect to last page if request page is more than total pages
64+
// redirect to the last page if request page is more than total pages
8265
pager := context.NewPagination(int(total), perPage, page, 5)
8366
if pager.Paginater.Current() < page {
8467
ctx.Redirect(fmt.Sprintf("%s/notifications?q=%s&page=%d", setting.AppSubURL, url.QueryEscape(ctx.FormString("q")), pager.Paginater.Current()))
8568
return
8669
}
8770

88-
statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned}
71+
statuses := []activities_model.NotificationStatus{queryStatus, activities_model.NotificationStatusPinned}
8972
nls, err := db.Find[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
9073
ListOptions: db.ListOptions{
9174
PageSize: perPage,
@@ -142,51 +125,37 @@ func getNotifications(ctx *context.Context) {
142125
}
143126

144127
ctx.Data["Title"] = ctx.Tr("notifications")
145-
ctx.Data["Keyword"] = keyword
146-
ctx.Data["Status"] = status
128+
ctx.Data["PageType"] = pageType
147129
ctx.Data["Notifications"] = notifications
148-
130+
ctx.Data["Link"] = setting.AppSubURL + "/notifications"
131+
ctx.Data["SequenceNumber"] = ctx.FormString("sequence-number")
149132
pager.AddParamFromRequest(ctx.Req)
150133
ctx.Data["Page"] = pager
151134
}
152135

153136
// NotificationStatusPost is a route for changing the status of a notification
154137
func NotificationStatusPost(ctx *context.Context) {
155-
var (
156-
notificationID = ctx.FormInt64("notification_id")
157-
statusStr = ctx.FormString("status")
158-
status activities_model.NotificationStatus
159-
)
160-
161-
switch statusStr {
162-
case "read":
138+
notificationID := ctx.FormInt64("notification_id")
139+
var status activities_model.NotificationStatus
140+
switch ctx.FormString("notification_action") {
141+
case "mark_as_read":
163142
status = activities_model.NotificationStatusRead
164-
case "unread":
143+
case "mark_as_unread":
165144
status = activities_model.NotificationStatusUnread
166-
case "pinned":
145+
case "pin":
167146
status = activities_model.NotificationStatusPinned
168147
default:
169-
ctx.ServerError("InvalidNotificationStatus", errors.New("Invalid notification status"))
170-
return
148+
return // ignore user's invalid input
171149
}
172-
173150
if _, err := activities_model.SetNotificationStatus(ctx, notificationID, ctx.Doer, status); err != nil {
174151
ctx.ServerError("SetNotificationStatus", err)
175152
return
176153
}
177154

178-
if !ctx.FormBool("noredirect") {
179-
url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, url.QueryEscape(ctx.FormString("page")))
180-
ctx.Redirect(url, http.StatusSeeOther)
181-
}
182-
183-
getNotifications(ctx)
155+
prepareUserNotificationsData(ctx)
184156
if ctx.Written() {
185157
return
186158
}
187-
ctx.Data["Link"] = setting.AppSubURL + "/notifications"
188-
ctx.Data["SequenceNumber"] = ctx.Req.PostFormValue("sequence-number")
189-
190159
ctx.HTML(http.StatusOK, tplNotificationDiv)
191160
}
192161

templates/user/notification/notification_div.tmpl

Lines changed: 74 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,97 @@
11
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
22
<div class="ui container">
3+
{{$statusUnread := 1}}{{$statusRead := 2}}{{$statusPinned := 3}}
34
{{$notificationUnreadCount := call .PageGlobalData.GetNotificationUnreadCount}}
4-
<div class="tw-flex tw-items-center tw-justify-between tw-mb-[--page-spacing]">
5+
{{$pageTypeIsRead := eq $.PageType "read"}}
6+
<div class="flex-text-block tw-justify-between tw-mb-[--page-spacing]">
57
<div class="small-menu-items ui compact tiny menu">
6-
<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
8+
<a class="{{if not $pageTypeIsRead}}active{{end}} item" href="{{AppSubUrl}}/notifications?type=unread">
79
{{ctx.Locale.Tr "notification.unread"}}
810
<div class="notifications-unread-count ui label {{if not $notificationUnreadCount}}tw-hidden{{end}}">{{$notificationUnreadCount}}</div>
911
</a>
10-
<a class="{{if eq .Status 2}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=read">
12+
<a class="{{if $pageTypeIsRead}}active{{end}} item" href="{{AppSubUrl}}/notifications?type=read">
1113
{{ctx.Locale.Tr "notification.read"}}
1214
</a>
1315
</div>
14-
{{if and (eq .Status 1)}}
16+
{{if and (not $pageTypeIsRead) $notificationUnreadCount}}
1517
<form action="{{AppSubUrl}}/notifications/purge" method="post">
1618
{{$.CsrfTokenHtml}}
17-
<div class="{{if not $notificationUnreadCount}}tw-hidden{{end}}">
18-
<button class="ui mini button primary tw-mr-0" title="{{ctx.Locale.Tr "notification.mark_all_as_read"}}">
19-
{{svg "octicon-checklist"}}
20-
</button>
21-
</div>
19+
<button class="ui mini button primary tw-mr-0" title="{{ctx.Locale.Tr "notification.mark_all_as_read"}}">
20+
{{svg "octicon-checklist"}}
21+
</button>
2222
</form>
2323
{{end}}
2424
</div>
25-
<div class="tw-p-0">
26-
<div id="notification_table">
27-
{{if not .Notifications}}
28-
<div class="tw-flex tw-items-center tw-flex-col tw-p-4">
29-
{{svg "octicon-inbox" 56 "tw-mb-4"}}
30-
{{if eq .Status 1}}
31-
{{ctx.Locale.Tr "notification.no_unread"}}
25+
<div id="notification_table">
26+
{{range $one := .Notifications}}
27+
<div class="notifications-item" id="notification_{{$one.ID}}" data-status="{{$one.Status}}">
28+
<div class="tw-self-start tw-mt-[2px]">
29+
{{if $one.Issue}}
30+
{{template "shared/issueicon" $one.Issue}}
3231
{{else}}
33-
{{ctx.Locale.Tr "notification.no_read"}}
32+
{{svg "octicon-repo" 16 "text grey"}}
3433
{{end}}
3534
</div>
36-
{{else}}
37-
{{range $notification := .Notifications}}
38-
<div class="notifications-item tw-flex tw-items-center tw-flex-wrap tw-gap-2 tw-p-2" id="notification_{{.ID}}" data-status="{{.Status}}">
39-
<div class="notifications-icon tw-ml-2 tw-mr-1 tw-self-start tw-mt-1">
40-
{{if .Issue}}
41-
{{template "shared/issueicon" .Issue}}
42-
{{else}}
43-
{{svg "octicon-repo" 16 "text grey"}}
44-
{{end}}
45-
</div>
46-
<a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}">
47-
<div class="notifications-top-row tw-text-13 tw-break-anywhere">
48-
{{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}}
49-
{{if eq .Status 3}}
50-
{{svg "octicon-pin" 13 "text blue tw-mt-0.5 tw-ml-1"}}
51-
{{end}}
52-
</div>
53-
<div class="notifications-bottom-row tw-text-16 tw-py-0.5">
54-
<span class="issue-title tw-break-anywhere">
55-
{{if .Issue}}
56-
{{.Issue.Title | ctx.RenderUtils.RenderIssueSimpleTitle}}
57-
{{else}}
58-
{{.Repository.FullName}}
59-
{{end}}
60-
</span>
61-
</div>
62-
</a>
63-
<div class="notifications-updated tw-items-center tw-mr-2">
64-
{{if .Issue}}
65-
{{DateUtils.TimeSince .Issue.UpdatedUnix}}
66-
{{else}}
67-
{{DateUtils.TimeSince .UpdatedUnix}}
68-
{{end}}
69-
</div>
70-
<div class="notifications-buttons tw-items-center tw-justify-end tw-gap-1 tw-px-1">
71-
{{if ne .Status 3}}
72-
<button type="button" class="btn interact-bg tw-p-2" title="{{ctx.Locale.Tr "notification.pin"}}"
73-
hx-post="{{AppSubUrl}}/notifications/status?q={{ $.Keyword }}"
74-
hx-target="#notification_div"
75-
hx-swap="outerHTML"
76-
hx-vals='{"notification_id": "{{.ID}}", "status": "pinned", "page": "{{$.Page.Paginater.Current}}", "_csrf": "{{$.CsrfToken}}", "noredirect": "true" }'
77-
{{if eq .Status 3}}disabled{{end}}
78-
>
79-
{{svg "octicon-pin"}}
80-
</button>
81-
{{end}}
82-
{{if or (eq .Status 1) (eq .Status 3)}}
83-
<button type="button" class="btn interact-bg tw-p-2" title="{{ctx.Locale.Tr "notification.mark_as_read"}}"
84-
hx-post="{{AppSubUrl}}/notifications/status?q={{ $.Keyword }}"
85-
hx-target="#notification_div"
86-
hx-swap="outerHTML"
87-
hx-vals='{"notification_id": "{{.ID}}", "status": "read", "page": "{{$.Page.Paginater.Current}}", "_csrf": "{{$.CsrfToken}}", "noredirect": "true"}'
88-
>
89-
{{svg "octicon-check"}}
90-
</button>
91-
{{else if eq .Status 2}}
92-
<button type="button" class="btn interact-bg tw-p-2" title="{{ctx.Locale.Tr "notification.mark_as_unread"}}"
93-
hx-post="{{AppSubUrl}}/notifications/status?q={{ $.Keyword }}"
94-
hx-target="#notification_div"
95-
hx-swap="outerHTML"
96-
hx-vals='{"notification_id": "{{.ID}}", "status": "unread", "page": "{{$.Page.Paginater.Current}}", "_csrf": "{{$.CsrfToken}}", "noredirect": "true"}'
97-
>
98-
{{svg "octicon-bell"}}
99-
</button>
100-
{{end}}
101-
</div>
35+
<a class="notifications-link silenced tw-flex-1" href="{{$one.Link ctx}}">
36+
<div class="flex-text-block tw-text-[0.95em]">
37+
{{$one.Repository.FullName}} {{if $one.Issue}}<span class="text light-3">#{{$one.Issue.Index}}</span>{{end}}
38+
{{if eq $one.Status $statusPinned}}
39+
{{svg "octicon-pin" 13 "text blue"}}
40+
{{end}}
41+
</div>
42+
<div class="tw-text-16 tw-py-0.5">
43+
{{if $one.Issue}}
44+
{{$one.Issue.Title | ctx.RenderUtils.RenderIssueSimpleTitle}}
45+
{{else}}
46+
{{$one.Repository.FullName}}
47+
{{end}}
10248
</div>
49+
</a>
50+
<div class="notifications-updated flex-text-inline">
51+
{{if $one.Issue}}
52+
{{DateUtils.TimeSince $one.Issue.UpdatedUnix}}
53+
{{else}}
54+
{{DateUtils.TimeSince $one.UpdatedUnix}}
55+
{{end}}
56+
</div>
57+
<form class="notifications-buttons" action="{{AppSubUrl}}/notifications/status?type={{$.PageType}}" method="post"
58+
hx-boost="true" hx-target="#notification_div" hx-swap="outerHTML"
59+
>
60+
{{$.CsrfTokenHtml}}
61+
<input type="hidden" name="notification_id" value="{{$one.ID}}">
62+
<input type="hidden" name="page" value="{{$.Page.Paginater.Current}}">
63+
{{if ne $one.Status $statusPinned}}
64+
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "notification.pin"}}"
65+
name="notification_action" value="pin"
66+
>
67+
{{svg "octicon-pin"}}
68+
</button>
69+
{{end}}
70+
{{if or (eq $one.Status $statusUnread) (eq $one.Status $statusPinned)}}
71+
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "notification.mark_as_read"}}"
72+
name="notification_action" value="mark_as_read"
73+
>
74+
{{svg "octicon-check"}}
75+
</button>
76+
{{else if eq $one.Status $statusRead}}
77+
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "notification.mark_as_unread"}}"
78+
name="notification_action" value="mark_as_unread"
79+
>
80+
{{svg "octicon-bell"}}
81+
</button>
82+
{{end}}
83+
</form>
84+
</div>
85+
{{else}}
86+
<div class="empty-placeholder">
87+
{{svg "octicon-inbox" 56 "tw-mb-4"}}
88+
{{if eq .Status 1}}
89+
{{ctx.Locale.Tr "notification.no_unread"}}
90+
{{else}}
91+
{{ctx.Locale.Tr "notification.no_read"}}
10392
{{end}}
104-
{{end}}
105-
</div>
93+
</div>
94+
{{end}}
10695
</div>
10796
{{template "base/paginate" .}}
10897
</div>

web_src/css/user.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@
114114
border-radius: var(--border-radius);
115115
}
116116

117+
.notifications-item {
118+
display: flex;
119+
align-items: center;
120+
flex-wrap: wrap;
121+
gap: 0.5em;
122+
padding: 0.5em 1em;
123+
}
124+
117125
.notifications-item:hover {
118126
background: var(--color-hover);
119127
}
@@ -129,6 +137,9 @@
129137

130138
.notifications-item:hover .notifications-buttons {
131139
display: flex;
140+
align-items: center;
141+
justify-content: end;
142+
gap: 0.25em;
132143
}
133144

134145
.notifications-item:hover .notifications-updated {

0 commit comments

Comments
 (0)