add better reaction popup
All checks were successful
Android Build / publish (push) Successful in 32s
Linux Build / publish (push) Successful in 50s

This commit is contained in:
olcxja 2026-06-18 08:11:38 +02:00
commit ecf7e3b4ce
14 changed files with 31442 additions and 14 deletions

View file

@ -1057,8 +1057,10 @@ async function ensureUserKeys() {
async function mainJS() {
await initBlahs();
await loadEmotes();
passwordHash = await hashSHA3_512(password);
if (localStorage.getItem('lang') != null) {
lang = localStorage.getItem('lang');
}
@ -2503,13 +2505,76 @@ async function replyMessage(msgId) {
input.focus();
}
var reactionPickerMenuHtml = "";
var loadedEmotes = [];
async function loadEmotes() {
try {
let res = await fetch("emotes.json");
let emotesObj = await res.json();
let skinTones = ['\u{1F3FB}', '\u{1F3FC}', '\u{1F3FD}', '\u{1F3FE}', '\u{1F3FF}'];
loadedEmotes = Object.keys(emotesObj).flatMap(char => {
let baseEmote = { char: char, name: emotesObj[char].name, slug: emotesObj[char].slug };
if (emotesObj[char].skin_tone_support) {
let variants = skinTones.map(tone => ({
char: char + tone,
name: emotesObj[char].name + " skin tone",
slug: emotesObj[char].slug
}));
return [baseEmote, ...variants];
}
return [baseEmote];
});
reactionPickerMenuHtml = `
<div style="display: flex; flex-direction: column; gap: 0.4rem; padding: 0.2rem; min-width: 14rem;">
<input type="text" id="reaction-picker-search" placeholder="Type text / emoji..." oninput="filterReactions(this.value)" onkeydown="if(event.key === 'Enter') { if(this.value.trim() !== '') reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(this.value)); }" style="width: 100%; box-sizing: border-box; padding: 0.5rem 0.6rem; border-radius: 0.65rem; border: var(--border-width) solid var(--light-border-color); background: transparent; color: var(--text-color); font-family: inherit;">
<div id="reaction-picker-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.2rem; max-height: 12rem; overflow-y: auto; overflow-x: hidden;">
${loadedEmotes.map(emoji => `<button onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent('${emoji.char}'))" data-name="${emoji.name}" style="min-width: unset; width: 100%; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; padding: 0; font-size: 1.2rem; border-radius: 0.65rem;" class="reaction-emoji-btn">${emoji.char}</button>`).join('')}
</div>
<button onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(document.getElementById('reaction-picker-search').value))" style="margin: 0; padding: 0.5rem; justify-content: center; border-radius: 0.65rem; width: 100%;">
<span class="blah">title.text.react</span>
</button>
</div>
`;
} catch (e) {
console.error("Failed to load emotes", e);
}
}
function filterReactions(query) {
let grid = document.getElementById("reaction-picker-grid");
if (!grid) return;
let buttons = grid.querySelectorAll(".reaction-emoji-btn");
let lowerQuery = query.toLowerCase();
buttons.forEach(btn => {
let name = btn.getAttribute("data-name");
if (name && name.toLowerCase().indexOf(lowerQuery) > -1) {
btn.style.display = "flex";
} else {
btn.style.display = "none";
}
});
}
function openReactionPicker(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
if (!reactionPickerMenuHtml) return;
let rect = fixedContextMenu.getBoundingClientRect();
showFixedContextMenu({top: rect.top, right: rect.left, bottom: rect.top, left: rect.left}, reactionPickerMenuHtml);
let searchInput = document.getElementById('reaction-picker-search');
if (searchInput) searchInput.focus();
}
async function reactMessagePrompt(msgId, quickReaction = null) {
if (fixedContextMenu) fixedContextMenu.classList.remove("show");
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
let reaction = quickReaction;
if (!reaction) {
reaction = prompt("Enter reaction (emoji or text):");
if (!reaction) return;
return;
} else {
reaction = decodeURIComponent(reaction);
}