forked from Ivasoft/mattermost-mobile
[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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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(() => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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']),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'}]]),
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
1
app/utils/emoji.json
Normal file
File diff suppressed because one or more lines are too long
@@ -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|<3)(?=$|\s)/g, // <3
|
||||
broken_heart: /(^|\s)(<\/3|</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);
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -693,7 +693,7 @@ SPEC CHECKSUMS:
|
||||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
||||
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
||||
FBReactNativeSpec: cef0cc6d50abc92e8cf52f140aa22b5371cfec0b
|
||||
FBReactNativeSpec: 74185edd6e8e5cb35512747bd840f8f9ad2381ff
|
||||
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
|
||||
jail-monkey: 01cd0a75aa1034d08fd851869e6e6c3b063242d7
|
||||
libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
|
||||
|
||||
Reference in New Issue
Block a user