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
					
				|  | @ -19,3 +19,25 @@ | |||
|   issue_id: 2 | ||||
|   created_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 | ||||
| } | ||||
| 
 | ||||
| // 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} | ||||
| 	notfs, err := NotificationsForUser(user, statuses, 1, 10) | ||||
| 	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, 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) { | ||||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 	user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 	cnt, err := GetNotificationCount(user, NotificationStatusUnread) | ||||
| 	user := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) | ||||
| 	cnt, err := GetNotificationCount(user, NotificationStatusRead) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.EqualValues(t, 0, cnt) | ||||
| 
 | ||||
| 	cnt, err = GetNotificationCount(user, NotificationStatusRead) | ||||
| 	cnt, err = GetNotificationCount(user, NotificationStatusUnread) | ||||
| 	assert.NoError(t, err) | ||||
| 	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(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 | ||||
| mark_as_read = Mark as read | ||||
| mark_as_unread = Mark as unread | ||||
| mark_all_as_read = Mark all as read | ||||
| 
 | ||||
| [gpg] | ||||
| error.extract_sign = Failed to extract signature | ||||
|  |  | |||
|  | @ -706,6 +706,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| 	m.Group("/notifications", func() { | ||||
| 		m.Get("", user.Notifications) | ||||
| 		m.Post("/status", user.NotificationStatusPost) | ||||
| 		m.Post("/purge", user.NotificationPurgePost) | ||||
| 	}, reqSignIn) | ||||
| 
 | ||||
| 	m.Group("/api", func() { | ||||
|  |  | |||
|  | @ -112,3 +112,15 @@ func NotificationStatusPost(c *context.Context) { | |||
| 	url := fmt.Sprintf("%s/notifications", setting.AppSubURL) | ||||
| 	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"> | ||||
| 				{{.i18n.Tr "notification.read"}} | ||||
| 			</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 class="ui bottom attached active tab segment"> | ||||
| 			{{if eq (len .Notifications) 0}} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue