Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add filterFunctions prop to enable emojis filtering #34

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
}

<EmojiInput
onEmojiSelected={this.handleEmojiSelected}
ref={emojiInput => 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.
11 changes: 7 additions & 4 deletions example/src/EmojiInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,12 @@ class EmojiInput extends React.PureComponent {
}
};

emojiRenderer = emoji => {
emojiRenderer = emojis => {
let dataProvider = new DataProvider((e1, e2) => {
return e1.char !== e2.char;
});

this.filteredEmojis = _(emojis).pickBy(emoji => _.every(this.props.filterFunctions, fn => fn(emoji))).value();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to assign filteredEmojis to this component? The value will only be used inside the function, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filteredEmoji is not needed anymore so I have removed that.

this.emoji = [];
let categoryIndexMap = _(category)
.map((v, idx) => ({ ...v, idx }))
Expand All @@ -298,7 +299,7 @@ class EmojiInput extends React.PureComponent {
.map((v, k) => [
{ char: category[k].key, categoryMarker: true, ...category[k] }
]);
_(emoji)
_(this.filteredEmojis)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do the following

_(emojis)
  .values()
  .filter(emoji => _.every(this.props.filterFunctions, fn => fn(emoji)))
  .each(e => {
  ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed 👍

.values()
.each(e => {
if (_.has(categoryIndexMap, e.category)) {
Expand Down Expand Up @@ -633,7 +634,8 @@ EmojiInput.defaultProps = {
},
emojiFontSize: 40,
categoryFontSize: 20,
resetSearch: false
resetSearch: false,
filterFunctions: []
};

EmojiInput.propTypes = {
Expand All @@ -657,7 +659,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 = {
Expand Down
11 changes: 7 additions & 4 deletions src/EmojiInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,12 @@ class EmojiInput extends React.PureComponent {
}
};

emojiRenderer = emoji => {
emojiRenderer = emojis => {
let dataProvider = new DataProvider((e1, e2) => {
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 }))
Expand All @@ -298,7 +299,7 @@ class EmojiInput extends React.PureComponent {
.map((v, k) => [
{ char: category[k].key, categoryMarker: true, ...category[k] }
]);
_(emoji)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename it to emojis

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the param of function to emojis instead of emoji - let me know if this is what You meant.

_(this.filteredEmojis)
.values()
.each(e => {
if (_.has(categoryIndexMap, e.category)) {
Expand Down Expand Up @@ -633,7 +634,8 @@ EmojiInput.defaultProps = {
},
emojiFontSize: 40,
categoryFontSize: 20,
resetSearch: false
resetSearch: false,
filterFunctions: []
};

EmojiInput.propTypes = {
Expand All @@ -657,7 +659,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 = {
Expand Down