@@ -44,8 +44,10 @@ type Notification struct {
4444 Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"`
4545 Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"`
4646
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:"-"`
4951
5052 UpdatedBy int64 `xorm:"INDEX NOT NULL"`
5153
@@ -58,22 +60,27 @@ type Notification struct {
5860
5961// CreateOrUpdateIssueNotifications creates an issue notification
6062// 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 {
6264 sess := x .NewSession ()
6365 defer sess .Close ()
6466 if err := sess .Begin (); err != nil {
6567 return err
6668 }
6769
68- if err := createOrUpdateIssueNotifications (sess , issue , notificationAuthorID ); err != nil {
70+ if err := createOrUpdateIssueNotifications (sess , issueID , commentID , notificationAuthorID ); err != nil {
6971 return err
7072 }
7173
7274 return sess .Commit ()
7375}
7476
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 )
7784 if err != nil {
7885 return err
7986 }
@@ -83,7 +90,7 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
8390 return err
8491 }
8592
86- notifications , err := getNotificationsByIssueID (e , issue . ID )
93+ notifications , err := getNotificationsByIssueID (e , issueID )
8794 if err != nil {
8895 return err
8996 }
@@ -102,9 +109,9 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
102109 alreadyNotified [userID ] = struct {}{}
103110
104111 if notificationExists (notifications , issue .ID , userID ) {
105- return updateIssueNotification (e , userID , issue .ID , notificationAuthorID )
112+ return updateIssueNotification (e , userID , issue .ID , commentID , notificationAuthorID )
106113 }
107- return createIssueNotification (e , userID , issue , notificationAuthorID )
114+ return createIssueNotification (e , userID , issue , commentID , notificationAuthorID )
108115 }
109116
110117 for _ , issueWatch := range issueWatches {
@@ -157,12 +164,13 @@ func notificationExists(notifications []*Notification, issueID, userID int64) bo
157164 return false
158165}
159166
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 {
161168 notification := & Notification {
162169 UserID : userID ,
163170 RepoID : issue .RepoID ,
164171 Status : NotificationStatusUnread ,
165172 IssueID : issue .ID ,
173+ CommentID : commentID ,
166174 UpdatedBy : updatedByID ,
167175 }
168176
@@ -176,16 +184,25 @@ func createIssueNotification(e Engine, userID int64, issue *Issue, updatedByID i
176184 return err
177185}
178186
179- func updateIssueNotification (e Engine , userID , issueID , updatedByID int64 ) error {
187+ func updateIssueNotification (e Engine , userID , issueID , commentID , updatedByID int64 ) error {
180188 notification , err := getIssueNotification (e , userID , issueID )
181189 if err != nil {
182190 return err
183191 }
184192
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+ }
187204
188- _ , err = e .ID (notification .ID ).Update (notification )
205+ _ , err = e .ID (notification .ID ).Cols ( cols ... ). Update (notification )
189206 return err
190207}
191208
@@ -199,7 +216,7 @@ func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error
199216}
200217
201218// 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 ) {
203220 return notificationsForUser (x , user , statuses , page , perPage )
204221}
205222
@@ -239,6 +256,204 @@ func (n *Notification) GetIssue() (*Issue, error) {
239256 return n .Issue , err
240257}
241258
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+
242457// GetNotificationCount returns the notification count for user
243458func GetNotificationCount (user * User , status NotificationStatus ) (int64 , error ) {
244459 return getNotificationCount (x , user , status )
0 commit comments