fix crashes and re-add twemoji

This commit is contained in:
hippoz 2023-03-23 01:48:08 +02:00
parent b86967fc3c
commit 745cd87b7f
Signed by: hippoz
GPG key ID: 56C4E02A85F2FBED
9 changed files with 143 additions and 24 deletions

View file

@ -45,7 +45,8 @@
"react-dom": "17.0.2", "react-dom": "17.0.2",
"react-modal": "3.16.1", "react-modal": "3.16.1",
"sanitize-html": "2.8.0", "sanitize-html": "2.8.0",
"tippy.js": "6.3.7" "tippy.js": "6.3.7",
"twemoji": "14.0.2"
}, },
"devDependencies": { "devDependencies": {
"@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-globals-polyfill": "0.2.3",

View file

@ -9,6 +9,10 @@
} }
} }
.emoji {
content-visibility: auto;
}
.text { .text {
margin: 0; margin: 0;
padding: 0; padding: 0;

View file

@ -131,7 +131,7 @@ function SearchedEmoji() {
function EmojiBoard({ onSelect, searchRef }) { function EmojiBoard({ onSelect, searchRef }) {
const scrollEmojisRef = useRef(null); const scrollEmojisRef = useRef(null);
const emojiInfo = useRef(null); const [emojiInfo, setEmojiInfo] = useState(null);
function isTargetNotEmoji(target) { function isTargetNotEmoji(target) {
return target.classList.contains('emoji') === false; return target.classList.contains('emoji') === false;
@ -159,25 +159,15 @@ function EmojiBoard({ onSelect, searchRef }) {
if (emoji.hexcode) addRecentEmoji(emoji.unicode); if (emoji.hexcode) addRecentEmoji(emoji.unicode);
} }
function setEmojiInfo(emoji) {
const infoEmoji = emojiInfo.current.firstElementChild.firstElementChild;
const infoShortcode = emojiInfo.current.lastElementChild;
infoEmoji.src = emoji.src;
infoEmoji.alt = emoji.unicode;
infoShortcode.textContent = `:${emoji.shortcode}:`;
}
function hoverEmoji(e) { function hoverEmoji(e) {
if (isTargetNotEmoji(e.target)) return; if (isTargetNotEmoji(e.target)) return;
const emoji = e.target; const emoji = e.target;
const { shortcodes, unicode } = getEmojiDataFromTarget(emoji); const { shortcodes, unicode } = getEmojiDataFromTarget(emoji);
const { src } = e.target;
if (searchRef.current.placeholder === shortcodes[0]) return; if (searchRef.current.placeholder === shortcodes[0]) return;
searchRef.current.setAttribute('placeholder', shortcodes[0]); searchRef.current.setAttribute('placeholder', shortcodes[0]);
setEmojiInfo({ shortcode: shortcodes[0], src, unicode }); setEmojiInfo({ shortcode: shortcodes[0], unicode });
} }
function handleSearchChange() { function handleSearchChange() {
@ -318,9 +308,9 @@ function EmojiBoard({ onSelect, searchRef }) {
</div> </div>
</ScrollView> </ScrollView>
</div> </div>
<div ref={emojiInfo} className="emoji-board__content__info"> <div className="emoji-board__content__info">
<div><span className="emoji" unicode="🙂" shortcodes="slight_smile">🙂</span></div> <div><span className="emoji">{ emojiInfo ? emojiInfo.unicode : '' }</span></div>
<Text>:slight_smile:</Text> <Text>{emojiInfo ? emojiInfo.shortcode : ''}</Text>
</div> </div>
</div> </div>
</div> </div>

View file

@ -4,7 +4,7 @@
.emoji-board { .emoji-board {
--emoji-board-height: 390px; --emoji-board-height: 390px;
--emoji-board-width: 286px; --emoji-board-width: 218px;
display: flex; display: flex;
max-width: 90vw; max-width: 90vw;
max-height: 90vh; max-height: 90vh;
@ -121,6 +121,7 @@
@include dir.side(margin, var(--left-margin), var(--right-margin)); @include dir.side(margin, var(--left-margin), var(--right-margin));
} }
& .emoji { & .emoji {
display: block;
max-width: 38px; max-width: 38px;
max-height: 38px; max-height: 38px;
width: 100%; width: 100%;

View file

@ -128,7 +128,7 @@ function PeopleDrawer({ roomId }) {
<div className="people-drawer"> <div className="people-drawer">
<Header> <Header>
<TitleWrapper> <TitleWrapper>
<Text primary> <Text span primary>
People People
<Text className="people-drawer__member-count" variant="b3">{`${room.getJoinedMemberCount()} members`}</Text> <Text className="people-drawer__member-count" variant="b3">{`${room.getJoinedMemberCount()} members`}</Text>
</Text> </Text>

View file

@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomViewCmdBar.scss'; import './RoomViewCmdBar.scss';
import { twemojify } from '../../../util/twemojify'; import { singleEmojiToJSX, twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { getEmojiForCompletion } from '../emoji-board/custom-emoji'; import { getEmojiForCompletion } from '../emoji-board/custom-emoji';
@ -49,6 +49,34 @@ function renderSuggestions({ prefix, option, suggestions }, fireCmd) {
function renderEmojiSuggestion(emPrefix, emos) { function renderEmojiSuggestion(emPrefix, emos) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
// Renders a small Twemoji
function renderTwemoji(emoji) {
return singleEmojiToJSX(emoji);
}
// Render a custom emoji
function renderCustomEmoji(emoji) {
return (
<img
className="emoji"
draggable="false"
loading="lazy"
referrerPolicy="no-referrer"
src={mx.mxcUrlToHttp(emoji.mxc)}
data-mx-emoticon=""
alt={`:${emoji.shortcode}:`}
/>
);
}
// Dynamically render either a custom emoji or twemoji based on what the input is
function renderEmoji(emoji) {
if (emoji.mxc) {
return renderCustomEmoji(emoji);
}
return renderTwemoji(emoji);
}
return emos.map((emoji) => ( return emos.map((emoji) => (
<CmdItem <CmdItem
key={emoji.shortcode} key={emoji.shortcode}
@ -59,7 +87,7 @@ function renderSuggestions({ prefix, option, suggestions }, fireCmd) {
}) })
} }
> >
<Text variant="b1">{emoji}</Text> <Text variant="b1">{renderEmoji(emoji)}</Text>
<Text variant="b2">{`:${emoji.shortcode}:`}</Text> <Text variant="b2">{`:${emoji.shortcode}:`}</Text>
</CmdItem> </CmdItem>
)); ));

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { StrictMode } from 'react';
import ReactDom from 'react-dom'; import ReactDom from 'react-dom';
import './font'; import './font';
import './index.scss'; import './index.scss';
@ -9,4 +9,9 @@ import App from './app/pages/App';
settings.applyTheme(); settings.applyTheme();
ReactDom.render(<App />, document.getElementById('root')); ReactDom.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById('root')
);

View file

@ -1,20 +1,65 @@
import linkifyHtml from 'linkify-html'; import linkifyHtml from 'linkify-html';
import parse from 'html-react-parser'; import parse from 'html-react-parser';
import twemoji from 'twemoji';
import { sanitizeText } from './sanitize'; import { sanitizeText } from './sanitize';
export function twemojify(text, _opts=null, linkify=false, sanitize=true, _maths=false) { export const TWEMOJI_BASE_URL = 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/';
// Start modified block from `twemoji` code:
// MIT License
// Copyright (c) 2021 Twitter
const UFE0Fg = /\uFE0F/g;
const U200D = String.fromCharCode(0x200D);
function grabTheRightIcon(rawText) {
// if variant is present as \uFE0F
return twemoji.convert.toCodePoint(rawText.indexOf(U200D) < 0 ?
rawText.replace(UFE0Fg, '') :
rawText
);
}
// End function from `twemoji`
export function singleEmojiToJSX({ shortcodes, unicode, hexcode }) {
return <img
className="emoji"
draggable="false"
loading="lazy"
referrerPolicy="no-referrer"
crossOrigin="anonymous"
alt={unicode}
unicode={unicode}
shortcodes={shortcodes}
hexcode={hexcode}
src={`${TWEMOJI_BASE_URL}/72x72/${grabTheRightIcon(unicode)}.png`}
></img>;
}
/**
* @param {string} text - text to twemojify
* @param {object|undefined} opts - options for tweomoji.parse
* @param {boolean} [linkify=false] - convert links to html tags (default: false)
* @param {boolean} [sanitize=true] - sanitize html text (default: true)
* @returns React component
*/
export function twemojify(text, opts, linkify = false, sanitize = true) {
if (typeof text !== 'string') return text; if (typeof text !== 'string') return text;
let content = text; let content = text;
const options = opts ?? { base: TWEMOJI_BASE_URL };
if (!options.base) {
options.base = TWEMOJI_BASE_URL;
}
if (sanitize) { if (sanitize) {
content = sanitizeText(content); content = sanitizeText(content);
} }
content = twemoji.parse(content, options);
if (linkify) { if (linkify) {
content = linkifyHtml(content, { content = linkifyHtml(content, {
target: '_blank', target: '_blank',
rel: 'noreferrer noopener', rel: 'noreferrer noopener',
}); });
} }
return parse(content); return parse(content, null);
} }

View file

@ -1633,6 +1633,15 @@ fs-extra@^11.1.0:
jsonfile "^6.0.1" jsonfile "^6.0.1"
universalify "^2.0.0" universalify "^2.0.0"
fs-extra@^8.0.1:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -2080,6 +2089,22 @@ json5@^2.2.2:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922"
integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==
dependencies:
universalify "^0.1.2"
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^6.0.1: jsonfile@^6.0.1:
version "6.1.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@ -2906,6 +2931,21 @@ tsutils@^3.21.0:
dependencies: dependencies:
tslib "^1.8.1" tslib "^1.8.1"
twemoji-parser@14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-14.0.0.tgz#13dabcb6d3a261d9efbf58a1666b182033bf2b62"
integrity sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==
twemoji@14.0.2:
version "14.0.2"
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-14.0.2.tgz#c53adb01dab22bf4870f648ca8cc347ce99ee37e"
integrity sha512-BzOoXIe1QVdmsUmZ54xbEH+8AgtOKUiG53zO5vVP2iUu6h5u9lN15NcuS6te4OY96qx0H7JK9vjjl9WQbkTRuA==
dependencies:
fs-extra "^8.0.1"
jsonfile "^5.0.0"
twemoji-parser "14.0.0"
universalify "^0.1.2"
type-check@^0.4.0, type-check@~0.4.0: type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@ -2952,6 +2992,11 @@ unhomoglyph@^1.0.6:
resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3" resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3"
integrity sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg== integrity sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==
universalify@^0.1.0, universalify@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^2.0.0: universalify@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"