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, |   setVideoTime, | ||||||
|   setPlaying, |   setPlaying, | ||||||
| } from "./watch-session.mjs?v=19ef791"; | } 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) { | function setCaretPosition(elem, caretPos) { | ||||||
|   if (elem.createTextRange) { |   if (elem.createTextRange) { | ||||||
|  | @ -26,14 +26,16 @@ const setupChatboxEvents = (socket) => { | ||||||
|   const emojiAutocomplete = chatForm.querySelector("#emoji-autocomplete"); |   const emojiAutocomplete = chatForm.querySelector("#emoji-autocomplete"); | ||||||
|   oldChatForm.replaceWith(chatForm); |   oldChatForm.replaceWith(chatForm); | ||||||
| 
 | 
 | ||||||
|   let autocompleting = false; |   let autocompleting = false, | ||||||
|  |     showListTimer; | ||||||
| 
 | 
 | ||||||
|   const replaceMessage = (message) => () => { |   const replaceMessage = (message) => () => { | ||||||
|     messageInput.value = message; |     messageInput.value = message; | ||||||
|     autocomplete(); |     autocomplete(); | ||||||
|   }; |   }; | ||||||
|   async function autocomplete() { |   async function autocomplete(fromListTimeout) { | ||||||
|     if (autocompleting) return; |     if (autocompleting) return; | ||||||
|  |     clearInterval(showListTimer); | ||||||
|     emojiAutocomplete.textContent = ""; |     emojiAutocomplete.textContent = ""; | ||||||
|     autocompleting = true; |     autocompleting = true; | ||||||
|     let text = messageInput.value.slice(0, messageInput.selectionStart); |     let text = messageInput.value.slice(0, messageInput.selectionStart); | ||||||
|  | @ -41,25 +43,26 @@ const setupChatboxEvents = (socket) => { | ||||||
|     if (!match || match[1]) return (autocompleting = false); // We don't need to autocomplete.
 |     if (!match || match[1]) return (autocompleting = false); // We don't need to autocomplete.
 | ||||||
|     const prefix = text.slice(0, match.index); |     const prefix = text.slice(0, match.index); | ||||||
|     const search = text.slice(match.index + 1); |     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); |     const suffix = messageInput.value.slice(messageInput.selectionStart); | ||||||
|  |     let selected; | ||||||
|     const select = (button) => { |     const select = (button) => { | ||||||
|       const selected = document.querySelector(".emoji-option.selected"); |  | ||||||
|       if (selected) selected.classList.remove("selected"); |       if (selected) selected.classList.remove("selected"); | ||||||
|  |       selected = button; | ||||||
|       button.classList.add("selected"); |       button.classList.add("selected"); | ||||||
|     }; |     }; | ||||||
|     emojiAutocomplete.append( |     emojiAutocomplete.append( | ||||||
|       ...(await emojis) |       ...(await findEmojis(search)).map(([name, replaceWith, ext], i) => { | ||||||
|         .filter(([name]) => name.toLowerCase().startsWith(search.toLowerCase())) |  | ||||||
|         .map(([name, replaceWith, ext], i) => { |  | ||||||
|         const button = Object.assign(document.createElement("button"), { |         const button = Object.assign(document.createElement("button"), { | ||||||
|             className: "emoji-option" + (i === 0 ? " selected" : ""), |           className: "emoji-option", | ||||||
|           onmousedown: (e) => e.preventDefault(), |           onmousedown: (e) => e.preventDefault(), | ||||||
|           onclick: () => { |           onclick: () => { | ||||||
|             messageInput.value = prefix + replaceWith + " " + suffix; |             messageInput.value = prefix + replaceWith + " " + suffix; | ||||||
|               setCaretPosition( |             setCaretPosition(messageInput, (prefix + " " + replaceWith).length); | ||||||
|                 messageInput, |  | ||||||
|                 (prefix + " " + replaceWith).length |  | ||||||
|               ); |  | ||||||
|           }, |           }, | ||||||
|           onmouseover: () => select(button), |           onmouseover: () => select(button), | ||||||
|           onfocus: () => select(button), |           onfocus: () => select(button), | ||||||
|  | @ -85,12 +88,14 @@ const setupChatboxEvents = (socket) => { | ||||||
|         return button; |         return button; | ||||||
|       }) |       }) | ||||||
|     ); |     ); | ||||||
|     if (emojiAutocomplete.children[0]) |     if (emojiAutocomplete.children[0]) { | ||||||
|       emojiAutocomplete.children[0].scrollIntoView(); |       emojiAutocomplete.children[0].scrollIntoView(); | ||||||
|  |       select(emojiAutocomplete.children[0]); | ||||||
|  |     } | ||||||
|     autocompleting = false; |     autocompleting = false; | ||||||
|   } |   } | ||||||
|   messageInput.addEventListener("input", autocomplete); |   messageInput.addEventListener("input", () => autocomplete()); | ||||||
|   messageInput.addEventListener("selectionchange", autocomplete); |   messageInput.addEventListener("selectionchange", () => autocomplete()); | ||||||
|   messageInput.addEventListener("keydown", (event) => { |   messageInput.addEventListener("keydown", (event) => { | ||||||
|     if (event.key == "ArrowUp" || event.key == "ArrowDown") { |     if (event.key == "ArrowUp" || event.key == "ArrowDown") { | ||||||
|       let selected = document.querySelector(".emoji-option.selected"); |       let selected = document.querySelector(".emoji-option.selected"); | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| export async function emojify(text) { | export async function emojify(text) { | ||||||
|   const emojiList = await emojis; |   await emojisLoaded; | ||||||
|   let last = 0; |   let last = 0; | ||||||
|   let nodes = []; |   let nodes = []; | ||||||
|   text.replace(/:([^\s:]+):/g, (match, name, index) => { |   text.replace(/:([^\s:]+):/g, (match, name, index) => { | ||||||
|     if (last <= index) |     if (last <= index) | ||||||
|       nodes.push(document.createTextNode(text.slice(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) { |     if (!emoji) { | ||||||
|       nodes.push(document.createTextNode(match)); |       nodes.push(document.createTextNode(match)); | ||||||
|     } else { |     } else { | ||||||
|  | @ -26,11 +26,44 @@ export async function emojify(text) { | ||||||
|   if (last < text.length) nodes.push(document.createTextNode(text.slice(last))); |   if (last < text.length) nodes.push(document.createTextNode(text.slice(last))); | ||||||
|   return nodes; |   return nodes; | ||||||
| } | } | ||||||
| export const emojis = Promise.all([ | const emojis = {}; | ||||||
|  | 
 | ||||||
|  | export const emojisLoaded = Promise.all([ | ||||||
|   fetch("/emojis") |   fetch("/emojis") | ||||||
|     .then((e) => e.json()) |     .then((e) => e.json()) | ||||||
|     .then((e) => |     .then((a) => { | ||||||
|       e.map((e) => [e.slice(0, -4), ":" + e.slice(0, -4) + ":", e.slice(-4)]) |       for (let e of a) { | ||||||
|     ), |         const name = e.slice(0, -4), | ||||||
|   fetch("/emojis/unicode.json").then((e) => e.json()), |           lower = name.toLowerCase(); | ||||||
| ]).then((e) => e.flat(1)); |         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