Add 'mark all read' option to notifications (#3097)
* Add 'mark all read' option to notifications Signed-off-by: Sasha Varlamov <sasha@sashavarlamov.com> * Fix exported comment Signed-off-by: Sasha Varlamov <sasha@sashavarlamov.com> * Format method comments Signed-off-by: Sasha Varlamov <sasha@sashavarlamov.com> * Fix exported comment Signed-off-by: Sasha Varlamov <sasha@sashavarlamov.com> Format method comments Signed-off-by: Sasha Varlamov <sasha@sashavarlamov.com> Tests for reactions (#3083) * Unit tests for reactions * Fix import order Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv> Fix reaction possition when there is attachments (#3099) Refactor notifications swap function * Accept change to drop beforeupdate call * Update purge notifications error message for consistency * Drop unnecessary check for mark all as read button * Remove debugging comment
This commit is contained in:
parent
1ed7f18815
commit
7ec6cddd27
7 changed files with 78 additions and 4 deletions
|
@ -19,3 +19,25 @@
|
||||||
issue_id: 2
|
issue_id: 2
|
||||||
created_unix: 946684800
|
created_unix: 946684800
|
||||||
updated_unix: 946684800
|
updated_unix: 946684800
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 3
|
||||||
|
user_id: 2
|
||||||
|
repo_id: 1
|
||||||
|
status: 3 # pinned
|
||||||
|
source: 1 # issue
|
||||||
|
updated_by: 1
|
||||||
|
issue_id: 2
|
||||||
|
created_unix: 946684800
|
||||||
|
updated_unix: 946684800
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 4
|
||||||
|
user_id: 2
|
||||||
|
repo_id: 1
|
||||||
|
status: 1 # unread
|
||||||
|
source: 1 # issue
|
||||||
|
updated_by: 1
|
||||||
|
issue_id: 2
|
||||||
|
created_unix: 946684800
|
||||||
|
updated_unix: 946684800
|
|
@ -311,3 +311,13 @@ func getNotificationByID(notificationID int64) (*Notification, error) {
|
||||||
|
|
||||||
return notification, nil
|
return notification, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateNotificationStatuses updates the statuses of all of a user's notifications that are of the currentStatus type to the desiredStatus
|
||||||
|
func UpdateNotificationStatuses(user *User, currentStatus NotificationStatus, desiredStatus NotificationStatus) error {
|
||||||
|
n := &Notification{Status: desiredStatus, UpdatedBy: user.ID}
|
||||||
|
_, err := x.
|
||||||
|
Where("user_id = ? AND status = ?", user.ID, currentStatus).
|
||||||
|
Cols("status", "updated_by", "updated_unix").
|
||||||
|
Update(n)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -31,9 +31,11 @@ func TestNotificationsForUser(t *testing.T) {
|
||||||
statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
|
statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
|
||||||
notfs, err := NotificationsForUser(user, statuses, 1, 10)
|
notfs, err := NotificationsForUser(user, statuses, 1, 10)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
if assert.Len(t, notfs, 1) {
|
if assert.Len(t, notfs, 2) {
|
||||||
assert.EqualValues(t, 2, notfs[0].ID)
|
assert.EqualValues(t, 2, notfs[0].ID)
|
||||||
assert.EqualValues(t, user.ID, notfs[0].UserID)
|
assert.EqualValues(t, user.ID, notfs[0].UserID)
|
||||||
|
assert.EqualValues(t, 4, notfs[1].ID)
|
||||||
|
assert.EqualValues(t, user.ID, notfs[1].UserID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +59,12 @@ func TestNotification_GetIssue(t *testing.T) {
|
||||||
|
|
||||||
func TestGetNotificationCount(t *testing.T) {
|
func TestGetNotificationCount(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
user := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
|
||||||
cnt, err := GetNotificationCount(user, NotificationStatusUnread)
|
cnt, err := GetNotificationCount(user, NotificationStatusRead)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 0, cnt)
|
assert.EqualValues(t, 0, cnt)
|
||||||
|
|
||||||
cnt, err = GetNotificationCount(user, NotificationStatusRead)
|
cnt, err = GetNotificationCount(user, NotificationStatusUnread)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 1, cnt)
|
assert.EqualValues(t, 1, cnt)
|
||||||
}
|
}
|
||||||
|
@ -79,3 +81,21 @@ func TestSetNotificationStatus(t *testing.T) {
|
||||||
assert.Error(t, SetNotificationStatus(1, user, NotificationStatusRead))
|
assert.Error(t, SetNotificationStatus(1, user, NotificationStatusRead))
|
||||||
assert.Error(t, SetNotificationStatus(NonexistentID, user, NotificationStatusRead))
|
assert.Error(t, SetNotificationStatus(NonexistentID, user, NotificationStatusRead))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateNotificationStatuses(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
|
notfUnread := AssertExistsAndLoadBean(t,
|
||||||
|
&Notification{UserID: user.ID, Status: NotificationStatusUnread}).(*Notification)
|
||||||
|
notfRead := AssertExistsAndLoadBean(t,
|
||||||
|
&Notification{UserID: user.ID, Status: NotificationStatusRead}).(*Notification)
|
||||||
|
notfPinned := AssertExistsAndLoadBean(t,
|
||||||
|
&Notification{UserID: user.ID, Status: NotificationStatusPinned}).(*Notification)
|
||||||
|
assert.NoError(t, UpdateNotificationStatuses(user, NotificationStatusUnread, NotificationStatusRead))
|
||||||
|
AssertExistsAndLoadBean(t,
|
||||||
|
&Notification{ID: notfUnread.ID, Status: NotificationStatusRead})
|
||||||
|
AssertExistsAndLoadBean(t,
|
||||||
|
&Notification{ID: notfRead.ID, Status: NotificationStatusRead})
|
||||||
|
AssertExistsAndLoadBean(t,
|
||||||
|
&Notification{ID: notfPinned.ID, Status: NotificationStatusPinned})
|
||||||
|
}
|
||||||
|
|
|
@ -1547,6 +1547,7 @@ no_read = You do not have any read notifications.
|
||||||
pin = Pin notification
|
pin = Pin notification
|
||||||
mark_as_read = Mark as read
|
mark_as_read = Mark as read
|
||||||
mark_as_unread = Mark as unread
|
mark_as_unread = Mark as unread
|
||||||
|
mark_all_as_read = Mark all as read
|
||||||
|
|
||||||
[gpg]
|
[gpg]
|
||||||
error.extract_sign = Failed to extract signature
|
error.extract_sign = Failed to extract signature
|
||||||
|
|
|
@ -706,6 +706,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/notifications", func() {
|
m.Group("/notifications", func() {
|
||||||
m.Get("", user.Notifications)
|
m.Get("", user.Notifications)
|
||||||
m.Post("/status", user.NotificationStatusPost)
|
m.Post("/status", user.NotificationStatusPost)
|
||||||
|
m.Post("/purge", user.NotificationPurgePost)
|
||||||
}, reqSignIn)
|
}, reqSignIn)
|
||||||
|
|
||||||
m.Group("/api", func() {
|
m.Group("/api", func() {
|
||||||
|
|
|
@ -112,3 +112,15 @@ func NotificationStatusPost(c *context.Context) {
|
||||||
url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
|
url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
|
||||||
c.Redirect(url, 303)
|
c.Redirect(url, 303)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotificationPurgePost is a route for 'purging' the list of notifications - marking all unread as read
|
||||||
|
func NotificationPurgePost(c *context.Context) {
|
||||||
|
err := models.UpdateNotificationStatuses(c.User, models.NotificationStatusUnread, models.NotificationStatusRead)
|
||||||
|
if err != nil {
|
||||||
|
c.Handle(500, "ErrUpdateNotificationStatuses", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
|
||||||
|
c.Redirect(url, 303)
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,14 @@
|
||||||
<a href="{{AppSubUrl}}/notifications?q=read" class="{{if eq .Status 2}}active{{end}} item">
|
<a href="{{AppSubUrl}}/notifications?q=read" class="{{if eq .Status 2}}active{{end}} item">
|
||||||
{{.i18n.Tr "notification.read"}}
|
{{.i18n.Tr "notification.read"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{if and (eq .Status 1) (.NotificationUnreadCount)}}
|
||||||
|
<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"}}'>
|
||||||
|
<i class="octicon octicon-checklist"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui bottom attached active tab segment">
|
<div class="ui bottom attached active tab segment">
|
||||||
{{if eq (len .Notifications) 0}}
|
{{if eq (len .Notifications) 0}}
|
||||||
|
|
Reference in a new issue