-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Detailed list of reaction senders when long-/right-pressing a reaction #15685
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
Changes from 19 commits
f6279ea
bc5a517
5732ada
72d53e9
0280636
82b02be
156d6ef
c66388b
0317bd7
e36ba93
b16febf
fbc69cf
502920e
5bc555d
6bc54ce
e96d82b
acf6f2e
838585d
01848c6
5523614
e528087
b91f52c
96d05f6
524546b
b4343a8
d129b8c
6ebe8ac
308f673
38db154
b2a1c65
4ffd2ff
6df949e
907fd16
a09ad6b
cb14807
5dada1a
a989192
3604015
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ const defaultProps = { | |
preventDefaultContentMenu: true, | ||
inline: false, | ||
withoutFocusOnSecondaryInteraction: false, | ||
isLongPressEnabledWithHover: false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NAB: Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since I've extended the properties of this component, I think it's reasonable to define a default value for this optional property. This would make it more predictable for other developers who use this component There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More specifically, I meant that this prop name is quite long and didn't make sense to me immediately. |
||
}; | ||
|
||
export {propTypes, defaultProps}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,11 @@ import styles from '../../styles/styles'; | |
import EmojiReactionBubble from './EmojiReactionBubble'; | ||
import emojis from '../../../assets/emojis'; | ||
import AddReactionBubble from './AddReactionBubble'; | ||
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; | ||
import getPreferredEmojiCode from './getPreferredEmojiCode'; | ||
|
||
import * as Report from '../../libs/actions/Report'; | ||
|
||
import Tooltip from '../Tooltip'; | ||
import ReactionTooltipContent from './ReactionTooltipContent'; | ||
|
||
|
@@ -52,6 +56,21 @@ const propTypes = { | |
* hence this function asks to toggle the reaction by emoji. | ||
*/ | ||
toggleReaction: PropTypes.func.isRequired, | ||
|
||
/** A ref to PressableWithSecondaryInteraction */ | ||
forwardedRef: PropTypes.oneOfType([ | ||
PropTypes.func, | ||
PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), | ||
]).isRequired, | ||
|
||
/** Function which opens Reaction List popup */ | ||
onReactionListOpen: PropTypes.func.isRequired, | ||
|
||
...withCurrentUserPersonalDetailsPropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
...withCurrentUserPersonalDetailsDefaultProps, | ||
}; | ||
|
||
const ReportActionItemReactions = (props) => { | ||
|
@@ -64,10 +83,14 @@ const ReportActionItemReactions = (props) => { | |
const reactionUsers = _.map(reaction.users, sender => sender.accountID.toString()); | ||
const emoji = _.find(emojis, e => e.name === reaction.emoji); | ||
const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); | ||
const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); | ||
|
||
const onPress = () => { | ||
props.toggleReaction(emoji); | ||
}; | ||
const onReactionListOpen = (event) => { | ||
props.onReactionListOpen(event, reactionUsers, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); | ||
}; | ||
|
||
return ( | ||
<Tooltip | ||
|
@@ -80,13 +103,17 @@ const ReportActionItemReactions = (props) => { | |
)} | ||
> | ||
<EmojiReactionBubble | ||
ref={props.forwardedRef} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✋ Coming from #25976 We are preventing the popover from closing when clicking on the popover and the anchor. In this case, the entire View component where the reactions sit is used as the anchor, causing clicks to be ignored in this area. |
||
key={reaction.emoji} | ||
count={reactionCount} | ||
emojiCodes={emojiCodes} | ||
onPress={onPress} | ||
reactionUsers={reactionUsers} | ||
hasUserReacted={hasUserReacted} | ||
onReactionListOpen={onReactionListOpen} | ||
/> | ||
</Tooltip> | ||
|
||
); | ||
})} | ||
{reactionsWithCount.length > 0 && <AddReactionBubble onSelectEmoji={props.toggleReaction} />} | ||
|
@@ -96,4 +123,9 @@ const ReportActionItemReactions = (props) => { | |
|
||
ReportActionItemReactions.displayName = 'ReportActionItemReactions'; | ||
ReportActionItemReactions.propTypes = propTypes; | ||
export default ReportActionItemReactions; | ||
ReportActionItemReactions.defaultProps = defaultProps; | ||
export default withCurrentUserPersonalDetails(React.forwardRef((props, ref) => ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
<ReportActionItemReactions {...props} forwardedRef={ref} /> | ||
))); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import React from 'react'; | ||
import {View, FlatList} from 'react-native'; | ||
import PropTypes from 'prop-types'; | ||
import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; | ||
import ReactionListItem from './ReactionListItem'; | ||
import styles from '../../../../styles/styles'; | ||
import HeaderReactionList from './HeaderReactionList'; | ||
import CONST from '../../../../CONST'; | ||
import participantPropTypes from '../../../../components/participantPropTypes'; | ||
import { | ||
propTypes as reactionPropTypes, | ||
defaultProps as reactionDefaultProps, | ||
} from './HeaderReactionList/reactionPropTypes'; | ||
|
||
const propTypes = { | ||
|
||
/** | ||
* Array of personal detail objects | ||
*/ | ||
users: PropTypes.arrayOf(participantPropTypes), | ||
|
||
/** | ||
* Returns true if the current account has reacted to the report action (with the given skin tone). | ||
*/ | ||
hasUserReacted: PropTypes.bool, | ||
perunt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
...reactionPropTypes, | ||
...withLocalizePropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
...reactionDefaultProps, | ||
}; | ||
|
||
const renderItem = ({item}) => <ReactionListItem item={item} />; | ||
const keyExtractor = item => `${item.accountID}`; | ||
const getItemLayout = (_, index) => ({ | ||
index, | ||
length: CONST.REACTION_LIST_ITEM_HEIGHT, | ||
offset: CONST.REACTION_LIST_ITEM_HEIGHT * index, | ||
}); | ||
|
||
// const BaseReactionList = props => (props.isVisible) && ( | ||
perunt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const BaseReactionList = (props) => { | ||
if (!props.isVisible) { | ||
return null; | ||
} | ||
return ( | ||
<View style={styles.reactionListContainer}> | ||
<HeaderReactionList | ||
onClose={props.onClose} | ||
emojiName={props.emojiName} | ||
emojiCodes={props.emojiCodes} | ||
emojiCount={props.emojiCount} | ||
hasUserReacted={props.hasUserReacted} | ||
sizeScale={props.sizeScale} | ||
/> | ||
<FlatList | ||
data={props.users} | ||
renderItem={renderItem} | ||
keyExtractor={keyExtractor} | ||
getItemLayout={getItemLayout} | ||
style={[styles.pb3, styles.pt2]} | ||
/> | ||
</View> | ||
); | ||
}; | ||
|
||
BaseReactionList.propTypes = propTypes; | ||
BaseReactionList.defaultProps = defaultProps; | ||
BaseReactionList.displayName = 'BaseReactionList'; | ||
|
||
export default withLocalize(BaseReactionList); | ||
perunt marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React from 'react'; | ||
import {View} from 'react-native'; | ||
import styles from '../../../../../styles/styles'; | ||
import Text from '../../../../../components/Text'; | ||
import * as StyleUtils from '../../../../../styles/StyleUtils'; | ||
import { | ||
propTypes as reactionPropTypes, | ||
defaultProps as reactionDefaultProps, | ||
} from './reactionPropTypes'; | ||
|
||
const propTypes = { | ||
...reactionPropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
...reactionDefaultProps, | ||
}; | ||
|
||
const HeaderReactionList = props => ( | ||
<View style={[styles.pt4, styles.mh5, styles.emojiReactionListHeader, styles.flexRow]}> | ||
<View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}> | ||
<Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}> | ||
{props.emojiCodes.join('')} | ||
</Text> | ||
<Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}> | ||
{props.emojiCount} | ||
</Text> | ||
</View> | ||
<Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text> | ||
</View> | ||
); | ||
|
||
HeaderReactionList.propTypes = propTypes; | ||
HeaderReactionList.defaultProps = defaultProps; | ||
HeaderReactionList.displayName = 'HeaderReactionList'; | ||
|
||
export default HeaderReactionList; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import React from 'react'; | ||
import {View, TouchableOpacity} from 'react-native'; | ||
import styles from '../../../../../styles/styles'; | ||
import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; | ||
import Text from '../../../../../components/Text'; | ||
import Icon from '../../../../../components/Icon'; | ||
import * as Expensicons from '../../../../../components/Icon/Expensicons'; | ||
import * as StyleUtils from '../../../../../styles/StyleUtils'; | ||
import { | ||
propTypes as reactionPropTypes, | ||
defaultProps as reactionDefaultProps, | ||
} from './reactionPropTypes'; | ||
|
||
const propTypes = { | ||
...reactionPropTypes, | ||
...withLocalizePropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
...reactionDefaultProps, | ||
}; | ||
|
||
const HeaderReactionList = props => ( | ||
<View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.emojiReactionListHeader]}> | ||
<View style={styles.flexRow}> | ||
<View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}> | ||
<Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}> | ||
{props.emojiCodes.join('')} | ||
</Text> | ||
<Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}> | ||
{props.emojiCount} | ||
</Text> | ||
</View> | ||
<Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text> | ||
</View> | ||
|
||
<TouchableOpacity | ||
onPress={props.onClose} | ||
accessibilityRole="button" | ||
accessibilityLabel={props.translate('common.close')} | ||
> | ||
<Icon src={Expensicons.Close} /> | ||
</TouchableOpacity> | ||
</View> | ||
); | ||
|
||
HeaderReactionList.propTypes = propTypes; | ||
HeaderReactionList.defaultProps = defaultProps; | ||
HeaderReactionList.displayName = 'HeaderReactionList'; | ||
|
||
export default withLocalize(HeaderReactionList); | ||
|
Uh oh!
There was an error while loading. Please reload this page.