From 60feb0cd873055a4d9874a378151a963cfc9b912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20G=C3=B3rak?= Date: Sat, 6 Apr 2019 13:58:44 +0200 Subject: [PATCH 1/4] Add filterFunctions prop allowing user to specify filter functions that are invoked for every emoji before actual rendering making it possible to show only subset of emojis instead of all of them always --- README.md | 30 +++++++++++++++++++++++++++++- example/src/EmojiInput.js | 11 ++++++++--- src/EmojiInput.js | 11 ++++++++--- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d57c1af..eaeaa4b 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ npx babel-node scripts/compile.js | `resetSearch` | Pass this in if you want to clear the the search | boolean | false | | `loggingFunction` | Logging function to be called when applicable.\* | function | none | | `verboseLoggingFunction` | Same as loggingFunction but also provides strategy used to determine failed search | boolean | false | - +| `filterFunctions` | Array of functions that are used to limit which emojis should be rendered. Each of this function will be invoked with single parameter being `emoji` data and if every function returns `true` for `emoji` then this emoji will be included and displayed.| Array(function) | [] | > \* When the search function yields this function is called. Additionally when the user clears the query box this function is called with the previous longest query since the last time the query box was empty. By default the function is called with one parameter, a string representing the query. If the verbose logging function parameter is set to true the function is called with a second parameter that is a string specifying why the function was called (either 'emptySearchResult' or 'longestPreviousQuery'). ## Usage @@ -88,3 +88,31 @@ Finally compile the data file that used in the keyboard. ```shell node-babel ./scripts/compile.js ``` + +## Missing Emojis on some devices + +So why Emojis are displayed as `X` / other characters insetad of actual Emojis for some of your users? +That is because this library renders Emojis based on Font and Font need to support Emoji character in order to render it. And those fonts are updated with every system release, but because there is a lot of Android device manufacturers who are actually using Android as a base to come up with their own UI layer then it is harder for them to keep up with system updates. +You can read more about it here [Emojipedia article link](https://blog.emojipedia.org/androids-emoji-problem/) + +So what can you do? +Apps such as `Slack` / `WhatsApp` are actually providing Emojis as little images so that they can be render regardless of operating system on mobile phone. The problem in `React-Native` is that there is no support for placing images in `Input` element at time of writing this. +Other solution is to limit number of possible emojis to most basic ones which are supported on most devices. Choosing emojis from `Unicode 6.0` seems like solid solution: [Unicode 6.0 Emojis List](https://emojipedia.org/unicode-6.0/) - you get tons of Emojis that are most likely to be correctly rendered across most of the devices. + +In `React-Native-Emoji-Input` you can limit emojis displayed by using `filterFunctions` prop and passing array of functions. Each of this function takes `emoji` as an single parameter and if every function passed in `filterFunctions` prop returns `true` then emoji will be included in final list. +We can use that to show only emojis which are part of `Unicode 6.0` or `Unicode 6.1` like so: +``` + filterFunctionByUnicode = emoji => { + return emoji.lib.added_in === "6.0" || emoji.lib.added_in === "6.1" + } + + this._emojiInput = emojiInput} + resetSearch={this.state.reset} + loggingFunction={this.verboseLoggingFunction.bind(this)} + verboseLoggingFunction={true} + filterFunctions={[this.filterFunctionByUnicode]} + /> +``` +This will render only emojis from Unicode 6.0 and Unicode 6.1 in input. \ No newline at end of file diff --git a/example/src/EmojiInput.js b/example/src/EmojiInput.js index a98deb9..6790591 100644 --- a/example/src/EmojiInput.js +++ b/example/src/EmojiInput.js @@ -287,6 +287,9 @@ class EmojiInput extends React.PureComponent { return e1.char !== e2.char; }); + this.filtered = this.props.filterFunctions.length === 0 + ? emoji + : _.pickBy(emoji, value => _.every(this.props.filterFunctions, fn => fn(value))) this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) @@ -298,7 +301,7 @@ class EmojiInput extends React.PureComponent { .map((v, k) => [ { char: category[k].key, categoryMarker: true, ...category[k] } ]); - _(emoji) + _(this.filtered) .values() .each(e => { if (_.has(categoryIndexMap, e.category)) { @@ -633,7 +636,8 @@ EmojiInput.defaultProps = { }, emojiFontSize: 40, categoryFontSize: 20, - resetSearch: false + resetSearch: false, + filterFunctions: [] }; EmojiInput.propTypes = { @@ -657,7 +661,8 @@ EmojiInput.propTypes = { enableFrequentlyUsedEmoji: PropTypes.bool, numFrequentlyUsedEmoji: PropTypes.number, defaultFrequentlyUsedEmoji: PropTypes.arrayOf(PropTypes.string), - resetSearch: PropTypes.bool + resetSearch: PropTypes.bool, + filterFunctions: PropTypes.arrayOf(PropTypes.func) }; const styles = { diff --git a/src/EmojiInput.js b/src/EmojiInput.js index a98deb9..6790591 100644 --- a/src/EmojiInput.js +++ b/src/EmojiInput.js @@ -287,6 +287,9 @@ class EmojiInput extends React.PureComponent { return e1.char !== e2.char; }); + this.filtered = this.props.filterFunctions.length === 0 + ? emoji + : _.pickBy(emoji, value => _.every(this.props.filterFunctions, fn => fn(value))) this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) @@ -298,7 +301,7 @@ class EmojiInput extends React.PureComponent { .map((v, k) => [ { char: category[k].key, categoryMarker: true, ...category[k] } ]); - _(emoji) + _(this.filtered) .values() .each(e => { if (_.has(categoryIndexMap, e.category)) { @@ -633,7 +636,8 @@ EmojiInput.defaultProps = { }, emojiFontSize: 40, categoryFontSize: 20, - resetSearch: false + resetSearch: false, + filterFunctions: [] }; EmojiInput.propTypes = { @@ -657,7 +661,8 @@ EmojiInput.propTypes = { enableFrequentlyUsedEmoji: PropTypes.bool, numFrequentlyUsedEmoji: PropTypes.number, defaultFrequentlyUsedEmoji: PropTypes.arrayOf(PropTypes.string), - resetSearch: PropTypes.bool + resetSearch: PropTypes.bool, + filterFunctions: PropTypes.arrayOf(PropTypes.func) }; const styles = { From b3176f63be45c8cb7320d16c574041b18fac82ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20G=C3=B3rak?= Date: Sun, 7 Apr 2019 10:58:18 +0200 Subject: [PATCH 2/4] Chain lodash call to filter emojis. Drop checking for 0 length of filterFunctions props as every will return true if collection is empty. Rename emoji param to emojis --- example/src/EmojiInput.js | 8 +++----- src/EmojiInput.js | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/example/src/EmojiInput.js b/example/src/EmojiInput.js index 6790591..e9bc7c3 100644 --- a/example/src/EmojiInput.js +++ b/example/src/EmojiInput.js @@ -282,14 +282,12 @@ class EmojiInput extends React.PureComponent { } }; - emojiRenderer = emoji => { + emojiRenderer = emojis => { let dataProvider = new DataProvider((e1, e2) => { return e1.char !== e2.char; }); - this.filtered = this.props.filterFunctions.length === 0 - ? emoji - : _.pickBy(emoji, value => _.every(this.props.filterFunctions, fn => fn(value))) + this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))) this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) @@ -301,7 +299,7 @@ class EmojiInput extends React.PureComponent { .map((v, k) => [ { char: category[k].key, categoryMarker: true, ...category[k] } ]); - _(this.filtered) + _(this.filteredEmojis) .values() .each(e => { if (_.has(categoryIndexMap, e.category)) { diff --git a/src/EmojiInput.js b/src/EmojiInput.js index 6790591..e9bc7c3 100644 --- a/src/EmojiInput.js +++ b/src/EmojiInput.js @@ -282,14 +282,12 @@ class EmojiInput extends React.PureComponent { } }; - emojiRenderer = emoji => { + emojiRenderer = emojis => { let dataProvider = new DataProvider((e1, e2) => { return e1.char !== e2.char; }); - this.filtered = this.props.filterFunctions.length === 0 - ? emoji - : _.pickBy(emoji, value => _.every(this.props.filterFunctions, fn => fn(value))) + this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))) this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) @@ -301,7 +299,7 @@ class EmojiInput extends React.PureComponent { .map((v, k) => [ { char: category[k].key, categoryMarker: true, ...category[k] } ]); - _(this.filtered) + _(this.filteredEmojis) .values() .each(e => { if (_.has(categoryIndexMap, e.category)) { From fcf92d5e5c6a13494e5bb94b793ddefbec11730d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20G=C3=B3rak?= Date: Sun, 7 Apr 2019 11:05:35 +0200 Subject: [PATCH 3/4] Add call to value at the end of Lodash chain. Add missing semicolon --- example/src/EmojiInput.js | 2 +- src/EmojiInput.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/src/EmojiInput.js b/example/src/EmojiInput.js index e9bc7c3..e09b8a7 100644 --- a/example/src/EmojiInput.js +++ b/example/src/EmojiInput.js @@ -287,7 +287,7 @@ class EmojiInput extends React.PureComponent { return e1.char !== e2.char; }); - this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))) + this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))).value(); this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) diff --git a/src/EmojiInput.js b/src/EmojiInput.js index e9bc7c3..e09b8a7 100644 --- a/src/EmojiInput.js +++ b/src/EmojiInput.js @@ -287,7 +287,7 @@ class EmojiInput extends React.PureComponent { return e1.char !== e2.char; }); - this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))) + this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))).value(); this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) From 4857b89e5296b1754020f0ca22e5cd5f4d2f3f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20G=C3=B3rak?= Date: Mon, 8 Apr 2019 22:05:27 +0200 Subject: [PATCH 4/4] Move filtering to one place in common chain. Remove filteredEmojis variable as it is not requried anymore --- example/src/EmojiInput.js | 4 ++-- src/EmojiInput.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/src/EmojiInput.js b/example/src/EmojiInput.js index e09b8a7..fbae677 100644 --- a/example/src/EmojiInput.js +++ b/example/src/EmojiInput.js @@ -287,7 +287,6 @@ class EmojiInput extends React.PureComponent { return e1.char !== e2.char; }); - this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))).value(); this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) @@ -299,8 +298,9 @@ class EmojiInput extends React.PureComponent { .map((v, k) => [ { char: category[k].key, categoryMarker: true, ...category[k] } ]); - _(this.filteredEmojis) + _(emojis) .values() + .filter(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))) .each(e => { if (_.has(categoryIndexMap, e.category)) { tempEmoji[categoryIndexMap[e.category].idx].push(e); diff --git a/src/EmojiInput.js b/src/EmojiInput.js index e09b8a7..fbae677 100644 --- a/src/EmojiInput.js +++ b/src/EmojiInput.js @@ -287,7 +287,6 @@ class EmojiInput extends React.PureComponent { return e1.char !== e2.char; }); - this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))).value(); this.emoji = []; let categoryIndexMap = _(category) .map((v, idx) => ({ ...v, idx })) @@ -299,8 +298,9 @@ class EmojiInput extends React.PureComponent { .map((v, k) => [ { char: category[k].key, categoryMarker: true, ...category[k] } ]); - _(this.filteredEmojis) + _(emojis) .values() + .filter(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))) .each(e => { if (_.has(categoryIndexMap, e.category)) { tempEmoji[categoryIndexMap[e.category].idx].push(e);