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

feat: support Spanish emojis #20828

Merged
merged 49 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
d38f775
feat: new emoji database
sangar-1028 Jun 15, 2023
08496e8
feat: update quick reactions(support es)
sangar-1028 Jun 15, 2023
51ac473
feat: all emojis and categories support es
sangar-1028 Jun 15, 2023
07b59e3
fix: import error
sangar-1028 Jun 15, 2023
8c18175
lint error
sangar-1028 Jun 15, 2023
91839ed
fix: merge main and fix conflicts
sangar-1028 Jun 15, 2023
d9835f9
fix: emoji suggestion bug
sangar-1028 Jun 15, 2023
b309e68
fix: Emoji and Report tests
sangar-1028 Jun 15, 2023
d3778b2
fix: conflicts
sangar-1028 Jun 15, 2023
23bb285
fix: nested property
sangar-1028 Jun 16, 2023
99b8343
fix: prettier
sangar-1028 Jun 16, 2023
f742447
fix: remove people-and-body category and corresponding icon, change c…
sangar-1028 Jun 16, 2023
2551336
fix: header emojis back to original
sangar-1028 Jun 17, 2023
61be178
fix: toggleReaction, quick emoji back to original
sangar-1028 Jun 17, 2023
d9820a1
Merge branch 'main' into fix/issue-16086
sangar-1028 Jun 17, 2023
e9eccaf
fix: remove wrong comments
sangar-1028 Jun 17, 2023
fe1d94b
fix: rename getEmojiName -> getLocalizedEmojiName
sangar-1028 Jun 17, 2023
1977161
fix: lint error
sangar-1028 Jun 17, 2023
ac4a5c2
fix: shortcode -> name
sangar-1028 Jun 17, 2023
a32d5a3
fix: toggle emoji reaction
sangar-1028 Jun 18, 2023
746c5af
fix: test and lint
sangar-1028 Jun 18, 2023
221c763
fix: remove shortcodes from tests
sangar-1028 Jun 18, 2023
fe06885
fix: remove differences
sangar-1028 Jun 18, 2023
555c8b4
fix: remove differences
sangar-1028 Jun 18, 2023
680fc05
fix: prettier error
sangar-1028 Jun 18, 2023
2dc64dd
fix: missing emoji
sangar-1028 Jun 18, 2023
fc90352
fix: emoji util function getEmojiName created
sangar-1028 Jun 18, 2023
18646ed
fix: prettier changes
sangar-1028 Jun 18, 2023
7ef92fc
fix: skintones order
sangar-1028 Jun 18, 2023
2ab63d5
fix: testing error due to skintones
sangar-1028 Jun 18, 2023
11f77ac
fix: use old English names
sangar-1028 Jun 18, 2023
2dbe1ed
feat: new emoji language files
sangar-1028 Jun 20, 2023
45dec32
feat: rename emoji language files
sangar-1028 Jun 20, 2023
361f3d9
fix: new emoji structure
sangar-1028 Jun 20, 2023
29070e3
fix: new structure of emoji
sangar-1028 Jun 20, 2023
70bc730
feat: support En as well for other locales
sangar-1028 Jun 20, 2023
353ace4
fix: tests
sangar-1028 Jun 20, 2023
71110cb
Merge branch 'main' into fix/issue-16086
sangar-1028 Jun 20, 2023
4bed2dd
fix: lint errors
sangar-1028 Jun 20, 2023
0df9868
fix: remove unused comments
sangar-1028 Jun 20, 2023
70cce1e
fix: conflicts
sangar-1028 Jun 28, 2023
467f49f
fix: conflicts
sangar-1028 Jun 28, 2023
a878f3a
fix: conflicts
sangar-1028 Jun 28, 2023
f9192ee
Merge branch 'main' into fix/issue-16086
sangar-1028 Jul 3, 2023
d7d91b8
fix: preferredLocaled added to BaseQuickEmojiReactions
sangar-1028 Jul 3, 2023
bf8e80e
fix: lint error
sangar-1028 Jul 3, 2023
6461f53
fix: conflicts
sangar-1028 Jul 3, 2023
cb4e9cf
fix: conflicts
sangar-1028 Jul 5, 2023
a3cd2c9
fix: rename emojiToReact -> reactionEmoji
sangar-1028 Jul 5, 2023
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
18,921 changes: 14,905 additions & 4,016 deletions assets/emojis.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions assets/images/emojiCategoryIcons/people.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -1331,20 +1331,20 @@ const CONST = {

QUICK_REACTIONS: [
{
name: '+1',
shortcode: {en: '+1', es: '+1'},
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you stick to original structure? I don't see any reason why name should be changed to shortcode

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is for clarity. They are not names of emojis, just short codes of them. I mentioned this in issue before

Copy link
Contributor

Choose a reason for hiding this comment

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

Strictly, they are "short name", not code.
If I follow you, code = '😄', short code = 'smile',
Why code length should be smaller than short code length?
As we don't support name for now, we can just use name instead of short name.
So let's keep original structure.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that the name should always be in english. In the database we save the reactions like this:

{"emoji":"heart","users":[{"accountID":8505565,"skinTone":-1}...]

If we start changing that then you would be able to react with the same emoji in both english and spanish.

code: '👍',
types: ['👍🏿', '👍🏾', '👍🏽', '👍🏼', '👍🏻'],
types: ['👍🏻', '👍🏼', '👍🏽', '👍🏾', '👍🏿'],
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB but why orders changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In ND app, their order is the same as is. That's the reason I reversed the order. I think this is reasonable.

image

Copy link
Contributor

Choose a reason for hiding this comment

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

I am also confused. As far as I can tell the types are already displayed in that order, so I don't understand why we need to change this

Copy link
Contributor Author

@dummy-1111 dummy-1111 Jun 17, 2023

Choose a reason for hiding this comment

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

As you can see below, skintone numbering is weird. I think this change is reasonable.

App/assets/emojis.js

Lines 56 to 81 in 83ab016

const skinTones = [
{
code: '🖐',
skinTone: -1,
},
{
code: '🖐🏻',
skinTone: 4,
},
{
code: '🖐🏼',
skinTone: 3,
},
{
code: '🖐🏽',
skinTone: 2,
},
{
code: '🖐🏾',
skinTone: 1,
},
{
code: '🖐🏿',
skinTone: 0,
},
];

Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure that this order change doesn't cause any regressions even if it's minor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah. I'm sure

},
{
name: 'heart',
shortcode: {en: 'heart', es: 'corazón'},
code: '❤️',
},
{
name: 'joy',
shortcode: {en: 'joy', es: 'alegría'},
code: '😂',
},
{
name: 'fire',
shortcode: {en: 'fire', es: 'fuego'},
code: '🔥',
},
],
Expand Down
4 changes: 2 additions & 2 deletions src/components/EmojiPicker/CategoryShortcutBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const propTypes = {
/** The emojis consisting emoji code and indices that the icons should link to */
headerEmojis: PropTypes.arrayOf(
PropTypes.shape({
code: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here. I don't see any reason of code -> name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Headers has no code unlike emojis. It has only name and icon. I wanted to add meanings to the properties

Copy link
Contributor

Choose a reason for hiding this comment

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

We already have localized strings here:

App/src/languages/en.js

Lines 1221 to 1233 in edf529e

headers: {
frequentlyUsed: 'Frequently Used',
smileysAndEmotion: 'Smileys & Emotion',
peopleAndBody: 'People & Body',
animalsAndNature: 'Animals & Nature',
foodAndDrink: 'Food & Drinks',
travelAndPlaces: 'Travel & Places',
activities: 'Activities',
objects: 'Objects',
symbols: 'Symbols',
flags: 'Flags',
},
},

We can still use frequentlyUsed, etc for code.

Copy link
Contributor

Choose a reason for hiding this comment

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

The current Spanish copies are already verified by marketing team and cannot be changed.

Before:

Screenshot 2023-06-16 at 7 54 26 PM

After:

Screenshot 2023-06-16 at 7 54 07 PM

Please revert back to original structure where header code is smileysAndEmotion and copies are localized in en.js and es.js

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'll change the headers back to original Spanish.
But let's keep the current emoji data structure as is. We have all Spanish emojis in its own structure, and we treat headers as emojis as well. Why don't we translate headers in the same place? I don't understand why headers should be in en/es.js. We can safely remove all emojis from en/es.js. This is more clear and reasonable, I think.

Copy link
Contributor

Choose a reason for hiding this comment

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

@stitesExpensify do you agree with this new structure?

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 think we should think about extension of supported languages, not about original structure.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused @s-alves10 what is the benefit of this change? In the future we're going to add other files like de.js which with have german translations etc. for the whole app, so it seems like keeping the current structure makes more sense than changing it

index: PropTypes.number.isRequired,
icon: PropTypes.func.isRequired,
}),
Expand All @@ -27,7 +27,7 @@ function CategoryShortcutBar(props) {
icon={headerEmoji.icon}
onPress={() => props.onPress(headerEmoji.index)}
key={`categoryShortcut${i}`}
code={headerEmoji.code}
name={headerEmoji.name}
/>
))}
</View>
Expand Down
6 changes: 3 additions & 3 deletions src/components/EmojiPicker/CategoryShortcutButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import themeColors from '../../styles/themes/default';
import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback';

const propTypes = {
/** The emoji code of the category header */
code: PropTypes.string.isRequired,
/** The emoji name of the category header */
name: PropTypes.string.isRequired,

/** The icon representation of the category that this button links to */
icon: PropTypes.func.isRequired,
Expand All @@ -34,7 +34,7 @@ class CategoryShortcutButton extends PureComponent {
render() {
return (
<Tooltip
text={this.props.translate(`emojiPicker.headers.${this.props.code}`)}
text={this.props.name}
shiftVertical={-4}
>
<PressableWithoutFeedback
Expand Down
13 changes: 7 additions & 6 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ class EmojiPickerMenu extends Component {

// If we're on Windows, don't display the flag emojis (the last category),
// since Windows doesn't support them
const flagHeaderIndex = _.findIndex(emojis, (emoji) => emoji.header && emoji.code === 'flags');
const flagHeaderIndex = _.findIndex(emojis, (emoji) => emoji.header && _.get(emoji, ['name', 'en']) === 'Flags');
this.emojis =
getOperatingSystem() === CONST.OS.WINDOWS
? EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis.slice(0, flagHeaderIndex))
: EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis);

// Get the header emojis along with the code, index and icon.
// index is the actual header index starting at the first emoji and counting each one
this.headerEmojis = EmojiUtils.getHeaderEmojis(this.emojis);
this.headerEmojis = EmojiUtils.getHeaderEmojis(this.emojis, this.props.preferredLocale);

// This is the indices of each header's Row
// The positions are static, and are calculated as index/numColumns (8 in our case)
Expand Down Expand Up @@ -395,7 +395,7 @@ class EmojiPickerMenu extends Component {
this.setFirstNonHeaderIndex(this.emojis);
return;
}
const newFilteredEmojiList = EmojiUtils.suggestEmojis(`:${normalizedSearchTerm}`, this.emojis.length);
const newFilteredEmojiList = EmojiUtils.suggestEmojis(`:${normalizedSearchTerm}`, this.props.preferredLocale, this.emojis.length);

// Remove sticky header indices. There are no headers while searching and we don't want to make emojis sticky
this.setState({filteredEmojis: newFilteredEmojiList, headerIndices: [], highlightedIndex: 0});
Expand Down Expand Up @@ -426,10 +426,11 @@ class EmojiPickerMenu extends Component {
* Return a unique key for each emoji item
*
* @param {Object} item
* @param {Number} index
* @returns {String}
*/
keyExtractor(item) {
return `emoji_picker_${item.code}`;
keyExtractor(item, index) {
return `emoji_picker_${item.code}_${index}`;
}

/**
Expand All @@ -450,7 +451,7 @@ class EmojiPickerMenu extends Component {
if (header) {
return (
<View style={styles.emojiHeaderContainer}>
<Text style={styles.textLabelSupporting}>{this.props.translate(`emojiPicker.headers.${code}`)}</Text>
<Text style={styles.textLabelSupporting}>{item.name[this.props.preferredLocale]}</Text>
</View>
);
}
Expand Down
8 changes: 4 additions & 4 deletions src/components/EmojiPicker/EmojiPickerMenu/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class EmojiPickerMenu extends Component {

// Get the header emojis along with the code, index and icon.
// index is the actual header index starting at the first emoji and counting each one
this.headerEmojis = EmojiUtils.getHeaderEmojis(this.emojis);
this.headerEmojis = EmojiUtils.getHeaderEmojis(this.emojis, this.props.preferredLocale);

// This is the indices of each header's Row
// The positions are static, and are calculated as index/numColumns (8 in our case)
Expand Down Expand Up @@ -93,7 +93,7 @@ class EmojiPickerMenu extends Component {

return;
}
const newFilteredEmojiList = EmojiUtils.suggestEmojis(`:${normalizedSearchTerm}`, this.emojis.length);
const newFilteredEmojiList = EmojiUtils.suggestEmojis(`:${normalizedSearchTerm}`, this.props.preferredLocale, this.emojis.length);

this.setState({
filteredEmojis: newFilteredEmojiList,
Expand Down Expand Up @@ -150,7 +150,7 @@ class EmojiPickerMenu extends Component {
* @returns {String}
*/
keyExtractor(item, index) {
return `${index}${item.code}`;
return `emoji_picker_${item.code}_${index}`;
}

/**
Expand All @@ -170,7 +170,7 @@ class EmojiPickerMenu extends Component {
if (item.header) {
return (
<View style={styles.emojiHeaderContainer}>
<Text style={styles.textLabelSupporting}>{this.props.translate(`emojiPicker.headers.${item.code}`)}</Text>
<Text style={styles.textLabelSupporting}>{item.name[this.props.preferredLocale]}</Text>
</View>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Reactions/MiniQuickEmojiReactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ function MiniQuickEmojiReactions(props) {
<View style={styles.flexRow}>
{_.map(CONST.QUICK_REACTIONS, (emoji) => (
<BaseMiniContextMenuItem
key={emoji.name}
key={emoji.code}
isDelayButtonStateComplete={false}
tooltipText={`:${emoji.name}:`}
tooltipText={`:${emoji.shortcode[props.preferredLocale]}:`}
onPress={Session.checkIfActionIsAllowed(() => props.onEmojiSelected(emoji))}
>
<Text style={[styles.miniQuickEmojiReactionText, styles.userSelectNone]}>{EmojiUtils.getPreferredEmojiCode(emoji, props.preferredSkinTone)}</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ function BaseQuickEmojiReactions(props) {
<View style={styles.quickReactionsContainer}>
{_.map(CONST.QUICK_REACTIONS, (emoji) => (
<Tooltip
text={`:${emoji.name}:`}
key={emoji.name}
text={`:${emoji.shortcode[props.preferredLocale]}:`}
key={emoji.code}
>
<View>
<EmojiReactionBubble
Expand Down
8 changes: 5 additions & 3 deletions src/components/Reactions/ReportActionItemReactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import EmojiReactionBubble from './EmojiReactionBubble';
import emojis from '../../../assets/emojis';
import AddReactionBubble from './AddReactionBubble';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails';
import withLocalize from '../withLocalize';
import compose from '../../libs/compose';
import * as Report from '../../libs/actions/Report';
import Tooltip from '../Tooltip';
import ReactionTooltipContent from './ReactionTooltipContent';
Expand Down Expand Up @@ -59,7 +61,7 @@ function ReportActionItemReactions(props) {
{_.map(reactionsWithCount, (reaction) => {
const reactionCount = reaction.users.length;
const reactionUsers = _.map(reaction.users, (sender) => sender.accountID);
const emoji = _.find(emojis, (e) => e.name === reaction.emoji);
const emoji = _.find(emojis, (e) => _.get(e, ['shortcode', 'en'], '') === reaction.emoji);
const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emoji, reaction.users);
const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers);

Expand All @@ -75,7 +77,7 @@ function ReportActionItemReactions(props) {
<Tooltip
renderTooltipContent={() => (
<ReactionTooltipContent
emojiName={reaction.emoji}
emojiName={EmojiUtils.getEmojiName(reaction.emoji, props.preferredLocale)}
emojiCodes={emojiCodes}
accountIDs={reactionUsers}
currentUserPersonalDetails={props.currentUserPersonalDetails}
Expand Down Expand Up @@ -106,4 +108,4 @@ function ReportActionItemReactions(props) {
ReportActionItemReactions.displayName = 'ReportActionItemReactions';
ReportActionItemReactions.propTypes = propTypes;
ReportActionItemReactions.defaultProps = defaultProps;
export default withCurrentUserPersonalDetails(ReportActionItemReactions);
export default compose(withLocalize, withCurrentUserPersonalDetails)(ReportActionItemReactions);
59 changes: 39 additions & 20 deletions src/libs/EmojiTrie.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
import _ from 'underscore';
import emojis from '../../assets/emojis';
import Trie from './Trie';
import Timing from './actions/Timing';
import CONST from '../CONST';

Timing.start(CONST.TIMING.TRIE_INITIALIZATION);

const emojisTrie = new Trie();
const supportedLanguages = ['en', 'es'];

// Inserting all emojis into the Trie object
for (let i = 0; i < emojis.length; i++) {
if (emojis[i].name) {
const node = emojisTrie.search(emojis[i].name);
function createTrie(lang = 'en') {
const trie = new Trie();

// Inserting all emojis into the Trie object
_.forEach(emojis, (item) => {
if (item.header) {
return;
}

const shortcode = item.shortcode[lang];
if (!shortcode) {
return;
}

const node = trie.search(shortcode);
if (!node) {
emojisTrie.add(emojis[i].name, {code: emojis[i].code, types: emojis[i].types, suggestions: []});
trie.add(shortcode, {code: item.code, types: item.types, shortcode: item.shortcode, suggestions: []});
} else {
emojisTrie.update(emojis[i].name, {code: emojis[i].code, types: emojis[i].types, suggestions: node.metaData.suggestions});
trie.update(shortcode, {code: item.code, types: item.types, shortcode: item.shortcode, suggestions: node.metaData.suggestions});
}

if (emojis[i].keywords) {
for (let j = 0; j < emojis[i].keywords.length; j++) {
const keywordNode = emojisTrie.search(emojis[i].keywords[j]);
if (!keywordNode) {
emojisTrie.add(emojis[i].keywords[j], {suggestions: [{code: emojis[i].code, types: emojis[i].types, name: emojis[i].name}]});
} else {
emojisTrie.update(emojis[i].keywords[j], {
...keywordNode.metaData,
suggestions: [...keywordNode.metaData.suggestions, {code: emojis[i].code, types: emojis[i].types, name: emojis[i].name}],
});
}
const keywords = item.keywords[lang];
for (let j = 0; j < keywords.length; j++) {
const keywordNode = trie.search(keywords[j]);
if (!keywordNode) {
trie.add(keywords[j], {suggestions: [{code: item.code, types: item.types, name: shortcode, shortcode: item.shortcode}]});
} else {
trie.update(keywords[j], {
...keywordNode.metaData,
suggestions: [...keywordNode.metaData.suggestions, {code: item.code, types: item.types, name: shortcode, shortcode: item.shortcode}],
});
}
}
}
});

return trie;
}

const emojiTrie = {};
_.forEach(supportedLanguages, (lang) => {
emojiTrie[lang] = createTrie(lang);
});

Timing.end(CONST.TIMING.TRIE_INITIALIZATION);

export default emojisTrie;
export default emojiTrie;
Loading