forked from lavender/watch-party
		
	faster emoji search (kinda)
This commit is contained in:
		
							parent
							
								
									d8d22ed99e
								
							
						
					
					
						commit
						a6a856c6a5
					
				
					 2 changed files with 89 additions and 51 deletions
				
			
		|  | @ -3,7 +3,7 @@ import { | |||
|   setVideoTime, | ||||
|   setPlaying, | ||||
| } from "./watch-session.mjs?v=19ef791"; | ||||
| import { emojify, emojis } from "./emojis.mjs?v=19ef791"; | ||||
| import { emojify, findEmojis } from "./emojis.mjs?v=19ef791"; | ||||
| 
 | ||||
| function setCaretPosition(elem, caretPos) { | ||||
|   if (elem.createTextRange) { | ||||
|  | @ -26,14 +26,16 @@ const setupChatboxEvents = (socket) => { | |||
|   const emojiAutocomplete = chatForm.querySelector("#emoji-autocomplete"); | ||||
|   oldChatForm.replaceWith(chatForm); | ||||
| 
 | ||||
|   let autocompleting = false; | ||||
|   let autocompleting = false, | ||||
|     showListTimer; | ||||
| 
 | ||||
|   const replaceMessage = (message) => () => { | ||||
|     messageInput.value = message; | ||||
|     autocomplete(); | ||||
|   }; | ||||
|   async function autocomplete() { | ||||
|   async function autocomplete(fromListTimeout) { | ||||
|     if (autocompleting) return; | ||||
|     clearInterval(showListTimer); | ||||
|     emojiAutocomplete.textContent = ""; | ||||
|     autocompleting = true; | ||||
|     let text = messageInput.value.slice(0, messageInput.selectionStart); | ||||
|  | @ -41,56 +43,59 @@ const setupChatboxEvents = (socket) => { | |||
|     if (!match || match[1]) return (autocompleting = false); // We don't need to autocomplete.
 | ||||
|     const prefix = text.slice(0, match.index); | ||||
|     const search = text.slice(match.index + 1); | ||||
|     if (search.length < 1 && !fromListTimeout) { | ||||
|       autocompleting = false; | ||||
|       showListTimer = setTimeout(() => autocomplete(true), 1000); | ||||
|       return; | ||||
|     } | ||||
|     const suffix = messageInput.value.slice(messageInput.selectionStart); | ||||
|     let selected; | ||||
|     const select = (button) => { | ||||
|       const selected = document.querySelector(".emoji-option.selected"); | ||||
|       if (selected) selected.classList.remove("selected"); | ||||
|       selected = button; | ||||
|       button.classList.add("selected"); | ||||
|     }; | ||||
|     emojiAutocomplete.append( | ||||
|       ...(await emojis) | ||||
|         .filter(([name]) => name.toLowerCase().startsWith(search.toLowerCase())) | ||||
|         .map(([name, replaceWith, ext], i) => { | ||||
|           const button = Object.assign(document.createElement("button"), { | ||||
|             className: "emoji-option" + (i === 0 ? " selected" : ""), | ||||
|             onmousedown: (e) => e.preventDefault(), | ||||
|             onclick: () => { | ||||
|               messageInput.value = prefix + replaceWith + " " + suffix; | ||||
|               setCaretPosition( | ||||
|                 messageInput, | ||||
|                 (prefix + " " + replaceWith).length | ||||
|               ); | ||||
|             }, | ||||
|             onmouseover: () => select(button), | ||||
|             onfocus: () => select(button), | ||||
|             type: "button", | ||||
|             title: name, | ||||
|           }); | ||||
|           button.append( | ||||
|             replaceWith[0] !== ":" | ||||
|               ? Object.assign(document.createElement("span"), { | ||||
|                   textContent: replaceWith, | ||||
|                   className: "emoji", | ||||
|                 }) | ||||
|               : Object.assign(new Image(), { | ||||
|                   loading: "lazy", | ||||
|                   src: `/emojis/${name}${ext}`, | ||||
|                   className: "emoji", | ||||
|                 }), | ||||
|             Object.assign(document.createElement("span"), { | ||||
|               textContent: name, | ||||
|               className: "emoji-name", | ||||
|             }) | ||||
|           ); | ||||
|           return button; | ||||
|         }) | ||||
|       ...(await findEmojis(search)).map(([name, replaceWith, ext], i) => { | ||||
|         const button = Object.assign(document.createElement("button"), { | ||||
|           className: "emoji-option", | ||||
|           onmousedown: (e) => e.preventDefault(), | ||||
|           onclick: () => { | ||||
|             messageInput.value = prefix + replaceWith + " " + suffix; | ||||
|             setCaretPosition(messageInput, (prefix + " " + replaceWith).length); | ||||
|           }, | ||||
|           onmouseover: () => select(button), | ||||
|           onfocus: () => select(button), | ||||
|           type: "button", | ||||
|           title: name, | ||||
|         }); | ||||
|         button.append( | ||||
|           replaceWith[0] !== ":" | ||||
|             ? Object.assign(document.createElement("span"), { | ||||
|                 textContent: replaceWith, | ||||
|                 className: "emoji", | ||||
|               }) | ||||
|             : Object.assign(new Image(), { | ||||
|                 loading: "lazy", | ||||
|                 src: `/emojis/${name}${ext}`, | ||||
|                 className: "emoji", | ||||
|               }), | ||||
|           Object.assign(document.createElement("span"), { | ||||
|             textContent: name, | ||||
|             className: "emoji-name", | ||||
|           }) | ||||
|         ); | ||||
|         return button; | ||||
|       }) | ||||
|     ); | ||||
|     if (emojiAutocomplete.children[0]) | ||||
|     if (emojiAutocomplete.children[0]) { | ||||
|       emojiAutocomplete.children[0].scrollIntoView(); | ||||
|       select(emojiAutocomplete.children[0]); | ||||
|     } | ||||
|     autocompleting = false; | ||||
|   } | ||||
|   messageInput.addEventListener("input", autocomplete); | ||||
|   messageInput.addEventListener("selectionchange", autocomplete); | ||||
|   messageInput.addEventListener("input", () => autocomplete()); | ||||
|   messageInput.addEventListener("selectionchange", () => autocomplete()); | ||||
|   messageInput.addEventListener("keydown", (event) => { | ||||
|     if (event.key == "ArrowUp" || event.key == "ArrowDown") { | ||||
|       let selected = document.querySelector(".emoji-option.selected"); | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| export async function emojify(text) { | ||||
|   const emojiList = await emojis; | ||||
|   await emojisLoaded; | ||||
|   let last = 0; | ||||
|   let nodes = []; | ||||
|   text.replace(/:([^\s:]+):/g, (match, name, index) => { | ||||
|     if (last <= index) | ||||
|       nodes.push(document.createTextNode(text.slice(last, index))); | ||||
|     let emoji = emojiList.find((e) => e[0] == name); | ||||
|     let emoji = emojis[name.toLowerCase()[0]].find((e) => e[0] == name); | ||||
|     if (!emoji) { | ||||
|       nodes.push(document.createTextNode(match)); | ||||
|     } else { | ||||
|  | @ -26,11 +26,44 @@ export async function emojify(text) { | |||
|   if (last < text.length) nodes.push(document.createTextNode(text.slice(last))); | ||||
|   return nodes; | ||||
| } | ||||
| export const emojis = Promise.all([ | ||||
| const emojis = {}; | ||||
| 
 | ||||
| export const emojisLoaded = Promise.all([ | ||||
|   fetch("/emojis") | ||||
|     .then((e) => e.json()) | ||||
|     .then((e) => | ||||
|       e.map((e) => [e.slice(0, -4), ":" + e.slice(0, -4) + ":", e.slice(-4)]) | ||||
|     ), | ||||
|   fetch("/emojis/unicode.json").then((e) => e.json()), | ||||
| ]).then((e) => e.flat(1)); | ||||
|     .then((a) => { | ||||
|       for (let e of a) { | ||||
|         const name = e.slice(0, -4), | ||||
|           lower = name.toLowerCase(); | ||||
|         emojis[lower[0]] = emojis[lower[0]] || []; | ||||
|         emojis[lower[0]].push([name, ":" + name + ":", e.slice(-4), lower]); | ||||
|       } | ||||
|     }), | ||||
|   fetch("/emojis/unicode.json") | ||||
|     .then((e) => e.json()) | ||||
|     .then((a) => { | ||||
|       for (let e of a) { | ||||
|         emojis[e[0][0]] = emojis[e[0][0]] || []; | ||||
|         emojis[e[0][0]].push([e[0], e[1], null, e[0]]); | ||||
|       } | ||||
|     }), | ||||
| ]); | ||||
| 
 | ||||
| export async function findEmojis(search) { | ||||
|   await emojisLoaded; | ||||
|   let groups = [[], []]; | ||||
|   if (search.length < 1) { | ||||
|     for (let letter in emojis) | ||||
|       for (let emoji of emojis[letter]) { | ||||
|         (emoji[1][0] === ":" ? groups[0] : groups[1]).push(emoji); | ||||
|       } | ||||
|   } else { | ||||
|     search = search.toLowerCase(); | ||||
|     for (let emoji of emojis[search[0]]) { | ||||
|       if (search.length == 1 || emoji[3].startsWith(search)) { | ||||
|         (emoji[1][0] === ":" ? groups[0] : groups[1]).push(emoji); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return [...groups[0], ...groups[1]]; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue