this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add rkeys to comments

this allows to actually edit and delete them from pds etc.

Akshay 32304c5d 3d95c2e4

+149 -52
+8
appview/db/db.go
··· 248 248 return nil 249 249 }) 250 250 251 + runMigration(db, "add-rkey-to-comments", func(tx *sql.Tx) error { 252 + _, err := tx.Exec(` 253 + alter table comments drop column comment_at; 254 + alter table comments add column rkey text; 255 + `) 256 + return err 257 + }) 258 + 251 259 runMigration(db, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error { 252 260 // add unconstrained column 253 261 _, err := tx.Exec(`
+52 -9
appview/db/issues.go
··· 27 27 type Comment struct { 28 28 OwnerDid string 29 29 RepoAt syntax.ATURI 30 - CommentAt syntax.ATURI 30 + Rkey string 31 31 Issue int 32 32 CommentId int 33 33 Body string ··· 201 201 return &issue, comments, nil 202 202 } 203 203 204 - func NewComment(e Execer, comment *Comment) error { 205 - query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` 204 + func NewIssueComment(e Execer, comment *Comment) error { 205 + query := `insert into comments (owner_did, repo_at, rkey, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` 206 206 _, err := e.Exec( 207 207 query, 208 208 comment.OwnerDid, 209 209 comment.RepoAt, 210 - comment.CommentAt, 210 + comment.Rkey, 211 211 comment.Issue, 212 212 comment.CommentId, 213 213 comment.Body, ··· 218 218 func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) { 219 219 var comments []Comment 220 220 221 - 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) 221 + rows, err := e.Query(` 222 + select 223 + owner_did, 224 + issue_id, 225 + comment_id, 226 + rkey, 227 + body, 228 + created, 229 + edited, 230 + deleted 231 + from 232 + comments 233 + where 234 + repo_at = ? and issue_id = ? 235 + order by 236 + created asc`, 237 + repoAt, 238 + issueId, 239 + ) 222 240 if err == sql.ErrNoRows { 223 241 return []Comment{}, nil 224 242 } ··· 230 248 for rows.Next() { 231 249 var comment Comment 232 250 var createdAt string 233 - err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt) 251 + var deletedAt, editedAt, rkey sql.NullString 252 + err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &rkey, &comment.Body, &createdAt, &editedAt, &deletedAt) 234 253 if err != nil { 235 254 return nil, err 236 255 } ··· 241 260 } 242 261 comment.Created = &createdAtTime 243 262 263 + if deletedAt.Valid { 264 + deletedTime, err := time.Parse(time.RFC3339, deletedAt.String) 265 + if err != nil { 266 + return nil, err 267 + } 268 + comment.Deleted = &deletedTime 269 + } 270 + 271 + if editedAt.Valid { 272 + editedTime, err := time.Parse(time.RFC3339, editedAt.String) 273 + if err != nil { 274 + return nil, err 275 + } 276 + comment.Edited = &editedTime 277 + } 278 + 279 + if rkey.Valid { 280 + comment.Rkey = rkey.String 281 + } 282 + 244 283 comments = append(comments, comment) 245 284 } 246 285 ··· 254 293 func GetComment(e Execer, repoAt syntax.ATURI, issueId, commentId int) (*Comment, error) { 255 294 query := ` 256 295 select 257 - owner_did, body, comment_at, created, deleted, edited 296 + owner_did, body, rkey, created, deleted, edited 258 297 from 259 298 comments where repo_at = ? and issue_id = ? and comment_id = ? 260 299 ` ··· 262 301 263 302 var comment Comment 264 303 var createdAt string 265 - var deletedAt, editedAt sql.NullString 266 - err := row.Scan(&comment.OwnerDid, &comment.Body, &comment.CommentAt, &createdAt, &deletedAt, &editedAt) 304 + var deletedAt, editedAt, rkey sql.NullString 305 + err := row.Scan(&comment.OwnerDid, &comment.Body, &rkey, &createdAt, &deletedAt, &editedAt) 267 306 if err != nil { 268 307 return nil, err 269 308 } ··· 288 327 return nil, err 289 328 } 290 329 comment.Edited = &editedTime 330 + } 331 + 332 + if rkey.Valid { 333 + comment.Rkey = rkey.String 291 334 } 292 335 293 336 comment.RepoAt = repoAt
+15 -8
appview/pages/templates/fragments/issueComment.html
··· 9 9 {{ $isIssueAuthor := eq .OwnerDid $.Issue.OwnerDid }} 10 10 {{ if $isIssueAuthor }} 11 11 <span class="before:content-['·']"></span> 12 - <span class="rounded bg-gray-100 text-black font-mono px-2 mx-1/2 inline-flex items-center"> 12 + <span class="rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 13 13 author 14 14 </span> 15 15 {{ end }} ··· 19 19 href="#{{ .CommentId }}" 20 20 class="text-gray-500 hover:text-gray-500 hover:underline no-underline" 21 21 id="{{ .CommentId }}"> 22 - {{ .Created | timeFmt }} 22 + {{ if .Deleted }} 23 + deleted {{ .Deleted | timeFmt }} 24 + {{ else if .Edited }} 25 + edited {{ .Edited | timeFmt }} 26 + {{ else }} 27 + {{ .Created | timeFmt }} 28 + {{ end }} 23 29 </a> 24 30 25 31 {{ $isCommentOwner := eq $.LoggedInUser.Did .OwnerDid }} ··· 32 38 > 33 39 {{ i "pencil" "w-4 h-4" }} 34 40 </button> 35 - <button class="btn px-2 py-1 text-sm text-red-500" hx-delete=""> 41 + <button 42 + class="btn px-2 py-1 text-sm text-red-500" 43 + hx-delete="/{{ $.RepoInfo.FullName }}/issues/{{ .Issue }}/comment/{{ .CommentId }}/" 44 + hx-swap="outerHTML" 45 + hx-target="#comment-container-{{.CommentId}}" 46 + > 36 47 {{ i "trash-2" "w-4 h-4" }} 37 48 </button> 38 49 {{ end }} 39 50 40 - {{ if .Deleted }} 41 - <span class="before:content-['·']">deleted {{ .Deleted | timeFmt }}</span> 42 - {{ end }} 43 - 44 51 </div> 45 52 {{ if not .Deleted }} 46 - <div class="prose"> 53 + <div class="prose dark:prose-invert"> 47 54 {{ .Body | markdown }} 48 55 </div> 49 56 {{ end }}
+74 -35
appview/state/repo.go
··· 933 933 } 934 934 935 935 commentId := rand.IntN(1000000) 936 + rkey := s.TID() 936 937 937 - err := db.NewComment(s.db, &db.Comment{ 938 + err := db.NewIssueComment(s.db, &db.Comment{ 938 939 OwnerDid: user.Did, 939 940 RepoAt: f.RepoAt, 940 941 Issue: issueIdInt, 941 942 CommentId: commentId, 942 943 Body: body, 944 + Rkey: rkey, 943 945 }) 944 946 if err != nil { 945 947 log.Println("failed to create comment", err) ··· 962 964 _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 963 965 Collection: tangled.RepoIssueCommentNSID, 964 966 Repo: user.Did, 965 - Rkey: s.TID(), 967 + Rkey: rkey, 966 968 Record: &lexutil.LexiconTypeDecoder{ 967 969 Val: &tangled.RepoIssueComment{ 968 970 Repo: &atUri, ··· 1098 1100 // extract form value 1099 1101 newBody := r.FormValue("body") 1100 1102 client, _ := s.auth.AuthorizedClient(r) 1101 - log.Println("comment at", comment.CommentAt) 1102 - rkey := comment.CommentAt.RecordKey() 1103 + rkey := comment.Rkey 1103 1104 1104 1105 // optimistic update 1106 + edited := time.Now() 1105 1107 err = db.EditComment(s.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody) 1106 1108 if err != nil { 1107 1109 log.Println("failed to perferom update-description query", err) ··· 1109 1111 return 1110 1112 } 1111 1113 1112 - // update the record on pds 1113 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey.String()) 1114 - if err != nil { 1115 - // failed to get record 1116 - log.Println(err, rkey.String()) 1117 - s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 1118 - return 1119 - } 1120 - value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json 1121 - record, _ := data.UnmarshalJSON(value) 1114 + // rkey is optional, it was introduced later 1115 + if comment.Rkey != "" { 1116 + // update the record on pds 1117 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) 1118 + if err != nil { 1119 + // failed to get record 1120 + log.Println(err, rkey) 1121 + s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 1122 + return 1123 + } 1124 + value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json 1125 + record, _ := data.UnmarshalJSON(value) 1122 1126 1123 - repoAt := record["repo"].(string) 1124 - issueAt := record["issue"].(string) 1125 - createdAt := record["createdAt"].(string) 1126 - commentIdInt64 := int64(commentIdInt) 1127 + repoAt := record["repo"].(string) 1128 + issueAt := record["issue"].(string) 1129 + createdAt := record["createdAt"].(string) 1130 + commentIdInt64 := int64(commentIdInt) 1127 1131 1128 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1129 - Collection: tangled.RepoNSID, 1130 - Repo: user.Did, 1131 - Rkey: rkey.String(), 1132 - SwapRecord: ex.Cid, 1133 - Record: &lexutil.LexiconTypeDecoder{ 1134 - Val: &tangled.RepoIssueComment{ 1135 - Repo: &repoAt, 1136 - Issue: issueAt, 1137 - CommentId: &commentIdInt64, 1138 - Owner: &comment.OwnerDid, 1139 - Body: &newBody, 1140 - CreatedAt: &createdAt, 1132 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1133 + Collection: tangled.RepoIssueCommentNSID, 1134 + Repo: user.Did, 1135 + Rkey: rkey, 1136 + SwapRecord: ex.Cid, 1137 + Record: &lexutil.LexiconTypeDecoder{ 1138 + Val: &tangled.RepoIssueComment{ 1139 + Repo: &repoAt, 1140 + Issue: issueAt, 1141 + CommentId: &commentIdInt64, 1142 + Owner: &comment.OwnerDid, 1143 + Body: &newBody, 1144 + CreatedAt: &createdAt, 1145 + }, 1141 1146 }, 1142 - }, 1143 - }) 1144 - if err != nil { 1145 - log.Println(err) 1147 + }) 1148 + if err != nil { 1149 + log.Println(err) 1150 + } 1146 1151 } 1147 1152 1148 1153 // optimistic update for htmx ··· 1150 1155 user.Did: user.Handle, 1151 1156 } 1152 1157 comment.Body = newBody 1158 + comment.Edited = &edited 1153 1159 1154 1160 // return new comment body with htmx 1155 1161 s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ ··· 1181 1187 return 1182 1188 } 1183 1189 1190 + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1191 + if err != nil { 1192 + log.Println("failed to get issue", err) 1193 + s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1194 + return 1195 + } 1196 + 1184 1197 commentId := chi.URLParam(r, "comment_id") 1185 1198 commentIdInt, err := strconv.Atoi(commentId) 1186 1199 if err != nil { ··· 1206 1219 } 1207 1220 1208 1221 // optimistic deletion 1222 + deleted := time.Now() 1209 1223 err = db.DeleteComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1210 1224 if err != nil { 1211 1225 log.Println("failed to delete comment") ··· 1214 1228 } 1215 1229 1216 1230 // delete from pds 1231 + if comment.Rkey != "" { 1232 + client, _ := s.auth.AuthorizedClient(r) 1233 + _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 1234 + Collection: tangled.GraphFollowNSID, 1235 + Repo: user.Did, 1236 + Rkey: comment.Rkey, 1237 + }) 1238 + if err != nil { 1239 + log.Println(err) 1240 + } 1241 + } 1242 + 1243 + // optimistic update for htmx 1244 + didHandleMap := map[string]string{ 1245 + user.Did: user.Handle, 1246 + } 1247 + comment.Body = "" 1248 + comment.Deleted = &deleted 1217 1249 1218 1250 // htmx fragment of comment after deletion 1251 + s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1252 + LoggedInUser: user, 1253 + RepoInfo: f.RepoInfo(s, user), 1254 + DidHandleMap: didHandleMap, 1255 + Issue: issue, 1256 + Comment: comment, 1257 + }) 1219 1258 return 1220 1259 } 1221 1260