frontend,api: open source comment sticky

This commit is contained in:
Adhityaa Chandrasekar 2018-12-18 18:57:32 -05:00
parent cf0b394b05
commit 610b61831d
6 changed files with 88 additions and 11 deletions

View File

@ -7,4 +7,5 @@ type page struct {
Path string `json:"path"` Path string `json:"path"`
IsLocked bool `json:"isLocked"` IsLocked bool `json:"isLocked"`
CommentCount int `json:"commentCount"` CommentCount int `json:"commentCount"`
StickyCommentHex string `json:"stickyCommentHex"`
} }

View File

@ -11,20 +11,21 @@ func pageGet(domain string, path string) (page, error) {
} }
statement := ` statement := `
SELECT isLocked, commentCount SELECT isLocked, commentCount, stickyCommentHex
FROM pages FROM pages
WHERE domain=$1 AND path=$2; WHERE domain=$1 AND path=$2;
` `
row := db.QueryRow(statement, domain, path) row := db.QueryRow(statement, domain, path)
p := page{Domain: domain, Path: path} p := page{Domain: domain, Path: path}
if err := row.Scan(&p.IsLocked, &p.CommentCount); err != nil { if err := row.Scan(&p.IsLocked, &p.CommentCount, &p.StickyCommentHex); err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
// If there haven't been any comments, there won't be a record for this // If there haven't been any comments, there won't be a record for this
// page. The sane thing to do is return defaults. // page. The sane thing to do is return defaults.
// TODO: the defaults are hard-coded in two places: here and the schema // TODO: the defaults are hard-coded in two places: here and the schema
p.IsLocked = false p.IsLocked = false
p.CommentCount = 0 p.CommentCount = 0
p.StickyCommentHex = "none"
} else { } else {
logger.Errorf("error scanning page: %v", err) logger.Errorf("error scanning page: %v", err)
return page{}, errorInternal return page{}, errorInternal

View File

@ -13,12 +13,12 @@ func pageUpdate(p page) error {
// commentCount // commentCount
statement := ` statement := `
INSERT INTO INSERT INTO
pages (domain, path, isLocked) pages (domain, path, isLocked, stickyCommentHex)
VALUES ($1, $2, $3 ) VALUES ($1, $2, $3, $4 )
ON CONFLICT (domain, path) DO ON CONFLICT (domain, path) DO
UPDATE SET isLocked = $3; UPDATE SET isLocked = $3, stickyCommentHex = $4;
` `
_, err := db.Exec(statement, p.Domain, p.Path, p.IsLocked) _, err := db.Exec(statement, p.Domain, p.Path, p.IsLocked, p.StickyCommentHex)
if err != nil { if err != nil {
logger.Errorf("error setting page attributes: %v", err) logger.Errorf("error setting page attributes: %v", err)
return errorInternal return errorInternal

View File

@ -0,0 +1,2 @@
ALTER TABLE pages
ADD stickyCommentHex TEXT NOT NULL DEFAULT 'none';

View File

@ -47,6 +47,7 @@
var ID_DOWNVOTE = "commento-comment-downvote-"; var ID_DOWNVOTE = "commento-comment-downvote-";
var ID_APPROVE = "commento-comment-approve-"; var ID_APPROVE = "commento-comment-approve-";
var ID_REMOVE = "commento-comment-remove-"; var ID_REMOVE = "commento-comment-remove-";
var ID_STICKY = "commento-comment-sticky-";
var ID_CONTENTS = "commento-comment-contents-"; var ID_CONTENTS = "commento-comment-contents-";
var ID_SUBMIT_BUTTON = "commento-submit-button-"; var ID_SUBMIT_BUTTON = "commento-submit-button-";
var ID_FOOTER = "commento-footer"; var ID_FOOTER = "commento-footer";
@ -67,6 +68,7 @@
var shownSubmitButton = {"root": false}; var shownSubmitButton = {"root": false};
var chosenAnonymous = false; var chosenAnonymous = false;
var isLocked = false; var isLocked = false;
var stickyCommentHex = "none";
var shownReply = {}; var shownReply = {};
var configuredOauths = []; var configuredOauths = [];
var loginBoxType = "signup"; var loginBoxType = "signup";
@ -341,6 +343,7 @@
isFrozen = resp.isFrozen; isFrozen = resp.isFrozen;
isLocked = resp.attributes.isLocked; isLocked = resp.attributes.isLocked;
stickyCommentHex = resp.attributes.stickyCommentHex;
comments = resp.comments; comments = resp.comments;
commenters = resp.commenters; commenters = resp.commenters;
@ -449,10 +452,14 @@
commentsArea.innerHTML = ""; commentsArea.innerHTML = "";
if (!isLocked) if (isLocked) {
append(mainArea, textareaCreate("root")); if (isAuthenticated)
append(mainArea, messageCreate("This thread is locked. You cannot add new comments."));
else else
append(mainArea, messageCreate("This thread is locked. You cannot create new comments.")); append(mainArea, textareaCreate("root"));
}
else
append(mainArea, textareaCreate("root"));
append(mainArea, commentsArea); append(mainArea, commentsArea);
append(root, mainArea); append(root, mainArea);
@ -588,6 +595,10 @@
} }
cur.sort(function(a, b) { cur.sort(function(a, b) {
if (a.commentHex == stickyCommentHex)
return -Infinity;
if (b.commentHex == stickyCommentHex)
return Infinity;
return b.score - a.score; return b.score - a.score;
}); });
@ -608,6 +619,7 @@
var downvote = create("button"); var downvote = create("button");
var approve = create("button"); var approve = create("button");
var remove = create("button"); var remove = create("button");
var sticky = create("button");
var children = commentsRecurse(parentMap, comment.commentHex); var children = commentsRecurse(parentMap, comment.commentHex);
var contents = create("div"); var contents = create("div");
var color = colorGet(commenter.name); var color = colorGet(commenter.name);
@ -629,6 +641,7 @@
downvote.id = ID_DOWNVOTE + comment.commentHex; downvote.id = ID_DOWNVOTE + comment.commentHex;
approve.id = ID_APPROVE + comment.commentHex; approve.id = ID_APPROVE + comment.commentHex;
remove.id = ID_REMOVE + comment.commentHex; remove.id = ID_REMOVE + comment.commentHex;
sticky.id = ID_STICKY + comment.commentHex;
contents.id = ID_CONTENTS + comment.commentHex; contents.id = ID_CONTENTS + comment.commentHex;
collapse.title = "Collapse"; collapse.title = "Collapse";
@ -638,6 +651,14 @@
reply.title = "Reply"; reply.title = "Reply";
approve.title = "Approve"; approve.title = "Approve";
remove.title = "Remove"; remove.title = "Remove";
if (stickyCommentHex == comment.commentHex) {
if (isModerator)
sticky.title = "Unsticky";
else
sticky.title = "This comment has been stickied";
}
else
sticky.title = "Sticky";
card.style["borderLeft"] = "2px solid " + color; card.style["borderLeft"] = "2px solid " + color;
name.innerText = commenter.name; name.innerText = commenter.name;
@ -684,6 +705,11 @@
classAdd(approve, "option-approve"); classAdd(approve, "option-approve");
classAdd(remove, "option-button"); classAdd(remove, "option-button");
classAdd(remove, "option-remove"); classAdd(remove, "option-remove");
classAdd(sticky, "option-button");
if (stickyCommentHex == comment.commentHex)
classAdd(sticky, "option-unsticky");
else
classAdd(sticky, "option-sticky");
if (isAuthenticated) { if (isAuthenticated) {
if (comment.direction > 0) if (comment.direction > 0)
@ -696,6 +722,7 @@
attrSet(collapse, "onclick", "commentCollapse('" + comment.commentHex + "')"); attrSet(collapse, "onclick", "commentCollapse('" + comment.commentHex + "')");
attrSet(approve, "onclick", "commentApprove('" + comment.commentHex + "')"); attrSet(approve, "onclick", "commentApprove('" + comment.commentHex + "')");
attrSet(remove, "onclick", "commentDelete('" + comment.commentHex + "')"); attrSet(remove, "onclick", "commentDelete('" + comment.commentHex + "')");
attrSet(sticky, "onclick", "commentSticky('" + comment.commentHex + "')");
if (isAuthenticated) { if (isAuthenticated) {
if (comment.direction > 0) { if (comment.direction > 0) {
@ -730,14 +757,19 @@
append(options, downvote); append(options, downvote);
append(options, upvote); append(options, upvote);
if (!isLocked)
append(options, reply); append(options, reply);
if (isModerator) { if (isModerator) {
if (parentHex == "root")
append(options, sticky);
append(options, remove); append(options, remove);
if (comment.state == "unapproved") if (comment.state == "unapproved")
append(options, approve); append(options, approve);
} }
else {
if (stickyCommentHex == comment.commentHex)
append(options, sticky);
}
attrSet(options, "style", "width: " + ((options.childNodes.length+1)*32) + "px;"); attrSet(options, "style", "width: " + ((options.childNodes.length+1)*32) + "px;");
for (var i = 0; i < options.childNodes.length; i++) for (var i = 0; i < options.childNodes.length; i++)
@ -1278,6 +1310,7 @@
function pageUpdate(callback) { function pageUpdate(callback) {
var attributes = { var attributes = {
"isLocked": isLocked, "isLocked": isLocked,
"stickyCommentHex": stickyCommentHex,
}; };
var json = { var json = {
@ -1314,6 +1347,32 @@
} }
global.commentSticky = function(commentHex) {
if (stickyCommentHex != "none") {
var sticky = $(ID_STICKY + stickyCommentHex);
classRemove(sticky, "option-unsticky");
classAdd(sticky, "option-sticky");
}
if (stickyCommentHex == commentHex)
stickyCommentHex = "none";
else
stickyCommentHex = commentHex;
pageUpdate(function(success) {
var sticky = $(ID_STICKY + commentHex);
if (stickyCommentHex == commentHex) {
classRemove(sticky, "option-sticky");
classAdd(sticky, "option-unsticky");
}
else {
classRemove(sticky, "option-unsticky");
classAdd(sticky, "option-sticky");
}
});
}
function mainAreaCreate() { function mainAreaCreate() {
var mainArea = create("div"); var mainArea = create("div");

View File

@ -83,6 +83,20 @@
background: $green-7; background: $green-7;
} }
.commento-option-sticky,
.commento-option-unsticky {
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 487.222 487.222" version="1.1" viewBox="0 0 487.22 487.22" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m486.55 186.81c-1.6-4.9-5.8-8.4-10.9-9.2l-152-21.6-68.4-137.5c-2.3-4.6-7-7.5-12.1-7.5s-9.8 2.9-12.1 7.6l-67.5 137.9-152 22.6c-5.1 0.8-9.3 4.3-10.9 9.2s-0.2 10.3 3.5 13.8l110.3 106.9-25.5 151.4c-0.9 5.1 1.2 10.2 5.4 13.2 2.3 1.7 5.1 2.6 7.9 2.6 2.2 0 4.3-0.5 6.3-1.6l135.7-71.9 136.1 71.1c2 1 4.1 1.5 6.2 1.5 7.4 0 13.5-6.1 13.5-13.5 0-1.1-0.1-2.1-0.4-3.1l-26.3-150.5 109.6-107.5c3.9-3.6 5.2-9 3.6-13.9zm-137 107.1c-3.2 3.1-4.6 7.6-3.8 12l22.9 131.3-118.2-61.7c-3.9-2.1-8.6-2-12.6 0l-117.8 62.4 22.1-131.5c0.7-4.4-0.7-8.8-3.9-11.9l-95.6-92.8 131.9-19.6c4.4-0.7 8.2-3.4 10.1-7.4l58.6-119.7 59.4 119.4c2 4 5.8 6.7 10.2 7.4l132 18.8-95.3 93.3z" fill="%231e2127"/></svg>');
margin: 12px 6px 12px 6px;
background: $gray-5;
}
.commento-option-unsticky {
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 487.22 487.22" xmlns="http://www.w3.org/2000/svg"><g><title>background</title><rect x="-1" y="-1" fill="none"/></g><g><title>Layer 1</title><path d="m486.55 186.81c-1.6-4.9-5.8-8.4-10.9-9.2l-152-21.6-68.4-137.5c-2.3-4.6-7-7.5-12.1-7.5s-9.8 2.9-12.1 7.6l-67.5 137.9-152 22.6c-5.1 0.8-9.3 4.3-10.9 9.2s-0.2 10.3 3.5 13.8l110.3 106.9-25.5 151.4c-0.9 5.1 1.2 10.2 5.4 13.2 2.3 1.7 5.1 2.6 7.9 2.6 2.2 0 4.3-0.5 6.3-1.6l135.7-71.9 136.1 71.1c2 1 4.1 1.5 6.2 1.5 7.4 0 13.5-6.1 13.5-13.5 0-1.1-0.1-2.1-0.4-3.1l-26.3-150.5 109.6-107.5c3.9-3.6 5.2-9 3.6-13.9z" fill="%231e2127"/></g></svg>');
background: $yellow-7;
}
.commento-option-button:focus { .commento-option-button:focus {
outline: none; outline: none;
} }