Skip to content

Commit

Permalink
Merge pull request #15513 from akshayasalvi/emoji-category-bar-tooltip
Browse files Browse the repository at this point in the history
Emoji Category Shortcuts Bar Tooltip
  • Loading branch information
stitesExpensify authored Mar 6, 2023
2 parents ace7d0a + fea8a48 commit 95cdfdb
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 52 deletions.
17 changes: 17 additions & 0 deletions assets/emojis.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import Smiley from './images/emoji.svg';
import AnimalsAndNature from './images/emojiCategoryIcons/plant.svg';
import FoodAndDrink from './images/emojiCategoryIcons/hamburger.svg';
import TravelAndPlaces from './images/emojiCategoryIcons/plane.svg';
import Activities from './images/emojiCategoryIcons/soccer-ball.svg';
import Objects from './images/emojiCategoryIcons/light-bulb.svg';
import Symbols from './images/emojiCategoryIcons/peace-sign.svg';
import Flags from './images/emojiCategoryIcons/flag.svg';

/*
* This list is generated from the code here https://github.com/github/gemoji/blob/master/db/emoji.json
* Each code is then converted to hex by replacing the "U+" with "0x"
Expand Down Expand Up @@ -68,6 +77,7 @@ const emojis = [
{
code: 'smileysAndEmotion',
header: true,
icon: Smiley,
},
{
name: 'grinning',
Expand Down Expand Up @@ -6965,6 +6975,7 @@ const emojis = [
{
code: 'animalsAndNature',
header: true,
icon: AnimalsAndNature,
},
{
name: 'monkey_face',
Expand Down Expand Up @@ -8138,6 +8149,7 @@ const emojis = [
{
code: 'foodAndDrink',
header: true,
icon: FoodAndDrink,
},
{
name: 'grapes',
Expand Down Expand Up @@ -9315,6 +9327,7 @@ const emojis = [
{
code: 'travelAndPlaces',
header: true,
icon: TravelAndPlaces,
},
{
name: 'earth_africa',
Expand Down Expand Up @@ -11434,6 +11447,7 @@ const emojis = [
{
code: 'activities',
header: true,
icon: Activities,
},
{
name: 'jack_o_lantern',
Expand Down Expand Up @@ -12271,6 +12285,7 @@ const emojis = [
{
code: 'objects',
header: true,
icon: Objects,
},
{
name: 'eyeglasses',
Expand Down Expand Up @@ -14600,6 +14615,7 @@ const emojis = [
{
code: 'symbols',
header: true,
icon: Symbols,
},
{
name: 'atm',
Expand Down Expand Up @@ -16590,6 +16606,7 @@ const emojis = [
{
code: 'flags',
header: true,
icon: Flags,
},
{
name: 'checkered_flag',
Expand Down
51 changes: 18 additions & 33 deletions src/components/EmojiPicker/CategoryShortcutBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,33 @@ import PropTypes from 'prop-types';
import {View} from 'react-native';
import _ from 'underscore';
import styles from '../../styles/styles';
import FrequentlyUsed from '../../../assets/images/history.svg';
import Smiley from '../../../assets/images/emoji.svg';
import AnimalsAndNature from '../../../assets/images/emojiCategoryIcons/plant.svg';
import FoodAndDrink from '../../../assets/images/emojiCategoryIcons/hamburger.svg';
import TravelAndPlaces from '../../../assets/images/emojiCategoryIcons/plane.svg';
import Activities from '../../../assets/images/emojiCategoryIcons/soccer-ball.svg';
import Objects from '../../../assets/images/emojiCategoryIcons/light-bulb.svg';
import Symbols from '../../../assets/images/emojiCategoryIcons/peace-sign.svg';
import Flags from '../../../assets/images/emojiCategoryIcons/flag.svg';
import CategoryShortcutButton from './CategoryShortcutButton';
import getOperatingSystem from '../../libs/getOperatingSystem';
import CONST from '../../CONST';

const propTypes = {
/** The function to call when an emoji is selected */
onPress: PropTypes.func.isRequired,

/** The indices that the icons should link to */
headerIndices: PropTypes.arrayOf(PropTypes.number).isRequired,
/** The emojis consisting emoji code and indices that the icons should link to */
headerEmojis: PropTypes.arrayOf(PropTypes.shape({
code: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
icon: PropTypes.func.isRequired,
})).isRequired,
};

const CategoryShortcutBar = (props) => {
const icons = [Smiley, AnimalsAndNature, FoodAndDrink, TravelAndPlaces, Activities, Objects, Symbols, Flags];
const CategoryShortcutBar = props => (
<View style={[styles.pt2, styles.ph4, styles.flexRow]}>
{_.map(props.headerEmojis, (headerEmoji, i) => (
<CategoryShortcutButton
icon={headerEmoji.icon}
onPress={() => props.onPress(headerEmoji.index)}
key={`categoryShortcut${i}`}
code={headerEmoji.code}
/>
))}
</View>
);

// If the user has frequently used emojis, there will be 9 headers, otherwise there will be 8
// Or for Windows OS there will be 8 headers, otherwise there will be 7
if (props.headerIndices.length === 9 || (getOperatingSystem() === CONST.OS.WINDOWS && props.headerIndices.length === 8)) {
icons.unshift(FrequentlyUsed);
}

return (
<View style={[styles.pt2, styles.ph4, styles.flexRow]}>
{_.map(props.headerIndices, (headerIndex, i) => (
<CategoryShortcutButton
icon={icons[i]}
onPress={() => props.onPress(headerIndex)}
key={`categoryShortcut${i}`}
/>
))}
</View>
);
};
CategoryShortcutBar.propTypes = propTypes;
CategoryShortcutBar.displayName = 'CategoryShortcutBar';

Expand Down
19 changes: 15 additions & 4 deletions src/components/EmojiPicker/CategoryShortcutButton.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Pressable, View} from 'react-native';
import {Pressable} from 'react-native';
import Icon from '../Icon';
import Tooltip from '../Tooltip';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
import variables from '../../styles/variables';
import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import getButtonState from '../../libs/getButtonState';
import themeColors from '../../styles/themes/default';

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

/** The icon representation of the category that this button links to */
icon: PropTypes.func.isRequired,

/** The function to call when an emoji is selected */
onPress: PropTypes.func.isRequired,

...withLocalizePropTypes,
};

class CategoryShortcutButton extends PureComponent {
Expand All @@ -36,18 +43,22 @@ class CategoryShortcutButton extends PureComponent {
this.state.isHighlighted && styles.emojiItemHighlighted,
])}
>
<View style={styles.alignSelfCenter}>
<Tooltip
containerStyles={[styles.flex1, styles.alignSelfStretch, styles.alignItemsCenter, styles.justifyContentCenter]}
text={this.props.translate(`emojiPicker.headers.${this.props.code}`)}
shiftVertical={-4}
>
<Icon
fill={themeColors.icon}
src={this.props.icon}
height={variables.iconSizeNormal}
width={variables.iconSizeNormal}
/>
</View>
</Tooltip>
</Pressable>
);
}
}
CategoryShortcutButton.propTypes = propTypes;

export default CategoryShortcutButton;
export default withLocalize(CategoryShortcutButton);
9 changes: 5 additions & 4 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ class EmojiPickerMenu extends Component {
? EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis.slice(0, flagHeaderIndex), this.props.frequentlyUsedEmojis)
: EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis, this.props.frequentlyUsedEmojis);

// This is the actual header index starting at the first emoji and counting each one
this.headerIndices = EmojiUtils.getHeaderIndices(this.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 is the indices of each header's Row
// The positions are static, and are calculated as index/numColumns (8 in our case)
// This is because each row of 8 emojis counts as one index to the flatlist
this.headerRowIndices = _.map(this.headerIndices, headerIndex => Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW));
this.headerRowIndices = _.map(this.headerEmojis, headerEmoji => Math.floor(headerEmoji.index / CONST.EMOJI_NUM_PER_ROW));

this.filterEmojis = _.debounce(this.filterEmojis.bind(this), 300);
this.highlightAdjacentEmoji = this.highlightAdjacentEmoji.bind(this);
Expand Down Expand Up @@ -491,7 +492,7 @@ class EmojiPickerMenu extends Component {
)}
{!isFiltered && (
<CategoryShortcutBar
headerIndices={this.headerIndices}
headerEmojis={this.headerEmojis}
onPress={this.scrollToHeader}
/>
)}
Expand Down
9 changes: 5 additions & 4 deletions src/components/EmojiPicker/EmojiPickerMenu/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ class EmojiPickerMenu extends Component {

this.emojis = EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis, this.props.frequentlyUsedEmojis);

// This is the actual header index starting at the first emoji and counting each one
this.headerIndices = EmojiUtils.getHeaderIndices(this.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 is the indices of each header's Row
// The positions are static, and are calculated as index/numColumns (8 in our case)
// This is because each row of 8 emojis counts as one index to the flatlist
this.headerRowIndices = _.map(this.headerIndices, headerIndex => Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW));
this.headerRowIndices = _.map(this.headerEmojis, headerEmoji => Math.floor(headerEmoji.index / CONST.EMOJI_NUM_PER_ROW));

this.renderItem = this.renderItem.bind(this);
this.isMobileLandscape = this.isMobileLandscape.bind(this);
Expand Down Expand Up @@ -151,7 +152,7 @@ class EmojiPickerMenu extends Component {
<View style={styles.emojiPickerContainer}>
<View>
<CategoryShortcutBar
headerIndices={this.headerIndices}
headerEmojis={this.headerEmojis}
onPress={this.scrollToHeader}
/>
</View>
Expand Down
12 changes: 7 additions & 5 deletions src/libs/EmojiUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Str from 'expensify-common/lib/str';
import CONST from '../CONST';
import * as User from './actions/User';
import emojisTrie from './EmojiTrie';
import FrequentlyUsed from '../../assets/images/history.svg';

/**
* Get the unicode code of an emoji in base 16.
Expand Down Expand Up @@ -82,17 +83,17 @@ function containsOnlyEmojis(message) {
}

/**
* Get the header indices based on the max emojis per row
* Get the header emojis with their code, icon and index
* @param {Object[]} emojis
* @returns {Number[]}
* @returns {Object[]}
*/
function getHeaderIndices(emojis) {
function getHeaderEmojis(emojis) {
const headerIndices = [];
_.each(emojis, (emoji, index) => {
if (!emoji.header) {
return;
}
headerIndices.push(index);
headerIndices.push({code: emoji.code, index, icon: emoji.icon});
});
return headerIndices;
}
Expand Down Expand Up @@ -149,6 +150,7 @@ function mergeEmojisWithFrequentlyUsedEmojis(emojis, frequentlyUsedEmojis = [])
let allEmojis = [{
header: true,
code: 'frequentlyUsed',
icon: FrequentlyUsed,
}];

allEmojis = allEmojis.concat(frequentlyUsedEmojis, emojis);
Expand Down Expand Up @@ -246,7 +248,7 @@ function suggestEmojis(text, limit = 5) {
}

export {
getHeaderIndices,
getHeaderEmojis,
mergeEmojisWithFrequentlyUsedEmojis,
addToFrequentlyUsedEmojis,
containsOnlyEmojis,
Expand Down
3 changes: 1 addition & 2 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -1536,9 +1536,8 @@ const styles = {
categoryShortcutButton: {
flex: 1,
borderRadius: 8,
paddingTop: 2,
paddingBottom: 2,
height: CONST.EMOJI_PICKER_ITEM_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
},

Expand Down

0 comments on commit 95cdfdb

Please sign in to comment.