code.gitea.io/gitea@v1.19.3/modules/notification/ui/ui.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package ui 5 6 import ( 7 "context" 8 9 activities_model "code.gitea.io/gitea/models/activities" 10 "code.gitea.io/gitea/models/db" 11 issues_model "code.gitea.io/gitea/models/issues" 12 repo_model "code.gitea.io/gitea/models/repo" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/container" 15 "code.gitea.io/gitea/modules/graceful" 16 "code.gitea.io/gitea/modules/log" 17 "code.gitea.io/gitea/modules/notification/base" 18 "code.gitea.io/gitea/modules/queue" 19 ) 20 21 type ( 22 notificationService struct { 23 base.NullNotifier 24 issueQueue queue.Queue 25 } 26 27 issueNotificationOpts struct { 28 IssueID int64 29 CommentID int64 30 NotificationAuthorID int64 31 ReceiverID int64 // 0 -- ALL Watcher 32 } 33 ) 34 35 var _ base.Notifier = ¬ificationService{} 36 37 // NewNotifier create a new notificationService notifier 38 func NewNotifier() base.Notifier { 39 ns := ¬ificationService{} 40 ns.issueQueue = queue.CreateQueue("notification-service", ns.handle, issueNotificationOpts{}) 41 return ns 42 } 43 44 func (ns *notificationService) handle(data ...queue.Data) []queue.Data { 45 for _, datum := range data { 46 opts := datum.(issueNotificationOpts) 47 if err := activities_model.CreateOrUpdateIssueNotifications(opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil { 48 log.Error("Was unable to create issue notification: %v", err) 49 } 50 } 51 return nil 52 } 53 54 func (ns *notificationService) Run() { 55 graceful.GetManager().RunWithShutdownFns(ns.issueQueue.Run) 56 } 57 58 func (ns *notificationService) NotifyCreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, 59 issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, 60 ) { 61 opts := issueNotificationOpts{ 62 IssueID: issue.ID, 63 NotificationAuthorID: doer.ID, 64 } 65 if comment != nil { 66 opts.CommentID = comment.ID 67 } 68 _ = ns.issueQueue.Push(opts) 69 for _, mention := range mentions { 70 opts := issueNotificationOpts{ 71 IssueID: issue.ID, 72 NotificationAuthorID: doer.ID, 73 ReceiverID: mention.ID, 74 } 75 if comment != nil { 76 opts.CommentID = comment.ID 77 } 78 _ = ns.issueQueue.Push(opts) 79 } 80 } 81 82 func (ns *notificationService) NotifyNewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) { 83 _ = ns.issueQueue.Push(issueNotificationOpts{ 84 IssueID: issue.ID, 85 NotificationAuthorID: issue.Poster.ID, 86 }) 87 for _, mention := range mentions { 88 _ = ns.issueQueue.Push(issueNotificationOpts{ 89 IssueID: issue.ID, 90 NotificationAuthorID: issue.Poster.ID, 91 ReceiverID: mention.ID, 92 }) 93 } 94 } 95 96 func (ns *notificationService) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { 97 _ = ns.issueQueue.Push(issueNotificationOpts{ 98 IssueID: issue.ID, 99 NotificationAuthorID: doer.ID, 100 CommentID: actionComment.ID, 101 }) 102 } 103 104 func (ns *notificationService) NotifyIssueChangeTitle(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldTitle string) { 105 if err := issue.LoadPullRequest(ctx); err != nil { 106 log.Error("issue.LoadPullRequest: %v", err) 107 return 108 } 109 if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() { 110 _ = ns.issueQueue.Push(issueNotificationOpts{ 111 IssueID: issue.ID, 112 NotificationAuthorID: doer.ID, 113 }) 114 } 115 } 116 117 func (ns *notificationService) NotifyMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { 118 _ = ns.issueQueue.Push(issueNotificationOpts{ 119 IssueID: pr.Issue.ID, 120 NotificationAuthorID: doer.ID, 121 }) 122 } 123 124 func (ns *notificationService) NotifyAutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { 125 ns.NotifyMergePullRequest(ctx, doer, pr) 126 } 127 128 func (ns *notificationService) NotifyNewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) { 129 if err := pr.LoadIssue(ctx); err != nil { 130 log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err) 131 return 132 } 133 toNotify := make(container.Set[int64], 32) 134 repoWatchers, err := repo_model.GetRepoWatchersIDs(ctx, pr.Issue.RepoID) 135 if err != nil { 136 log.Error("GetRepoWatchersIDs: %v", err) 137 return 138 } 139 for _, id := range repoWatchers { 140 toNotify.Add(id) 141 } 142 issueParticipants, err := issues_model.GetParticipantsIDsByIssueID(ctx, pr.IssueID) 143 if err != nil { 144 log.Error("GetParticipantsIDsByIssueID: %v", err) 145 return 146 } 147 for _, id := range issueParticipants { 148 toNotify.Add(id) 149 } 150 delete(toNotify, pr.Issue.PosterID) 151 for _, mention := range mentions { 152 toNotify.Add(mention.ID) 153 } 154 for receiverID := range toNotify { 155 _ = ns.issueQueue.Push(issueNotificationOpts{ 156 IssueID: pr.Issue.ID, 157 NotificationAuthorID: pr.Issue.PosterID, 158 ReceiverID: receiverID, 159 }) 160 } 161 } 162 163 func (ns *notificationService) NotifyPullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, c *issues_model.Comment, mentions []*user_model.User) { 164 opts := issueNotificationOpts{ 165 IssueID: pr.Issue.ID, 166 NotificationAuthorID: r.Reviewer.ID, 167 } 168 if c != nil { 169 opts.CommentID = c.ID 170 } 171 _ = ns.issueQueue.Push(opts) 172 for _, mention := range mentions { 173 opts := issueNotificationOpts{ 174 IssueID: pr.Issue.ID, 175 NotificationAuthorID: r.Reviewer.ID, 176 ReceiverID: mention.ID, 177 } 178 if c != nil { 179 opts.CommentID = c.ID 180 } 181 _ = ns.issueQueue.Push(opts) 182 } 183 } 184 185 func (ns *notificationService) NotifyPullRequestCodeComment(ctx context.Context, pr *issues_model.PullRequest, c *issues_model.Comment, mentions []*user_model.User) { 186 for _, mention := range mentions { 187 _ = ns.issueQueue.Push(issueNotificationOpts{ 188 IssueID: pr.Issue.ID, 189 NotificationAuthorID: c.Poster.ID, 190 CommentID: c.ID, 191 ReceiverID: mention.ID, 192 }) 193 } 194 } 195 196 func (ns *notificationService) NotifyPullRequestPushCommits(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) { 197 opts := issueNotificationOpts{ 198 IssueID: pr.IssueID, 199 NotificationAuthorID: doer.ID, 200 CommentID: comment.ID, 201 } 202 _ = ns.issueQueue.Push(opts) 203 } 204 205 func (ns *notificationService) NotifyPullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { 206 opts := issueNotificationOpts{ 207 IssueID: review.IssueID, 208 NotificationAuthorID: doer.ID, 209 CommentID: comment.ID, 210 } 211 _ = ns.issueQueue.Push(opts) 212 } 213 214 func (ns *notificationService) NotifyIssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { 215 if !removed && doer.ID != assignee.ID { 216 opts := issueNotificationOpts{ 217 IssueID: issue.ID, 218 NotificationAuthorID: doer.ID, 219 ReceiverID: assignee.ID, 220 } 221 222 if comment != nil { 223 opts.CommentID = comment.ID 224 } 225 226 _ = ns.issueQueue.Push(opts) 227 } 228 } 229 230 func (ns *notificationService) NotifyPullReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { 231 if isRequest { 232 opts := issueNotificationOpts{ 233 IssueID: issue.ID, 234 NotificationAuthorID: doer.ID, 235 ReceiverID: reviewer.ID, 236 } 237 238 if comment != nil { 239 opts.CommentID = comment.ID 240 } 241 242 _ = ns.issueQueue.Push(opts) 243 } 244 } 245 246 func (ns *notificationService) NotifyRepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) { 247 err := db.WithTx(ctx, func(ctx context.Context) error { 248 return activities_model.CreateRepoTransferNotification(ctx, doer, newOwner, repo) 249 }) 250 if err != nil { 251 log.Error("CreateRepoTransferNotification: %v", err) 252 } 253 }