package views import ( "strconv" "hearthforge/internal/config" "hearthforge/internal/db" "hearthforge/internal/util" g "maragu.dev/gomponents" . "maragu.dev/gomponents/html" ) // ThreadComment is one comment of an issue or patch, with its rendered body. type ThreadComment struct { ID int64 AuthorID *int64 AuthorUsername string AuthorAvatarVersion *int64 Body string BodyHTML string CreatedAt string EditedAt *string } // CommentThread renders the comment list and the new comment form. // parentStatus is the status of the issue or patch, "open" allows author edits. func CommentThread(cfg *config.Config, user *db.SessionUser, comments []ThreadComment, commentReactions map[int64][]ReactionCount, baseURL, parentStatus string, ) g.Node { reactURL := baseURL + "/react" canEdit := func(c ThreadComment) bool { if user == nil { return false } return user.IsAdmin || (c.AuthorID != nil && user.ID == *c.AuthorID && parentStatus == "open") } return g.Group{ g.Map(comments, func(comment ThreadComment) g.Node { return Div(Class("timeline-item"), Div(Class("timeline-author"), Avatar(comment.AuthorID, comment.AuthorAvatarVersion, 24), Strong(g.Text(util.DisplayName(comment.AuthorUsername, cfg.OwnerDisplayName))), Div(Class("timeline-author-right"), g.If(canEdit(comment), Details(Class("inline-edit-details"), Summary(Class("btn btn-xs"), Span(Class("when-closed"), g.Text("Edit")), Span(Class("when-open"), g.Text("Stop editing")), ), )), DateWithEdited(comment.CreatedAt, comment.EditedAt), ), ), g.If(canEdit(comment), Div(Class("inline-edit-form-area"), Form(Method("POST"), Action(baseURL+"/comments/"+strconv.FormatInt(comment.ID, 10)+"/edit"), Class("inline-edit-form"), Div(Class("form-group"), Textarea(Class("form-input"), Name("edit_body"), Rows("6"), MaxLength(strconv.Itoa(cfg.MaxTextBodyBytes)), g.Text(comment.Body)), ), Div(Class("form-actions"), Button(Type("submit"), Class("btn btn-sm btn-primary"), g.Text("Save")), ), ), )), // The markdown renderer already sanitised this HTML. Div(Class("timeline-body markdown-body"), g.Raw(comment.BodyHTML)), ReactionBar(commentReactions[comment.ID], reactURL, comment.ID, user), ) }), g.If(user != nil, Div(Class("timeline-item timeline-item-new"), H3(Class("section-title"), g.Text("Add a comment")), Form(Method("POST"), Action(baseURL+"/comments"), Div(Class("form-group"), Textarea(Name("body"), Rows("6"), MaxLength(strconv.Itoa(cfg.MaxTextBodyBytes)), Placeholder("Leave a comment (Markdown supported)"), Required()), ), Div(Class("form-actions"), Button(Type("submit"), Class("btn btn-primary"), g.Text("Comment")), ), ), )), g.If(user == nil, P(Class("text-muted"), A(Href("/login"), g.Text("Sign in")), g.Text(" to leave a comment."))), } }