From 65212087e35695b7c5b1fb5e073a1f7d25835ce5 Mon Sep 17 00:00:00 2001 From: easrng Date: Wed, 16 Feb 2022 10:30:47 -0500 Subject: [PATCH] add unicode emoji autocomplete --- frontend/lib/chat.mjs | 45 +++++++++++++++++++---------------- frontend/lib/emojis.mjs | 10 +++++--- frontend/styles.css | 4 ++++ scripts/get_unicode_emojis.sh | 6 +++++ 4 files changed, 41 insertions(+), 24 deletions(-) create mode 100755 scripts/get_unicode_emojis.sh diff --git a/frontend/lib/chat.mjs b/frontend/lib/chat.mjs index 404c11c..a956b66 100644 --- a/frontend/lib/chat.mjs +++ b/frontend/lib/chat.mjs @@ -5,21 +5,19 @@ import { } from "./watch-session.mjs?v=1e57e6"; import { emojify, emojis } from "./emojis.mjs?v=1e57e6"; -function insertAtCursor(input, textToInsert) { - const isSuccess = document.execCommand("insertText", false, textToInsert); - - // Firefox (non-standard method) - if (!isSuccess && typeof input.setRangeText === "function") { - const start = input.selectionStart; - input.setRangeText(textToInsert); - // update cursor to be at the end of insertion - input.selectionStart = input.selectionEnd = start + textToInsert.length; - - // Notify any possible listeners of the change - const e = document.createEvent("UIEvent"); - e.initEvent("input", true, false); - input.dispatchEvent(e); - } +function setCaretPosition(elem, caretPos) { + if(elem.createTextRange) { + var range = elem.createTextRange(); + range.move('character', caretPos); + range.select(); + } else { + if(elem.selectionStart) { + elem.focus(); + elem.setSelectionRange(caretPos, caretPos); + } + else + elem.focus(); + } } const setupChatboxEvents = (socket) => { @@ -53,22 +51,27 @@ const setupChatboxEvents = (socket) => { }; emojiAutocomplete.append( ...(await emojis) - .filter((e) => e.toLowerCase().startsWith(search.toLowerCase())) - .map((name, i) => { + .filter(([name]) => name.toLowerCase().startsWith(search.toLowerCase())) + .map(([name, replaceWith], i) => { const button = Object.assign(document.createElement("button"), { className: "emoji-option" + (i === 0 ? " selected" : ""), onmousedown: (e) => e.preventDefault(), - onclick: () => - insertAtCursor(button, name.slice(match[2].length) + ": "), + onclick: () => { + messageInput.value=prefix+replaceWith+" "+suffix; + setCaretPosition(messageInput, (prefix+" "+replaceWith).length) + }, onmouseover: () => select(button), onfocus: () => select(button), }); button.append( - Object.assign(new Image(), { + (replaceWith[0]!==":"?Object.assign(document.createElement("span"), { + textContent: replaceWith, + className: "emoji", + }):Object.assign(new Image(), { loading: "lazy", src: `/emojis/${name}.png`, className: "emoji", - }), + })), Object.assign(document.createElement("span"), { textContent: name }) ); return button; diff --git a/frontend/lib/emojis.mjs b/frontend/lib/emojis.mjs index 7729889..b5429ec 100644 --- a/frontend/lib/emojis.mjs +++ b/frontend/lib/emojis.mjs @@ -21,6 +21,10 @@ export async function emojify(text) { if (last < text.length) nodes.push(document.createTextNode(text.slice(last))); return nodes; } -export const emojis = fetch("/emojis") - .then((e) => e.json()) - .then((e) => e.map((e) => e.slice(0, -4))); +export const emojis = Promise.all([ + fetch("/emojis") + .then((e) => e.json()) + .then((e) => e.map((e) => [e.slice(0, -4), ":"+e.slice(0, -4)+":"])), + fetch('/emojis/unicode.json') + .then((e) => e.json()) +]).then(e=>e.flat(1)); \ No newline at end of file diff --git a/frontend/styles.css b/frontend/styles.css index 635d908..afc2d81 100644 --- a/frontend/styles.css +++ b/frontend/styles.css @@ -287,6 +287,10 @@ button.small-button { width: 1.25rem; height: 1.25rem; margin: 0 0.5rem 0 0; + font-size: 2.25ch; + display: flex; + align-items: center; + justify-content: center; } .emoji-option.selected { diff --git a/scripts/get_unicode_emojis.sh b/scripts/get_unicode_emojis.sh new file mode 100755 index 0000000..e06a983 --- /dev/null +++ b/scripts/get_unicode_emojis.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Get emoji folder +emojiFolder="$(readlink -f "$(dirname $0)/../frontend/emojis/")" + +curl 'https://raw.githubusercontent.com/iamcal/emoji-data/master/emoji.json' | jq '. | map(. as $emoji | .short_names | map([., ($emoji.unified | split("-") | map(. | split("") | map(. as $nibble | (["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"] | index($nibble))) | reduce .[] as $item (0; (. * 16) + $item)) | map(if (. < 65536) then (.) else [55296 - 64 + (. / 1024 | floor), 56320 + (((. / 1024) - (. / 1024 | floor)) * 1024)] end) | flatten(1) | map(("\\u" + ("0000" + ({"str": "", "num": .} | until(.num < 1; {"str": (["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"][((.num / 16) - (.num / 16 | floor)) * 16] + .str), "num": (.num / 16) | floor})).str)[-4:])) | join("") | "\"" + . + "\"" | fromjson)])) | flatten(1)' --raw-output >"$emojiFolder/unicode.json"