diff options
Diffstat (limited to 'static/js/comment-reaction.js')
| -rw-r--r-- | static/js/comment-reaction.js | 140 |
1 files changed, 75 insertions, 65 deletions
diff --git a/static/js/comment-reaction.js b/static/js/comment-reaction.js index 4536717..1723c64 100644 --- a/static/js/comment-reaction.js +++ b/static/js/comment-reaction.js @@ -54,80 +54,90 @@ function postReaction(reaction, callback) { xhr.send(encodeAsFormData(reaction)); } -function selectorWidgetName(id) { - return `emoji-picker-${id}`; -} +let allReactions = null; + +async function promptForReaction() { + const modal = document.getElementById('reaction-modal'); + const searchInput = document.getElementById('reaction-search'); + const listContainer = document.getElementById('reaction-list'); + const closeBtn = document.getElementById('close-modal'); -// Simple Emoji picker widget. -function selectorWidget(id) { - function emojiButton(codepoint) { - let span = document.createElement("span"); - span.innerHTML = String.fromCodePoint(codepoint); - return span; + if (!allReactions) { + try { + const response = await fetch('/static/data-by-emoji.json'); + allReactions = await response.json(); + } catch (e) { + console.error("Failed to load reactions", e); + return null; + } } - let selector = document.createElement("div"); - selector.setAttribute("id", selectorWidgetName(id)); - selector.setAttribute("class", "emoji-picker"); + return new Promise((resolve) => { + const render = (data) => { + listContainer.innerHTML = ''; + Object.entries(data).forEach(([reaction, info]) => { + const span = document.createElement('span'); + span.className = 'reaction-item'; + span.textContent = reaction; + span.title = info.name; + span.onclick = () => cleanup(reaction); + listContainer.appendChild(span); + }); + }; - 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; -} + const handleSearch = (e) => { + const term = e.target.value.toLowerCase(); + const filtered = Object.fromEntries( + Object.entries(allReactions).filter(([_, info]) => + info.name.toLowerCase().includes(term) || info.slug.toLowerCase().includes(term) + ) + ); + render(filtered); + }; -// Add a "react" button to every local comment. -window.addEventListener("load", () => { - // Unhide additional comment actions if Javascript is enabled. - let additionalActionsRows = document.querySelectorAll(".comment-additional-actions"); - for (let row of additionalActionsRows) { - row.removeAttribute("hidden"); - } + const cleanup = (result) => { + modal.style.display = 'none'; + closeBtn.onclick = null; + searchInput.oninput = null; + resolve(result); + }; - let reactButtons = document.querySelectorAll(".comment-react-button"); - for (let button of reactButtons) { - let id = button.getAttribute("data-reply-to-id"); + searchInput.value = ''; + render(allReactions); + modal.style.display = 'flex'; - button.addEventListener('click', (e) => { - // If we already made a selector widget for this comment, let's "hide" the - // one that exists. - let existingSelectorWidget = document.getElementById(selectorWidgetName(id)); - if (existingSelectorWidget !== null) { - existingSelectorWidget.remove(); - e.preventDefault(); - return; - } + closeBtn.onclick = () => cleanup(null); + searchInput.oninput = handleSearch; - let emojiPicker = selectorWidget(id); - button.parentNode.parentNode.insertBefore(emojiPicker, button.parentNode); - for (let button of emojiPicker.children) { - button.addEventListener('click', (e) => { - postReaction({ - "id": button.getAttribute("data-react-to-id"), - "reaction": button.innerHTML - }, () => { - let reactAcknowledgement = document.createElement("span"); - reactAcknowledgement.innerHTML = "successfully reacted!" - button.replaceWith(reactAcknowledgement); - }); - emojiPicker.remove(); - }); - } + modal.onclick = (e) => { if(e.target === modal) cleanup(null); }; + }); +} +window.addEventListener("load", () => { + const modalHTML = ` +<div id="reaction-modal" class="modal-overlay"> + <div class="modal-content"> + <div class="modal-header"> + <span>Select a reaction</span> + <button id="close-modal">X</button> + </div> + <div class="search-container"> + <input type="text" id="reaction-search" placeholder="Search reactions..." /> + </div> + <div id="reaction-list" class="reaction-grid"></div> + </div> +</div>`; + document.body.insertAdjacentHTML('beforeend', modalHTML); + const reactButtons = document.querySelectorAll(".add-reaction"); + for (const button of reactButtons) { + button.removeAttribute("hidden"); + button.addEventListener('click', async (e) => { + let reaction = await promptForReaction(); + console.log(reaction); + postReaction({ + "id": button.getAttribute("data-reply-to-id"), + "reaction": reaction + }, window.location.reload); e.preventDefault(); }); } |