From 32304c5dedac43b9da02e6c58345a5096340c7c6 Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 27 Mar 2025 21:02:37 +0000 Subject: [PATCH] add rkeys to comments this allows to actually edit and delete them from pds etc. --- appview/db/db.go | 8 ++ appview/db/issues.go | 61 ++++++++-- .../templates/fragments/issueComment.html | 23 ++-- appview/state/repo.go | 113 ++++++++++++------ 4 files changed, 151 insertions(+), 54 deletions(-) diff --git a/appview/db/db.go b/appview/db/db.go index 8fac9204..5ff663d3 100644 --- a/appview/db/db.go +++ b/appview/db/db.go @@ -248,6 +248,14 @@ func Make(dbPath string) (*DB, error) { return nil }) + runMigration(db, "add-rkey-to-comments", func(tx *sql.Tx) error { + _, err := tx.Exec(` + alter table comments drop column comment_at; + alter table comments add column rkey text; + `) + return err + }) + runMigration(db, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error { // add unconstrained column _, err := tx.Exec(` diff --git a/appview/db/issues.go b/appview/db/issues.go index cd41b60c..9de11bff 100644 --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -27,7 +27,7 @@ type IssueMetadata struct { type Comment struct { OwnerDid string RepoAt syntax.ATURI - CommentAt syntax.ATURI + Rkey string Issue int CommentId int Body string @@ -201,13 +201,13 @@ func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, [ return &issue, comments, nil } -func NewComment(e Execer, comment *Comment) error { - query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` +func NewIssueComment(e Execer, comment *Comment) error { + query := `insert into comments (owner_did, repo_at, rkey, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` _, err := e.Exec( query, comment.OwnerDid, comment.RepoAt, - comment.CommentAt, + comment.Rkey, comment.Issue, comment.CommentId, comment.Body, @@ -218,7 +218,25 @@ func NewComment(e Execer, comment *Comment) error { func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) { var comments []Comment - rows, err := e.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId) + rows, err := e.Query(` + select + owner_did, + issue_id, + comment_id, + rkey, + body, + created, + edited, + deleted + from + comments + where + repo_at = ? and issue_id = ? + order by + created asc`, + repoAt, + issueId, + ) if err == sql.ErrNoRows { return []Comment{}, nil } @@ -230,7 +248,8 @@ func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) for rows.Next() { var comment Comment var createdAt string - err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt) + var deletedAt, editedAt, rkey sql.NullString + err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &rkey, &comment.Body, &createdAt, &editedAt, &deletedAt) if err != nil { return nil, err } @@ -241,6 +260,26 @@ func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) } comment.Created = &createdAtTime + if deletedAt.Valid { + deletedTime, err := time.Parse(time.RFC3339, deletedAt.String) + if err != nil { + return nil, err + } + comment.Deleted = &deletedTime + } + + if editedAt.Valid { + editedTime, err := time.Parse(time.RFC3339, editedAt.String) + if err != nil { + return nil, err + } + comment.Edited = &editedTime + } + + if rkey.Valid { + comment.Rkey = rkey.String + } + comments = append(comments, comment) } @@ -254,7 +293,7 @@ func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) func GetComment(e Execer, repoAt syntax.ATURI, issueId, commentId int) (*Comment, error) { query := ` select - owner_did, body, comment_at, created, deleted, edited + owner_did, body, rkey, created, deleted, edited from comments where repo_at = ? and issue_id = ? and comment_id = ? ` @@ -262,8 +301,8 @@ func GetComment(e Execer, repoAt syntax.ATURI, issueId, commentId int) (*Comment var comment Comment var createdAt string - var deletedAt, editedAt sql.NullString - err := row.Scan(&comment.OwnerDid, &comment.Body, &comment.CommentAt, &createdAt, &deletedAt, &editedAt) + var deletedAt, editedAt, rkey sql.NullString + err := row.Scan(&comment.OwnerDid, &comment.Body, &rkey, &createdAt, &deletedAt, &editedAt) if err != nil { return nil, err } @@ -290,6 +329,10 @@ func GetComment(e Execer, repoAt syntax.ATURI, issueId, commentId int) (*Comment comment.Edited = &editedTime } + if rkey.Valid { + comment.Rkey = rkey.String + } + comment.RepoAt = repoAt comment.Issue = issueId comment.CommentId = commentId diff --git a/appview/pages/templates/fragments/issueComment.html b/appview/pages/templates/fragments/issueComment.html index ed05504d..21b12662 100644 --- a/appview/pages/templates/fragments/issueComment.html +++ b/appview/pages/templates/fragments/issueComment.html @@ -9,7 +9,7 @@ {{ $isIssueAuthor := eq .OwnerDid $.Issue.OwnerDid }} {{ if $isIssueAuthor }} - + author {{ end }} @@ -19,7 +19,13 @@ href="#{{ .CommentId }}" class="text-gray-500 hover:text-gray-500 hover:underline no-underline" id="{{ .CommentId }}"> - {{ .Created | timeFmt }} + {{ if .Deleted }} + deleted {{ .Deleted | timeFmt }} + {{ else if .Edited }} + edited {{ .Edited | timeFmt }} + {{ else }} + {{ .Created | timeFmt }} + {{ end }} {{ $isCommentOwner := eq $.LoggedInUser.Did .OwnerDid }} @@ -32,18 +38,19 @@ > {{ i "pencil" "w-4 h-4" }} - {{ end }} - {{ if .Deleted }} - deleted {{ .Deleted | timeFmt }} - {{ end }} - {{ if not .Deleted }} -
+
{{ .Body | markdown }}
{{ end }} diff --git a/appview/state/repo.go b/appview/state/repo.go index 8cc10c8e..9b300de9 100644 --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -933,13 +933,15 @@ func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { } commentId := rand.IntN(1000000) + rkey := s.TID() - err := db.NewComment(s.db, &db.Comment{ + err := db.NewIssueComment(s.db, &db.Comment{ OwnerDid: user.Did, RepoAt: f.RepoAt, Issue: issueIdInt, CommentId: commentId, Body: body, + Rkey: rkey, }) if err != nil { log.Println("failed to create comment", err) @@ -962,7 +964,7 @@ func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueCommentNSID, Repo: user.Did, - Rkey: s.TID(), + Rkey: rkey, Record: &lexutil.LexiconTypeDecoder{ Val: &tangled.RepoIssueComment{ Repo: &atUri, @@ -1098,10 +1100,10 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { // extract form value newBody := r.FormValue("body") client, _ := s.auth.AuthorizedClient(r) - log.Println("comment at", comment.CommentAt) - rkey := comment.CommentAt.RecordKey() + rkey := comment.Rkey // optimistic update + edited := time.Now() err = db.EditComment(s.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody) if err != nil { log.Println("failed to perferom update-description query", err) @@ -1109,40 +1111,43 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { return } - // update the record on pds - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey.String()) - if err != nil { - // failed to get record - log.Println(err, rkey.String()) - s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") - return - } - value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json - record, _ := data.UnmarshalJSON(value) - - repoAt := record["repo"].(string) - issueAt := record["issue"].(string) - createdAt := record["createdAt"].(string) - commentIdInt64 := int64(commentIdInt) - - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ - Collection: tangled.RepoNSID, - Repo: user.Did, - Rkey: rkey.String(), - SwapRecord: ex.Cid, - Record: &lexutil.LexiconTypeDecoder{ - Val: &tangled.RepoIssueComment{ - Repo: &repoAt, - Issue: issueAt, - CommentId: &commentIdInt64, - Owner: &comment.OwnerDid, - Body: &newBody, - CreatedAt: &createdAt, + // rkey is optional, it was introduced later + if comment.Rkey != "" { + // update the record on pds + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) + if err != nil { + // failed to get record + log.Println(err, rkey) + s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") + return + } + value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json + record, _ := data.UnmarshalJSON(value) + + repoAt := record["repo"].(string) + issueAt := record["issue"].(string) + createdAt := record["createdAt"].(string) + commentIdInt64 := int64(commentIdInt) + + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + Collection: tangled.RepoIssueCommentNSID, + Repo: user.Did, + Rkey: rkey, + SwapRecord: ex.Cid, + Record: &lexutil.LexiconTypeDecoder{ + Val: &tangled.RepoIssueComment{ + Repo: &repoAt, + Issue: issueAt, + CommentId: &commentIdInt64, + Owner: &comment.OwnerDid, + Body: &newBody, + CreatedAt: &createdAt, + }, }, - }, - }) - if err != nil { - log.Println(err) + }) + if err != nil { + log.Println(err) + } } // optimistic update for htmx @@ -1150,6 +1155,7 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { user.Did: user.Handle, } comment.Body = newBody + comment.Edited = &edited // return new comment body with htmx s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ @@ -1181,6 +1187,13 @@ func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { return } + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) + if err != nil { + log.Println("failed to get issue", err) + s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") + return + } + commentId := chi.URLParam(r, "comment_id") commentIdInt, err := strconv.Atoi(commentId) if err != nil { @@ -1206,6 +1219,7 @@ func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { } // optimistic deletion + deleted := time.Now() err = db.DeleteComment(s.db, f.RepoAt, issueIdInt, commentIdInt) if err != nil { log.Println("failed to delete comment") @@ -1214,8 +1228,33 @@ func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { } // delete from pds + if comment.Rkey != "" { + client, _ := s.auth.AuthorizedClient(r) + _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + Collection: tangled.GraphFollowNSID, + Repo: user.Did, + Rkey: comment.Rkey, + }) + if err != nil { + log.Println(err) + } + } + + // optimistic update for htmx + didHandleMap := map[string]string{ + user.Did: user.Handle, + } + comment.Body = "" + comment.Deleted = &deleted // htmx fragment of comment after deletion + s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), + DidHandleMap: didHandleMap, + Issue: issue, + Comment: comment, + }) return } -- 2.51.2