diff --git a/js/modules/emojis.js b/js/modules/emojis.js
index c4220f1cbb..47730c2f33 100644
--- a/js/modules/emojis.js
+++ b/js/modules/emojis.js
@@ -1,15 +1,11 @@
const { take } = require('lodash');
const { getRecentEmojis } = require('./data');
-const {
- replaceColons,
- hasVariation,
-} = require('../../ts/components/emoji/lib');
+const { replaceColons } = require('../../ts/components/emoji/lib');
module.exports = {
getInitialState,
load,
replaceColons,
- hasVariation,
};
let initialState = null;
diff --git a/js/modules/signal.js b/js/modules/signal.js
index 2f206fdbab..3aed32062e 100644
--- a/js/modules/signal.js
+++ b/js/modules/signal.js
@@ -6,7 +6,6 @@ const Crypto = require('./crypto');
const Data = require('./data');
const Database = require('./database');
const Emojis = require('./emojis');
-const Emoji = require('../../ts/util/emoji');
const EmojiLib = require('../../ts/components/emoji/lib');
const IndexedDB = require('./indexeddb');
const Notifications = require('../../ts/notifications');
@@ -333,7 +332,6 @@ exports.setup = (options = {}) => {
Data,
Database,
Emojis,
- Emoji,
EmojiLib,
IndexedDB,
LinkPreviews,
diff --git a/package.json b/package.json
index ae11cb0361..6b3b84aa16 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,6 @@
"electron-is-dev": "0.3.0",
"emoji-datasource": "4.1.0",
"emoji-datasource-apple": "4.1.0",
- "emoji-js": "3.4.0",
"emoji-regex": "8.0.0",
"filesize": "3.6.1",
"firstline": "1.2.1",
diff --git a/ts/components/MessageBodyHighlight.tsx b/ts/components/MessageBodyHighlight.tsx
index d28676c72a..642adb4835 100644
--- a/ts/components/MessageBodyHighlight.tsx
+++ b/ts/components/MessageBodyHighlight.tsx
@@ -4,7 +4,7 @@ import { MessageBody } from './conversation/MessageBody';
import { Emojify } from './conversation/Emojify';
import { AddNewLines } from './conversation/AddNewLines';
-import { SizeClassType } from '../util/emoji';
+import { SizeClassType } from './emoji/lib';
import { LocalizerType, RenderTextCallbackType } from '../types/Util';
diff --git a/ts/components/conversation/Emojify.tsx b/ts/components/conversation/Emojify.tsx
index 61ffc1c80e..39021608a7 100644
--- a/ts/components/conversation/Emojify.tsx
+++ b/ts/components/conversation/Emojify.tsx
@@ -1,16 +1,11 @@
import React from 'react';
import classNames from 'classnames';
-import is from '@sindresorhus/is';
-import {
- findImage,
- getRegex,
- getReplacementData,
- SizeClassType,
-} from '../../util/emoji';
+import emojiRegex from 'emoji-regex';
import { RenderTextCallbackType } from '../../types/Util';
+import { emojiToImage, SizeClassType } from '../emoji/lib';
// Some of this logic taken from emoji-js/replacement
function getImageTag({
@@ -22,18 +17,9 @@ function getImageTag({
sizeClass?: SizeClassType;
key: string | number;
}) {
- const result = getReplacementData(match[0], match[1], match[2]);
+ const img = emojiToImage(match[0]);
- if (is.string(result)) {
- return match[0];
- }
-
- const img = findImage(result.value, result.variation);
-
- if (
- !img.path ||
- !img.path.startsWith('node_modules/emoji-datasource-apple')
- ) {
+ if (!img) {
return match[0];
}
@@ -41,10 +27,9 @@ function getImageTag({
// tslint:disable-next-line react-a11y-img-has-alt
);
@@ -66,7 +51,7 @@ export class Emojify extends React.Component {
public render() {
const { text, sizeClass, renderNonEmoji } = this.props;
const results: Array = [];
- const regex = getRegex();
+ const regex = emojiRegex();
// We have to do this, because renderNonEmoji is not required in our Props object,
// but it is always provided via defaultProps.
diff --git a/ts/components/conversation/MessageBody.tsx b/ts/components/conversation/MessageBody.tsx
index 1801ed9ed2..c4b7ce2496 100644
--- a/ts/components/conversation/MessageBody.tsx
+++ b/ts/components/conversation/MessageBody.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { getSizeClass, SizeClassType } from '../../util/emoji';
+import { getSizeClass, SizeClassType } from '../emoji/lib';
import { Emojify } from './Emojify';
import { AddNewLines } from './AddNewLines';
import { Linkify } from './Linkify';
diff --git a/ts/components/emoji/lib.ts b/ts/components/emoji/lib.ts
index 072621f78a..edddb196cc 100644
--- a/ts/components/emoji/lib.ts
+++ b/ts/components/emoji/lib.ts
@@ -1,5 +1,6 @@
// @ts-ignore: untyped json
import untypedData from 'emoji-datasource';
+import emojiRegex from 'emoji-regex';
import {
compact,
flatMap,
@@ -13,12 +14,14 @@ import {
} from 'lodash';
import Fuse from 'fuse.js';
import PQueue from 'p-queue';
+import is from '@sindresorhus/is';
export type ValuesOf> = T[number];
export const skinTones = ['1F3FB', '1F3FC', '1F3FD', '1F3FE', '1F3FF'];
export type SkinToneKey = '1F3FB' | '1F3FC' | '1F3FD' | '1F3FE' | '1F3FF';
+export type SizeClassType = '' | 'small' | 'medium' | 'large' | 'jumbo';
export type EmojiSkinVariation = {
unified: string;
@@ -111,16 +114,8 @@ export const preloadImages = async () => {
console.log(`Done preloading emoji images in ${end - start}ms`);
};
-export const dataByShortName = keyBy(data, 'short_name');
-
-data.forEach(emoji => {
- const { short_names } = emoji;
- if (short_names) {
- short_names.forEach(name => {
- dataByShortName[name] = emoji;
- });
- }
-});
+const dataByShortName = keyBy(data, 'short_name');
+const imageByEmoji: { [key: string]: string } = {};
export const dataByCategory = mapValues(
groupBy(data, ({ category }) => {
@@ -221,34 +216,19 @@ export function unifiedToEmoji(unified: string) {
.join('');
}
-export function hasVariation(shortName: string, skinTone: number = 0) {
- if (skinTone === 0) {
- return false;
- }
-
- const base = dataByShortName[shortName];
- if (!base) {
- return false;
- }
-
- if (skinTone > 0 && base.skin_variations) {
- const toneKey = skinTones[skinTone - 1];
-
- return Boolean(base.skin_variations[toneKey]);
- }
-
- return false;
-}
-
-export function convertShortName(shortName: string, skinTone: number = 0) {
+export function convertShortName(
+ shortName: string,
+ skinTone: number | SkinToneKey = 0
+) {
const base = dataByShortName[shortName];
if (!base) {
return '';
}
- if (skinTone > 0 && base.skin_variations) {
- const toneKey = skinTones[skinTone - 1];
+ const toneKey = is.number(skinTone) ? skinTones[skinTone - 1] : skinTone;
+
+ if (skinTone && base.skin_variations) {
const variation = base.skin_variations[toneKey];
if (variation) {
return unifiedToEmoji(variation.unified);
@@ -258,6 +238,10 @@ export function convertShortName(shortName: string, skinTone: number = 0) {
return unifiedToEmoji(base.unified);
}
+export function emojiToImage(emoji: string): string | undefined {
+ return imageByEmoji[emoji];
+}
+
export function replaceColons(str: string) {
return str.replace(/:[a-z0-9-_+]+:(?::skin-tone-[1-5]:)?/gi, m => {
const [shortName = '', skinTone = '0'] = m
@@ -273,3 +257,60 @@ export function replaceColons(str: string) {
return m;
});
}
+
+function getCountOfAllMatches(str: string, regex: RegExp) {
+ let match = regex.exec(str);
+ let count = 0;
+
+ if (!regex.global) {
+ return match ? 1 : 0;
+ }
+
+ while (match) {
+ count += 1;
+ match = regex.exec(str);
+ }
+
+ return count;
+}
+
+export function getSizeClass(str: string): SizeClassType {
+ // Do we have non-emoji characters?
+ if (str.replace(emojiRegex(), '').trim().length > 0) {
+ return '';
+ }
+
+ const emojiCount = getCountOfAllMatches(str, emojiRegex());
+
+ if (emojiCount > 8) {
+ return '';
+ } else if (emojiCount > 6) {
+ return 'small';
+ } else if (emojiCount > 4) {
+ return 'medium';
+ } else if (emojiCount > 2) {
+ return 'large';
+ } else {
+ return 'jumbo';
+ }
+}
+
+data.forEach(emoji => {
+ const { short_name, short_names, skin_variations, image } = emoji;
+
+ if (short_names) {
+ short_names.forEach(name => {
+ dataByShortName[name] = emoji;
+ });
+ }
+
+ imageByEmoji[convertShortName(short_name)] = makeImagePath(image);
+
+ if (skin_variations) {
+ Object.entries(skin_variations).forEach(([tone, variation]) => {
+ imageByEmoji[
+ convertShortName(short_name, tone as SkinToneKey)
+ ] = makeImagePath(variation.image);
+ });
+ }
+});
diff --git a/ts/util/emoji.ts b/ts/util/emoji.ts
deleted file mode 100644
index 9593aa6257..0000000000
--- a/ts/util/emoji.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-// @ts-ignore
-import EmojiConvertor from 'emoji-js';
-
-const instance = new EmojiConvertor();
-instance.init_unified();
-instance.init_colons();
-instance.img_sets.apple.path =
- 'node_modules/emoji-datasource-apple/img/apple/64/';
-instance.include_title = true;
-instance.replace_mode = 'img';
-instance.supports_css = false; // needed to avoid spans with background-image
-
-export type SizeClassType = '' | 'small' | 'medium' | 'large' | 'jumbo';
-
-export function getRegex(): RegExp {
- return instance.rx_unified;
-}
-
-export function getTitle(value: string): string | undefined {
- return instance.data[value][3][0];
-}
-
-export function findImage(value: string, variation?: string) {
- return instance.find_image(value, variation);
-}
-
-function getCountOfAllMatches(str: string, regex: RegExp) {
- let match = regex.exec(str);
- let count = 0;
-
- if (!regex.global) {
- return match ? 1 : 0;
- }
-
- while (match) {
- count += 1;
- match = regex.exec(str);
- }
-
- return count;
-}
-
-function hasNormalCharacters(str: string) {
- const noEmoji = str.replace(instance.rx_unified, '').trim();
-
- return noEmoji.length > 0;
-}
-
-export function getSizeClass(str: string): SizeClassType {
- if (hasNormalCharacters(str)) {
- return '';
- }
-
- const emojiCount = getCountOfAllMatches(str, instance.rx_unified);
- if (emojiCount > 8) {
- return '';
- } else if (emojiCount > 6) {
- return 'small';
- } else if (emojiCount > 4) {
- return 'medium';
- } else if (emojiCount > 2) {
- return 'large';
- } else {
- return 'jumbo';
- }
-}
-
-const VARIATION_LOOKUP: { [index: string]: string } = {
- '\uD83C\uDFFB': '1f3fb',
- '\uD83C\uDFFC': '1f3fc',
- '\uD83C\uDFFD': '1f3fd',
- '\uD83C\uDFFE': '1f3fe',
- '\uD83C\uDFFF': '1f3ff',
-};
-
-// Taken from emoji-js/replace_unified
-export function getReplacementData(
- m: string,
- p1: string | undefined,
- p2: string | undefined
-): string | { value: string; variation?: string } {
- const unified = instance.map.unified[p1];
- if (unified) {
- const variation = VARIATION_LOOKUP[p2 || ''];
- if (variation) {
- return {
- value: unified,
- variation,
- };
- }
-
- return {
- value: unified,
- };
- }
-
- const unifiedVars = instance.map.unified_vars[p1];
- if (unifiedVars) {
- return {
- value: unifiedVars[0],
- variation: unifiedVars[1],
- };
- }
-
- return m;
-}
diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json
index e0cd083386..694e2afc2c 100644
--- a/ts/util/lint/exceptions.json
+++ b/ts/util/lint/exceptions.json
@@ -231,7 +231,7 @@
"rule": "jQuery-load(",
"path": "js/modules/emojis.js",
"line": "async function load() {",
- "lineNumber": 17,
+ "lineNumber": 13,
"reasonCategory": "falseMatch",
"updated": "2019-05-23T22:27:53.554Z"
},
@@ -3549,24 +3549,6 @@
"reasonCategory": "otherUtilityCode",
"updated": "2019-04-03T00:52:04.925Z"
},
- {
- "rule": "jQuery-$(",
- "path": "node_modules/emoji-js/lib/jquery.emoji.js",
- "line": "\t\t$(this).html(function (i, oldHtml){",
- "lineNumber": 5,
- "reasonCategory": "usageTrusted",
- "updated": "2018-09-19T21:59:32.770Z",
- "reasonDetail": "Protected from arbitrary input"
- },
- {
- "rule": "jQuery-html(",
- "path": "node_modules/emoji-js/lib/jquery.emoji.js",
- "line": "\t\t$(this).html(function (i, oldHtml){",
- "lineNumber": 5,
- "reasonCategory": "usageTrusted",
- "updated": "2018-09-18T19:19:27.699Z",
- "reasonDetail": "It's setting the html of the element to the previous HTML, just with the emoji replaced"
- },
{
"rule": "thenify-multiArgs",
"path": "node_modules/es6-promisify/dist/promisify.js",
@@ -7904,4 +7886,4 @@
"reasonCategory": "falseMatch",
"updated": "2019-05-02T20:44:56.470Z"
}
-]
\ No newline at end of file
+]
diff --git a/yarn.lock b/yarn.lock
index ba0e3a37af..d1c801cbdf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2793,23 +2793,11 @@ emoji-datasource-apple@4.1.0:
resolved "https://registry.yarnpkg.com/emoji-datasource-apple/-/emoji-datasource-apple-4.1.0.tgz#e6725311b115144a32fb60043416a755fea30bf5"
integrity sha1-5nJTEbEVFEoy+2AENBanVf6jC/U=
-emoji-datasource@4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/emoji-datasource/-/emoji-datasource-4.0.0.tgz#3fc9c0c2f4fb321d9291138819f6d100603d3e2f"
- integrity sha1-P8nAwvT7Mh2SkROIGfbRAGA9Pi8=
-
emoji-datasource@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/emoji-datasource/-/emoji-datasource-4.1.0.tgz#b44557f78a2dfac2f350393391b170a567ec28ad"
integrity sha1-tEVX94ot+sLzUDkzkbFwpWfsKK0=
-emoji-js@3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/emoji-js/-/emoji-js-3.4.0.tgz#dabdeda60c92d1948a5177e51ba9421d2029b052"
- integrity sha1-2r3tpgyS0ZSKUXflG6lCHSApsFI=
- dependencies:
- emoji-datasource "4.0.0"
-
emoji-regex@8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"