@@ -44,8 +44,10 @@ type Notification struct {
44
44
Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"`
45
45
Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"`
46
46
47
- IssueID int64 `xorm:"INDEX NOT NULL"`
48
- CommitID string `xorm:"INDEX"`
47
+ IssueID int64 `xorm:"INDEX NOT NULL"`
48
+ CommitID string `xorm:"INDEX"`
49
+ CommentID int64
50
+ Comment * Comment `xorm:"-"`
49
51
50
52
UpdatedBy int64 `xorm:"INDEX NOT NULL"`
51
53
@@ -58,22 +60,27 @@ type Notification struct {
58
60
59
61
// CreateOrUpdateIssueNotifications creates an issue notification
60
62
// for each watcher, or updates it if already exists
61
- func CreateOrUpdateIssueNotifications (issue * Issue , notificationAuthorID int64 ) error {
63
+ func CreateOrUpdateIssueNotifications (issueID , commentID int64 , notificationAuthorID int64 ) error {
62
64
sess := x .NewSession ()
63
65
defer sess .Close ()
64
66
if err := sess .Begin (); err != nil {
65
67
return err
66
68
}
67
69
68
- if err := createOrUpdateIssueNotifications (sess , issue , notificationAuthorID ); err != nil {
70
+ if err := createOrUpdateIssueNotifications (sess , issueID , commentID , notificationAuthorID ); err != nil {
69
71
return err
70
72
}
71
73
72
74
return sess .Commit ()
73
75
}
74
76
75
- func createOrUpdateIssueNotifications (e Engine , issue * Issue , notificationAuthorID int64 ) error {
76
- issueWatches , err := getIssueWatchers (e , issue .ID )
77
+ func createOrUpdateIssueNotifications (e Engine , issueID , commentID int64 , notificationAuthorID int64 ) error {
78
+ issueWatches , err := getIssueWatchers (e , issueID )
79
+ if err != nil {
80
+ return err
81
+ }
82
+
83
+ issue , err := getIssueByID (e , issueID )
77
84
if err != nil {
78
85
return err
79
86
}
@@ -83,7 +90,7 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
83
90
return err
84
91
}
85
92
86
- notifications , err := getNotificationsByIssueID (e , issue . ID )
93
+ notifications , err := getNotificationsByIssueID (e , issueID )
87
94
if err != nil {
88
95
return err
89
96
}
@@ -102,9 +109,9 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
102
109
alreadyNotified [userID ] = struct {}{}
103
110
104
111
if notificationExists (notifications , issue .ID , userID ) {
105
- return updateIssueNotification (e , userID , issue .ID , notificationAuthorID )
112
+ return updateIssueNotification (e , userID , issue .ID , commentID , notificationAuthorID )
106
113
}
107
- return createIssueNotification (e , userID , issue , notificationAuthorID )
114
+ return createIssueNotification (e , userID , issue , commentID , notificationAuthorID )
108
115
}
109
116
110
117
for _ , issueWatch := range issueWatches {
@@ -157,12 +164,13 @@ func notificationExists(notifications []*Notification, issueID, userID int64) bo
157
164
return false
158
165
}
159
166
160
- func createIssueNotification (e Engine , userID int64 , issue * Issue , updatedByID int64 ) error {
167
+ func createIssueNotification (e Engine , userID int64 , issue * Issue , commentID , updatedByID int64 ) error {
161
168
notification := & Notification {
162
169
UserID : userID ,
163
170
RepoID : issue .RepoID ,
164
171
Status : NotificationStatusUnread ,
165
172
IssueID : issue .ID ,
173
+ CommentID : commentID ,
166
174
UpdatedBy : updatedByID ,
167
175
}
168
176
@@ -176,16 +184,25 @@ func createIssueNotification(e Engine, userID int64, issue *Issue, updatedByID i
176
184
return err
177
185
}
178
186
179
- func updateIssueNotification (e Engine , userID , issueID , updatedByID int64 ) error {
187
+ func updateIssueNotification (e Engine , userID , issueID , commentID , updatedByID int64 ) error {
180
188
notification , err := getIssueNotification (e , userID , issueID )
181
189
if err != nil {
182
190
return err
183
191
}
184
192
185
- notification .Status = NotificationStatusUnread
186
- notification .UpdatedBy = updatedByID
193
+ // NOTICE: Only update comment id when the before notification on this issue is read, otherwise you may miss some old comments.
194
+ // But we need update update_by so that the notification will be reorder
195
+ var cols []string
196
+ if notification .Status == NotificationStatusRead {
197
+ notification .Status = NotificationStatusUnread
198
+ notification .CommentID = commentID
199
+ cols = []string {"status" , "update_by" , "comment_id" }
200
+ } else {
201
+ notification .UpdatedBy = updatedByID
202
+ cols = []string {"update_by" }
203
+ }
187
204
188
- _ , err = e .ID (notification .ID ).Update (notification )
205
+ _ , err = e .ID (notification .ID ).Cols ( cols ... ). Update (notification )
189
206
return err
190
207
}
191
208
@@ -199,7 +216,7 @@ func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error
199
216
}
200
217
201
218
// NotificationsForUser returns notifications for a given user and status
202
- func NotificationsForUser (user * User , statuses []NotificationStatus , page , perPage int ) ([] * Notification , error ) {
219
+ func NotificationsForUser (user * User , statuses []NotificationStatus , page , perPage int ) (NotificationList , error ) {
203
220
return notificationsForUser (x , user , statuses , page , perPage )
204
221
}
205
222
@@ -239,6 +256,204 @@ func (n *Notification) GetIssue() (*Issue, error) {
239
256
return n .Issue , err
240
257
}
241
258
259
+ // HTMLURL formats a URL-string to the notification
260
+ func (n * Notification ) HTMLURL () string {
261
+ if n .Comment != nil {
262
+ return n .Comment .HTMLURL ()
263
+ }
264
+ return n .Issue .HTMLURL ()
265
+ }
266
+
267
+ // NotificationList contains a list of notifications
268
+ type NotificationList []* Notification
269
+
270
+ func (nl NotificationList ) getPendingRepoIDs () []int64 {
271
+ var ids = make (map [int64 ]struct {}, len (nl ))
272
+ for _ , notification := range nl {
273
+ if notification .Repository != nil {
274
+ continue
275
+ }
276
+ if _ , ok := ids [notification .RepoID ]; ! ok {
277
+ ids [notification .RepoID ] = struct {}{}
278
+ }
279
+ }
280
+ return keysInt64 (ids )
281
+ }
282
+
283
+ // LoadRepos loads repositories from database
284
+ func (nl NotificationList ) LoadRepos () (RepositoryList , error ) {
285
+ if len (nl ) == 0 {
286
+ return RepositoryList {}, nil
287
+ }
288
+
289
+ var repoIDs = nl .getPendingRepoIDs ()
290
+ var repos = make (map [int64 ]* Repository , len (repoIDs ))
291
+ var left = len (repoIDs )
292
+ for left > 0 {
293
+ var limit = defaultMaxInSize
294
+ if left < limit {
295
+ limit = left
296
+ }
297
+ rows , err := x .
298
+ In ("id" , repoIDs [:limit ]).
299
+ Rows (new (Repository ))
300
+ if err != nil {
301
+ return nil , err
302
+ }
303
+
304
+ for rows .Next () {
305
+ var repo Repository
306
+ err = rows .Scan (& repo )
307
+ if err != nil {
308
+ rows .Close ()
309
+ return nil , err
310
+ }
311
+
312
+ repos [repo .ID ] = & repo
313
+ }
314
+ _ = rows .Close ()
315
+
316
+ left -= limit
317
+ repoIDs = repoIDs [limit :]
318
+ }
319
+
320
+ var reposList = make (RepositoryList , 0 , len (repoIDs ))
321
+ for _ , notification := range nl {
322
+ if notification .Repository == nil {
323
+ notification .Repository = repos [notification .RepoID ]
324
+ }
325
+ var found bool
326
+ for _ , r := range reposList {
327
+ if r .ID == notification .Repository .ID {
328
+ found = true
329
+ break
330
+ }
331
+ }
332
+ if ! found {
333
+ reposList = append (reposList , notification .Repository )
334
+ }
335
+ }
336
+ return reposList , nil
337
+ }
338
+
339
+ func (nl NotificationList ) getPendingIssueIDs () []int64 {
340
+ var ids = make (map [int64 ]struct {}, len (nl ))
341
+ for _ , notification := range nl {
342
+ if notification .Issue != nil {
343
+ continue
344
+ }
345
+ if _ , ok := ids [notification .IssueID ]; ! ok {
346
+ ids [notification .IssueID ] = struct {}{}
347
+ }
348
+ }
349
+ return keysInt64 (ids )
350
+ }
351
+
352
+ // LoadIssues loads issues from database
353
+ func (nl NotificationList ) LoadIssues () error {
354
+ if len (nl ) == 0 {
355
+ return nil
356
+ }
357
+
358
+ var issueIDs = nl .getPendingIssueIDs ()
359
+ var issues = make (map [int64 ]* Issue , len (issueIDs ))
360
+ var left = len (issueIDs )
361
+ for left > 0 {
362
+ var limit = defaultMaxInSize
363
+ if left < limit {
364
+ limit = left
365
+ }
366
+ rows , err := x .
367
+ In ("id" , issueIDs [:limit ]).
368
+ Rows (new (Issue ))
369
+ if err != nil {
370
+ return err
371
+ }
372
+
373
+ for rows .Next () {
374
+ var issue Issue
375
+ err = rows .Scan (& issue )
376
+ if err != nil {
377
+ rows .Close ()
378
+ return err
379
+ }
380
+
381
+ issues [issue .ID ] = & issue
382
+ }
383
+ _ = rows .Close ()
384
+
385
+ left -= limit
386
+ issueIDs = issueIDs [limit :]
387
+ }
388
+
389
+ for _ , notification := range nl {
390
+ if notification .Issue == nil {
391
+ notification .Issue = issues [notification .IssueID ]
392
+ notification .Issue .Repo = notification .Repository
393
+ }
394
+ }
395
+ return nil
396
+ }
397
+
398
+ func (nl NotificationList ) getPendingCommentIDs () []int64 {
399
+ var ids = make (map [int64 ]struct {}, len (nl ))
400
+ for _ , notification := range nl {
401
+ if notification .CommentID == 0 || notification .Comment != nil {
402
+ continue
403
+ }
404
+ if _ , ok := ids [notification .CommentID ]; ! ok {
405
+ ids [notification .CommentID ] = struct {}{}
406
+ }
407
+ }
408
+ return keysInt64 (ids )
409
+ }
410
+
411
+ // LoadComments loads comments from database
412
+ func (nl NotificationList ) LoadComments () error {
413
+ if len (nl ) == 0 {
414
+ return nil
415
+ }
416
+
417
+ var commentIDs = nl .getPendingCommentIDs ()
418
+ var comments = make (map [int64 ]* Comment , len (commentIDs ))
419
+ var left = len (commentIDs )
420
+ for left > 0 {
421
+ var limit = defaultMaxInSize
422
+ if left < limit {
423
+ limit = left
424
+ }
425
+ rows , err := x .
426
+ In ("id" , commentIDs [:limit ]).
427
+ Rows (new (Comment ))
428
+ if err != nil {
429
+ return err
430
+ }
431
+
432
+ for rows .Next () {
433
+ var comment Comment
434
+ err = rows .Scan (& comment )
435
+ if err != nil {
436
+ rows .Close ()
437
+ return err
438
+ }
439
+
440
+ comments [comment .ID ] = & comment
441
+ }
442
+ _ = rows .Close ()
443
+
444
+ left -= limit
445
+ commentIDs = commentIDs [limit :]
446
+ }
447
+
448
+ for _ , notification := range nl {
449
+ if notification .CommentID > 0 && notification .Comment == nil {
450
+ notification .Comment = comments [notification .CommentID ]
451
+ notification .Comment .Issue = notification .Issue
452
+ }
453
+ }
454
+ return nil
455
+ }
456
+
242
457
// GetNotificationCount returns the notification count for user
243
458
func GetNotificationCount (user * User , status NotificationStatus ) (int64 , error ) {
244
459
return getNotificationCount (x , user , status )
0 commit comments