diff options
| author | Jakob L. Kreuze | 2023-03-14 18:07:28 -0400 |
|---|---|---|
| committer | Jakob L. Kreuze | 2023-03-14 18:07:28 -0400 |
| commit | 1f2123ce316fac9e259b8dd44e03ef24c1b2e490 (patch) | |
| tree | f6baa02622c80f87af4042b2634a6dc4627b7e92 /haunt/static/js | |
| parent | 45f5c3c0f8e745bc2392a8c5821f09913604da04 (diff) | |
Merge branch 'feature/comment-reactions'
commit 626e7b65e9a7c00ed68c78400e03e27cbd4c77c0
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Tue Mar 14 18:05:47 2023 -0400
Indicator when reaction attempt succeeded
commit e446f67898a05091b2f05df9ed81ca73764f6069
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Tue Mar 14 18:05:16 2023 -0400
Bug fixes
commit ab6edd57158574df80fe33138e99a317c64a0861
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sun Mar 12 08:43:03 2023 -0400
Better error reporting for reactions
commit 9d0638a78333ca35aa1bd12c5c6ced8bd2d7687d
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sun Mar 12 08:38:49 2023 -0400
Update comment and weblabels
commit 4a8378715c2e4e559731b319cac920c00cb32116
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sun Mar 12 08:37:40 2023 -0400
Allow for more emojis
commit 655f40bb267cc6df82356a0422146e555b94cf99
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sat Mar 11 19:35:05 2023 -0500
Rate limit `put-reaction'
commit 6463a9f09032f22c8e39ccea757b8a7054f199ba
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sat Mar 11 19:33:53 2023 -0500
Guard against non-emoji reactions
commit 32e642f3789d163fae18824ec04e7e833713b26c
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sat Mar 11 19:32:05 2023 -0500
Reaction UI
commit 4d07077daedc30271be69c86c855795affdea860
Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
Date: Sat Mar 11 16:51:46 2023 -0500
Address parsing issues in `put-reaction' endpoint
Diffstat (limited to 'haunt/static/js')
| -rw-r--r-- | haunt/static/js/comment-reaction.js | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/haunt/static/js/comment-reaction.js b/haunt/static/js/comment-reaction.js new file mode 100644 index 0000000..db980b3 --- /dev/null +++ b/haunt/static/js/comment-reaction.js @@ -0,0 +1,129 @@ +/* + * comment-reaction.js -- Simple emoji picker and glue for reaction endpoint + * 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, callback) { + 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) { + switch (xhr.status) { + case 200: + callback(); + break; + case 429: + alert("Failed -- you can react at most once an hour."); + break; + default: + let resp = JSON.parse(xhr.response); + alert("Failed -- " + resp.error); + break; + } + } + 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.appendChild(emojiButton(i)); + } + selector.appendChild(emojiButton(0x1F37B)); + selector.appendChild(emojiButton(0x1F440)); + selector.appendChild(emojiButton(0x1F446)); + selector.appendChild(emojiButton(0x1F447)); + selector.appendChild(emojiButton(0x1F448)); + selector.appendChild(emojiButton(0x1F449)); + selector.appendChild(emojiButton(0x1F450)); + selector.appendChild(emojiButton(0x270A)); + selector.appendChild(emojiButton(0x270B)); + selector.appendChild(emojiButton(0x270C)); + selector.appendChild(emojiButton(0x270D)); + for (let i = 0x1F90C; i < 0x1F9FF; i++) { + selector.appendChild(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 + }, () => { + let reactAcknowledgement = document.createElement("span"); + reactAcknowledgement.innerHTML = "successfully reacted!" + reactButton.replaceWith(reactAcknowledgement); + }); + emojiPicker.remove(); + }); + } + + e.preventDefault(); + }); + } +}); |