diff --git a/src/CONST.js b/src/CONST.js
index 30f9e24ae3e7..bbd8f98ab690 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -806,6 +806,7 @@ const CONST = {
// eslint-disable-next-line max-len, no-misleading-character-class
EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu,
+ EMOJI_SURROGATE: /(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])/,
TAX_ID: /^\d{9}$/,
NON_NUMERIC: /\D/g,
EMOJI_NAME: /:[\w+-]+:/g,
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index b97a1fa070c3..6944b1990dee 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -17,6 +17,7 @@ import SelectCircle from './SelectCircle';
import colors from '../styles/colors';
import variables from '../styles/variables';
import MultipleAvatars from './MultipleAvatars';
+import TextEmoji from './TextEmoji';
const propTypes = {
...menuItemPropTypes,
@@ -134,7 +135,7 @@ const MenuItem = (props) => {
style={titleTextStyle}
numberOfLines={1}
>
- {props.title}
+ {props.title}
)}
{Boolean(props.description) && !props.shouldShowDescriptionOnTop && (
diff --git a/src/components/TextEmoji.js b/src/components/TextEmoji.js
new file mode 100644
index 000000000000..e6af964b0433
--- /dev/null
+++ b/src/components/TextEmoji.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {View} from 'react-native';
+import _ from 'underscore';
+import Text from './Text';
+import * as EmojiUtils from '../libs/EmojiUtils';
+import * as StyleUtils from '../styles/StyleUtils';
+import stylePropTypes from '../styles/stylePropTypes';
+
+const propTypes = {
+ /** The message text to render */
+ children: PropTypes.string.isRequired,
+
+ /** The message text additional style */
+ style: stylePropTypes,
+
+ /** The emoji text additional style */
+ emojiContainerStyle: stylePropTypes,
+
+ /** The plain text additional style */
+ plainTextContainerStyle: stylePropTypes,
+};
+
+const defaultProps = {
+ style: [],
+};
+
+const TextEmoji = (props) => {
+ const words = EmojiUtils.getAllEmojiFromText(props.children);
+ const propsStyle = StyleUtils.parseStyleAsArray(props.style);
+
+ return _.map(words, ({text, isEmoji}, index) => (isEmoji
+ ? (
+
+
+ {text}
+
+
+ ) : (
+
+
+ {text}
+
+
+ )));
+};
+
+TextEmoji.displayName = 'TextEmoji';
+TextEmoji.defaultProps = defaultProps;
+TextEmoji.propTypes = propTypes;
+
+export default TextEmoji;
diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js
index bee46547a3e8..95fce2cb82b1 100644
--- a/src/libs/EmojiUtils.js
+++ b/src/libs/EmojiUtils.js
@@ -1,48 +1,47 @@
import _ from 'underscore';
import lodashOrderBy from 'lodash/orderBy';
import moment from 'moment';
-import Str from 'expensify-common/lib/str';
import CONST from '../CONST';
import * as User from './actions/User';
import emojisTrie from './EmojiTrie';
-/**
- * Get the unicode code of an emoji in base 16.
- * @param {String} input
- * @returns {String}
- */
-const getEmojiUnicode = _.memoize((input) => {
- if (input.length === 0) {
- return '';
- }
+// /**
+// * Get the unicode code of an emoji in base 16.
+// * @param {String} input
+// * @returns {String}
+// */
+// const getEmojiUnicode = _.memoize((input) => {
+// if (input.length === 0) {
+// return '';
+// }
- if (input.length === 1) {
- return _.map(input.charCodeAt(0).toString().split(' '), val => parseInt(val, 10).toString(16)).join(' ');
- }
+// if (input.length === 1) {
+// return _.map(input.charCodeAt(0).toString().split(' '), val => parseInt(val, 10).toString(16)).join(' ');
+// }
- const pairs = [];
-
- // Some Emojis in UTF-16 are stored as pair of 2 Unicode characters (eg Flags)
- // The first char is generally between the range U+D800 to U+DBFF called High surrogate
- // & the second char between the range U+DC00 to U+DFFF called low surrogate
- // More info in the following links:
- // 1. https://docs.microsoft.com/en-us/windows/win32/intl/surrogates-and-supplementary-characters
- // 2. https://thekevinscott.com/emojis-in-javascript/
- for (let i = 0; i < input.length; i++) {
- if (input.charCodeAt(i) >= 0xd800 && input.charCodeAt(i) <= 0xdbff) { // high surrogate
- if (input.charCodeAt(i + 1) >= 0xdc00 && input.charCodeAt(i + 1) <= 0xdfff) { // low surrogate
- pairs.push(
- ((input.charCodeAt(i) - 0xd800) * 0x400)
- + (input.charCodeAt(i + 1) - 0xdc00) + 0x10000,
- );
- }
- } else if (input.charCodeAt(i) < 0xd800 || input.charCodeAt(i) > 0xdfff) {
- // modifiers and joiners
- pairs.push(input.charCodeAt(i));
- }
- }
- return _.map(pairs, val => parseInt(val, 10).toString(16)).join(' ');
-});
+// const pairs = [];
+
+// // Some Emojis in UTF-16 are stored as pair of 2 Unicode characters (eg Flags)
+// // The first char is generally between the range U+D800 to U+DBFF called High surrogate
+// // & the second char between the range U+DC00 to U+DFFF called low surrogate
+// // More info in the following links:
+// // 1. https://docs.microsoft.com/en-us/windows/win32/intl/surrogates-and-supplementary-characters
+// // 2. https://thekevinscott.com/emojis-in-javascript/
+// for (let i = 0; i < input.length; i++) {
+// if (input.charCodeAt(i) >= 0xd800 && input.charCodeAt(i) <= 0xdbff) { // high surrogate
+// if (input.charCodeAt(i + 1) >= 0xdc00 && input.charCodeAt(i + 1) <= 0xdfff) { // low surrogate
+// pairs.push(
+// ((input.charCodeAt(i) - 0xd800) * 0x400)
+// + (input.charCodeAt(i + 1) - 0xdc00) + 0x10000,
+// );
+// }
+// } else if (input.charCodeAt(i) < 0xd800 || input.charCodeAt(i) > 0xdfff) {
+// // modifiers and joiners
+// pairs.push(input.charCodeAt(i));
+// }
+// }
+// return _.map(pairs, val => parseInt(val, 10).toString(16)).join(' ');
+// });
/**
* Function to remove Skin Tone and utf16 surrogates from Emoji
@@ -59,27 +58,27 @@ function trimEmojiUnicode(emojiCode) {
* @param {String} message
* @returns {Boolean}
*/
-function containsOnlyEmojis(message) {
- const trimmedMessage = Str.replaceAll(message.replace(/ /g, ''), '\n', '');
- const match = trimmedMessage.match(CONST.REGEX.EMOJIS);
+// function containsOnlyEmojis(message) {
+// const trimmedMessage = Str.replaceAll(message.replace(/ /g, ''), '\n', '');
+// const match = trimmedMessage.match(CONST.REGEX.EMOJIS);
- if (!match) {
- return false;
- }
+// if (!match) {
+// return false;
+// }
- const codes = [];
- _.map(match, emoji => _.map(getEmojiUnicode(emoji).split(' '), (code) => {
- if (!CONST.INVISIBLE_CODEPOINTS.includes(code)) {
- codes.push(code);
- }
- return code;
- }));
+// const codes = [];
+// _.map(match, emoji => _.map(getEmojiUnicode(emoji).split(' '), (code) => {
+// if (!CONST.INVISIBLE_CODEPOINTS.includes(code)) {
+// 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 && !CONST.INVISIBLE_CODEPOINTS.includes(string));
- return codes.length === messageCodes.length;
-}
+// // 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 && !CONST.INVISIBLE_CODEPOINTS.includes(string));
+// return codes.length === messageCodes.length;
+// }
/**
* Get the header indices based on the max emojis per row
@@ -245,6 +244,141 @@ function suggestEmojis(text, limit = 5) {
return [];
}
+// /**
+// * Validates that this message contains emojis
+// *
+// * @param {String} message
+// * @returns {Boolean}
+// */
+// function hasEmojis(message) {
+// if (!message) {
+// return false;
+// }
+// const trimmedMessage = Str.replaceAll(message.replace(/ /g, ''), '\n', '');
+
+// // return CONST.REGEX.EMOJIS.test(trimmedMessage);
+// return Boolean(trimmedMessage.match(CONST.REGEX.EMOJIS));
+// }
+
+/**
+ * Get all the emojis in the message
+ * @param {String} text
+ * @returns {Array}
+ */
+function getAllEmojiFromText(text) {
+ // return an empty array when no text is passed
+ if (!text) {
+ return [];
+ }
+
+ // Unicode Character 'ZERO WIDTH JOINER' (U+200D) is usually used to join surrogate pair together without breaking the emoji
+ const zeroWidthJoiner = '\u200D'; // https://codepoints.net/U+200D?lang=en
+ const splittedMessage = text.split('');
+ const result = [];
+
+ let wordHolder = ''; // word counter
+ let emojiHolder = ''; // emoji counter
+
+ const setResult = (word, isEmoji = false) => {
+ // for some weird reason javascript sees the empty string `"` as a word with `length = 1`
+ // this is caused after splitting the text empty spaces are added to both the start and the end of all emojis and text
+ // given the empty space is close to a text then its length is counted as 0
+ // while if it's before or after an emoji then it's counted as 1, so we remove the word where word.length equals 1
+ // NOTE: this does not affect a single character element example typing `[i | J]` cause after splitting its empty word.length is calculated as 0
+ if (!isEmoji && word.length === 1) {
+ return;
+ }
+
+ result.push({text: word, isEmoji});
+ };
+
+ _.forEach(splittedMessage, (word, index) => {
+ if (CONST.REGEX.EMOJI_SURROGATE.test(word) || word === zeroWidthJoiner) {
+ setResult(wordHolder);
+ wordHolder = '';
+ emojiHolder += word;
+ } else {
+ setResult(emojiHolder, true);
+ emojiHolder = '';
+ wordHolder += word;
+ }
+
+ if (index === splittedMessage.length - 1) {
+ setResult(emojiHolder, true);
+ setResult(wordHolder);
+ }
+ });
+
+ // remove none text characters like '' only return where text is a word or white space ' '
+ return _.filter(result, res => res.text);
+}
+
+// function getAllEmojiFromText(text) {
+// if (!text) {
+// return [];
+// }
+
+// const splitText = [];
+// let reResult;
+// let lastMatchIndexEnd = 0;
+// do {
+// // Look for an emoji chunk in the string
+// reResult = CONST.REGEX.EMOJIS.exec(text);
+
+// // If we reached the end of the string and it wasn't included in a previous match
+// // the chunk between the end of the last match and the end of the string is plain text
+// if (reResult === null && lastMatchIndexEnd !== text.length - 1) {
+// splitText.push({
+// text: text.slice(lastMatchIndexEnd, text.length),
+// isEmoji: false,
+// });
+// // eslint-disable-next-line no-continue
+// continue;
+// }
+
+// const matchIndexStart = reResult.indices[0][0];
+// const matchIndexEnd = reResult.indices[0][1];
+
+// // The chunk between the end of the last match and the start of the new one is plain-text
+// splitText.push({
+// text: text.slice(lastMatchIndexEnd, matchIndexStart),
+// isEmoji: false,
+// });
+
+// // Everything captured by the regex itself is emoji + whitespace
+// splitText.push({
+// text: text.slice(matchIndexStart, matchIndexEnd),
+// isEmoji: true,
+// });
+
+// lastMatchIndexEnd = matchIndexEnd;
+// } while (reResult !== null);
+
+// return _.filter(splitText, res => res.text);
+// }
+
+/**
+ * Validates that this message contains has emojis
+ *
+ * @param {String} message
+ * @returns {Boolean}
+ */
+function hasEmojis(message) {
+ const splitText = getAllEmojiFromText(message);
+ return _.find(splitText, chunk => chunk.isEmoji) !== undefined;
+}
+
+/**
+ * Validates that this message contains only emojis
+ *
+ * @param {String} message
+ * @returns {Boolean}
+ */
+function containsOnlyEmojis(message) {
+ const splitText = getAllEmojiFromText(message);
+ return _.every(splitText, chunk => chunk.isEmoji);
+}
+
export {
getHeaderIndices,
mergeEmojisWithFrequentlyUsedEmojis,
@@ -253,4 +387,6 @@ export {
replaceEmojis,
suggestEmojis,
trimEmojiUnicode,
+ getAllEmojiFromText,
+ hasEmojis,
};
diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index dcc6d74270b0..e9be0b429ebc 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -25,6 +25,7 @@ import PressableWithoutFocus from '../components/PressableWithoutFocus';
import * as Report from '../libs/actions/Report';
import OfflineWithFeedback from '../components/OfflineWithFeedback';
import AutoUpdateTime from '../components/AutoUpdateTime';
+import TextEmoji from '../components/TextEmoji';
const matchType = PropTypes.shape({
params: PropTypes.shape({
@@ -148,7 +149,16 @@ class DetailsPage extends React.PureComponent {
{details.displayName && (
- {isSMSLogin ? this.props.toLocalPhone(details.displayName) : details.displayName}
+ {isSMSLogin
+ ? this.props.toLocalPhone(details.displayName)
+ : (
+
+ {details.displayName}
+
+ )}
)}
{details.login ? (
diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js
index c4f593517df5..c493183dcacf 100644
--- a/src/pages/home/report/ReportActionItemFragment.js
+++ b/src/pages/home/report/ReportActionItemFragment.js
@@ -15,6 +15,7 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal
import * as DeviceCapabilities from '../../../libs/DeviceCapabilities';
import compose from '../../../libs/compose';
import * as StyleUtils from '../../../styles/StyleUtils';
+import TextEmoji from '../../../components/TextEmoji';
const propTypes = {
/** The message fragment needing to be displayed */
@@ -121,9 +122,21 @@ const ReportActionItemFragment = (props) => {
- {StyleUtils.convertToLTR(Str.htmlDecode(text))}
+
+ {StyleUtils.convertToLTR(Str.htmlDecode(text))}
+
{props.fragment.isEdited && (
{
numberOfLines={props.isSingleLine ? 1 : undefined}
style={[styles.chatItemMessageHeaderSender]}
>
- {Str.htmlDecode(props.fragment.text)}
+
+ {props.fragment.text}
+
);
diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage/AboutPage.js
index 7c7840fdbbdb..6e8a1d8fd621 100644
--- a/src/pages/settings/AboutPage/AboutPage.js
+++ b/src/pages/settings/AboutPage/AboutPage.js
@@ -113,7 +113,7 @@ const AboutPage = (props) => {
{props.translate(
diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js
index f10bca4c6037..0954a1f37646 100755
--- a/src/pages/settings/InitialSettingsPage.js
+++ b/src/pages/settings/InitialSettingsPage.js
@@ -34,6 +34,7 @@ import ConfirmModal from '../../components/ConfirmModal';
import * as ReportUtils from '../../libs/ReportUtils';
import * as Link from '../../libs/actions/Link';
import OfflineWithFeedback from '../../components/OfflineWithFeedback';
+import TextEmoji from '../../components/TextEmoji';
const propTypes = {
/* Onyx Props */
@@ -270,7 +271,14 @@ class InitialSettingsPage extends React.Component {
{this.props.currentUserPersonalDetails.displayName
- ? this.props.currentUserPersonalDetails.displayName
+ ? (
+
+ {this.props.currentUserPersonalDetails.displayName}
+
+ )
: Str.removeSMSDomain(this.props.session.email)}
diff --git a/src/styles/styles.js b/src/styles/styles/baseStyles.js
similarity index 97%
rename from src/styles/styles.js
rename to src/styles/styles/baseStyles.js
index 50104821d8fa..082a0dce5539 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles/baseStyles.js
@@ -1,25 +1,25 @@
-import fontFamily from './fontFamily';
-import addOutlineWidth from './addOutlineWidth';
-import themeColors from './themes/default';
-import fontWeightBold from './fontWeight/bold';
-import variables from './variables';
-import spacing from './utilities/spacing';
-import sizing from './utilities/sizing';
-import flex from './utilities/flex';
-import display from './utilities/display';
-import overflow from './utilities/overflow';
-import whiteSpace from './utilities/whiteSpace';
-import wordBreak from './utilities/wordBreak';
-import positioning from './utilities/positioning';
-import codeStyles from './codeStyles';
-import visibility from './utilities/visibility';
-import writingDirection from './utilities/writingDirection';
-import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles';
-import emojiHeaderContainerPlatformStyles from './emojiHeaderContainerPlatformStyles';
-import pointerEventsNone from './pointerEventsNone';
-import pointerEventsAuto from './pointerEventsAuto';
-import overflowXHidden from './overflowXHidden';
-import CONST from '../CONST';
+import fontFamily from '../fontFamily';
+import addOutlineWidth from '../addOutlineWidth';
+import themeColors from '../themes/default';
+import fontWeightBold from '../fontWeight/bold';
+import variables from '../variables';
+import spacing from '../utilities/spacing';
+import sizing from '../utilities/sizing';
+import flex from '../utilities/flex';
+import display from '../utilities/display';
+import overflow from '../utilities/overflow';
+import whiteSpace from '../utilities/whiteSpace';
+import wordBreak from '../utilities/wordBreak';
+import positioning from '../utilities/positioning';
+import codeStyles from '../codeStyles';
+import visibility from '../utilities/visibility';
+import writingDirection from '../utilities/writingDirection';
+import optionAlternateTextPlatformStyles from '../optionAlternateTextPlatformStyles';
+import emojiHeaderContainerPlatformStyles from '../emojiHeaderContainerPlatformStyles';
+import pointerEventsNone from '../pointerEventsNone';
+import pointerEventsAuto from '../pointerEventsAuto';
+import overflowXHidden from '../overflowXHidden';
+import CONST from '../../CONST';
const picker = {
backgroundColor: themeColors.transparent,
@@ -305,6 +305,8 @@ const styles = {
textDecorationLine: 'none',
},
+ displayNameText: {},
+
textWhite: {
color: themeColors.textLight,
},
@@ -999,6 +1001,13 @@ const styles = {
width: '100%',
},
+ sidebarFooterItem: {
+ flexShrink: 0,
+ color: themeColors.textSupporting,
+ fontSize: variables.fontSizeSmall,
+ paddingTop: 2,
+ },
+
sidebarAvatar: {
backgroundColor: themeColors.icon,
borderRadius: 20,
@@ -1101,6 +1110,27 @@ const styles = {
lineHeight: variables.fontSizeOnlyEmojisHeight,
},
+ inboxMessageText: {},
+
+ emojiMessageText: {
+ position: 'relative',
+ fontSize: variables.fontSizeEmoji,
+ lineHeight: variables.fontSizeEmojiHeight,
+ top: 2,
+ },
+
+ inboxEmojiMessageText: {},
+
+ messageTextWithoutEmoji: {
+ height: '100%',
+ },
+
+ profileEmojiText: {
+ fontSize: variables.fontSizeEmojiProfile,
+ lineHeight: variables.fontSizeEmojiProfileHeight,
+ top: 4,
+ },
+
createMenuPositionSidebar: {
left: 18,
bottom: 100,
diff --git a/src/styles/styles/index.android.js b/src/styles/styles/index.android.js
new file mode 100644
index 000000000000..a1b5e10240a0
--- /dev/null
+++ b/src/styles/styles/index.android.js
@@ -0,0 +1,39 @@
+import _ from 'lodash';
+import variables from '../variables';
+import baseStyles from './baseStyles';
+
+const styles = {
+ ...baseStyles.styles,
+ displayNameText: {
+ marginTop: 4,
+ },
+
+ emojiMessageText: {
+ top: 3,
+ position: 'relative',
+ fontSize: variables.fontSizeEmoji,
+ lineHeight: variables.fontSizeEmojiHeight,
+ },
+
+ inboxEmojiMessageText: {
+ marginTop: 6,
+ },
+
+ inboxMessageText: {
+ marginTop: 1,
+ },
+
+ onlyEmojisText: {
+ marginTop: 3,
+ fontSize: variables.fontSizeOnlyEmojis,
+ lineHeight: variables.fontSizeOnlyEmojisHeight,
+ },
+
+ profileEmojiText: {
+ fontSize: variables.fontSizeEmojiProfile,
+ lineHeight: variables.fontSizeEmojiProfileHeight,
+ top: 4,
+ },
+};
+
+export default _.assign(baseStyles, styles);
diff --git a/src/styles/styles/index.ios.js b/src/styles/styles/index.ios.js
new file mode 100644
index 000000000000..1c88a7b5ae85
--- /dev/null
+++ b/src/styles/styles/index.ios.js
@@ -0,0 +1,47 @@
+import _ from 'lodash';
+import variables from '../variables';
+import baseStyles from './baseStyles';
+import themeColors from '../themes/default';
+
+const styles = {
+ ...baseStyles.styles,
+ displayNameText: {
+ marginTop: 6,
+ },
+
+ emojiMessageText: {
+ position: 'relative',
+ fontSize: variables.fontSizeEmoji,
+ lineHeight: variables.fontSizeEmojiHeight,
+ },
+
+ inboxEmojiMessageText: {
+ marginTop: 1,
+ },
+
+ onlyEmojisText: {
+ marginTop: 1,
+ fontSize: variables.fontSizeOnlyEmojis,
+ lineHeight: variables.fontSizeOnlyEmojisHeight,
+ },
+
+ inboxMessageText: {
+ marginTop: 0,
+ },
+
+ profileEmojiText: {
+ top: 0,
+ fontSize: variables.fontSizeEmojiProfile,
+ lineHeight: variables.fontSizeEmojiProfileHeight,
+ },
+
+ chatItemMessageHeaderTimestamp: {
+ flexShrink: 0,
+ paddingTop: 0,
+ color: themeColors.textSupporting,
+ fontSize: variables.fontSizeSmall,
+ bottom: 2.5,
+ },
+};
+
+export default _.assign(baseStyles, styles);
diff --git a/src/styles/styles/index.js b/src/styles/styles/index.js
new file mode 100644
index 000000000000..8cdaab8a7d11
--- /dev/null
+++ b/src/styles/styles/index.js
@@ -0,0 +1,3 @@
+import styles from './baseStyles';
+
+export default styles;
diff --git a/src/styles/variables.js b/src/styles/variables/baseVariables.js
similarity index 97%
rename from src/styles/variables.js
rename to src/styles/variables/baseVariables.js
index 00ac9cf6ae5e..4282b316b7e1 100644
--- a/src/styles/variables.js
+++ b/src/styles/variables/baseVariables.js
@@ -39,6 +39,10 @@ export default {
defaultAvatarPreviewSize: 360,
fontSizeOnlyEmojis: 30,
fontSizeOnlyEmojisHeight: 35,
+ fontSizeEmojiHeight: 24,
+ fontSizeEmojiProfileHeight: 25,
+ fontSizeEmojiProfile: 21,
+ fontSizeEmoji: 19,
fontSizeSmall: getValueUsingPixelRatio(11, 17),
fontSizeExtraSmall: 9,
fontSizeLabel: getValueUsingPixelRatio(13, 19),
diff --git a/src/styles/variables/index.js b/src/styles/variables/index.js
new file mode 100644
index 000000000000..49a16647bba5
--- /dev/null
+++ b/src/styles/variables/index.js
@@ -0,0 +1,3 @@
+import variables from './baseVariables';
+
+export default variables;
diff --git a/src/styles/variables/index.web.js b/src/styles/variables/index.web.js
new file mode 100644
index 000000000000..5fa61a0b73d1
--- /dev/null
+++ b/src/styles/variables/index.web.js
@@ -0,0 +1,7 @@
+import variables from './baseVariables';
+
+export default {
+ ...variables,
+ fontSizeEmojiHeight: 26,
+ fontSizeEmojiProfileHeight: 29,
+};