[MM-35909] Support for emoji 13 (#5497)

* support for emoji 13

* fix some tests

* more test fixed

* fix typo

* rename system to standard

* remove unused functions

* fix +1 emojis
This commit is contained in:
Guillermo Vayá
2021-06-30 00:41:11 +02:00
committed by GitHub
parent 15a311fcec
commit 1f843ddb4f
22 changed files with 11307 additions and 6820 deletions

View File

@@ -120,7 +120,8 @@ export function addRecentUsedEmojisInMessage(message) {
const unicode = emojiUnicode(emoji);
const index = EmojiIndicesByUnicode.get(unicode || '');
if (index) {
emojisAvailableWithMattermost.push(Emojis[index].aliases[0]);
const name = 'short_name' in Emojis[index] ? Emojis[index].short_name : Emojis[index].name;
emojisAvailableWithMattermost.push(name);
}
}
}
@@ -128,7 +129,8 @@ export function addRecentUsedEmojisInMessage(message) {
for (const emoji of namedEmojis) {
const index = EmojiIndicesByAlias.get(emoji.slice(1, -1));
if (index) {
emojisAvailableWithMattermost.push(Emojis[index].aliases[0]);
const name = 'short_name' in Emojis[index] ? Emojis[index].short_name : Emojis[index].name;
emojisAvailableWithMattermost.push(name);
}
}
}

View File

@@ -15,7 +15,6 @@ export interface ClientEmojisMix {
getCustomEmojiByName: (name: string) => Promise<CustomEmoji>;
getCustomEmojis: (page?: number, perPage?: number, sort?: string) => Promise<CustomEmoji[]>;
deleteCustomEmoji: (emojiId: string) => Promise<any>;
getSystemEmojiImageUrl: (filename: string) => string;
getCustomEmojiImageUrl: (id: string) => string;
searchCustomEmoji: (term: string, options?: Record<string, any>) => Promise<CustomEmoji[]>;
autocompleteCustomEmoji: (name: string) => Promise<CustomEmoji[]>;
@@ -75,10 +74,6 @@ const ClientEmojis = (superclass: any) => class extends superclass {
);
};
getSystemEmojiImageUrl = (filename: string) => {
return `${this.url}/static/emoji/${filename}.png`;
};
getCustomEmojiImageUrl = (id: string) => {
return `${this.getEmojiRoute(id)}/image`;
};

View File

@@ -46,9 +46,8 @@ describe('components/autocomplete/emoji_suggestion', () => {
});
test('searchEmojis should return the right values on fuse', () => {
const output1 = ['100', '1234', '1st_place_medal', '+1', '-1', 'clock1', 'clock10', 'clock1030', 'clock11', 'clock1130', 'clock12', 'clock1230', 'clock130', 'rage1', 'u7121', 'u7981'];
const output1 = ['100', '1234', '1st_place_medal', '+1', '-1', 'clock1', 'clock10', 'clock1030', 'clock11', 'clock1130', 'clock12', 'clock1230', 'clock130', 'u7121', 'u7981'];
const output2 = ['+1'];
const output3 = ['-1', 'e-mail', 'non-potable_water'];
const output4 = ['checkered_flag', 'ballot_box_with_check', 'heavy_check_mark', 'white_check_mark'];
const wrapper = shallowWithIntl(<EmojiSuggestion {...baseProps}/>);
@@ -67,12 +66,6 @@ describe('components/autocomplete/emoji_suggestion', () => {
expect(wrapper.state('dataSource')).toEqual(output2);
}, 100);
wrapper.instance().searchEmojis('-');
jest.runAllTimers();
setTimeout(() => {
expect(wrapper.state('dataSource')).toEqual(output3);
}, 100);
wrapper.instance().searchEmojis('check');
jest.runAllTimers();
setTimeout(() => {

View File

@@ -10,7 +10,7 @@ import {Client4} from '@client/rest';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {GlobalState} from '@mm-redux/types/store';
import {BuiltInEmojis, EmojiIndicesByAlias, Emojis} from '@utils/emojis';
import {EmojiIndicesByAlias, Emojis} from '@utils/emojis';
import Emoji from './emoji';
@@ -28,16 +28,9 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
let unicode;
let isCustomEmoji = false;
let displayTextOnly = false;
if (EmojiIndicesByAlias.has(emojiName) || BuiltInEmojis.includes(emojiName)) {
if (EmojiIndicesByAlias.has(emojiName)) {
const emoji = Emojis[EmojiIndicesByAlias.get(emojiName)!];
unicode = emoji.filename;
if (BuiltInEmojis.includes(emojiName)) {
if (serverUrl) {
imageUrl = Client4.getSystemEmojiImageUrl(emoji.filename);
} else {
displayTextOnly = true;
}
}
unicode = emoji.image;
} else if (customEmojis.has(emojiName) && serverUrl) {
const emoji = customEmojis.get(emojiName);
imageUrl = Client4.getCustomEmojiImageUrl(emoji!.id);

View File

@@ -72,7 +72,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
test('searchEmojis should return the right values on fuse', async () => {
const input = '1';
const output = ['100', '1234', '1st_place_medal', '+1', '-1', 'clock1', 'clock10', 'clock1030', 'clock11', 'clock1130', 'clock12', 'clock1230', 'clock130', 'rage1', 'u7121', 'u7981'];
const output = ['100', '1234', '1st_place_medal', '+1', '-1', 'clock1', 'clock10', 'clock1030', 'clock11', 'clock1130', 'clock12', 'clock1230', 'clock130', 'u7121', 'u7981'];
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
const result = wrapper.instance().searchEmojis(input);

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';

View File

@@ -51,16 +51,18 @@ export default class EmojiPickerRow extends Component {
);
}
const name = 'short_name' in emoji ? emoji.short_name : emoji.name;
return (
<TouchableOpacity
key={emoji.name}
key={name}
style={style}
onPress={() => {
this.props.onEmojiPress(emoji.name);
this.props.onEmojiPress(name);
}}
>
<Emoji
emojiName={emoji.name}
emojiName={name}
textStyle={styles.emojiText}
size={emojiSize}
/>

View File

@@ -8,7 +8,6 @@ import {getCustomEmojisByName as selectCustomEmojisByName} from '@mm-redux/selec
import {parseNeededCustomEmojisFromText} from '@mm-redux/utils/emoji_utils';
import {GetStateFunc, DispatchFunc, ActionFunc, ActionResult} from '@mm-redux/types/actions';
import {getSystemEmojis} from 'app/utils/emojis';
import {logError} from './errors';
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
@@ -86,9 +85,8 @@ export function getCustomEmojisInText(text: string): ActionFunc {
const state = getState();
const nonExistentEmoji = state.entities.emojis.nonExistentEmoji;
const customEmojisByName = selectCustomEmojisByName(state);
const systemEmojis: Set<string> = getSystemEmojis();
const emojisToLoad = parseNeededCustomEmojisFromText(text, systemEmojis, customEmojisByName, nonExistentEmoji);
const emojisToLoad = parseNeededCustomEmojisFromText(text, customEmojisByName, nonExistentEmoji);
return getCustomEmojisByName(Array.from(emojisToLoad))(dispatch, getState);
};

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import fs from 'fs';
import assert from 'assert';
@@ -548,21 +549,12 @@ describe('Actions.Posts', () => {
);
});
it('system emoji in post', () => {
assert.deepEqual(
Actions.getNeededCustomEmojis(state, [
{message: ':mattermost:'},
]),
new Set(),
);
});
it('mixed emojis in post', () => {
assert.deepEqual(
Actions.getNeededCustomEmojis(state, [
{message: ':mattermost: :name1: :name2: :name3:'},
]),
new Set(['name3']),
new Set(['mattermost', 'name3']),
);
});
@@ -634,7 +626,7 @@ describe('Actions.Posts', () => {
Actions.getNeededCustomEmojis(state, [
{message: '', props: {attachments: [{text: ':name4: :name1:', pretext: ':name3: :mattermost:', fields: [{value: ':name3:'}]}]}},
]),
new Set(['name3', 'name4']),
new Set(['name3', 'mattermost', 'name4']),
);
});

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import {Client4} from '@client/rest';
import {General, Preferences, Posts} from '@mm-redux/constants';
@@ -16,7 +17,6 @@ import {getUserIdFromChannelName} from '@mm-redux/utils/channel_utils';
import {parseNeededCustomEmojisFromText} from '@mm-redux/utils/emoji_utils';
import {isFromWebhook, isSystemMessage, shouldIgnorePost} from '@mm-redux/utils/post_utils';
import {isCombinedUserActivityPost} from '@mm-redux/utils/post_list';
import {getSystemEmojis} from 'app/utils/emojis';
import {getMyChannelMember, markChannelAsUnread, markChannelAsRead, markChannelAsViewed} from './channels';
import {getCustomEmojiByName, getCustomEmojisByName} from './emojis';
@@ -591,10 +591,6 @@ export function getCustomEmojiForReaction(name: string) {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
const nonExistentEmoji = getState().entities.emojis.nonExistentEmoji;
const customEmojisByName = selectCustomEmojisByName(getState());
const systemEmojis = getSystemEmojis();
if (systemEmojis.has(name)) {
return {data: true};
}
if (nonExistentEmoji.has(name)) {
return {data: true};
@@ -622,17 +618,11 @@ export function getReactionsForPost(postId: string) {
if (reactions && reactions.length > 0) {
const nonExistentEmoji = getState().entities.emojis.nonExistentEmoji;
const customEmojisByName = selectCustomEmojisByName(getState());
const systemEmojis = getSystemEmojis();
const emojisToLoad = new Set<string>();
reactions.forEach((r: Reaction) => {
const name = r.emoji_name;
if (systemEmojis.has(name)) {
// It's a system emoji, go the next match
return;
}
if (nonExistentEmoji.has(name)) {
// We've previously confirmed this is not a custom emoji
return;
@@ -1007,7 +997,6 @@ export function getNeededCustomEmojis(state: GlobalState, posts: Array<Post>): S
let customEmojisByName: Map<string, CustomEmoji>; // Populate this lazily since it's relatively expensive
const nonExistentEmoji = state.entities.emojis.nonExistentEmoji;
const systemEmojis = getSystemEmojis();
posts.forEach((post) => {
if (post.message.includes(':')) {
@@ -1015,7 +1004,7 @@ export function getNeededCustomEmojis(state: GlobalState, posts: Array<Post>): S
customEmojisByName = selectCustomEmojisByName(state);
}
const emojisFromPost = parseNeededCustomEmojisFromText(post.message, systemEmojis, customEmojisByName, nonExistentEmoji);
const emojisFromPost = parseNeededCustomEmojisFromText(post.message, customEmojisByName, nonExistentEmoji);
if (emojisFromPost.size > 0) {
customEmojisToLoad = new Set([...customEmojisToLoad, ...emojisFromPost]);
@@ -1031,7 +1020,7 @@ export function getNeededCustomEmojis(state: GlobalState, posts: Array<Post>): S
const attachmentText = buildPostAttachmentText(props.attachments);
if (attachmentText) {
const emojisFromAttachment = parseNeededCustomEmojisFromText(attachmentText, systemEmojis, customEmojisByName, nonExistentEmoji);
const emojisFromAttachment = parseNeededCustomEmojisFromText(attachmentText, customEmojisByName, nonExistentEmoji);
if (emojisFromAttachment.size > 0) {
customEmojisToLoad = new Set([...customEmojisToLoad, ...emojisFromAttachment]);

View File

@@ -3,16 +3,18 @@
export type EmojiCategory = (
| 'recent'
| 'people'
| 'nature'
| 'foods'
| 'activity'
| 'places'
| 'smileys-emotion'
| 'people-body'
| 'animals-nature'
| 'food-drink'
| 'travel-places'
| 'activities'
| 'objects'
| 'symbols'
| 'flags'
| 'custom'
);
export type CustomEmoji = {
id: string;
create_at: number;
@@ -22,13 +24,14 @@ export type CustomEmoji = {
name: string;
category: 'custom';
};
export type SystemEmoji = {
filename: string;
aliases: Array<string>;
export type StandardEmoji = {
image: string;
short_names: Array<string>;
short_name: string;
category: EmojiCategory;
batch: number;
};
export type Emoji = SystemEmoji | CustomEmoji;
export type Emoji = StandardEmoji | CustomEmoji;
export type EmojisState = {
customEmoji: {
[x: string]: CustomEmoji;

View File

@@ -10,7 +10,6 @@ describe('EmojiUtils', () => {
it('no emojis', () => {
const actual = EmojiUtils.parseNeededCustomEmojisFromText(
'This has no emojis',
new Set(),
new Map(),
new Map(),
);
@@ -22,7 +21,6 @@ describe('EmojiUtils', () => {
it('some emojis', () => {
const actual = EmojiUtils.parseNeededCustomEmojisFromText(
':this: :is_all: :emo-jis: :123:',
new Set(),
new Map(),
new Map(),
);
@@ -43,22 +41,9 @@ describe('EmojiUtils', () => {
assert.deepEqual(actual, expected);
});
it('system emojis', () => {
const actual = EmojiUtils.parseNeededCustomEmojisFromText(
':this: :is_all: :emo-jis: :123:',
new Set(['this', '123']),
new Map(),
new Map(),
);
const expected = new Set(['is_all', 'emo-jis']);
assert.deepEqual(actual, expected);
});
it('custom emojis', () => {
const actual = EmojiUtils.parseNeededCustomEmojisFromText(
':this: :is_all: :emo-jis: :123:',
new Set(),
new Map([['is_all', {name: 'is_all'}], ['emo-jis', {name: 'emo-jis'}]]),
new Map(),
);
@@ -70,7 +55,6 @@ describe('EmojiUtils', () => {
it('emojis that we\'ve already tried to load', () => {
const actual = EmojiUtils.parseNeededCustomEmojisFromText(
':this: :is_all: :emo-jis: :123:',
new Set(),
new Map(),
new Map([['this', {name: 'this'}], ['emo-jis', {name: 'emo-jis'}]]),
);

View File

@@ -1,27 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@client/rest';
import {Emoji, SystemEmoji, CustomEmoji} from '@mm-redux/types/emojis';
export function isSystemEmoji(emoji: Emoji): emoji is SystemEmoji {
return 'batch' in emoji;
}
import {Emoji, CustomEmoji} from '@mm-redux/types/emojis';
export function isCustomEmoji(emoji: Emoji): emoji is CustomEmoji {
return 'id' in emoji;
}
export function getEmojiImageUrl(emoji: Emoji): string {
if (isCustomEmoji(emoji)) {
return Client4.getEmojiRoute(emoji.id) + '/image';
}
const filename = emoji.filename || emoji.aliases[0];
return Client4.getSystemEmojiImageUrl(filename);
}
export function parseNeededCustomEmojisFromText(text: string, systemEmojis: Set<string>, customEmojisByName: Map<string, CustomEmoji>, nonExistentEmoji: Set<string>): Set<string> {
export function parseNeededCustomEmojisFromText(text: string, customEmojisByName: Map<string, CustomEmoji>, nonExistentEmoji: Set<string>): Set<string> {
if (!text.includes(':')) {
return new Set();
}
@@ -34,11 +20,6 @@ export function parseNeededCustomEmojisFromText(text: string, systemEmojis: Set<
continue;
}
if (systemEmojis.has(match[1])) {
// It's a system emoji, go the next match
continue;
}
if (nonExistentEmoji.has(match[1])) {
// We've previously confirmed this is not a custom emoji
continue;

View File

@@ -3,69 +3,38 @@
import {createSelector} from 'reselect';
import {t} from '@utils/i18n';
import {getCustomEmojisByName as selectCustomEmojisByName} from '@mm-redux/selectors/entities/emojis';
import {createIdsSelector} from '@mm-redux/utils/helpers';
import {BuiltInEmojis, CategoryNames, Emojis, EmojiIndicesByAlias, EmojiIndicesByCategory} from '@utils/emojis';
import {CategoryNames, CategoryTranslations, Emojis, EmojiIndicesByAlias, EmojiIndicesByCategory, CategoryMessage} from '@utils/emojis';
const categoryToI18n = {
activity: {
id: t('mobile.emoji_picker.activity'),
defaultMessage: 'ACTIVITY',
icon: 'basketball',
},
custom: {
id: t('mobile.emoji_picker.custom'),
defaultMessage: 'CUSTOM',
icon: 'emoticon-custom-outline',
},
flags: {
id: t('mobile.emoji_picker.flags'),
defaultMessage: 'FLAGS',
icon: 'flag-outline',
},
foods: {
id: t('mobile.emoji_picker.foods'),
defaultMessage: 'FOODS',
icon: 'food-apple',
},
nature: {
id: t('mobile.emoji_picker.nature'),
defaultMessage: 'NATURE',
icon: 'leaf-outline',
},
objects: {
id: t('mobile.emoji_picker.objects'),
defaultMessage: 'OBJECTS',
icon: 'lightbulb-outline',
},
people: {
id: t('mobile.emoji_picker.people'),
defaultMessage: 'PEOPLE',
icon: 'emoticon-happy-outline',
},
places: {
id: t('mobile.emoji_picker.places'),
defaultMessage: 'PLACES',
icon: 'airplane-variant',
},
recent: {
id: t('mobile.emoji_picker.recent'),
defaultMessage: 'RECENTLY USED',
icon: 'clock-outline',
},
symbols: {
id: t('mobile.emoji_picker.symbols'),
defaultMessage: 'SYMBOLS',
icon: 'heart-outline',
},
const icons = {
recent: 'clock-outline',
'smileys-emotion': 'emoticon-happy-outline',
'people-body': 'eye-outline',
'animals-nature': 'leaf-outline',
'food-drink': 'food-apple',
'travel-places': 'airplane-variant',
activities: 'basketball',
objects: 'lightbulb-outline',
symbols: 'heart-outline',
flags: 'flag-outline',
custom: 'emoticon-custom-outline',
};
const categoryToI18n = {};
CategoryNames.forEach((name) => {
categoryToI18n[name] = {
id: CategoryTranslations.get(name),
defaultMessage: CategoryMessage.get(name),
icon: icons[name],
};
});
function fillEmoji(indice) {
const emoji = Emojis[indice];
return {
name: emoji.aliases[0],
aliases: emoji.aliases,
name: 'short_name' in emoji ? emoji.short_name : emoji.name,
aliases: 'short_names' in emoji ? emoji.short_names : [],
};
}
@@ -87,47 +56,33 @@ export const selectEmojisBySection = createSelector(
selectCustomEmojisByName,
(state) => state.views.recentEmojis,
(customEmojis, recentEmojis) => {
const emoticons = CategoryNames.filter((name) => name !== 'custom' && name !== 'skintone').map((category) => {
const items = EmojiIndicesByCategory.get(category).map(fillEmoji);
const section = {
...categoryToI18n[category],
key: category,
data: items,
};
return section;
});
const customEmojiItems = [];
BuiltInEmojis.forEach((emoji) => {
customEmojiItems.push({
name: emoji,
});
});
for (const [key] of customEmojis) {
customEmojiItems.push({
name: key,
});
}
const recentItems = recentEmojis.map((emoji) => ({name: emoji}));
const filteredCategories = CategoryNames.filter((category) => category !== 'recent' || recentItems.length > 0);
emoticons.push({
...categoryToI18n.custom,
key: 'custom',
data: customEmojiItems,
const emoticons = filteredCategories.map((category) => {
// TODO: change default into user selected category once the user is able to choose it
const items = EmojiIndicesByCategory.get('default').get(category).map(fillEmoji);
const data = items;
if (category === 'custom') {
data.push(...customEmojiItems);
} else if (category === 'recent') {
data.push(...recentItems);
}
const section = {
...categoryToI18n[category],
key: category,
data,
};
return section;
});
if (recentEmojis.length) {
const items = recentEmojis.map((emoji) => ({name: emoji}));
emoticons.unshift({
...categoryToI18n.recent,
key: 'recent',
data: items,
});
}
return emoticons;
},
);

1
app/utils/emoji.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@ import emojiRegex from 'emoji-regex';
import {Emojis, EmojiIndicesByAlias} from './emojis';
const RE_NAMED_EMOJI = /(:([a-zA-Z0-9_-]+):)/g;
const RE_NAMED_EMOJI = /(:([a-zA-Z0-9_+-]+):)/g;
const RE_UNICODE_EMOJI = emojiRegex();
@@ -28,8 +28,6 @@ const RE_EMOTICON = {
mask: /(^|\s)(:-x)(?=$|\s)/gi, // :-x
heart: /(^|\s)(<3|&lt;3)(?=$|\s)/g, // <3
broken_heart: /(^|\s)(<\/3|&lt;&#x2F;3)(?=$|\s)/g, // </3
thumbsup: /(^|\s)(:\+1:)(?=$|\s)/g, // :+1:
thumbsdown: /(^|\s)(:-1:)(?=$|\s)/g, // :-1:
};
const MAX_JUMBO_EMOJIS = 4;
@@ -141,8 +139,25 @@ function doDefaultComparison(aName, bName) {
}
export function compareEmojis(emojiA, emojiB, searchedName) {
const aName = emojiA.name || (emojiA.aliases ? emojiA.aliases[0] : emojiA);
const bName = emojiB.name || (emojiB.aliases ? emojiB.aliases[0] : emojiB);
if (!emojiA) {
return 1;
}
if (!emojiB) {
return -1;
}
let aName;
if (typeof emojiA === 'string') {
aName = emojiA;
} else {
aName = 'short_name' in emojiA ? emojiA.short_name : emojiA.name;
}
let bName;
if (typeof emojiB === 'string') {
bName = emojiB;
} else {
bName = 'short_name' in emojiB ? emojiB.short_name : emojiB.name;
}
if (!searchedName) {
return doDefaultComparison(aName, bName);

View File

@@ -300,15 +300,15 @@ describe('doesMatchNamedEmoji', () => {
describe('compareEmojis', () => {
test('should sort an array of emojis alphabetically', () => {
const goatEmoji = {
name: 'goat',
short_name: 'goat',
};
const dashEmoji = {
name: 'dash',
short_name: 'dash',
};
const smileEmoji = {
name: 'smile',
short_name: 'smile',
};
const emojiArray = [goatEmoji, dashEmoji, smileEmoji];
@@ -319,15 +319,15 @@ describe('compareEmojis', () => {
test('should have partial matched emoji first', () => {
const goatEmoji = {
name: 'goat',
short_name: 'goat',
};
const dashEmoji = {
name: 'dash',
short_name: 'dash',
};
const smileEmoji = {
name: 'smile',
short_name: 'smile',
};
const emojiArray = [goatEmoji, dashEmoji, smileEmoji];
@@ -336,17 +336,17 @@ describe('compareEmojis', () => {
expect(emojiArray).toEqual([smileEmoji, dashEmoji, goatEmoji]);
});
test('should be able to sort on aliases', () => {
test('should be able to sort on short_name', () => {
const goatEmoji = {
aliases: [':goat:'],
short_name: ':goat:',
};
const dashEmoji = {
aliases: [':dash:'],
short_name: ':dash:',
};
const smileEmoji = {
aliases: [':smile:'],
short_name: ':smile:',
};
const emojiArray = [goatEmoji, dashEmoji, smileEmoji];
@@ -357,15 +357,15 @@ describe('compareEmojis', () => {
test('special case for thumbsup emoji should sort it before thumbsdown by aliases', () => {
const thumbsUpEmoji = {
aliases: ['+1'],
short_name: '+1',
};
const thumbsDownEmoji = {
aliases: ['-1'],
short_name: '-1',
};
const smileEmoji = {
aliases: ['smile'],
short_name: 'smile',
};
const emojiArray = [thumbsDownEmoji, thumbsUpEmoji, smileEmoji];
@@ -376,15 +376,15 @@ describe('compareEmojis', () => {
test('special case for thumbsup emoji should sort it before thumbsdown by names', () => {
const thumbsUpEmoji = {
name: 'thumbsup',
short_name: 'thumbsup',
};
const thumbsDownEmoji = {
name: 'thumbsdown',
short_name: 'thumbsdown',
};
const smileEmoji = {
name: 'smile',
short_name: 'smile',
};
const emojiArray = [thumbsDownEmoji, thumbsUpEmoji, smileEmoji];
@@ -395,15 +395,15 @@ describe('compareEmojis', () => {
test('special case for thumbsup emoji should sort it when emoji is matched', () => {
const thumbsUpEmoji = {
name: 'thumbsup',
short_name: 'thumbsup',
};
const thumbsDownEmoji = {
name: 'thumbsdown',
short_name: 'thumbsdown',
};
const smileEmoji = {
name: 'smile',
short_name: 'smile',
};
const emojiArray = [thumbsDownEmoji, thumbsUpEmoji, smileEmoji];
@@ -416,7 +416,7 @@ describe('compareEmojis', () => {
const thumbsUpEmoji = 'thumbsup';
const thumbsDownEmoji = 'thumbsdown';
const smileEmoji = {
name: 'smile',
short_name: 'smile',
};
const emojiArray = [thumbsDownEmoji, thumbsUpEmoji, smileEmoji];

File diff suppressed because one or more lines are too long

View File

@@ -141,6 +141,24 @@
"date_separator.yesterday": "Yesterday",
"edit_post.editPost": "Edit the post...",
"edit_post.save": "Save",
"emoji_picker.activities": "Activities",
"emoji_picker.animals-nature": "Animals & Nature",
"emoji_picker.custom": "Custom",
"emoji_picker.flags": "Flags",
"emoji_picker.food-drink": "Food & Drink",
"emoji_picker.objects": "Objects",
"emoji_picker.people-body": "People & Body",
"emoji_picker.recent": "Recent",
"emoji_picker.searchResults": "Search Results",
"emoji_picker.smileys-emotion": "Smileys & Emotion",
"emoji_picker.symbols": "Symbols",
"emoji_picker.travel-places": "Travel & Places",
"emoji_skin.dark_skin_tone": "dark skin tone",
"emoji_skin.default": "default skin tone",
"emoji_skin.light_skin_tone": "light skin tone",
"emoji_skin.medium_dark_skin_tone": "medium dark skin tone",
"emoji_skin.medium_light_skin_tone": "medium light skin tone",
"emoji_skin.medium_skin_tone": "medium skin tone",
"file_upload.fileAbove": "Files must be less than {max}",
"gallery.download_file": "Download file",
"gallery.footer.channel_name": "Shared in {channelName}",
@@ -278,18 +296,8 @@
"mobile.edit_channel": "Save",
"mobile.edit_post.title": "Editing Message",
"mobile.edit_profile.remove_profile_photo": "Remove Photo",
"mobile.emoji_picker.activity": "ACTIVITY",
"mobile.emoji_picker.custom": "CUSTOM",
"mobile.emoji_picker.flags": "FLAGS",
"mobile.emoji_picker.foods": "FOODS",
"mobile.emoji_picker.nature": "NATURE",
"mobile.emoji_picker.objects": "OBJECTS",
"mobile.emoji_picker.people": "PEOPLE",
"mobile.emoji_picker.places": "PLACES",
"mobile.emoji_picker.recent": "RECENTLY USED",
"mobile.emoji_picker.search.not_found_description": "Check the spelling or try another search.",
"mobile.emoji_picker.search.not_found_title": "No results found for \"{searchTerm}\"",
"mobile.emoji_picker.symbols": "SYMBOLS",
"mobile.error_handler.button": "Relaunch",
"mobile.error_handler.description": "\nTap relaunch to open the app again. After restart, you can report the problem from the settings menu.\n",
"mobile.error_handler.title": "Unexpected error occurred",

View File

@@ -693,7 +693,7 @@ SPEC CHECKSUMS:
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
FBReactNativeSpec: cef0cc6d50abc92e8cf52f140aa22b5371cfec0b
FBReactNativeSpec: 74185edd6e8e5cb35512747bd840f8f9ad2381ff
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
jail-monkey: 01cd0a75aa1034d08fd851869e6e6c3b063242d7
libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0