Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -1011,12 +1011,17 @@ const CONST = {
CODE_2FA: /^\d{6}$/,
ATTACHMENT_ID: /chat-attachments\/(\d+)/,
HAS_COLON_ONLY_AT_THE_BEGINNING: /^:[^:]+$/,
HAS_AT_MOST_TWO_AT_SIGNS: /^@[^@]*@?[^@]*$/,

// eslint-disable-next-line no-misleading-character-class
NEW_LINE_OR_WHITE_SPACE_OR_EMOJI: /[\n\s\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu,

// Define the regular expression pattern to match a string starting with a colon and ending with a space or newline character
EMOJI_REPLACER: /^:[^\n\r]+?(?=$|\s)/,

// Define the regular expression pattern to match a string starting with an at sign and ending with a space or newline character
MENTION_REPLACER: /^@[^\n\r]*?(?=$|\s)/,

MERGED_ACCOUNT_PREFIX: /^(MERGED_\d+@)/,
},

Expand Down
111 changes: 111 additions & 0 deletions src/components/MentionSuggestions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import styles from '../styles/styles';
import * as StyleUtils from '../styles/StyleUtils';
import Text from './Text';
import CONST from '../CONST';
import Avatar from './Avatar';
import AutoCompleteSuggestions from './AutoCompleteSuggestions';
import getStyledTextArray from '../libs/GetStyledTextArray';
import avatarPropTypes from './avatarPropTypes';

const propTypes = {
/** The index of the highlighted mention */
highlightedMentionIndex: PropTypes.number,

/** Array of suggested mentions */
mentions: PropTypes.arrayOf(
PropTypes.shape({
/** Display name of the user */
text: PropTypes.string,

/** Email/phone number of the user */
alternateText: PropTypes.string,

/** Array of icons of the user. We use the first element of this array */
icons: PropTypes.arrayOf(avatarPropTypes),
}),
).isRequired,

/** Fired when the user selects an mention */
onSelect: PropTypes.func.isRequired,

/** Mention prefix that follows the @ sign */
prefix: PropTypes.string.isRequired,

/** Show that we can use large mention picker.
* Depending on available space and whether the input is expanded, we can have a small or large mention suggester.
* When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */
isMentionPickerLarge: PropTypes.bool.isRequired,

/** Show that we should include ReportRecipientLocalTime view height */
shouldIncludeReportRecipientLocalTimeHeight: PropTypes.bool.isRequired,
};

const defaultProps = {
highlightedMentionIndex: 0,
};

/**
* Create unique keys for each mention item
* @param {Object} item
* @param {Number} index
* @returns {String}
*/
const keyExtractor = (item) => item.alternateText;

const MentionSuggestions = (props) => {
/**
* Render a suggestion menu item component.
* @param {Object} item
* @returns {JSX.Element}
*/
const renderSuggestionMenuItem = (item) => {
const displayedText = _.uniq([item.text, item.alternateText]).join(' - ');
const styledTextArray = getStyledTextArray(displayedText, props.prefix);

return (
<View style={[styles.autoCompleteSuggestionContainer, styles.ph2]}>
<Avatar
source={item.icons[0].source}
size={CONST.AVATAR_SIZE.SMALLER}
name={item.icons[0].name}
type={item.icons[0].type}
/>
<Text
style={styles.mentionSuggestionsText}
numberOfLines={1}
>
{_.map(styledTextArray, ({text, isColored}, i) => (
<Text
key={`${text}${i}`}
style={StyleUtils.getColoredBackgroundStyle(isColored)}
>
{text}
</Text>
))}
</Text>
</View>
);
};

return (
<AutoCompleteSuggestions
suggestions={props.mentions}
renderSuggestionMenuItem={renderSuggestionMenuItem}
keyExtractor={keyExtractor}
highlightedSuggestionIndex={props.highlightedMentionIndex}
onSelect={props.onSelect}
isSuggestionPickerLarge={props.isMentionPickerLarge}
shouldIncludeReportRecipientLocalTimeHeight={props.shouldIncludeReportRecipientLocalTimeHeight}
/>
);
};

MentionSuggestions.propTypes = propTypes;
MentionSuggestions.defaultProps = defaultProps;
MentionSuggestions.displayName = 'MentionSuggestions';

export default MentionSuggestions;
2 changes: 1 addition & 1 deletion src/libs/GetStyledTextArray.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Str from 'expensify-common/lib/str';
const getStyledTextArray = (name, prefix) => {
const texts = [];
const prefixLowercase = prefix.toLowerCase();
const prefixLocation = name.search(Str.escapeForRegExp(prefixLowercase));
const prefixLocation = name.toLowerCase().search(Str.escapeForRegExp(prefixLowercase));

if (prefixLocation === 0 && prefix.length === name.length) {
texts.push({text: prefixLowercase, isColored: true});
Expand Down
Loading