diff --git a/src/CONST.js b/src/CONST.js index e3a88d48cd7e..c0e26e78fca7 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -387,6 +387,8 @@ const CONST = { EMOJI_FREQUENT_ROW_COUNT: 3, + EMOJI_INVISIBLE_CODEPOINT: 'fe0f', + TOOLTIP_MAX_LINES: 3, LOGIN_TYPE: { diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 61684d37d0f9..208dd6b1cf4d 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -9,7 +9,7 @@ import * as User from './actions/User'; * @param {String} input * @returns {String} */ -function getEmojiUnicode(input) { +const getEmojiUnicode = _.memoize((input) => { if (input.length === 0) { return ''; } @@ -40,7 +40,7 @@ function getEmojiUnicode(input) { } } return _.map(pairs, val => parseInt(val, 10).toString(16)).join(' '); -} +}); /** * Function to remove Skin Tone and utf16 surrogates from Emoji @@ -70,6 +70,34 @@ function isSingleEmoji(message) { return matchedUnicode === currentMessageUnicode; } +/** + * Validates that this message contains only emojis + * + * @param {String} message + * @returns {Boolean} + */ +function containsOnlyEmojis(message) { + const trimmedMessage = message.replace(/ /g, '').replaceAll('\n', ''); + const match = trimmedMessage.match(CONST.REGEX.EMOJIS); + + if (!match) { + return false; + } + + const codes = []; + _.map(match, emoji => _.map(getEmojiUnicode(emoji).split(' '), (code) => { + if (code !== CONST.EMOJI_INVISIBLE_CODEPOINT) { + codes.push(code); + } + return code; + })); + + // Emojis are stored as multiple characters, so we're using spread operator + // to iterate over the actual emojis, not just characters that compose them + const messageCodes = _.filter(_.map([...trimmedMessage], char => getEmojiUnicode(char)), string => string.length > 0 && string !== CONST.EMOJI_INVISIBLE_CODEPOINT); + return codes.length === messageCodes.length; +} + /** * Get the header indices based on the max emojis per row * @param {Object[]} emojis @@ -176,4 +204,5 @@ export { getDynamicHeaderIndices, mergeEmojisWithFrequentlyUsedEmojis, addToFrequentlyUsedEmojis, + containsOnlyEmojis, }; diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 194a865636f7..8cfcfe9aac73 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -68,7 +68,7 @@ const defaultProps = { const ReportActionItemFragment = (props) => { switch (props.fragment.type) { - case 'COMMENT': + case 'COMMENT': { // If this is an attachment placeholder, return the placeholder component if (props.isAttachment && props.loading) { return ( @@ -97,6 +97,16 @@ const ReportActionItemFragment = (props) => { ); } + // If the only difference between fragment.text and fragment.html is
tags + // we replace them with line breaks and render it as text, not as html. + // This is done to render emojis with line breaks between them as text. + const differByLineBreaksOnly = props.fragment.html.replaceAll('
', ' ') === props.fragment.text; + if (differByLineBreaksOnly) { + const textWithLineBreaks = props.fragment.html.replaceAll('
', '\n'); + // eslint-disable-next-line no-param-reassign + props.fragment = {...props.fragment, text: textWithLineBreaks, html: textWithLineBreaks}; + } + // Only render HTML if we have html in the fragment return props.fragment.html !== props.fragment.text ? ( @@ -106,7 +116,7 @@ const ReportActionItemFragment = (props) => { ) : ( {Str.htmlDecode(props.fragment.text)} {props.fragment.isEdited && ( @@ -119,6 +129,7 @@ const ReportActionItemFragment = (props) => { )} ); + } case 'TEXT': return ( diff --git a/src/styles/styles.js b/src/styles/styles.js index f222d4f56246..4859c0470350 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1042,9 +1042,9 @@ const styles = { textDecorationLine: 'none', }, - singleEmojiText: { - fontSize: variables.fontSizeSingleEmoji, - lineHeight: variables.fontSizeSingleEmojiHeight, + onlyEmojisText: { + fontSize: variables.fontSizeOnlyEmojis, + lineHeight: variables.fontSizeOnlyEmojisHeight, }, createMenuPositionSidebar: { diff --git a/src/styles/variables.js b/src/styles/variables.js index 4302947ac879..506755712154 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -14,8 +14,8 @@ export default { avatarSizeSmall: 28, avatarSizeSubscript: 20, avatarSizeSmallSubscript: 14, - fontSizeSingleEmoji: 30, - fontSizeSingleEmojiHeight: 35, + fontSizeOnlyEmojis: 30, + fontSizeOnlyEmojisHeight: 35, fontSizeSmall: 11, fontSizeExtraSmall: 9, fontSizeLabel: 13,