commentthread.go
⎇
Raw
1package views
2
3import (
4 "strconv"
5
6 "hearthforge/internal/config"
7 "hearthforge/internal/db"
8 "hearthforge/internal/util"
9
10 g "maragu.dev/gomponents"
11 . "maragu.dev/gomponents/html"
12)
13
14// ThreadComment is one comment of an issue or patch, with its rendered body.
15type ThreadComment struct {
16 ID int64
17 AuthorID *int64
18 AuthorUsername string
19 AuthorAvatarVersion *int64
20 Body string
21 BodyHTML string
22 CreatedAt string
23 EditedAt *string
24}
25
26// CommentThread renders the comment list and the new comment form.
27// parentStatus is the status of the issue or patch, "open" allows author edits.
28func CommentThread(cfg *config.Config, user *db.SessionUser, comments []ThreadComment,
29 commentReactions map[int64][]ReactionCount, baseURL, parentStatus string,
30) g.Node {
31 reactURL := baseURL + "/react"
32 canEdit := func(c ThreadComment) bool { return CanEdit(user, c.AuthorID, parentStatus) }
33 return g.Group{
34 g.Map(comments, func(comment ThreadComment) g.Node {
35 return Div(Class("timeline-item"),
36 Div(Class("timeline-author"),
37 Avatar(comment.AuthorID, comment.AuthorAvatarVersion, 24),
38 Strong(g.Text(util.DisplayName(comment.AuthorUsername, cfg.OwnerDisplayName))),
39 Div(Class("timeline-author-right"),
40 g.If(canEdit(comment), Details(Class("inline-edit-details"),
41 Summary(Class("btn btn-xs"),
42 Span(Class("when-closed"), g.Text("Edit")),
43 Span(Class("when-open"), g.Text("Stop editing")),
44 ),
45 )),
46 DateWithEdited(comment.CreatedAt, comment.EditedAt),
47 ),
48 ),
49 g.If(canEdit(comment), Div(Class("inline-edit-form-area"),
50 Form(Method("POST"),
51 Action(baseURL+"/comments/"+strconv.FormatInt(comment.ID, 10)+"/edit"),
52 Class("inline-edit-form"),
53 Div(Class("form-group"),
54 Textarea(Class("form-input"), Name("edit_body"), Rows("6"),
55 MaxLength(strconv.Itoa(cfg.MaxTextBodyBytes)),
56 g.Text(comment.Body)),
57 ),
58 Div(Class("form-actions"),
59 Button(Type("submit"), Class("btn btn-sm btn-primary"), g.Text("Save")),
60 ),
61 ),
62 )),
63 // The markdown renderer already sanitised this HTML.
64 Div(Class("timeline-body markdown-body"), g.Raw(comment.BodyHTML)),
65 ReactionBar(commentReactions[comment.ID], reactURL, comment.ID, user),
66 )
67 }),
68 g.If(user != nil, Div(Class("timeline-item timeline-item-new"),
69 H3(Class("section-title"), g.Text("Add a comment")),
70 Form(Method("POST"), Action(baseURL+"/comments"),
71 Div(Class("form-group"),
72 Textarea(Name("body"), Rows("6"), MaxLength(strconv.Itoa(cfg.MaxTextBodyBytes)),
73 Placeholder("Leave a comment (Markdown supported)"), Required()),
74 ),
75 Div(Class("form-actions"),
76 Button(Type("submit"), Class("btn btn-primary"), g.Text("Comment")),
77 ),
78 ),
79 )),
80 g.If(user == nil, P(Class("text-muted"),
81 A(Href("/login"), g.Text("Sign in")), g.Text(" to leave a comment."))),
82 }
83}
84