appview: replace IssueComment to Comment #866

open
opened by boltless.me targeting master from sl/wnrvrwyvrlzo
Changed files
+93 -380
appview
db
issues
models
notify
pages
templates
repo
issues
state
validator
+6 -185
appview/db/issues.go
···
}
func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) {
-
issueMap := make(map[string]*models.Issue) // at-uri -> issue
+
issueMap := make(map[syntax.ATURI]*models.Issue) // at-uri -> issue
var conditions []string
var args []any
···
}
}
-
atUri := issue.AtUri().String()
-
issueMap[atUri] = &issue
+
issueMap[issue.AtUri()] = &issue
}
// collect reverse repos
···
// collect comments
issueAts := slices.Collect(maps.Keys(issueMap))
-
comments, err := GetIssueComments(e, FilterIn("issue_at", issueAts))
+
comments, err := GetComments(e, FilterIn("subject_at", issueAts))
if err != nil {
return nil, fmt.Errorf("failed to query comments: %w", err)
}
for i := range comments {
-
issueAt := comments[i].IssueAt
+
issueAt := comments[i].Subject
if issue, ok := issueMap[issueAt]; ok {
issue.Comments = append(issue.Comments, comments[i])
}
···
return nil, fmt.Errorf("failed to query labels: %w", err)
}
for issueAt, labels := range allLabels {
-
if issue, ok := issueMap[issueAt.String()]; ok {
+
if issue, ok := issueMap[issueAt]; ok {
issue.Labels = labels
}
}
···
return nil, fmt.Errorf("failed to query reference_links: %w", err)
}
for issueAt, references := range allReferencs {
-
if issue, ok := issueMap[issueAt.String()]; ok {
+
if issue, ok := issueMap[issueAt]; ok {
issue.References = references
}
}
···
return ids, nil
}
-
func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) {
-
result, err := tx.Exec(
-
`insert into issue_comments (
-
did,
-
rkey,
-
issue_at,
-
body,
-
reply_to,
-
created,
-
edited
-
)
-
values (?, ?, ?, ?, ?, ?, null)
-
on conflict(did, rkey) do update set
-
issue_at = excluded.issue_at,
-
body = excluded.body,
-
edited = case
-
when
-
issue_comments.issue_at != excluded.issue_at
-
or issue_comments.body != excluded.body
-
or issue_comments.reply_to != excluded.reply_to
-
then ?
-
else issue_comments.edited
-
end`,
-
c.Did,
-
c.Rkey,
-
c.IssueAt,
-
c.Body,
-
c.ReplyTo,
-
c.Created.Format(time.RFC3339),
-
time.Now().Format(time.RFC3339),
-
)
-
if err != nil {
-
return 0, err
-
}
-
-
id, err := result.LastInsertId()
-
if err != nil {
-
return 0, err
-
}
-
-
if err := putReferences(tx, c.AtUri(), c.References); err != nil {
-
return 0, fmt.Errorf("put reference_links: %w", err)
-
}
-
-
return id, nil
-
}
-
-
func DeleteIssueComments(e Execer, filters ...filter) error {
-
var conditions []string
-
var args []any
-
for _, filter := range filters {
-
conditions = append(conditions, filter.Condition())
-
args = append(args, filter.Arg()...)
-
}
-
-
whereClause := ""
-
if conditions != nil {
-
whereClause = " where " + strings.Join(conditions, " and ")
-
}
-
-
query := fmt.Sprintf(`update issue_comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause)
-
-
_, err := e.Exec(query, args...)
-
return err
-
}
-
-
func GetIssueComments(e Execer, filters ...filter) ([]models.IssueComment, error) {
-
commentMap := make(map[string]*models.IssueComment)
-
-
var conditions []string
-
var args []any
-
for _, filter := range filters {
-
conditions = append(conditions, filter.Condition())
-
args = append(args, filter.Arg()...)
-
}
-
-
whereClause := ""
-
if conditions != nil {
-
whereClause = " where " + strings.Join(conditions, " and ")
-
}
-
-
query := fmt.Sprintf(`
-
select
-
id,
-
did,
-
rkey,
-
issue_at,
-
reply_to,
-
body,
-
created,
-
edited,
-
deleted
-
from
-
issue_comments
-
%s
-
`, whereClause)
-
-
rows, err := e.Query(query, args...)
-
if err != nil {
-
return nil, err
-
}
-
-
for rows.Next() {
-
var comment models.IssueComment
-
var created string
-
var rkey, edited, deleted, replyTo sql.Null[string]
-
err := rows.Scan(
-
&comment.Id,
-
&comment.Did,
-
&rkey,
-
&comment.IssueAt,
-
&replyTo,
-
&comment.Body,
-
&created,
-
&edited,
-
&deleted,
-
)
-
if err != nil {
-
return nil, err
-
}
-
-
// this is a remnant from old times, newer comments always have rkey
-
if rkey.Valid {
-
comment.Rkey = rkey.V
-
}
-
-
if t, err := time.Parse(time.RFC3339, created); err == nil {
-
comment.Created = t
-
}
-
-
if edited.Valid {
-
if t, err := time.Parse(time.RFC3339, edited.V); err == nil {
-
comment.Edited = &t
-
}
-
}
-
-
if deleted.Valid {
-
if t, err := time.Parse(time.RFC3339, deleted.V); err == nil {
-
comment.Deleted = &t
-
}
-
}
-
-
if replyTo.Valid {
-
comment.ReplyTo = &replyTo.V
-
}
-
-
atUri := comment.AtUri().String()
-
commentMap[atUri] = &comment
-
}
-
-
if err = rows.Err(); err != nil {
-
return nil, err
-
}
-
-
// collect references for each comments
-
commentAts := slices.Collect(maps.Keys(commentMap))
-
allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts))
-
if err != nil {
-
return nil, fmt.Errorf("failed to query reference_links: %w", err)
-
}
-
for commentAt, references := range allReferencs {
-
if comment, ok := commentMap[commentAt.String()]; ok {
-
comment.References = references
-
}
-
}
-
-
var comments []models.IssueComment
-
for _, c := range commentMap {
-
comments = append(comments, *c)
-
}
-
-
sort.Slice(comments, func(i, j int) bool {
-
return comments[i].Created.After(comments[j].Created)
-
})
-
-
return comments, nil
-
}
-
func DeleteIssues(tx *sql.Tx, did, rkey string) error {
_, err := tx.Exec(
`delete from issues
+13 -24
appview/db/reference.go
···
"tangled.org/core/appview/models"
)
-
// ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs.
+
// ValidateReferenceLinks resolves refLinks to Issue/PR/Comment ATURIs.
// It will ignore missing refLinks.
func ValidateReferenceLinks(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) {
var (
···
values %s
)
select
-
i.did, i.rkey,
-
c.did, c.rkey
+
i.at_uri, c.at_uri
from input inp
join repos r
on r.did = inp.owner_did
···
join issues i
on i.repo_at = r.at_uri
and i.issue_id = inp.issue_id
-
left join issue_comments c
+
left join comments c
on inp.comment_id is not null
-
and c.issue_at = i.at_uri
+
and c.subject_at = i.at_uri
and c.id = inp.comment_id
`,
strings.Join(vals, ","),
···
for rows.Next() {
// Scan rows
-
var issueOwner, issueRkey string
-
var commentOwner, commentRkey sql.NullString
+
var issueUri string
+
var commentUri sql.NullString
var uri syntax.ATURI
-
if err := rows.Scan(&issueOwner, &issueRkey, &commentOwner, &commentRkey); err != nil {
+
if err := rows.Scan(&issueUri, &commentUri); err != nil {
return nil, err
}
-
if commentOwner.Valid && commentRkey.Valid {
-
uri = syntax.ATURI(fmt.Sprintf(
-
"at://%s/%s/%s",
-
commentOwner.String,
-
tangled.RepoIssueCommentNSID,
-
commentRkey.String,
-
))
+
if commentUri.Valid {
+
uri = syntax.ATURI(commentUri.String)
} else {
-
uri = syntax.ATURI(fmt.Sprintf(
-
"at://%s/%s/%s",
-
issueOwner,
-
tangled.RepoIssueNSID,
-
issueRkey,
-
))
+
uri = syntax.ATURI(issueUri)
}
uris = append(uris, uri)
}
···
return nil, fmt.Errorf("get issue backlinks: %w", err)
}
backlinks = append(backlinks, ls...)
-
ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.RepoIssueCommentNSID])
+
ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.CommentNSID])
if err != nil {
return nil, fmt.Errorf("get issue_comment backlinks: %w", err)
}
···
rows, err := e.Query(
fmt.Sprintf(
`select r.did, r.name, i.issue_id, c.id, i.title, i.open
-
from issue_comments c
+
from comments c
join issues i
-
on i.at_uri = c.issue_at
+
on i.at_uri = c.subject_at
join repos r
on r.at_uri = i.repo_at
where %s`,
+19 -11
appview/ingester.go
···
err = i.ingestString(e)
case tangled.RepoIssueNSID:
err = i.ingestIssue(ctx, e)
-
case tangled.RepoIssueCommentNSID:
-
err = i.ingestIssueComment(e)
+
case tangled.CommentNSID:
+
err = i.ingestComment(e)
case tangled.LabelDefinitionNSID:
err = i.ingestLabelDefinition(e)
case tangled.LabelOpNSID:
···
return nil
}
-
func (i *Ingester) ingestIssueComment(e *jmodels.Event) error {
+
func (i *Ingester) ingestComment(e *jmodels.Event) error {
did := e.Did
rkey := e.Commit.RKey
var err error
-
l := i.Logger.With("handler", "ingestIssueComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey)
+
l := i.Logger.With("handler", "ingestComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey)
l.Info("ingesting record")
ddb, ok := i.Db.Execer.(*db.DB)
···
switch e.Commit.Operation {
case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate:
raw := json.RawMessage(e.Commit.Record)
-
record := tangled.RepoIssueComment{}
+
record := tangled.Comment{}
err = json.Unmarshal(raw, &record)
if err != nil {
return fmt.Errorf("invalid record: %w", err)
}
-
comment, err := models.IssueCommentFromRecord(did, rkey, record)
+
comment, err := models.CommentFromRecord(did, rkey, record)
if err != nil {
return fmt.Errorf("failed to parse comment from record: %w", err)
}
-
if err := i.Validator.ValidateIssueComment(comment); err != nil {
+
// TODO: ingest pull comments
+
// we aren't ingesting pull comments yet because pull itself isn't fully atprotated.
+
// so we cannot know which round this comment is pointing to
+
if comment.Subject.Collection().String() == tangled.RepoPullNSID {
+
l.Info("skip ingesting pull comments")
+
return nil
+
}
+
+
if err := comment.Validate(); err != nil {
return fmt.Errorf("failed to validate comment: %w", err)
}
···
}
defer tx.Rollback()
-
_, err = db.AddIssueComment(tx, *comment)
+
err = db.PutComment(tx, comment)
if err != nil {
-
return fmt.Errorf("failed to create issue comment: %w", err)
+
return fmt.Errorf("failed to create comment: %w", err)
}
return tx.Commit()
case jmodels.CommitOperationDelete:
-
if err := db.DeleteIssueComments(
+
if err := db.DeleteComments(
ddb,
db.FilterEq("did", did),
db.FilterEq("rkey", rkey),
); err != nil {
-
return fmt.Errorf("failed to delete issue comment record: %w", err)
+
return fmt.Errorf("failed to delete comment record: %w", err)
}
return nil
+30 -28
appview/issues/issues.go
···
body := r.FormValue("body")
if body == "" {
-
rp.pages.Notice(w, "issue", "Body is required")
+
rp.pages.Notice(w, "issue-comment", "Body is required")
return
}
-
replyToUri := r.FormValue("reply-to")
-
var replyTo *string
-
if replyToUri != "" {
-
replyTo = &replyToUri
+
var replyTo *syntax.ATURI
+
replyToRaw := r.FormValue("reply-to")
+
if replyToRaw != "" {
+
aturi, err := syntax.ParseATURI(r.FormValue("reply-to"))
+
if err != nil {
+
rp.pages.Notice(w, "issue-comment", "reply-to should be valid AT-URI")
+
return
+
}
+
replyTo = &aturi
}
mentions, references := rp.refResolver.Resolve(r.Context(), body)
-
comment := models.IssueComment{
-
Did: user.Did,
+
comment := models.Comment{
+
Did: syntax.DID(user.Did),
Rkey: tid.TID(),
-
IssueAt: issue.AtUri().String(),
+
Subject: issue.AtUri(),
ReplyTo: replyTo,
Body: body,
Created: time.Now(),
Mentions: mentions,
References: references,
}
-
if err = rp.validator.ValidateIssueComment(&comment); err != nil {
+
if err = comment.Validate(); err != nil {
l.Error("failed to validate comment", "err", err)
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
return
···
// create a record first
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
-
Collection: tangled.RepoIssueCommentNSID,
-
Repo: comment.Did,
+
Collection: tangled.CommentNSID,
+
Repo: user.Did,
Rkey: comment.Rkey,
Record: &lexutil.LexiconTypeDecoder{
Val: &record,
···
}
defer tx.Rollback()
-
commentId, err := db.AddIssueComment(tx, comment)
+
err = db.PutComment(tx, &comment)
if err != nil {
l.Error("failed to create comment", "err", err)
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
···
// reset atUri to make rollback a no-op
atUri = ""
-
// notify about the new comment
-
comment.Id = commentId
-
rp.notifier.NewIssueComment(r.Context(), &comment, mentions)
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f)
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId))
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, comment.Id))
}
func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) {
···
}
commentId := chi.URLParam(r, "commentId")
-
comments, err := db.GetIssueComments(
+
comments, err := db.GetComments(
rp.db,
db.FilterEq("id", commentId),
)
···
}
commentId := chi.URLParam(r, "commentId")
-
comments, err := db.GetIssueComments(
+
comments, err := db.GetComments(
rp.db,
db.FilterEq("id", commentId),
)
···
}
comment := comments[0]
-
if comment.Did != user.Did {
+
if comment.Did.String() != user.Did {
l.Error("unauthorized comment edit", "expectedDid", comment.Did, "gotDid", user.Did)
http.Error(w, "you are not the author of this comment", http.StatusUnauthorized)
return
···
}
defer tx.Rollback()
-
_, err = db.AddIssueComment(tx, newComment)
+
err = db.PutComment(tx, &newComment)
if err != nil {
l.Error("failed to perferom update-description query", "err", err)
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
···
// rkey is optional, it was introduced later
if newComment.Rkey != "" {
// update the record on pds
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, comment.Rkey)
+
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.CommentNSID, user.Did, comment.Rkey)
if err != nil {
l.Error("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey)
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
···
}
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
-
Collection: tangled.RepoIssueCommentNSID,
+
Collection: tangled.CommentNSID,
Repo: user.Did,
Rkey: newComment.Rkey,
SwapRecord: ex.Cid,
···
}
commentId := chi.URLParam(r, "commentId")
-
comments, err := db.GetIssueComments(
+
comments, err := db.GetComments(
rp.db,
db.FilterEq("id", commentId),
)
···
}
commentId := chi.URLParam(r, "commentId")
-
comments, err := db.GetIssueComments(
+
comments, err := db.GetComments(
rp.db,
db.FilterEq("id", commentId),
)
···
}
commentId := chi.URLParam(r, "commentId")
-
comments, err := db.GetIssueComments(
+
comments, err := db.GetComments(
rp.db,
db.FilterEq("id", commentId),
)
···
}
comment := comments[0]
-
if comment.Did != user.Did {
+
if comment.Did.String() != user.Did {
l.Error("unauthorized action", "expectedDid", comment.Did, "gotDid", user.Did)
http.Error(w, "you are not the author of this comment", http.StatusUnauthorized)
return
···
// optimistic deletion
deleted := time.Now()
-
err = db.DeleteIssueComments(rp.db, db.FilterEq("id", comment.Id))
+
err = db.DeleteComments(rp.db, db.FilterEq("id", comment.Id))
if err != nil {
l.Error("failed to delete comment", "err", err)
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment")
···
return
}
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
-
Collection: tangled.RepoIssueCommentNSID,
+
Collection: tangled.CommentNSID,
Repo: user.Did,
Rkey: comment.Rkey,
})
+8 -89
appview/models/issue.go
···
// optionally, populate this when querying for reverse mappings
// like comment counts, parent repo etc.
-
Comments []IssueComment
+
Comments []Comment
Labels LabelState
Repo *Repo
}
···
}
type CommentListItem struct {
-
Self *IssueComment
-
Replies []*IssueComment
+
Self *Comment
+
Replies []*Comment
}
func (it *CommentListItem) Participants() []syntax.DID {
···
func (i *Issue) CommentList() []CommentListItem {
// Create a map to quickly find comments by their aturi
-
toplevel := make(map[string]*CommentListItem)
-
var replies []*IssueComment
+
toplevel := make(map[syntax.ATURI]*CommentListItem)
+
var replies []*Comment
// collect top level comments into the map
for _, comment := range i.Comments {
if comment.IsTopLevel() {
-
toplevel[comment.AtUri().String()] = &CommentListItem{
+
toplevel[comment.AtUri()] = &CommentListItem{
Self: &comment,
}
} else {
···
}
// sort everything
-
sortFunc := func(a, b *IssueComment) bool {
+
sortFunc := func(a, b *Comment) bool {
return a.Created.Before(b.Created)
}
sort.Slice(listing, func(i, j int) bool {
···
addParticipant(i.Did)
for _, c := range i.Comments {
-
addParticipant(c.Did)
+
addParticipant(c.Did.String())
}
return participants
···
Open: true, // new issues are open by default
}
}
-
-
type IssueComment struct {
-
Id int64
-
Did string
-
Rkey string
-
IssueAt string
-
ReplyTo *string
-
Body string
-
Created time.Time
-
Edited *time.Time
-
Deleted *time.Time
-
Mentions []syntax.DID
-
References []syntax.ATURI
-
}
-
-
func (i *IssueComment) AtUri() syntax.ATURI {
-
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey))
-
}
-
-
func (i *IssueComment) AsRecord() tangled.RepoIssueComment {
-
mentions := make([]string, len(i.Mentions))
-
for i, did := range i.Mentions {
-
mentions[i] = string(did)
-
}
-
references := make([]string, len(i.References))
-
for i, uri := range i.References {
-
references[i] = string(uri)
-
}
-
return tangled.RepoIssueComment{
-
Body: i.Body,
-
Issue: i.IssueAt,
-
CreatedAt: i.Created.Format(time.RFC3339),
-
ReplyTo: i.ReplyTo,
-
Mentions: mentions,
-
References: references,
-
}
-
}
-
-
func (i *IssueComment) IsTopLevel() bool {
-
return i.ReplyTo == nil
-
}
-
-
func (i *IssueComment) IsReply() bool {
-
return i.ReplyTo != nil
-
}
-
-
func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) {
-
created, err := time.Parse(time.RFC3339, record.CreatedAt)
-
if err != nil {
-
created = time.Now()
-
}
-
-
ownerDid := did
-
-
if _, err = syntax.ParseATURI(record.Issue); err != nil {
-
return nil, err
-
}
-
-
i := record
-
mentions := make([]syntax.DID, len(record.Mentions))
-
for i, did := range record.Mentions {
-
mentions[i] = syntax.DID(did)
-
}
-
references := make([]syntax.ATURI, len(record.References))
-
for i, uri := range i.References {
-
references[i] = syntax.ATURI(uri)
-
}
-
-
comment := IssueComment{
-
Did: ownerDid,
-
Rkey: rkey,
-
Body: record.Body,
-
IssueAt: record.Issue,
-
ReplyTo: record.ReplyTo,
-
Created: created,
-
Mentions: mentions,
-
References: references,
-
}
-
-
return &comment, nil
-
}
+4 -4
appview/notify/db/db.go
···
)
}
-
func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) {
-
issues, err := db.GetIssues(n.db, db.FilterEq("at_uri", comment.IssueAt))
+
func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) {
+
issues, err := db.GetIssues(n.db, db.FilterEq("at_uri", comment.Subject))
if err != nil {
log.Printf("NewIssueComment: failed to get issues: %v", err)
return
}
if len(issues) == 0 {
-
log.Printf("NewIssueComment: no issue found for %s", comment.IssueAt)
+
log.Printf("NewIssueComment: no issue found for %s", comment.Subject)
return
}
issue := issues[0]
···
// find the parent thread, and add all DIDs from here to the recipient list
for _, t := range allThreads {
-
if t.Self.AtUri().String() == parentAtUri {
+
if t.Self.AtUri() == parentAtUri {
recipients = append(recipients, t.Participants()...)
}
}
+1 -1
appview/notify/merged_notifier.go
···
m.fanout("NewIssue", ctx, issue, mentions)
}
-
func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) {
+
func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) {
m.fanout("NewIssueComment", ctx, comment, mentions)
}
+2 -2
appview/notify/notifier.go
···
DeleteStar(ctx context.Context, star *models.Star)
NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID)
-
NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID)
+
NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID)
NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue)
DeleteIssue(ctx context.Context, issue *models.Issue)
···
func (m *BaseNotifier) DeleteStar(ctx context.Context, star *models.Star) {}
func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {}
-
func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) {
+
func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) {
}
func (m *BaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {}
func (m *BaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) {}
+3 -3
appview/notify/posthog/notifier.go
···
}
}
-
func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) {
+
func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) {
err := n.client.Enqueue(posthog.Capture{
-
DistinctId: comment.Did,
+
DistinctId: comment.Did.String(),
Event: "new_issue_comment",
Properties: posthog.Properties{
-
"issue_at": comment.IssueAt,
+
"issue_at": comment.Subject,
"mentions": mentions,
},
})
+4 -4
appview/pages/pages.go
···
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Issue *models.Issue
-
Comment *models.IssueComment
+
Comment *models.Comment
}
func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error {
···
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Issue *models.Issue
-
Comment *models.IssueComment
+
Comment *models.Comment
func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error {
···
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Issue *models.Issue
-
Comment *models.IssueComment
+
Comment *models.Comment
func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error {
···
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Issue *models.Issue
-
Comment *models.IssueComment
+
Comment *models.Comment
func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error {
+2 -2
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
···
{{ define "repo/issues/fragments/issueCommentHeader" }}
<div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400 ">
-
{{ template "user/fragments/picHandleLink" .Comment.Did }}
+
{{ template "user/fragments/picHandleLink" .Comment.Did.String }}
{{ template "hats" $ }}
{{ template "timestamp" . }}
-
{{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }}
+
{{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did.String) }}
{{ if and $isCommentOwner (not .Comment.Deleted) }}
{{ template "editIssueComment" . }}
{{ template "deleteIssueComment" . }}
+1 -1
appview/state/state.go
···
tangled.SpindleNSID,
tangled.StringNSID,
tangled.RepoIssueNSID,
-
tangled.RepoIssueCommentNSID,
+
tangled.CommentNSID,
tangled.LabelDefinitionNSID,
tangled.LabelOpNSID,
},
-26
appview/validator/issue.go
···
"fmt"
"strings"
-
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
)
-
func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error {
-
// if comments have parents, only ingest ones that are 1 level deep
-
if comment.ReplyTo != nil {
-
parents, err := db.GetIssueComments(v.db, db.FilterEq("at_uri", *comment.ReplyTo))
-
if err != nil {
-
return fmt.Errorf("failed to fetch parent comment: %w", err)
-
}
-
if len(parents) != 1 {
-
return fmt.Errorf("incorrect number of parent comments returned: %d", len(parents))
-
}
-
-
// depth check
-
parent := parents[0]
-
if parent.ReplyTo != nil {
-
return fmt.Errorf("incorrect depth, this comment is replying at depth >1")
-
}
-
}
-
-
if sb := strings.TrimSpace(v.sanitizer.SanitizeDefault(comment.Body)); sb == "" {
-
return fmt.Errorf("body is empty after HTML sanitization")
-
}
-
-
return nil
-
}
-
func (v *Validator) ValidateIssue(issue *models.Issue) error {
if issue.Title == "" {
return fmt.Errorf("issue title is empty")