api, frontend: add comment editing
This commit is contained in:
parent
ce19cb8842
commit
cc1dfee017
66
api/comment_edit.go
Normal file
66
api/comment_edit.go
Normal 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})
|
||||
}
|
@ -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")
|
||||
|
@ -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";
|
||||
submitButton.innerText = "Add Comment";
|
||||
if (edit === true) {
|
||||
submitButton.innerText = "Save Changes";
|
||||
} else {
|
||||
submitButton.innerText = "Add Comment";
|
||||
}
|
||||
markdownButton.innerHTML = "<b>M ↓</b> Markdown";
|
||||
|
||||
textarea.oninput = autoExpander(textarea);
|
||||
onclick(submitButton, submitAccountDecide, id);
|
||||
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);
|
||||
|
||||
append(options, reply);
|
||||
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");
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user