diff --git a/appview/notify/merged_notifier.go b/appview/notify/merged_notifier.go --- a/appview/notify/merged_notifier.go +++ b/appview/notify/merged_notifier.go @@ -82,8 +82,8 @@ m.fanout("NewPull", ctx, pull) } -func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) { - m.fanout("NewPullComment", ctx, comment) +func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { + m.fanout("NewPullComment", ctx, comment, mentions) } func (m *mergedNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) { diff --git a/appview/notify/notifier.go b/appview/notify/notifier.go --- a/appview/notify/notifier.go +++ b/appview/notify/notifier.go @@ -22,7 +22,7 @@ DeleteFollow(ctx context.Context, follow *models.Follow) NewPull(ctx context.Context, pull *models.Pull) - NewPullComment(ctx context.Context, comment *models.PullComment) + NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) UpdateProfile(ctx context.Context, profile *models.Profile) @@ -51,8 +51,9 @@ func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {} func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {} -func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {} -func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.PullComment) {} +func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {} +func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.PullComment, mentions []syntax.DID) { +} func (m *BaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) {} func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) {} diff --git a/appview/pulls/pulls.go b/appview/pulls/pulls.go --- a/appview/pulls/pulls.go +++ b/appview/pulls/pulls.go @@ -691,6 +691,7 @@ } func (s *Pulls) PullComment(w http.ResponseWriter, r *http.Request) { + l := s.logger.With("handler", "PullComment") user := s.oauth.GetUser(r) f, err := s.repoResolver.Resolve(r) if err != nil { @@ -788,7 +789,16 @@ return } - s.notifier.NewPullComment(r.Context(), comment) + rawMentions := markup.FindUserMentions(comment.Body) + idents := s.idResolver.ResolveIdents(r.Context(), rawMentions) + l.Debug("parsed mentions", "raw", rawMentions, "idents", idents) + var mentions []syntax.DID + for _, ident := range idents { + if ident != nil && !ident.Handle.IsInvalidHandle() { + mentions = append(mentions, ident.DID) + } + } + s.notifier.NewPullComment(r.Context(), comment, mentions) s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId)) return diff --git a/appview/notify/db/db.go b/appview/notify/db/db.go --- a/appview/notify/db/db.go +++ b/appview/notify/db/db.go @@ -239,7 +239,7 @@ ) } -func (n *databaseNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) { +func (n *databaseNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { pull, err := db.GetPull(n.db, syntax.ATURI(comment.RepoAt), comment.PullId, @@ -277,6 +277,16 @@ actorDid, recipients, eventType, + entityType, + entityId, + repoId, + issueId, + pullId, + ) + n.notifyEvent( + actorDid, + mentions, + models.NotificationTypeUserMentioned, entityType, entityId, repoId, diff --git a/appview/notify/posthog/notifier.go b/appview/notify/posthog/notifier.go --- a/appview/notify/posthog/notifier.go +++ b/appview/notify/posthog/notifier.go @@ -86,13 +86,14 @@ } } -func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) { +func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { err := n.client.Enqueue(posthog.Capture{ DistinctId: comment.OwnerDid, Event: "new_pull_comment", Properties: posthog.Properties{ - "repo_at": comment.RepoAt, - "pull_id": comment.PullId, + "repo_at": comment.RepoAt, + "pull_id": comment.PullId, + "mentions": mentions, }, }) if err != nil { diff --git a/appview/pages/markup/extension/atlink.go b/appview/pages/markup/extension/atlink.go --- a/appview/pages/markup/extension/atlink.go +++ b/appview/pages/markup/extension/atlink.go @@ -16,7 +16,7 @@ // An AtNode struct represents an AtNode type AtNode struct { - handle string + Handle string ast.BaseInline } @@ -59,7 +59,7 @@ block.Advance(m[1]) node := &AtNode{} node.AppendChild(node, ast.NewTextSegment(atSegment)) - node.handle = string(atSegment.Value(block.Source())[1:]) + node.Handle = string(atSegment.Value(block.Source())[1:]) return node } @@ -88,7 +88,7 @@ func (r *atHtmlRenderer) renderAt(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { if entering { w.WriteString(``) } else { w.WriteString("")