Emoji parsing fixes
This commit is contained in:
parent
c002c43a08
commit
d9fb248fc3
@ -27,6 +27,7 @@ import ListenerSetter from "../../helpers/listenerSetter";
|
||||
import blurActiveElement from "../../helpers/dom/blurActiveElement";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import whichChild from "../../helpers/dom/whichChild";
|
||||
import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
|
||||
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
|
||||
|
||||
@ -158,7 +159,7 @@ export class EmoticonsDropdown {
|
||||
});
|
||||
|
||||
this.deleteBtn = this.element.querySelector('.emoji-tabs-delete');
|
||||
this.deleteBtn.addEventListener('click', () => {
|
||||
this.deleteBtn.addEventListener('click', (e) => {
|
||||
const input = appImManager.chat.input.messageInput;
|
||||
if((input.lastChild as any)?.tagName) {
|
||||
input.lastElementChild.remove();
|
||||
@ -173,6 +174,8 @@ export class EmoticonsDropdown {
|
||||
const event = new Event('input', {bubbles: true, cancelable: true});
|
||||
appImManager.chat.input.messageInput.dispatchEvent(event);
|
||||
//appSidebarRight.stickersTab.init();
|
||||
|
||||
cancelEvent(e);
|
||||
});
|
||||
|
||||
(this.tabsEl.children[1] as HTMLLIElement).click(); // set emoji tab
|
||||
@ -254,7 +257,7 @@ export class EmoticonsDropdown {
|
||||
this.events.onOpen.forEach(cb => cb());
|
||||
|
||||
const sel = document.getSelection();
|
||||
if(!sel.isCollapsed) {
|
||||
if(sel.rangeCount) {
|
||||
this.savedRange = sel.getRangeAt(0);
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
import emoticonsDropdown, { EmoticonsDropdown, EmoticonsTab } from "..";
|
||||
import { cancelEvent } from "../../../helpers/dom/cancelEvent";
|
||||
import findUpClassName from "../../../helpers/dom/findUpClassName";
|
||||
import { fastRaf, pause } from "../../../helpers/schedulers";
|
||||
import { isTouchSupported } from "../../../helpers/touchSupport";
|
||||
import appEmojiManager from "../../../lib/appManagers/appEmojiManager";
|
||||
import appImManager from "../../../lib/appManagers/appImManager";
|
||||
import Config from "../../../lib/config";
|
||||
@ -30,6 +32,7 @@ export function appendEmoji(emoji: string, container: HTMLElement, prepend = fal
|
||||
if(unify) {
|
||||
kek = RichTextProcessor.wrapSingleEmoji(emoji);
|
||||
} else {
|
||||
emoji = RichTextProcessor.fixEmoji(emoji);
|
||||
kek = RichTextProcessor.wrapEmojiText(emoji);
|
||||
}
|
||||
|
||||
@ -87,7 +90,7 @@ export function appendEmoji(emoji: string, container: HTMLElement, prepend = fal
|
||||
|
||||
export function getEmojiFromElement(element: HTMLElement) {
|
||||
if(element.nodeType === 3) return element.nodeValue;
|
||||
if(element.tagName === 'SPAN' && !element.classList.contains('emoji')) {
|
||||
if(element.tagName === 'SPAN' && !element.classList.contains('emoji') && element.firstElementChild) {
|
||||
element = element.firstElementChild as HTMLElement;
|
||||
}
|
||||
|
||||
@ -236,6 +239,7 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
}
|
||||
|
||||
onContentClick = (e: MouseEvent) => {
|
||||
cancelEvent(e);
|
||||
let target = e.target as HTMLElement;
|
||||
//if(target.tagName !== 'SPAN') return;
|
||||
|
||||
@ -249,9 +253,10 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
} else if(target.tagName === 'DIV') return;
|
||||
|
||||
// set selection range
|
||||
const savedRange = emoticonsDropdown.getSavedRange();
|
||||
const savedRange = isTouchSupported ? undefined : emoticonsDropdown.getSavedRange();
|
||||
let sel: Selection;
|
||||
if(savedRange) {
|
||||
const sel = document.getSelection();
|
||||
sel = document.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(savedRange);
|
||||
}
|
||||
@ -260,8 +265,17 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
(target.nodeType === 3 ? target.nodeValue : target.innerHTML) :
|
||||
target.outerHTML;
|
||||
|
||||
// insert emoji in input
|
||||
document.execCommand('insertHTML', true, html);
|
||||
if((document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.hasAttribute('contenteditable'))) ||
|
||||
savedRange) {
|
||||
document.execCommand('insertHTML', true, html);
|
||||
} else {
|
||||
appImManager.chat.input.messageInput.innerHTML += html;
|
||||
}
|
||||
|
||||
/* if(sel && isTouchSupported) {
|
||||
sel.removeRange(savedRange);
|
||||
blurActiveElement();
|
||||
} */
|
||||
|
||||
// Recent
|
||||
const emoji = getEmojiFromElement(target);
|
||||
|
@ -2403,9 +2403,11 @@ export class AppMessagesManager {
|
||||
} */
|
||||
|
||||
if(message.message && message.message.length && !message.totalEntities) {
|
||||
const apiEntities = message.entities ? message.entities.slice() : [];
|
||||
message.message = RichTextProcessor.fixEmoji(message.message, apiEntities);
|
||||
|
||||
const myEntities = RichTextProcessor.parseEntities(message.message);
|
||||
const apiEntities = message.entities || [];
|
||||
message.totalEntities = RichTextProcessor.mergeEntities(apiEntities.slice(), myEntities); // ! only in this order, otherwise bold and emoji formatting won't work
|
||||
message.totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities); // ! only in this order, otherwise bold and emoji formatting won't work
|
||||
}
|
||||
|
||||
storage[mid] = message;
|
||||
|
@ -24,14 +24,14 @@ export class AppStickersManager {
|
||||
private getStickersByEmoticonsPromises: {[emoticon: string]: Promise<Document[]>} = {};
|
||||
|
||||
constructor() {
|
||||
this.getStickerSet({id: 'emoji', access_hash: ''}, {overwrite: true});
|
||||
this.getStickerSet({id: 'emoji', access_hash: ''});
|
||||
|
||||
rootScope.addMultipleEventsListeners({
|
||||
updateNewStickerSet: (update) => {
|
||||
this.saveStickerSet(update.stickerset, update.stickerset.set.id);
|
||||
rootScope.broadcast('stickers_installed', update.stickerset.set);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
public saveStickers(docs: Document[]) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -117,20 +117,21 @@ namespace RichTextProcessor {
|
||||
export const emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) !== -1/* && false *//* || true */;
|
||||
|
||||
export function getEmojiSpritesheetCoords(emojiCode: string) {
|
||||
let unified = encodeEmoji(emojiCode);
|
||||
let unified = encodeEmoji(emojiCode).replace(/-?fe0f/g, '');
|
||||
|
||||
if(unified === '1f441-200d-1f5e8') {
|
||||
/* if(unified === '1f441-200d-1f5e8') {
|
||||
//unified = '1f441-fe0f-200d-1f5e8-fe0f';
|
||||
unified = '1f441-fe0f-200d-1f5e8';
|
||||
}
|
||||
} */
|
||||
|
||||
if(!emojiData.hasOwnProperty(unified) && !emojiData.hasOwnProperty(unified.replace(/-?fe0f$/, ''))/* && !emojiData.hasOwnProperty(unified.replace(/(-fe0f|fe0f)/g, '')) */) {
|
||||
//if(!emojiData.hasOwnProperty(emojiCode) && !emojiData.hasOwnProperty(emojiCode.replace(/[\ufe0f\u200d]/g, ''))) {
|
||||
if(!emojiData.hasOwnProperty(unified)
|
||||
// && !emojiData.hasOwnProperty(unified.replace(/-?fe0f$/, ''))
|
||||
) {
|
||||
//console.error('lol', unified);
|
||||
return null;
|
||||
}
|
||||
|
||||
return unified.replace(/-?fe0f/g, '');
|
||||
return unified;
|
||||
}
|
||||
|
||||
export function parseEntities(text: string) {
|
||||
@ -530,12 +531,13 @@ namespace RichTextProcessor {
|
||||
}
|
||||
|
||||
case 'messageEntityEmoji': {
|
||||
if(!(options.wrappingDraft && emojiSupported)) { // * fix safari emoji
|
||||
if(emojiSupported) { // ! contenteditable="false" нужен для поля ввода, иначе там будет меняться шрифт в Safari, или же рендерить смайлик напрямую, без контейнера
|
||||
insertPart(entity, '<span class="emoji">', '</span>');
|
||||
} else {
|
||||
//if(!(options.wrappingDraft && emojiSupported)) { // * fix safari emoji
|
||||
if(!emojiSupported) { // no wrapping needed
|
||||
// if(emojiSupported) { // ! contenteditable="false" нужен для поля ввода, иначе там будет меняться шрифт в Safari, или же рендерить смайлик напрямую, без контейнера
|
||||
// insertPart(entity, '<span class="emoji">', '</span>');
|
||||
// } else {
|
||||
insertPart(entity, `<img src="assets/img/emoji/${entity.unicode}.png" alt="`, `" class="emoji">`);
|
||||
}
|
||||
// }
|
||||
}
|
||||
/* if(!emojiSupported) {
|
||||
insertPart(entity, `<img src="assets/img/emoji/${entity.unicode}.png" alt="`, `" class="emoji">`);
|
||||
@ -665,6 +667,34 @@ namespace RichTextProcessor {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fixEmoji(text: string, entities?: MessageEntity[]) {
|
||||
/* if(!emojiSupported) {
|
||||
return text;
|
||||
} */
|
||||
// '$`\ufe0f'
|
||||
|
||||
text = text.replace(/[\u2640\u2642\u2764](?!\ufe0f)/g, (match, offset, string) => {
|
||||
if(entities) {
|
||||
const length = match.length;
|
||||
|
||||
offset += length;
|
||||
entities.forEach(entity => {
|
||||
const end = entity.offset + entity.length;
|
||||
if(end === offset) { // current entity
|
||||
entity.length += length;
|
||||
} else if(end > offset) {
|
||||
entity.offset += length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// console.log([match, offset, string]);
|
||||
return match + '\ufe0f';
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
export function wrapDraftText(text: string, options: Partial<{
|
||||
entities: MessageEntity[]
|
||||
}> = {}) {
|
||||
|
156
src/scripts/emoji_compile_regex.js
Normal file
156
src/scripts/emoji_compile_regex.js
Normal file
@ -0,0 +1,156 @@
|
||||
// @ts-check
|
||||
const fs = require('fs');
|
||||
|
||||
const data = fs.readFileSync(__dirname + '/in/emoji_test.txt').toString();
|
||||
|
||||
/** @type {number[][]} */
|
||||
const codepoints = [];
|
||||
|
||||
/** @type {Map<number, number[][]>} */
|
||||
const codepointsByLength = new Map();
|
||||
|
||||
data.split('\n').forEach(line => {
|
||||
if(!line || /^#/.test(line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const splitted = line.split(';');
|
||||
if(splitted.length < 2 || !splitted[1].includes('fully-qualified')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const a = String.fromCodePoint(...splitted[0].trim().split(' ').map((hex) => parseInt(hex, 16))).split('').map(str => str.charCodeAt(0));
|
||||
codepoints.push(a);
|
||||
|
||||
let byLength = codepointsByLength.get(a.length);
|
||||
if(!byLength) {
|
||||
byLength = [];
|
||||
codepointsByLength.set(a.length, byLength);
|
||||
}
|
||||
|
||||
byLength.push(a);
|
||||
});
|
||||
|
||||
/** @type {(codepoints: number[][]) => void} */
|
||||
const sort = (codepoints) => {
|
||||
codepoints.sort((a, b) => {
|
||||
const length = Math.min(a.length, b.length);
|
||||
for(let i = 0; i < length; ++i) {
|
||||
const diff = a[i] - b[i];
|
||||
if(diff) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
||||
return a.length - b.length;
|
||||
});
|
||||
};
|
||||
|
||||
sort(codepoints);
|
||||
|
||||
/** @type {(arr1: number[], arr2: number[]) => boolean} */
|
||||
const isEqualArray = (arr1, arr2) => {
|
||||
if(arr1.length !== arr2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(let i = 0; i < arr1.length; ++i) {
|
||||
if(arr1[i] !== arr2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/** @type {(num: number) => string} */
|
||||
const ttt = (num) => {
|
||||
return '\\u' + num.toString(16);
|
||||
};
|
||||
|
||||
/** @type {(arr: number[][], j: number) => string} */
|
||||
const makeGroup = (arr, j) => {
|
||||
let str = '';
|
||||
if(arr.length > 1) str += '[';
|
||||
str += arr.map(e => e.slice(0, j).map(ttt)).join('');
|
||||
if(arr.length > 1) str += ']';
|
||||
return str;
|
||||
};
|
||||
|
||||
let str = '(?:';
|
||||
let groups = [];
|
||||
/* codepointsByLength.forEach((value) => {
|
||||
sort(value);
|
||||
|
||||
value.forEach(s => {
|
||||
str += s.reduce((acc, v) => acc + '\\u' + v.toString(16), '');
|
||||
});
|
||||
}); */
|
||||
// for(let j = 1; j < 5; ++j) {
|
||||
// for(let i = 0; i < codepoints.length; ++i) {
|
||||
// const a = codepoints[i];
|
||||
// /** @type {number[][]} */
|
||||
// const set = [];
|
||||
|
||||
// //for(let j = 1; j < a.length; ++j) {
|
||||
// const ending = a.slice(j);
|
||||
|
||||
// for(let k = i + 1; k < codepoints.length; ++k) {
|
||||
// const b = codepoints[k];
|
||||
// const e = b.slice(j);
|
||||
|
||||
// if(isEqualArray(ending, e)) {
|
||||
// codepoints.splice(k, 1);
|
||||
// set.push(b);
|
||||
// }
|
||||
// }
|
||||
// //}
|
||||
|
||||
// if(set.length) {
|
||||
// set.unshift(a);
|
||||
// codepoints.splice(i, 1);
|
||||
// console.log(set.length);
|
||||
// } else if(j !== (5 - 1)) {
|
||||
// continue;
|
||||
// } else {
|
||||
// set.push(a);
|
||||
// }
|
||||
|
||||
// let group = makeGroup(set, j);
|
||||
// group += ending.map(ttt).join('');
|
||||
// groups.push(group);
|
||||
// str += group;
|
||||
// }
|
||||
// }
|
||||
/* codepointsByLength.forEach((codepoints) => {
|
||||
for(let i = 0; i < codepoints.length; ++i) {
|
||||
const a = codepoints[i];
|
||||
}
|
||||
}); */
|
||||
for(let i = 0; i < codepoints.length; ++i) {
|
||||
const a = codepoints[i];
|
||||
|
||||
for(let j = i + 1; j < codepoints.length; ++j) {
|
||||
|
||||
}
|
||||
}
|
||||
str += ')';
|
||||
|
||||
//console.log(codepointsByLength.get(1));
|
||||
|
||||
console.log(str);
|
||||
|
||||
/* let i = 0;
|
||||
let s = [codepoints[i++][0]];
|
||||
for(; i < codepoints.length; ++i) {
|
||||
const c = codepoints[i];
|
||||
if((c[0] - s[s.length - 1]) > 1) {
|
||||
if(s.length > 1) {
|
||||
console.log('start from', s);
|
||||
}
|
||||
s = [c[0]];
|
||||
} else {
|
||||
s.push(c[0]);
|
||||
}
|
||||
} */
|
||||
//console.log(codepoints);
|
27
src/scripts/format_emoji_regex.js
Normal file
27
src/scripts/format_emoji_regex.js
Normal file
File diff suppressed because one or more lines are too long
@ -153,8 +153,8 @@ if(false) {
|
||||
.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
|
||||
|
||||
emoji = encodeEmoji(emoji);
|
||||
//emoji = emoji.replace(/(-fe0f|fe0f)/g, '');
|
||||
emoji = emoji.replace(/-?fe0f$/, '');
|
||||
emoji = emoji.replace(/-?fe0f/g, '');
|
||||
//emoji = emoji.replace(/-?fe0f$/, '');
|
||||
|
||||
let c = categories[category] === undefined ? 9 : categories[category];
|
||||
//obj[emoji] = '' + c + sort_order;
|
||||
|
4879
src/scripts/in/emoji_test.txt
Normal file
4879
src/scripts/in/emoji_test.txt
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1
src/scripts/out/emoji_regex.txt
Normal file
1
src/scripts/out/emoji_regex.txt
Normal file
File diff suppressed because one or more lines are too long
20
src/vendor/emoji/regex.ts
vendored
20
src/vendor/emoji/regex.ts
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user