feat(inbox): auto-scroll to comment from notification and add jump-to-bottom button

When clicking an inbox notification, the issue detail now scrolls to and
briefly highlights the relevant comment. Also adds a floating "Jump to
bottom" button on issue pages with long timelines.

Backend: store comment_id in inbox notification details for new_comment
and reaction_added events. Frontend: pass highlightCommentId through to
IssueDetail, add id attributes to comment elements, and track scroll
position for the jump-to-bottom button.
This commit is contained in:
Jiang Bohan 2026-04-02 13:43:05 +08:00
parent 57e48c1d6b
commit 575bbd7f60
5 changed files with 99 additions and 21 deletions

View file

@ -435,13 +435,15 @@ func registerNotificationListeners(bus *events.Bus, queries *db.Queries) {
// The comment payload can come as handler.CommentResponse from the
// HTTP handler, or as map[string]any from the agent comment path in
// task.go. Handle both.
var issueID, commentContent string
var issueID, commentID, commentContent string
switch c := payload["comment"].(type) {
case handler.CommentResponse:
issueID = c.IssueID
commentID = c.ID
commentContent = c.Content
case map[string]any:
issueID, _ = c["issue_id"].(string)
commentID, _ = c["id"].(string)
commentContent, _ = c["content"].(string)
default:
return
@ -450,17 +452,24 @@ func registerNotificationListeners(bus *events.Bus, queries *db.Queries) {
issueTitle, _ := payload["issue_title"].(string)
issueStatus, _ := payload["issue_status"].(string)
commentDetails := emptyDetails
if commentID != "" {
commentDetails, _ = json.Marshal(map[string]string{
"comment_id": commentID,
})
}
notifySubscribers(ctx, queries, bus, issueID, issueStatus, e.WorkspaceID, e,
nil, "new_comment", "info",
issueTitle, commentContent,
emptyDetails)
commentDetails)
// Notify @mentions in comment content.
mentions := parseMentions(commentContent)
if len(mentions) > 0 {
skip := map[string]bool{e.ActorID: true}
notifyMentionedMembers(bus, queries, e, mentions, issueID, issueTitle, issueStatus,
issueTitle, skip, emptyDetails)
issueTitle, skip, commentDetails)
}
})
@ -513,6 +522,7 @@ func registerNotificationListeners(bus *events.Bus, queries *db.Queries) {
commentAuthorType, _ := payload["comment_author_type"].(string)
commentAuthorID, _ := payload["comment_author_id"].(string)
commentID, _ := payload["comment_id"].(string)
issueID, _ := payload["issue_id"].(string)
issueTitle, _ := payload["issue_title"].(string)
issueStatus, _ := payload["issue_status"].(string)
@ -521,9 +531,13 @@ func registerNotificationListeners(bus *events.Bus, queries *db.Queries) {
return
}
details, _ := json.Marshal(map[string]string{
detailsMap := map[string]string{
"emoji": reaction.Emoji,
})
}
if commentID != "" {
detailsMap["comment_id"] = commentID
}
details, _ := json.Marshal(detailsMap)
notifyDirect(ctx, queries, bus,
commentAuthorType, commentAuthorID,