fix crashes and re-add twemoji
This commit is contained in:
parent
b86967fc3c
commit
745cd87b7f
9 changed files with 143 additions and 24 deletions
|
@ -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",
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
content-visibility: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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%;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
));
|
));
|
||||||
|
|
|
@ -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')
|
||||||
|
);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
45
yarn.lock
45
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue