diff --git a/src/app/organisms/emoji-board/EmojiBoard.jsx b/src/app/organisms/emoji-board/EmojiBoard.jsx index d976232..84c4130 100644 --- a/src/app/organisms/emoji-board/EmojiBoard.jsx +++ b/src/app/organisms/emoji-board/EmojiBoard.jsx @@ -13,6 +13,7 @@ import cons from '../../../client/state/cons'; import navigation from '../../../client/state/navigation'; import AsyncSearch from '../../../util/AsyncSearch'; import { addRecentEmoji, getRecentEmojis } from './recent'; +import { TWEMOJI_BASE_URL } from '../../../util/twemojify'; import Text from '../../atoms/text/Text'; import RawIcon from '../../atoms/system-icons/RawIcon'; @@ -46,45 +47,49 @@ const EmojiGroup = React.memo(({ name, groupEmojis }) => { const emoji = groupEmojis[emojiIndex]; emojiRow.push( - { - emoji.hexcode - // This is a unicode emoji, and should be rendered with twemoji - ? parse(twemoji.parse( - emoji.unicode, - { - attributes: () => ({ - unicode: emoji.unicode, - shortcodes: emoji.shortcodes?.toString(), - hexcode: emoji.hexcode, - loading: 'lazy', - }), - }, - )) - // This is a custom emoji, and should be render as an mxc - : ( - {emoji.shortcode} - ) - } - , + {emoji.hexcode ? ( + // This is a unicode emoji, and should be rendered with twemoji + parse( + twemoji.parse(emoji.unicode, { + attributes: () => ({ + unicode: emoji.unicode, + shortcodes: emoji.shortcodes?.toString(), + hexcode: emoji.hexcode, + loading: 'lazy', + }), + base: TWEMOJI_BASE_URL, + }) + ) + ) : ( + // This is a custom emoji, and should be render as an mxc + {emoji.shortcode} + )} + ); } - emojiBoard.push(
{emojiRow}
); + emojiBoard.push( +
+ {emojiRow} +
+ ); } return emojiBoard; } return (
- {name} + + {name} + {groupEmojis.length !== 0 &&
{getEmojiBoard()}
}
); @@ -92,17 +97,16 @@ const EmojiGroup = React.memo(({ name, groupEmojis }) => { EmojiGroup.propTypes = { name: PropTypes.string.isRequired, - groupEmojis: PropTypes.arrayOf(PropTypes.shape({ - length: PropTypes.number, - unicode: PropTypes.string, - hexcode: PropTypes.string, - mxc: PropTypes.string, - shortcode: PropTypes.string, - shortcodes: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.arrayOf(PropTypes.string), - ]), - })).isRequired, + groupEmojis: PropTypes.arrayOf( + PropTypes.shape({ + length: PropTypes.number, + unicode: PropTypes.string, + hexcode: PropTypes.string, + mxc: PropTypes.string, + shortcode: PropTypes.string, + shortcodes: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), + }) + ).isRequired, }; const asyncSearch = new AsyncSearch(); @@ -128,7 +132,13 @@ function SearchedEmoji() { if (searchedEmojis === null) return false; - return ; + return ( + + ); } function EmojiBoard({ onSelect, searchRef }) { @@ -146,7 +156,10 @@ function EmojiBoard({ onSelect, searchRef }) { if (typeof shortcodes === 'undefined') shortcodes = undefined; else shortcodes = shortcodes.split(','); return { - unicode, hexcode, shortcodes, mxc, + unicode, + hexcode, + shortcodes, + mxc, }; } @@ -211,10 +224,9 @@ function EmojiBoard({ onSelect, searchRef }) { const parentIds = initMatrix.roomList.getAllParentSpaces(room.roomId); const parentRooms = [...parentIds].map((id) => mx.getRoom(id)); if (room) { - const packs = getRelevantPacks( - room.client, - [room, ...parentRooms], - ).filter((pack) => pack.getEmojis().length !== 0); + const packs = getRelevantPacks(room.client, [room, ...parentRooms]).filter( + (pack) => pack.getEmojis().length !== 0 + ); // Set an index for each pack so that we know where to jump when the user uses the nav for (let i = 0; i < packs.length; i += 1) { @@ -263,44 +275,41 @@ function EmojiBoard({ onSelect, searchRef }) { /> )}
- { - availableEmojis.map((pack) => { - const src = initMatrix.matrixClient - .mxcUrlToHttp(pack.avatarUrl ?? pack.getEmojis()[0].mxc); - return ( - openGroup(recentOffset + pack.packIndex)} - src={src} - key={pack.packIndex} - tooltip={pack.displayName ?? 'Unknown'} - tooltipPlacement="left" - isImage - /> - ); - }) - } + {availableEmojis.map((pack) => { + const src = initMatrix.matrixClient.mxcUrlToHttp( + pack.avatarUrl ?? pack.getEmojis()[0].mxc + ); + return ( + openGroup(recentOffset + pack.packIndex)} + src={src} + key={pack.packIndex} + tooltip={pack.displayName ?? 'Unknown'} + tooltipPlacement="left" + isImage + /> + ); + })}
- { - [ - [0, EmojiIC, 'Smilies'], - [1, DogIC, 'Animals'], - [2, CupIC, 'Food'], - [3, BallIC, 'Activities'], - [4, PhotoIC, 'Travel'], - [5, BulbIC, 'Objects'], - [6, PeaceIC, 'Symbols'], - [7, FlagIC, 'Flags'], - ].map(([indx, ico, name]) => ( - openGroup(recentOffset + availableEmojis.length + indx)} - key={indx} - src={ico} - tooltip={name} - tooltipPlacement="left" - /> - )) - } + {[ + [0, EmojiIC, 'Smilies'], + [1, DogIC, 'Animals'], + [2, CupIC, 'Food'], + [3, BallIC, 'Activities'], + [4, PhotoIC, 'Travel'], + [5, BulbIC, 'Objects'], + [6, PeaceIC, 'Symbols'], + [7, FlagIC, 'Flags'], + ].map(([indx, ico, name]) => ( + openGroup(recentOffset + availableEmojis.length + indx)} + key={indx} + src={ico} + tooltip={name} + tooltipPlacement="left" + /> + ))}
@@ -313,27 +322,25 @@ function EmojiBoard({ onSelect, searchRef }) {
- {recentEmojis.length > 0 && } - { - availableEmojis.map((pack) => ( - - )) - } - { - emojiGroups.map((group) => ( - - )) - } + {recentEmojis.length > 0 && ( + + )} + {availableEmojis.map((pack) => ( + + ))} + {emojiGroups.map((group) => ( + + ))}
-
{ parse(twemoji.parse('🙂')) }
+
{parse(twemoji.parse('🙂', { base: TWEMOJI_BASE_URL }))}
:slight_smile:
diff --git a/src/app/organisms/room/RoomViewCmdBar.jsx b/src/app/organisms/room/RoomViewCmdBar.jsx index 8c390a0..0d21123 100644 --- a/src/app/organisms/room/RoomViewCmdBar.jsx +++ b/src/app/organisms/room/RoomViewCmdBar.jsx @@ -5,7 +5,7 @@ import './RoomViewCmdBar.scss'; import parse from 'html-react-parser'; import twemoji from 'twemoji'; -import { twemojify } from '../../../util/twemojify'; +import { twemojify, TWEMOJI_BASE_URL } from '../../../util/twemojify'; import initMatrix from '../../../client/initMatrix'; import { getEmojiForCompletion } from '../emoji-board/custom-emoji'; @@ -31,7 +31,7 @@ CmdItem.propTypes = { function renderSuggestions({ prefix, option, suggestions }, fireCmd) { function renderCmdSuggestions(cmdPrefix, cmds) { - const cmdOptString = (typeof option === 'string') ? `/${option}` : '/?'; + const cmdOptString = typeof option === 'string' ? `/${option}` : '/?'; return cmds.map((cmd) => ( ({ unicode: emoji.unicode, shortcodes: emoji.shortcodes?.toString(), }), - }, - )); + base: TWEMOJI_BASE_URL, + }) + ); } // Render a custom emoji @@ -87,10 +87,12 @@ function renderSuggestions({ prefix, option, suggestions }, fireCmd) { return emos.map((emoji) => ( fireCmd({ - prefix: emPrefix, - result: emoji, - })} + onClick={() => + fireCmd({ + prefix: emPrefix, + result: emoji, + }) + } > {renderEmoji(emoji)} {`:${emoji.shortcode}:`} @@ -187,10 +189,13 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) { }); }, '@': () => { - const members = mx.getRoom(roomId).getJoinedMembers().map((member) => ({ - name: member.name, - userId: member.userId.slice(1), - })); + const members = mx + .getRoom(roomId) + .getJoinedMembers() + .map((member) => ({ + name: member.name, + userId: member.userId.slice(1), + })); asyncSearch.setup(members, { keys: ['name', 'userId'], limit: 20 }); const endIndex = members.length > 20 ? 20 : members.length; setCmd({ prefix, suggestions: members.slice(0, endIndex) }); @@ -277,9 +282,7 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
-
- { renderSuggestions(cmd, fireCmd) } -
+
{renderSuggestions(cmd, fireCmd)}
diff --git a/src/util/twemojify.jsx b/src/util/twemojify.jsx index 0a4fede..abe82a6 100644 --- a/src/util/twemojify.jsx +++ b/src/util/twemojify.jsx @@ -6,6 +6,8 @@ import parse from 'html-react-parser'; import twemoji from 'twemoji'; import { sanitizeText } from './sanitize'; +export const TWEMOJI_BASE_URL = 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/'; + const Math = lazy(() => import('../app/atoms/math/Math')); const mathOptions = { @@ -38,11 +40,16 @@ const mathOptions = { export function twemojify(text, opts, linkify = false, sanitize = true, maths = false) { if (typeof text !== 'string') return text; let content = text; + const options = opts ?? { base: TWEMOJI_BASE_URL }; + if (!options.base) { + options.base = TWEMOJI_BASE_URL; + } if (sanitize) { content = sanitizeText(content); } - content = twemoji.parse(content, opts); + + content = twemoji.parse(content, options); if (linkify) { content = linkifyHtml(content, { target: '_blank',