diff options
| author | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2023-03-11 19:32:05 -0500 |
|---|---|---|
| committer | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2023-03-11 19:32:05 -0500 |
| commit | 32e642f3789d163fae18824ec04e7e833713b26c (patch) | |
| tree | ec2fe39a92d57be80fdc86746891614e65713e0a /haunt | |
| parent | 4d07077daedc30271be69c86c855795affdea860 (diff) | |
Reaction UI
Diffstat (limited to 'haunt')
| -rw-r--r-- | haunt/jakob/builder/blog.scm | 1 | ||||
| -rw-r--r-- | haunt/static/css/style.css | 15 | ||||
| -rw-r--r-- | haunt/static/js/comment-reaction.js | 103 |
3 files changed, 119 insertions, 0 deletions
diff --git a/haunt/jakob/builder/blog.scm b/haunt/jakob/builder/blog.scm index de1c788..3285a51 100644 --- a/haunt/jakob/builder/blog.scm +++ b/haunt/jakob/builder/blog.scm @@ -89,6 +89,7 @@ (input (@ (name "source") (type "url"))) (input (@ (value "Send Webmention") (type "submit")))) ,(script "section-folds.js") + ,(script "comment-reaction.js") ;; ,(script "webmention.js") ))) diff --git a/haunt/static/css/style.css b/haunt/static/css/style.css index b48108b..2672f7c 100644 --- a/haunt/static/css/style.css +++ b/haunt/static/css/style.css @@ -233,6 +233,21 @@ ul.webmention-container .comment .comment-source-identifier { max-width: 16px; } +/* Widget for comment reactions. */ + +.emoji-picker { + width: 16em; + height: 2.75em; + border: solid 1px; + overflow: scroll; +} + +.emoji-picker > span { + margin: 2px; + font-size: 2em; +} + + /* Webmention form. */ #webmention-form form { diff --git a/haunt/static/js/comment-reaction.js b/haunt/static/js/comment-reaction.js new file mode 100644 index 0000000..530abf0 --- /dev/null +++ b/haunt/static/js/comment-reaction.js @@ -0,0 +1,103 @@ +/* + * comment-reaction.js -- Glue for Nolan Lawson's JavaScript emoji picker + * Copyright © 2023 Jakob L. Kreuze <zerodaysfordays@sdf.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * <http://www.gnu.org/licenses/>. + */ + +function encodeAsFormData(obj) { + // Turn the data object into an array of URL-encoded key/value pairs. + let urlEncodedDataPairs = []; + for (let key in obj) { + urlEncodedDataPairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])); + } + return urlEncodedDataPairs.join('&'); +} + +function postReaction(reaction) { + let xhr = new XMLHttpRequest(); + xhr.open('POST', "/api/comment/react", true); + + // Endpoint expects HTML form data. + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + + xhr.onload = function(data) { + if (xhr.status != 200) { + alert("Failed to react."); + } else { + } + } + xhr.onerror = function(error) { + throw new Error(`Request failed: ${error}`); + } + + xhr.send(encodeAsFormData(reaction)); +} + + +// Simple Emoji picker widget. +function selectorWidget() { + function emojiButton(codepoint) { + let span = document.createElement("span"); + span.innerHTML = String.fromCodePoint(codepoint); + return span; + } + + let selector = document.createElement("div"); + selector.setAttribute("class", "emoji-picker"); + + for (let i = 0x1F600; i < 0x1F64F; i++) { + selector.appendElement(emojiButton(i)); + } + return selector; +} + +// Add a "react" button to every local comment. +window.addEventListener("load", () => { + // First pass to add a separator for the buttons. + let replyButtons = document.querySelectorAll(".comment-reply-button"); + for (let button of replyButtons) { + let parent = button.parentNode; + parent.innerHTML += " - "; + + } + + // Second pass to add the actual "react" button. + replyButtons = document.querySelectorAll(".comment-reply-button"); + for (let button of replyButtons) { + let reactButton = document.createElement("a"); + reactButton.setAttribute("class", "comment-react-button"); + reactButton.setAttribute("href", "#"); + reactButton.setAttribute("data-react-to-id", button.getAttribute("data-reply-to-id")); + reactButton.innerHTML = "react"; + button.parentNode.appendChild(reactButton); + + reactButton.addEventListener('click', (e) => { + let emojiPicker = selectorWidget(); + button.parentNode.parentNode.insertBefore(emojiPicker, button.parentNode); + for (let button of emojiPicker.children) { + button.addEventListener('click', (e) => { + postReaction({ + "id": reactButton.getAttribute("data-react-to-id"), + "reaction": button.innerHTML + }); + emojiPicker.remove(); + }); + } + + e.preventDefault(); + }); + } +}); |