diff --git a/app/soapbox/components/emoji_selector.tsx b/app/soapbox/components/emoji_selector.tsx index fa3004c19..8e8c94d49 100644 --- a/app/soapbox/components/emoji_selector.tsx +++ b/app/soapbox/components/emoji_selector.tsx @@ -5,7 +5,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; -import emojify from 'soapbox/features/emoji/emoji'; +import { Emoji } from 'soapbox/components/ui'; import type { List as ImmutableList } from 'immutable'; import type { RootState } from 'soapbox/store'; @@ -109,12 +109,9 @@ class EmojiSelector extends ImmutablePureComponent { const { visible, focused, allowedEmoji } = this.props; return ( - +
@@ -122,11 +119,12 @@ class EmojiSelector extends ImmutablePureComponent { ))}
diff --git a/app/soapbox/components/ui/emoji/emoji.tsx b/app/soapbox/components/ui/emoji/emoji.tsx new file mode 100644 index 000000000..d06b44d15 --- /dev/null +++ b/app/soapbox/components/ui/emoji/emoji.tsx @@ -0,0 +1,54 @@ +import classNames from 'classnames'; +import React from 'react'; + +import { joinPublicPath } from 'soapbox/utils/static'; + +// Taken from twemoji-parser +// https://github.com/twitter/twemoji-parser/blob/a97ef3994e4b88316812926844d51c296e889f76/src/index.js +const removeVS16s = (rawEmoji: string): string => { + const vs16RegExp = /\uFE0F/g; + const zeroWidthJoiner = String.fromCharCode(0x200d); + return rawEmoji.indexOf(zeroWidthJoiner) < 0 ? rawEmoji.replace(vs16RegExp, '') : rawEmoji; +}; + +const toCodePoints = (unicodeSurrogates: string): string[] => { + const points = []; + let char = 0; + let previous = 0; + let i = 0; + while (i < unicodeSurrogates.length) { + char = unicodeSurrogates.charCodeAt(i++); + if (previous) { + points.push((0x10000 + ((previous - 0xd800) << 10) + (char - 0xdc00)).toString(16)); + previous = 0; + } else if (char > 0xd800 && char <= 0xdbff) { + previous = char; + } else { + points.push(char.toString(16)); + } + } + return points; +}; + +interface IEmoji { + className?: string, + emoji: string, +} + +const Emoji: React.FC = ({ className, emoji }): JSX.Element | null => { + const codepoints = toCodePoints(removeVS16s(emoji)); + const filename = codepoints.join('-'); + + if (!filename) return null; + + return ( + {emoji} + ); +}; + +export default Emoji; diff --git a/app/soapbox/components/ui/index.ts b/app/soapbox/components/ui/index.ts index 302738ed9..d9cd27fb0 100644 --- a/app/soapbox/components/ui/index.ts +++ b/app/soapbox/components/ui/index.ts @@ -2,6 +2,7 @@ export { default as Avatar } from './avatar/avatar'; export { default as Button } from './button/button'; export { Card, CardBody, CardHeader, CardTitle } from './card/card'; export { default as Column } from './column/column'; +export { default as Emoji } from './emoji/emoji'; export { default as Form } from './form/form'; export { default as FormActions } from './form-actions/form-actions'; export { default as FormGroup } from './form-group/form-group';