tangled
alpha
login
or
join now
diffdown.com
/
diffdown-app
0
fork
atom
Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
0
fork
atom
overview
issues
10
pulls
pipelines
Add CommentUpdate handler for resolution toggle
diffdown.com
2 weeks ago
21336635
3e200ef9
+86
-10
3 changed files
expand all
collapse all
unified
split
cmd
server
main.go
internal
handler
handler.go
model
models.go
+1
cmd/server/main.go
reviewed
···
114
114
mux.HandleFunc("GET /docs/{rkey}/accept", h.AcceptInvite)
115
115
mux.HandleFunc("POST /api/docs/{rkey}/comments", h.CommentCreate)
116
116
mux.HandleFunc("GET /api/docs/{rkey}/comments", h.CommentList)
117
117
+
mux.HandleFunc("PATCH /api/docs/{rkey}/comments/{commentId}", h.CommentUpdate)
117
118
mux.HandleFunc("POST /api/docs/{rkey}/steps", h.SubmitSteps)
118
119
mux.HandleFunc("GET /api/docs/{rkey}/steps", h.GetSteps)
119
120
+84
-10
internal/handler/handler.go
reviewed
···
853
853
return
854
854
}
855
855
856
856
-
value, _, err := ownerClient.GetRecord(ownerDID, collectionDocument, rKey)
856
856
+
records, _, err := ownerClient.ListComments(ownerDID, rKey, 100, "")
857
857
if err != nil {
858
858
-
log.Printf("CommentList: GetRecord: %v", err)
859
859
-
http.Error(w, "Document not found", http.StatusNotFound)
858
858
+
log.Printf("CommentList: ListComments: %v", err)
859
859
+
h.jsonResponse(w, []model.CommentRecord{}, http.StatusOK)
860
860
+
return
861
861
+
}
862
862
+
863
863
+
comments := make([]model.CommentRecord, 0, len(records))
864
864
+
for _, rec := range records {
865
865
+
var comment model.CommentRecord
866
866
+
if err := json.Unmarshal(rec.Value, &comment); err != nil {
867
867
+
log.Printf("CommentList: unmarshal comment: %v", err)
868
868
+
continue
869
869
+
}
870
870
+
comment.ID = model.RKeyFromURI(rec.URI)
871
871
+
comments = append(comments, comment)
872
872
+
}
873
873
+
874
874
+
h.jsonResponse(w, comments, http.StatusOK)
875
875
+
}
876
876
+
877
877
+
func (h *Handler) CommentUpdate(w http.ResponseWriter, r *http.Request) {
878
878
+
user, _ := h.currentUser(r)
879
879
+
if user == nil {
880
880
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
860
881
return
861
882
}
862
862
-
var doc model.Document
863
863
-
if err := json.Unmarshal(value, &doc); err != nil {
864
864
-
http.Error(w, "Failed to parse document", http.StatusInternalServerError)
883
883
+
884
884
+
rKey := r.PathValue("rkey")
885
885
+
commentID := r.PathValue("commentId")
886
886
+
if rKey == "" || commentID == "" {
887
887
+
http.Error(w, "Invalid request", http.StatusBadRequest)
865
888
return
866
889
}
867
890
868
868
-
comments := doc.Comments
869
869
-
if comments == nil {
870
870
-
comments = []model.EmbeddedComment{}
891
891
+
var req struct {
892
892
+
OwnerDID string `json:"ownerDID"`
893
893
+
Resolved bool `json:"resolved"`
894
894
+
}
895
895
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
896
896
+
http.Error(w, "Invalid request", http.StatusBadRequest)
897
897
+
return
871
898
}
872
872
-
h.jsonResponse(w, comments, http.StatusOK)
899
899
+
900
900
+
session, err := h.DB.GetATProtoSession(user.ID)
901
901
+
if err != nil || session == nil {
902
902
+
http.Error(w, "Not authenticated with ATProto", http.StatusUnauthorized)
903
903
+
return
904
904
+
}
905
905
+
906
906
+
ownerUserID := user.ID
907
907
+
ownerDID := session.DID
908
908
+
if req.OwnerDID != "" && req.OwnerDID != session.DID {
909
909
+
ownerUser, err := h.DB.GetUserByDID(req.OwnerDID)
910
910
+
if err != nil {
911
911
+
http.Error(w, "Owner not found", http.StatusBadRequest)
912
912
+
return
913
913
+
}
914
914
+
ownerUserID = ownerUser.ID
915
915
+
ownerDID = req.OwnerDID
916
916
+
}
917
917
+
918
918
+
ownerClient, err := h.xrpcClient(ownerUserID)
919
919
+
if err != nil {
920
920
+
http.Error(w, "Failed to connect to owner PDS", http.StatusInternalServerError)
921
921
+
return
922
922
+
}
923
923
+
924
924
+
value, _, err := ownerClient.GetRecord(ownerDID, model.CollectionComment, commentID)
925
925
+
if err != nil {
926
926
+
log.Printf("CommentUpdate: GetRecord: %v", err)
927
927
+
http.Error(w, "Comment not found", http.StatusNotFound)
928
928
+
return
929
929
+
}
930
930
+
931
931
+
var comment model.CommentRecord
932
932
+
if err := json.Unmarshal(value, &comment); err != nil {
933
933
+
http.Error(w, "Failed to parse comment", http.StatusInternalServerError)
934
934
+
return
935
935
+
}
936
936
+
937
937
+
comment.Resolved = req.Resolved
938
938
+
939
939
+
_, _, err = ownerClient.UpdateComment(commentID, comment)
940
940
+
if err != nil {
941
941
+
log.Printf("CommentUpdate: UpdateComment: %v", err)
942
942
+
http.Error(w, "Failed to update comment", http.StatusInternalServerError)
943
943
+
return
944
944
+
}
945
945
+
946
946
+
h.jsonResponse(w, comment, http.StatusOK)
873
947
}
874
948
875
949
// --- API: Render markdown ---
+1
internal/model/models.go
reviewed
···
61
61
}
62
62
63
63
type CommentRecord struct {
64
64
+
ID string `json:"id"` // record key (rkey)
64
65
ThreadID string `json:"threadId"` // groups replies into threads
65
66
DocRKey string `json:"docRKey"` // document rkey this comment belongs to
66
67
DocOwnerDID string `json:"docOwnerDid"` // owner DID for quick filtering