Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com

Add CommentUpdate handler for resolution toggle

+86 -10
+1
cmd/server/main.go
··· 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 + 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
··· 853 853 return 854 854 } 855 855 856 - value, _, err := ownerClient.GetRecord(ownerDID, collectionDocument, rKey) 856 + records, _, err := ownerClient.ListComments(ownerDID, rKey, 100, "") 857 857 if err != nil { 858 - log.Printf("CommentList: GetRecord: %v", err) 859 - http.Error(w, "Document not found", http.StatusNotFound) 858 + log.Printf("CommentList: ListComments: %v", err) 859 + h.jsonResponse(w, []model.CommentRecord{}, http.StatusOK) 860 + return 861 + } 862 + 863 + comments := make([]model.CommentRecord, 0, len(records)) 864 + for _, rec := range records { 865 + var comment model.CommentRecord 866 + if err := json.Unmarshal(rec.Value, &comment); err != nil { 867 + log.Printf("CommentList: unmarshal comment: %v", err) 868 + continue 869 + } 870 + comment.ID = model.RKeyFromURI(rec.URI) 871 + comments = append(comments, comment) 872 + } 873 + 874 + h.jsonResponse(w, comments, http.StatusOK) 875 + } 876 + 877 + func (h *Handler) CommentUpdate(w http.ResponseWriter, r *http.Request) { 878 + user, _ := h.currentUser(r) 879 + if user == nil { 880 + http.Error(w, "Unauthorized", http.StatusUnauthorized) 860 881 return 861 882 } 862 - var doc model.Document 863 - if err := json.Unmarshal(value, &doc); err != nil { 864 - http.Error(w, "Failed to parse document", http.StatusInternalServerError) 883 + 884 + rKey := r.PathValue("rkey") 885 + commentID := r.PathValue("commentId") 886 + if rKey == "" || commentID == "" { 887 + http.Error(w, "Invalid request", http.StatusBadRequest) 865 888 return 866 889 } 867 890 868 - comments := doc.Comments 869 - if comments == nil { 870 - comments = []model.EmbeddedComment{} 891 + var req struct { 892 + OwnerDID string `json:"ownerDID"` 893 + Resolved bool `json:"resolved"` 894 + } 895 + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 896 + http.Error(w, "Invalid request", http.StatusBadRequest) 897 + return 871 898 } 872 - h.jsonResponse(w, comments, http.StatusOK) 899 + 900 + session, err := h.DB.GetATProtoSession(user.ID) 901 + if err != nil || session == nil { 902 + http.Error(w, "Not authenticated with ATProto", http.StatusUnauthorized) 903 + return 904 + } 905 + 906 + ownerUserID := user.ID 907 + ownerDID := session.DID 908 + if req.OwnerDID != "" && req.OwnerDID != session.DID { 909 + ownerUser, err := h.DB.GetUserByDID(req.OwnerDID) 910 + if err != nil { 911 + http.Error(w, "Owner not found", http.StatusBadRequest) 912 + return 913 + } 914 + ownerUserID = ownerUser.ID 915 + ownerDID = req.OwnerDID 916 + } 917 + 918 + ownerClient, err := h.xrpcClient(ownerUserID) 919 + if err != nil { 920 + http.Error(w, "Failed to connect to owner PDS", http.StatusInternalServerError) 921 + return 922 + } 923 + 924 + value, _, err := ownerClient.GetRecord(ownerDID, model.CollectionComment, commentID) 925 + if err != nil { 926 + log.Printf("CommentUpdate: GetRecord: %v", err) 927 + http.Error(w, "Comment not found", http.StatusNotFound) 928 + return 929 + } 930 + 931 + var comment model.CommentRecord 932 + if err := json.Unmarshal(value, &comment); err != nil { 933 + http.Error(w, "Failed to parse comment", http.StatusInternalServerError) 934 + return 935 + } 936 + 937 + comment.Resolved = req.Resolved 938 + 939 + _, _, err = ownerClient.UpdateComment(commentID, comment) 940 + if err != nil { 941 + log.Printf("CommentUpdate: UpdateComment: %v", err) 942 + http.Error(w, "Failed to update comment", http.StatusInternalServerError) 943 + return 944 + } 945 + 946 + h.jsonResponse(w, comment, http.StatusOK) 873 947 } 874 948 875 949 // --- API: Render markdown ---
+1
internal/model/models.go
··· 61 61 } 62 62 63 63 type CommentRecord struct { 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