api, frontend: add comment editing

This commit is contained in:
Adhityaa Chandrasekar 2019-05-15 10:46:51 -07:00
parent ce19cb8842
commit cc1dfee017
4 changed files with 200 additions and 7 deletions

66
api/comment_edit.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"net/http"
)
func commentEdit(commentHex string, markdown string) (string, error) {
if commentHex == "" {
return "", errorMissingField
}
html := markdownToHtml(markdown)
statement := `
UPDATE comments
SET markdown = $2, html = $3
WHERE commentHex=$1;
`
_, err := db.Exec(statement, commentHex, markdown, html)
if err != nil {
// TODO: make sure this is the error is actually non-existant commentHex
return "", errorNoSuchComment
}
return html, nil
}
func commentEditHandler(w http.ResponseWriter, r *http.Request) {
type request struct {
CommenterToken *string `json:"commenterToken"`
CommentHex *string `json:"commentHex"`
Markdown *string `json:"markdown"`
}
var x request
if err := bodyUnmarshal(r, &x); err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
cm, err := commentGetByCommentHex(*x.CommentHex)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
if cm.CommenterHex != c.CommenterHex {
bodyMarshal(w, response{"success": false, "message": errorNotAuthorised.Error()})
return
}
html, err := commentEdit(*x.CommentHex, *x.Markdown)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
bodyMarshal(w, response{"success": true, "html": html})
}

View File

@ -51,6 +51,7 @@ func apiRouterInit(router *mux.Router) error {
router.HandleFunc("/api/oauth/sso/callback", ssoCallbackHandler).Methods("GET")
router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST")
router.HandleFunc("/api/comment/edit", commentEditHandler).Methods("POST")
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
router.HandleFunc("/api/comment/vote", commentVoteHandler).Methods("POST")

View File

@ -68,6 +68,7 @@
var autoInit;
var isAuthenticated = false;
var comments = [];
var commentsMap = {};
var commenters = {};
var requireIdentification = true;
var isModerator = false;
@ -76,6 +77,7 @@
var isLocked = false;
var stickyCommentHex = "none";
var shownReply = {};
var shownEdit = {};
var configuredOauths = {};
var popupBoxType = "login";
var oauthButtonsShown = false;
@ -499,7 +501,7 @@
}
function textareaCreate(id) {
function textareaCreate(id, edit) {
var textareaSuperContainer = create("div");
var textareaContainer = create("div");
var textarea = create("textarea");
@ -529,11 +531,19 @@
attrSet(anonymousCheckboxLabel, "for", ID_ANONYMOUS_CHECKBOX + id);
anonymousCheckboxLabel.innerText = "Comment anonymously";
if (edit === true) {
submitButton.innerText = "Save Changes";
} else {
submitButton.innerText = "Add Comment";
}
markdownButton.innerHTML = "<b>M &#8595;</b> &nbsp; Markdown";
textarea.oninput = autoExpander(textarea);
if (edit === true) {
onclick(submitButton, commentEdit, id);
} else {
onclick(submitButton, submitAccountDecide, id);
}
onclick(markdownButton, markdownHelpShow, id);
append(textareaContainer, textarea);
@ -541,7 +551,7 @@
append(anonymousCheckboxContainer, anonymousCheckbox);
append(anonymousCheckboxContainer, anonymousCheckboxLabel);
append(textareaSuperContainer, submitButton);
if (!requireIdentification) {
if (!requireIdentification && edit !== true) {
append(textareaSuperContainer, anonymousCheckboxContainer);
}
append(textareaSuperContainer, markdownButton);
@ -911,6 +921,7 @@
}
}
onclick(edit, global.editShow, comment.commentHex);
onclick(collapse, global.commentCollapse, comment.commentHex);
onclick(approve, global.commentApprove, comment.commentHex);
onclick(remove, global.commentDelete, comment.commentHex);
@ -933,11 +944,14 @@
append(options, collapse);
// append(options, edit); // uncomment when implemented
append(options, downvote);
append(options, upvote);
if (comment.commenterHex === selfHex) {
append(options, edit);
} else {
append(options, reply);
}
if (isModerator && parentHex === "root") {
append(options, sticky);
@ -1109,6 +1123,106 @@
}
function commentEdit(id) {
var textarea = $(ID_TEXTAREA + id);
var markdown = textarea.value;
if (markdown === "") {
classAdd(textarea, "red-border");
return;
} else {
classRemove(textarea, "red-border");
}
var json = {
"commenterToken": commenterTokenGet(),
"commentHex": id,
"markdown": markdown,
};
post(origin + "/api/comment/edit", json, function(resp) {
if (!resp.success) {
errorShow(resp.message);
return;
} else {
errorHide();
}
commentsMap[id].markdown = markdown;
commentsMap[id].html = resp.html;
var editButton = $(ID_EDIT + id);
var textarea = $(ID_SUPER_CONTAINER + id);
textarea.innerHTML = commentsMap[id].html;
textarea.id = ID_TEXT + id;
delete shownEdit[id];
classAdd(editButton, "option-edit");
classRemove(editButton, "option-cancel");
editButton.title = "Edit comment";
editButton = removeAllEventListeners(editButton);
onclick(editButton, global.editShow, id)
var message = "";
if (resp.state === "unapproved") {
message = "Your comment is under moderation.";
} else if (resp.state === "flagged") {
message = "Your comment was flagged as spam and is under moderation.";
}
if (message !== "") {
prepend($(ID_SUPER_CONTAINER + id), messageCreate(message));
}
});
}
global.editShow = function(id) {
if (id in shownEdit && shownEdit[id]) {
return;
}
var text = $(ID_TEXT + id);
shownEdit[id] = true;
text.replaceWith(textareaCreate(id, true));
var textarea = $(ID_TEXTAREA + id);
textarea.innerText = commentsMap[id].markdown;
var editButton = $(ID_EDIT + id);
classRemove(editButton, "option-edit");
classAdd(editButton, "option-cancel");
editButton.title = "Cancel edit";
editButton = removeAllEventListeners(editButton);
onclick(editButton, global.editCollapse, id);
};
global.editCollapse = function(id) {
var editButton = $(ID_EDIT + id);
var textarea = $(ID_SUPER_CONTAINER + id);
textarea.innerHTML = commentsMap[id].html;
textarea.id = ID_TEXT + id;
delete shownEdit[id];
classAdd(editButton, "option-edit");
classRemove(editButton, "option-cancel");
editButton.title = "Edit comment";
editButton = removeAllEventListeners(editButton);
onclick(editButton, global.editShow, id)
}
global.replyShow = function(id) {
if (id in shownReply && shownReply[id]) {
return;
@ -1135,7 +1249,7 @@
var el = $(ID_SUPER_CONTAINER + id);
el.remove();
shownReply[id] = false;
delete shownReply[id];
classAdd(replyButton, "option-reply");
classRemove(replyButton, "option-cancel");
@ -1198,6 +1312,10 @@
comment.creationDate = new Date(comment.creationDate);
parentMap[parentHex].push(comment);
commentsMap[comment.commentHex] = {
"html": comment.html,
"markdown": comment.markdown,
};
});
var cards = commentsRecurse(parentMap, "root");

View File

@ -67,6 +67,14 @@
background: $indigo-6;
}
.commento-option-edit {
height: 14px;
width: 14px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 528.899 528.899" version="1.1" viewBox="0 0 528.899 528.899" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m328.88 89.125l107.59 107.59-272.34 272.34-107.53-107.59 272.28-272.34zm189.23-25.948l-47.981-47.981c-18.543-18.543-48.653-18.543-67.259 0l-45.961 45.961 107.59 107.59 53.611-53.611c14.382-14.383 14.382-37.577 0-51.959zm-517.81 449.51c-1.958 8.812 5.998 16.708 14.811 14.565l119.89-29.069-107.53-107.59-27.173 122.09z"/></svg>');
margin: 12px 6px 12px 6px;
background: $gray-5;
}
.commento-option-remove {
height: 14px;
width: 14px;