From f6279eaa17d910eefdd9a7bf3fdca285cdc01f25 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 6 Mar 2023 17:17:22 +0100 Subject: [PATCH 01/24] ReactionList --- .../Reactions/EmojiReactionBubble.js | 13 +- .../Reactions/ReportActionItemReactions.js | 22 +- .../report/ReactionList/BaseReactionList.js | 69 +++++ .../ReactionList/HeaderReactionList/index.js | 3 + .../HeaderReactionList/index.native.js | 41 +++ .../ReactionList/PopoverReactionList.js | 262 ++++++++++++++++++ .../home/report/ReactionList/ReactionList.js | 69 +++++ .../report/ReactionList/ReactionListItem.js | 53 ++++ src/pages/home/report/ReportActionItem.js | 15 +- src/pages/home/report/ReportActionsView.js | 6 + 10 files changed, 547 insertions(+), 6 deletions(-) create mode 100755 src/pages/home/report/ReactionList/BaseReactionList.js create mode 100644 src/pages/home/report/ReactionList/HeaderReactionList/index.js create mode 100644 src/pages/home/report/ReactionList/HeaderReactionList/index.native.js create mode 100644 src/pages/home/report/ReactionList/PopoverReactionList.js create mode 100644 src/pages/home/report/ReactionList/ReactionList.js create mode 100644 src/pages/home/report/ReactionList/ReactionListItem.js diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 5162f4258a46..7fbf65880cd2 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {Pressable} from 'react-native'; import _ from 'underscore'; import styles from '../../styles/styles'; import Text from '../Text'; @@ -11,6 +10,7 @@ import withCurrentUserPersonalDetails, { } from '../withCurrentUserPersonalDetails'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; +import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; const propTypes = { emojiName: PropTypes.string.isRequired, @@ -73,13 +73,15 @@ const EmojiReactionBubble = (props) => { /> )} > - [ styles.emojiReactionBubble, StyleUtils.getEmojiReactionBubbleStyle(hovered, hasUserReacted, props.sizeScale), ]} onPress={props.onPress} onLongPress={props.onReactionListOpen} + onSecondaryInteraction={props.onReactionListOpen} + ref={props.forwardedRef} > { {props.count} )} - + ); }; @@ -106,4 +108,7 @@ EmojiReactionBubble.propTypes = propTypes; EmojiReactionBubble.defaultProps = defaultProps; EmojiReactionBubble.displayName = 'EmojiReactionBubble'; -export default withCurrentUserPersonalDetails(EmojiReactionBubble); +export default withCurrentUserPersonalDetails(React.forwardRef((props, ref) => ( + /* eslint-disable-next-line react/jsx-props-no-spreading */ + +))); diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 27db8af8fb56..357293a85a33 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -40,6 +40,15 @@ 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 */ + onReactionListOpen: PropTypes.func.isRequired, }; const ReportActionItemReactions = props => ( @@ -58,14 +67,20 @@ const ReportActionItemReactions = props => ( return null; } + const onReactionListOpen = (e) => { + props.onReactionListOpen(e, reactionUsers, reaction.emoji); + }; + return ( ); })} @@ -75,4 +90,9 @@ const ReportActionItemReactions = props => ( ReportActionItemReactions.displayName = 'ReportActionItemReactions'; ReportActionItemReactions.propTypes = propTypes; -export default ReportActionItemReactions; + +export default React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +)); + diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js new file mode 100755 index 000000000000..e6c1195bb7ed --- /dev/null +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -0,0 +1,69 @@ +import React from 'react'; +import {View, FlatList} from 'react-native'; +import PropTypes from 'prop-types'; +import getReportActionContextMenuStyles from '../../../../styles/getReportActionContextMenuStyles'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; + +import compose from '../../../../libs/compose'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import {withBetas} from '../../../../components/OnyxProvider'; +import Text from '../../../../components/Text'; +import ReactionListItem from './ReactionListItem'; +import styles from '../../../../styles/styles'; +import colors from '../../../../styles/colors'; +import HeaderReactionList from './HeaderReactionList'; + +const propTypes = { + + contentRef: PropTypes.oneOfType([PropTypes.node, PropTypes.object, PropTypes.func]), + + ...withLocalizePropTypes, + ...windowDimensionsPropTypes, +}; + +const defaultProps = { + contentRef: null, +}; +class BaseReactionList extends React.Component { + constructor(props) { + super(props); + this.wrapperStyle = getReportActionContextMenuStyles(this.props.isMini); + + this.state = { + keepOpen: false, + }; + } + + renderItem({item}) { + return ; + } + + render() { + return (this.props.isVisible || this.state.keepOpen) && ( + + + {`:${this.props.emojiName}:`} + item.accountID} + style={styles.mb3} + /> + + ); + } +} + +BaseReactionList.propTypes = propTypes; +BaseReactionList.defaultProps = defaultProps; + +export default compose( + withLocalize, + withBetas(), + withWindowDimensions, +)(BaseReactionList); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.js new file mode 100644 index 000000000000..6ec5b6195bd7 --- /dev/null +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.js @@ -0,0 +1,3 @@ + +const HeaderReactionList = () => null; +export default HeaderReactionList; diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js new file mode 100644 index 000000000000..a70dac59d609 --- /dev/null +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js @@ -0,0 +1,41 @@ +import React from 'react'; +import {View, TouchableOpacity} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../../../../styles/styles'; +import compose from '../../../../../libs/compose'; +import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; +import {withPersonalDetails} from '../../../../../components/OnyxProvider'; +import Text from '../../../../../components/Text'; +import Icon from '../../../../../components/Icon'; +import * as Expensicons from '../../../../../components/Icon/Expensicons'; + +const propTypes = { + /** Children view component for this action item */ + onClose: PropTypes.func.isRequired, + + ...withLocalizePropTypes, +}; + +const HeaderReactionList = props => ( + + + Emoji reactions + + + + + +); + +HeaderReactionList.propTypes = propTypes; +HeaderReactionList.displayName = 'HeaderReactionList'; + +export default compose( + withLocalize, + withPersonalDetails(), +)(HeaderReactionList); + diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js new file mode 100644 index 000000000000..f2d2c62fb61e --- /dev/null +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -0,0 +1,262 @@ +import React from 'react'; +import {Dimensions} from 'react-native'; +import _ from 'underscore'; + +import lodashGet from 'lodash/get'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import PopoverWithMeasuredContent from '../../../../components/PopoverWithMeasuredContent'; + +import BaseReactionList from './BaseReactionList'; + +const propTypes = { + ...withLocalizePropTypes, +}; + +class PopoverReactionList extends React.Component { + constructor(props) { + super(props); + + this.state = { + reportID: '0', + reportAction: {}, + isPopoverVisible: false, + cursorRelativePosition: { + horizontal: 0, + vertical: 0, + }, + + // The horizontal and vertical position (relative to the screen) where the popover will display. + popoverAnchorPosition: { + horizontal: 0, + vertical: 0, + }, + users: [], + emojiName: '', + }; + + this.onPopoverShow = () => {}; + this.onPopoverHide = () => {}; + this.onPopoverHideActionCallback = () => {}; + this.contextMenuAnchor = undefined; + this.showReactionList = this.showReactionList.bind(this); + + this.hideReactionList = this.hideReactionList.bind(this); + this.measureContent = this.measureContent.bind(this); + this.measureReactionListPosition = this.measureReactionListPosition.bind(this); + this.runAndResetOnPopoverShow = this.runAndResetOnPopoverShow.bind(this); + this.runAndResetOnPopoverHide = this.runAndResetOnPopoverHide.bind(this); + this.getContextMenuMeasuredLocation = this.getContextMenuMeasuredLocation.bind(this); + + this.dimensionsEventListener = null; + + this.contentRef = React.createRef(); + this.setContentRef = (ref) => { + this.contentRef.current = ref; + }; + this.setContentRef = this.setContentRef.bind(this); + } + + componentDidMount() { + this.dimensionsEventListener = Dimensions.addEventListener('change', this.measureReactionListPosition); + } + + shouldComponentUpdate(nextProps, nextState) { + const previousLocale = lodashGet(this.props, 'preferredLocale', 'en'); + const nextLocale = lodashGet(nextProps, 'preferredLocale', 'en'); + return this.state.isPopoverVisible !== nextState.isPopoverVisible + || this.state.popoverAnchorPosition !== nextState.popoverAnchorPosition + || previousLocale !== nextLocale; + } + + componentWillUnmount() { + if (!this.dimensionsEventListener) { + return; + } + this.dimensionsEventListener.remove(); + } + + /** + * Get the Context menu anchor position + * We calculate the achor coordinates from measureInWindow async method + * + * @returns {Promise} + */ + getContextMenuMeasuredLocation() { + return new Promise((resolve) => { + if (this.contextMenuAnchor) { + this.contextMenuAnchor.measureInWindow((x, y) => resolve({x, y})); + } else { + resolve({x: 0, y: 0}); + } + }); + } + + /** + * Show the ReportActionContextMenu modal popover. + * + * @param {Object} [event] - A press event. + * @param {Element} contextMenuAnchor - popoverAnchor + * @param {Array} users - array of users id + * @param {String} emojiName - name of emoji + * @param {Function} [onShow] - Run a callback when Menu is shown + * @param {Function} [onHide] - Run a callback when Menu is hidden + */ + showReactionList( + event, + contextMenuAnchor, + users, + emojiName, + onShow, + onHide, + ) { + const nativeEvent = event.nativeEvent || {}; + + this.contextMenuAnchor = contextMenuAnchor; + this.contextMenuTargetNode = nativeEvent.target; + + // Singleton behaviour of ContextMenu creates race conditions when user requests multiple contextMenus. + // But it is possible that every new request registers new callbacks thus instanceID is used to corelate those callbacks + this.instanceID = Math.random().toString(36).substr(2, 5); + + // Register the onHide callback only when Popover is shown to remove the race conditions when there are mutltiple popover open requests + this.onPopoverShow = () => { + onShow(); + this.onPopoverHide = onHide; + }; + this.getContextMenuMeasuredLocation().then(({x, y}) => { + this.setState({ + cursorRelativePosition: { + horizontal: nativeEvent.pageX - x, + vertical: nativeEvent.pageY - y, + }, + popoverAnchorPosition: { + horizontal: nativeEvent.pageX, + vertical: nativeEvent.pageY, + }, + users, + emojiName, + isPopoverVisible: true, + }); + }); + } + + /** + * This gets called on Dimensions change to find the anchor coordinates for the action context menu. + */ + measureReactionListPosition() { + if (!this.state.isPopoverVisible) { + return; + } + this.getContextMenuMeasuredLocation().then(({x, y}) => { + if (!x || !y) { + return; + } + this.setState(prev => ({ + popoverAnchorPosition: { + horizontal: prev.cursorRelativePosition.horizontal + x, + vertical: prev.cursorRelativePosition.vertical + y, + }, + })); + }); + } + + /** + * After Popover shows, call the registered onPopoverShow callback and reset it + */ + runAndResetOnPopoverShow() { + this.onPopoverShow(); + + // After we have called the action, reset it. + this.onPopoverShow = () => {}; + } + + /** + * After Popover hides, call the registered onPopoverHide & onPopoverHideActionCallback callback and reset it + */ + runAndResetOnPopoverHide() { + this.setState({reportID: '0', reportAction: {}}, () => { + this.onPopoverHide = this.runAndResetCallback(this.onPopoverHide); + this.onPopoverHideActionCallback = this.runAndResetCallback(this.onPopoverHideActionCallback); + }); + } + + /** + * Hide the ReportActionContextMenu modal popover. + * @param {Function} onHideActionCallback Callback to be called after popover is completely hidden + */ + hideReactionList(onHideActionCallback) { + if (_.isFunction(onHideActionCallback)) { + this.onPopoverHideActionCallback = onHideActionCallback; + } + this.setState({ + isPopoverVisible: false, + }); + } + + /** + * Used to calculate the Context Menu Dimensions + * + * @returns {JSX} + */ + measureContent() { + return ( + + ); + } + + /** + * Run the callback and return a noop function to reset it + * @param {Function} callback + * @returns {Function} + */ + runAndResetCallback(callback) { + callback(); + return () => {}; + } + + render() { + return ( + <> + + + + + ); + } +} + +PopoverReactionList.propTypes = propTypes; + +export default withLocalize(PopoverReactionList); diff --git a/src/pages/home/report/ReactionList/ReactionList.js b/src/pages/home/report/ReactionList/ReactionList.js new file mode 100644 index 000000000000..42c07986c744 --- /dev/null +++ b/src/pages/home/report/ReactionList/ReactionList.js @@ -0,0 +1,69 @@ +import React from 'react'; + +const reactionListRef = React.createRef(); + +/** + * Show the ReportActionContextMenu modal popover. + * + * @param {Object} [event] - A press event. + * @param {Element} contextMenuAnchor - popoverAnchor + * @param {Array} users - array of users id + * @param {String} emojiName - name of emoji + * @param {Function} [onShow] - Run a callback when Menu is shown + * @param {Function} [onHide] - Run a callback when Menu is hidden + */ +function showReactionList( + event, + contextMenuAnchor, + users, + emojiName, + onShow = () => {}, + onHide = () => {}, +) { + if (!reactionListRef.current) { + return; + } + reactionListRef.current.showReactionList( + event, + contextMenuAnchor, + users, + emojiName, + onShow, + onHide, + ); +} + +/** + * Hide the ReportActionContextMenu modal popover. + * Hides the popover menu with an optional delay + * @param {Boolean} shouldDelay - whether the menu should close after a delay + * @param {Function} [onHideCallback=() => {}] - Callback to be called after Context Menu is completely hidden + */ +function hideReactionList(shouldDelay, onHideCallback = () => {}) { + if (!reactionListRef.current) { + return; + } + if (!shouldDelay) { + reactionListRef.current.hideReactionList(onHideCallback); + + return; + } + + // Save the active instanceID for which hide action was called. + // If menu is being closed with a delay, check that whether the same instance exists or a new was created. + // If instance is not same, cancel the hide action + const instanceID = reactionListRef.current.instanceID; + setTimeout(() => { + if (reactionListRef.current.instanceID !== instanceID) { + return; + } + + reactionListRef.current.hideReactionList(onHideCallback); + }, 800); +} + +export { + reactionListRef, + showReactionList, + hideReactionList, +}; diff --git a/src/pages/home/report/ReactionList/ReactionListItem.js b/src/pages/home/report/ReactionList/ReactionListItem.js new file mode 100644 index 000000000000..7ead609c963f --- /dev/null +++ b/src/pages/home/report/ReactionList/ReactionListItem.js @@ -0,0 +1,53 @@ +import React from 'react'; +import {View, Pressable} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../../../styles/styles'; +import colors from '../../../../styles/colors'; +import Avatar from '../../../../components/Avatar'; +import compose from '../../../../libs/compose'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import {withPersonalDetails} from '../../../../components/OnyxProvider'; +import ControlSelection from '../../../../libs/ControlSelection'; +import Text from '../../../../components/Text'; + +const propTypes = { + /** Styles for the outermost View */ + // eslint-disable-next-line react/forbid-prop-types + wrapperStyles: PropTypes.arrayOf(PropTypes.object), + + /** Children view component for this action item */ + + ...withLocalizePropTypes, +}; + +const defaultProps = { + wrapperStyles: [styles.chatItem], +}; + +const ReactionListItem = props => ( + + + + + + {props.item.displayName} + {props.item.login} + + +); + +ReactionListItem.propTypes = propTypes; +ReactionListItem.defaultProps = defaultProps; +ReactionListItem.displayName = 'ReactionListItem'; + +export default compose( + withLocalize, + withPersonalDetails(), +)(ReactionListItem); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index a1e9f802a0fc..7c8df4b5be36 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -36,7 +36,9 @@ import reportPropTypes from '../../reportPropTypes'; import {ShowContextMenuContext} from '../../../components/ShowContextMenuContext'; import focusTextInputAfterAnimation from '../../../libs/focusTextInputAfterAnimation'; import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions'; +import * as ReactionList from './ReactionList/ReactionList'; import * as Report from '../../../libs/actions/Report'; +import getPersonalDetailsByIDs from '../../../libs/getPersonalDetailsByIDs'; const propTypes = { /** Report for this action */ @@ -75,6 +77,7 @@ class ReportActionItem extends Component { constructor(props) { super(props); this.popoverAnchor = undefined; + this.popoverReactionListAnchor = undefined; this.state = { isContextMenuActive: ReportActionContextMenu.isActiveReportAction(props.action.reportActionID), }; @@ -203,14 +206,24 @@ class ReportActionItem extends Component { const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); const hasReactions = reactions.length > 0; - + const onReactionListOpen = (e, reactionUsers, emojiName) => { + const users = getPersonalDetailsByIDs(reactionUsers); + ReactionList.showReactionList( + e, + this.popoverReactionListAnchor, + users, + emojiName, + ); + }; return ( <> {children} {hasReactions && ( this.popoverReactionListAnchor = el} reactions={reactions} toggleReaction={this.toggleReaction} + onReactionListOpen={onReactionListOpen} /> )} diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 2c65533800f9..6d3c7899b370 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -24,6 +24,9 @@ import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; import * as ReportUtils from '../../../libs/ReportUtils'; import reportPropTypes from '../../reportPropTypes'; +import * as ReactionList from './ReactionList/ReactionList'; +import PopoverReactionList from './ReactionList/PopoverReactionList'; + const propTypes = { /** The report currently being looked at */ report: reportPropTypes.isRequired, @@ -366,6 +369,9 @@ class ReportActionsView extends React.Component { loadMoreChats={this.loadMoreChats} newMarkerReportActionID={this.state.newMarkerReportActionID} /> + )} From bc5a5170e0812c78e7b9dc3ab5b34e2af593cc5b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 7 Mar 2023 13:50:32 +0100 Subject: [PATCH 02/24] update styles --- .../Reactions/EmojiReactionBubble.js | 77 ++++++++--------- .../Reactions/ReportActionItemReactions.js | 22 +++-- .../report/ReactionList/BaseReactionList.js | 71 +++++----------- .../ReactionList/HeaderReactionList/index.js | 51 +++++++++++- .../HeaderReactionList/index.native.js | 41 +++++++--- .../ReactionList/PopoverReactionList.js | 82 ++++--------------- .../home/report/ReactionList/ReactionList.js | 17 ++-- .../report/ReactionList/ReactionListItem.js | 14 +++- src/pages/home/report/ReportActionItem.js | 6 +- src/styles/StyleUtils.js | 8 ++ src/styles/styles.js | 33 ++++++++ 11 files changed, 236 insertions(+), 186 deletions(-) diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 7fbf65880cd2..46bd98463bb8 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -1,13 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import _ from 'underscore'; import styles from '../../styles/styles'; import Text from '../Text'; import * as StyleUtils from '../../styles/StyleUtils'; -import withCurrentUserPersonalDetails, { - withCurrentUserPersonalDetailsDefaultProps, - withCurrentUserPersonalDetailsPropTypes, -} from '../withCurrentUserPersonalDetails'; +import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; @@ -60,55 +56,50 @@ const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, }; -const EmojiReactionBubble = (props) => { - const hasUserReacted = _.includes(props.reactionUsers, `${props.currentUserPersonalDetails.accountID}`); - - return ( - ( - - )} +const EmojiReactionBubble = props => ( + ( + + )} + > + [ + styles.emojiReactionBubble, + StyleUtils.getEmojiReactionBubbleStyle(hovered, props.hasUserReacted, props.sizeScale), + ]} + onPress={props.onPress} + onLongPress={props.onReactionListOpen} + onSecondaryInteraction={props.onReactionListOpen} + ref={props.forwardedRef} > - [ - styles.emojiReactionBubble, - StyleUtils.getEmojiReactionBubbleStyle(hovered, hasUserReacted, props.sizeScale), - ]} - onPress={props.onPress} - onLongPress={props.onReactionListOpen} - onSecondaryInteraction={props.onReactionListOpen} - ref={props.forwardedRef} + - - {props.emojiCodes.join('')} - - {props.count > 0 && ( + {props.emojiCodes.join('')} + + {props.count > 0 && ( {props.count} - )} - - - ); -}; - + )} + + +); EmojiReactionBubble.propTypes = propTypes; EmojiReactionBubble.defaultProps = defaultProps; EmojiReactionBubble.displayName = 'EmojiReactionBubble'; -export default withCurrentUserPersonalDetails(React.forwardRef((props, ref) => ( +export default React.forwardRef((props, ref) => ( /* eslint-disable-next-line react/jsx-props-no-spreading */ -))); +)); diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 357293a85a33..b644c3af5ecb 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -6,6 +6,7 @@ import styles from '../../styles/styles'; import EmojiReactionBubble from './EmojiReactionBubble'; import emojis from '../../../assets/emojis'; import AddReactionBubble from './AddReactionBubble'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; /** * Given an emoji object and a list of senders it will return an @@ -27,6 +28,10 @@ const getUniqueEmojiCodes = (emoji, users) => { return emojiCodes; }; +const defaultProps = { + ...withCurrentUserPersonalDetailsDefaultProps, +}; + const propTypes = { /** * An array of objects containing the reaction data. @@ -47,8 +52,11 @@ const propTypes = { PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), ]).isRequired, - /** Function which opens Reaction List */ + /** Function which opens Reaction List popup */ onReactionListOpen: PropTypes.func.isRequired, + + ...withCurrentUserPersonalDetailsPropTypes, + }; const ReportActionItemReactions = props => ( @@ -58,6 +66,7 @@ const ReportActionItemReactions = props => ( const reactionUsers = _.map(reaction.users, sender => `${sender.accountID}`); const emoji = _.find(emojis, e => e.name === reaction.emoji); const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); + const hasUserReacted = _.includes(reactionUsers, `${props.currentUserPersonalDetails.accountID}`); const onPress = () => { props.toggleReaction(emoji); @@ -67,8 +76,8 @@ const ReportActionItemReactions = props => ( return null; } - const onReactionListOpen = (e) => { - props.onReactionListOpen(e, reactionUsers, reaction.emoji); + const onReactionListOpen = (event) => { + props.onReactionListOpen(event, reactionUsers, reaction.emoji, emojiCodes, hasUserReacted); }; return ( @@ -80,6 +89,7 @@ const ReportActionItemReactions = props => ( emojiCodes={emojiCodes} onPress={onPress} reactionUsers={reactionUsers} + hasUserReacted={hasUserReacted} onReactionListOpen={onReactionListOpen} /> ); @@ -90,9 +100,9 @@ const ReportActionItemReactions = props => ( ReportActionItemReactions.displayName = 'ReportActionItemReactions'; ReportActionItemReactions.propTypes = propTypes; - -export default React.forwardRef((props, ref) => ( +ReportActionItemReactions.defaultProps = defaultProps; +export default withCurrentUserPersonalDetails(React.forwardRef((props, ref) => ( // eslint-disable-next-line react/jsx-props-no-spreading -)); +))); diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index e6c1195bb7ed..fcbc2839754a 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -1,69 +1,40 @@ import React from 'react'; import {View, FlatList} from 'react-native'; -import PropTypes from 'prop-types'; -import getReportActionContextMenuStyles from '../../../../styles/getReportActionContextMenuStyles'; import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; - -import compose from '../../../../libs/compose'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; -import {withBetas} from '../../../../components/OnyxProvider'; -import Text from '../../../../components/Text'; import ReactionListItem from './ReactionListItem'; import styles from '../../../../styles/styles'; -import colors from '../../../../styles/colors'; import HeaderReactionList from './HeaderReactionList'; const propTypes = { - - contentRef: PropTypes.oneOfType([PropTypes.node, PropTypes.object, PropTypes.func]), - ...withLocalizePropTypes, - ...windowDimensionsPropTypes, }; const defaultProps = { contentRef: null, }; -class BaseReactionList extends React.Component { - constructor(props) { - super(props); - this.wrapperStyle = getReportActionContextMenuStyles(this.props.isMini); - - this.state = { - keepOpen: false, - }; - } - - renderItem({item}) { - return ; - } - render() { - return (this.props.isVisible || this.state.keepOpen) && ( - - - {`:${this.props.emojiName}:`} - item.accountID} - style={styles.mb3} - /> - - ); - } -} +const renderItem = ({item}) => ; + +const BaseReactionList = props => (props.isVisible) && ( + + + `${item.accountID}`} + style={[styles.mb3, styles.mt2]} + /> + +); BaseReactionList.propTypes = propTypes; BaseReactionList.defaultProps = defaultProps; +BaseReactionList.displayName = 'BaseReactionList'; -export default compose( - withLocalize, - withBetas(), - withWindowDimensions, -)(BaseReactionList); +export default withLocalize(BaseReactionList); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.js index 6ec5b6195bd7..8dde2fe665f0 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.js @@ -1,3 +1,50 @@ +import React from 'react'; +import {View} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../../../../styles/styles'; +import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; +import Text from '../../../../../components/Text'; +import colors from '../../../../../styles/colors'; +import * as StyleUtils from '../../../../../styles/StyleUtils'; + +const propTypes = { + /** Selected emoji */ + /** The emoji codes */ + emojiCodes: PropTypes.arrayOf(PropTypes.string).isRequired, + + /** The name of the emoji */ + emojiName: PropTypes.string.isRequired, + + /** + * The default size of the reaction bubble is defined + * by the styles in styles.js. This scale factor can be used + * to make the bubble bigger or smaller. + */ + sizeScale: PropTypes.number, + + ...withLocalizePropTypes, +}; + +const defaultProps = { + sizeScale: 1, +}; + +const HeaderReactionList = props => ( + + + + + {props.emojiCodes.join('')} + + + {`:${props.emojiName}:`} + + +); + +HeaderReactionList.propTypes = propTypes; +HeaderReactionList.defaultProps = defaultProps; +HeaderReactionList.displayName = 'HeaderReactionList'; + +export default withLocalize(HeaderReactionList); -const HeaderReactionList = () => null; -export default HeaderReactionList; diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js index a70dac59d609..82b2fb12f578 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js @@ -2,25 +2,48 @@ import React from 'react'; import {View, TouchableOpacity} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../../../../styles/styles'; -import compose from '../../../../../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; -import {withPersonalDetails} from '../../../../../components/OnyxProvider'; import Text from '../../../../../components/Text'; import Icon from '../../../../../components/Icon'; import * as Expensicons from '../../../../../components/Icon/Expensicons'; +import colors from '../../../../../styles/colors'; +import * as StyleUtils from '../../../../../styles/StyleUtils'; const propTypes = { /** Children view component for this action item */ onClose: PropTypes.func.isRequired, + /** The emoji codes */ + emojiCodes: PropTypes.arrayOf(PropTypes.string).isRequired, + + /** The name of the emoji */ + emojiName: PropTypes.string.isRequired, + + /** + * The default size of the reaction bubble is defined + * by the styles in styles.js. This scale factor can be used + * to make the bubble bigger or smaller. + */ + sizeScale: PropTypes.number, + ...withLocalizePropTypes, }; +const defaultProps = { + sizeScale: 1, +}; + const HeaderReactionList = props => ( - - - Emoji reactions - + + + + + {props.emojiCodes.join('')} + + + {`:${props.emojiName}:`} + + ( ); HeaderReactionList.propTypes = propTypes; +HeaderReactionList.defaultProps = defaultProps; HeaderReactionList.displayName = 'HeaderReactionList'; -export default compose( - withLocalize, - withPersonalDetails(), -)(HeaderReactionList); +export default withLocalize(HeaderReactionList); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index f2d2c62fb61e..0578398f906f 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -1,6 +1,5 @@ import React from 'react'; import {Dimensions} from 'react-native'; -import _ from 'underscore'; import lodashGet from 'lodash/get'; import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; @@ -17,8 +16,6 @@ class PopoverReactionList extends React.Component { super(props); this.state = { - reportID: '0', - reportAction: {}, isPopoverVisible: false, cursorRelativePosition: { horizontal: 0, @@ -31,11 +28,12 @@ class PopoverReactionList extends React.Component { vertical: 0, }, users: [], + emojiCodes: [], emojiName: '', + sizeScale: 1, + hasUserReacted: false, }; - this.onPopoverShow = () => {}; - this.onPopoverHide = () => {}; this.onPopoverHideActionCallback = () => {}; this.contextMenuAnchor = undefined; this.showReactionList = this.showReactionList.bind(this); @@ -43,8 +41,6 @@ class PopoverReactionList extends React.Component { this.hideReactionList = this.hideReactionList.bind(this); this.measureContent = this.measureContent.bind(this); this.measureReactionListPosition = this.measureReactionListPosition.bind(this); - this.runAndResetOnPopoverShow = this.runAndResetOnPopoverShow.bind(this); - this.runAndResetOnPopoverHide = this.runAndResetOnPopoverHide.bind(this); this.getContextMenuMeasuredLocation = this.getContextMenuMeasuredLocation.bind(this); this.dimensionsEventListener = null; @@ -98,31 +94,25 @@ class PopoverReactionList extends React.Component { * @param {Element} contextMenuAnchor - popoverAnchor * @param {Array} users - array of users id * @param {String} emojiName - name of emoji - * @param {Function} [onShow] - Run a callback when Menu is shown - * @param {Function} [onHide] - Run a callback when Menu is hidden + * @param {Array} emojiCodes - The emoji codes to display in the bubble. + * @param {Boolean} [hasUserReacted] - whether the current user has reacted to this emoji + */ showReactionList( event, contextMenuAnchor, users, emojiName, - onShow, - onHide, + emojiCodes, + hasUserReacted, ) { const nativeEvent = event.nativeEvent || {}; this.contextMenuAnchor = contextMenuAnchor; - this.contextMenuTargetNode = nativeEvent.target; // Singleton behaviour of ContextMenu creates race conditions when user requests multiple contextMenus. // But it is possible that every new request registers new callbacks thus instanceID is used to corelate those callbacks this.instanceID = Math.random().toString(36).substr(2, 5); - - // Register the onHide callback only when Popover is shown to remove the race conditions when there are mutltiple popover open requests - this.onPopoverShow = () => { - onShow(); - this.onPopoverHide = onHide; - }; this.getContextMenuMeasuredLocation().then(({x, y}) => { this.setState({ cursorRelativePosition: { @@ -135,7 +125,9 @@ class PopoverReactionList extends React.Component { }, users, emojiName, + emojiCodes, isPopoverVisible: true, + hasUserReacted, }); }); } @@ -161,33 +153,9 @@ class PopoverReactionList extends React.Component { } /** - * After Popover shows, call the registered onPopoverShow callback and reset it - */ - runAndResetOnPopoverShow() { - this.onPopoverShow(); - - // After we have called the action, reset it. - this.onPopoverShow = () => {}; - } - - /** - * After Popover hides, call the registered onPopoverHide & onPopoverHideActionCallback callback and reset it - */ - runAndResetOnPopoverHide() { - this.setState({reportID: '0', reportAction: {}}, () => { - this.onPopoverHide = this.runAndResetCallback(this.onPopoverHide); - this.onPopoverHideActionCallback = this.runAndResetCallback(this.onPopoverHideActionCallback); - }); - } - - /** - * Hide the ReportActionContextMenu modal popover. - * @param {Function} onHideActionCallback Callback to be called after popover is completely hidden + * Hide the ReactionList modal popover. */ - hideReactionList(onHideActionCallback) { - if (_.isFunction(onHideActionCallback)) { - this.onPopoverHideActionCallback = onHideActionCallback; - } + hideReactionList() { this.setState({ isPopoverVisible: false, }); @@ -205,33 +173,20 @@ class PopoverReactionList extends React.Component { isVisible users={this.state.users} emojiName={this.state.emojiName} - reportID={this.state.reportID} - reportAction={this.state.reportAction} - anchor={this.contextMenuTargetNode} - contentRef={this.setContentRef} + emojiCodes={this.state.emojiCodes} onClose={this.hideReactionList} + sizeScale={this.state.sizeScale} + hasUserReacted={this.state.hasUserReacted} /> ); } - /** - * Run the callback and return a noop function to reset it - * @param {Function} callback - * @returns {Function} - */ - runAndResetCallback(callback) { - callback(); - return () => {}; - } - render() { return ( <> diff --git a/src/pages/home/report/ReactionList/ReactionList.js b/src/pages/home/report/ReactionList/ReactionList.js index 42c07986c744..e204e831eb04 100644 --- a/src/pages/home/report/ReactionList/ReactionList.js +++ b/src/pages/home/report/ReactionList/ReactionList.js @@ -8,17 +8,19 @@ const reactionListRef = React.createRef(); * @param {Object} [event] - A press event. * @param {Element} contextMenuAnchor - popoverAnchor * @param {Array} users - array of users id - * @param {String} emojiName - name of emoji - * @param {Function} [onShow] - Run a callback when Menu is shown - * @param {Function} [onHide] - Run a callback when Menu is hidden + * @param {String} emojiName - he emoji codes to display near the bubble. + * @param {String} emojiCodes - the emoji codes to display in the bubble. + * @param {Boolean} hasUserReacted - Show if user has reacted + * @param {Number} sizeScale - Set the sizeScale of emoji */ function showReactionList( event, contextMenuAnchor, users, emojiName, - onShow = () => {}, - onHide = () => {}, + emojiCodes, + hasUserReacted, + sizeScale, ) { if (!reactionListRef.current) { return; @@ -28,8 +30,9 @@ function showReactionList( contextMenuAnchor, users, emojiName, - onShow, - onHide, + emojiCodes, + hasUserReacted, + sizeScale, ); } diff --git a/src/pages/home/report/ReactionList/ReactionListItem.js b/src/pages/home/report/ReactionList/ReactionListItem.js index 7ead609c963f..06d3047581a9 100644 --- a/src/pages/home/report/ReactionList/ReactionListItem.js +++ b/src/pages/home/report/ReactionList/ReactionListItem.js @@ -15,13 +15,23 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types wrapperStyles: PropTypes.arrayOf(PropTypes.object), - /** Children view component for this action item */ + /** Personal details of the user */ + item: PropTypes.shape({ + // Display Name of participant + displayName: PropTypes.string, + + // Avatar url of participant + avatar: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + + /** First Name of the participant */ + firstName: PropTypes.string, + }).isRequired, ...withLocalizePropTypes, }; const defaultProps = { - wrapperStyles: [styles.chatItem], + wrapperStyles: [styles.reactionListItem], }; const ReactionListItem = props => ( diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 7c8df4b5be36..8e43b0ad0305 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -206,13 +206,15 @@ class ReportActionItem extends Component { const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); const hasReactions = reactions.length > 0; - const onReactionListOpen = (e, reactionUsers, emojiName) => { + const onReactionListOpen = (event, reactionUsers, emojiName, emojiCode, hasUserReacted) => { const users = getPersonalDetailsByIDs(reactionUsers); ReactionList.showReactionList( - e, + event, this.popoverReactionListAnchor, users, emojiName, + emojiCode, + hasUserReacted, ); }; return ( diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js index e6a808e180e9..de4c18f7fa9d 100644 --- a/src/styles/StyleUtils.js +++ b/src/styles/StyleUtils.js @@ -824,6 +824,13 @@ function getEmojiReactionBubbleStyle(isHovered, hasUserReacted, sizeScale = 1) { return sizeStyles; } +function getEmojiReactionListHeaderBubbleStyle(hasUserReacted) { + if (hasUserReacted) { + return {backgroundColor: themeColors.reactionActive}; + } + + return {backgroundColor: themeColors.border}; +} function getEmojiReactionTextStyle(sizeScale = 1) { return { @@ -893,4 +900,5 @@ export { getEmojiReactionBubbleStyle, getEmojiReactionTextStyle, getEmojiReactionCounterTextStyle, + getEmojiReactionListHeaderBubbleStyle, }; diff --git a/src/styles/styles.js b/src/styles/styles.js index e1d5c14a3c30..90d7599c198a 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -20,6 +20,7 @@ import pointerEventsNone from './pointerEventsNone'; import pointerEventsAuto from './pointerEventsAuto'; import overflowXHidden from './overflowXHidden'; import CONST from '../CONST'; +import colors from './colors'; const picker = { backgroundColor: themeColors.transparent, @@ -2926,6 +2927,31 @@ const styles = { marginRight: 4, }, + emojiReactionListHeader: { + marginTop: 8, + paddingBottom: 20, + borderBottomColor: colors.greenBorders, + borderBottomWidth: 1, + marginHorizontal: 20, + }, + emojiReactionListHeaderBubble: { + paddingVertical: 2, + paddingHorizontal: 8, + borderRadius: 28, + backgroundColor: themeColors.border, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + marginRight: 4, + }, + reactionListItem: { + display: 'flex', + flexDirection: 'row', + paddingVertical: 12, + paddingHorizontal: 20, + }, + emojiReactionText: { fontSize: 12, lineHeight: 20, @@ -2960,6 +2986,13 @@ const styles = { justifyContent: 'space-between', }, + reactionListContainer: { + width: '100%', + minWidth: 350, + minHeight: 100, + maxHeight: 450, + }, + magicCodeDigits: { color: themeColors.text, fontFamily: fontFamily.EXP_NEUE, From 5732ada539363f2ae10fffe35e74efe6f91edcb0 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 7 Mar 2023 13:51:01 +0100 Subject: [PATCH 03/24] add isLongPressEnabledWithHover to PressableWithSecondaryInteraction --- src/components/PressableWithSecondaryInteraction/index.js | 2 +- .../pressableWithSecondaryInteractionPropTypes.js | 1 + src/components/Reactions/EmojiReactionBubble.js | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js index f3ea25a471fd..05bc4822b2a5 100644 --- a/src/components/PressableWithSecondaryInteraction/index.js +++ b/src/components/PressableWithSecondaryInteraction/index.js @@ -57,7 +57,7 @@ class PressableWithSecondaryInteraction extends Component { style={this.props.inline && styles.dInline} onPressIn={this.props.onPressIn} onLongPress={(e) => { - if (DeviceCapabilities.hasHoverSupport()) { + if (DeviceCapabilities.hasHoverSupport() && !this.props.isLongPressEnabledWithHover) { return; } if (this.props.withoutFocusOnSecondaryInteraction && this.pressableRef) { diff --git a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js index bffe11bc4cd8..0a2d887affcf 100644 --- a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js +++ b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js @@ -43,6 +43,7 @@ const defaultProps = { preventDefaultContentMenu: true, inline: false, withoutFocusOnSecondaryInteraction: false, + isLongPressEnabledWithHover: false, }; export {propTypes, defaultProps}; diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 46bd98463bb8..de103b438747 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -7,6 +7,7 @@ import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetai import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; +import withWindowDimensions from '../withWindowDimensions'; const propTypes = { emojiName: PropTypes.string.isRequired, @@ -75,6 +76,7 @@ const EmojiReactionBubble = props => ( onLongPress={props.onReactionListOpen} onSecondaryInteraction={props.onReactionListOpen} ref={props.forwardedRef} + isLongPressEnabledWithHover={props.isSmallScreenWidth} > ( +export default withWindowDimensions(React.forwardRef((props, ref) => ( /* eslint-disable-next-line react/jsx-props-no-spreading */ -)); +))); From 72d53e953d2740d46759d923641327184b896c73 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 7 Mar 2023 14:16:54 +0100 Subject: [PATCH 04/24] reactionCount --- .../Reactions/ReportActionItemReactions.js | 2 +- .../home/report/ReactionList/BaseReactionList.js | 1 + .../report/ReactionList/HeaderReactionList/index.js | 9 +++++++-- .../ReactionList/HeaderReactionList/index.native.js | 9 +++++++-- .../home/report/ReactionList/PopoverReactionList.js | 12 +++++++++--- src/pages/home/report/ReportActionItem.js | 3 ++- src/styles/styles.js | 5 +++++ 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index b644c3af5ecb..fced9f6d7cd3 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -77,7 +77,7 @@ const ReportActionItemReactions = props => ( } const onReactionListOpen = (event) => { - props.onReactionListOpen(event, reactionUsers, reaction.emoji, emojiCodes, hasUserReacted); + props.onReactionListOpen(event, reactionUsers, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); }; return ( diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index fcbc2839754a..42bfc68ee311 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -21,6 +21,7 @@ const BaseReactionList = props => (props.isVisible) && ( onClose={props.onClose} emojiName={props.emojiName} emojiCodes={props.emojiCodes} + emojiCount={props.emojiCount} hasUserReacted={props.hasUserReacted} sizeScale={props.sizeScale} /> diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.js index 8dde2fe665f0..979a220424d4 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import styles from '../../../../../styles/styles'; import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; import Text from '../../../../../components/Text'; -import colors from '../../../../../styles/colors'; import * as StyleUtils from '../../../../../styles/StyleUtils'; const propTypes = { @@ -15,6 +14,9 @@ const propTypes = { /** The name of the emoji */ emojiName: PropTypes.string.isRequired, + /** Count of the emoji */ + emojiCount: PropTypes.number.isRequired, + /** * The default size of the reaction bubble is defined * by the styles in styles.js. This scale factor can be used @@ -36,8 +38,11 @@ const HeaderReactionList = props => ( {props.emojiCodes.join('')} + + {props.emojiCount} + - {`:${props.emojiName}:`} + {`:${props.emojiName}:`} ); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js index 82b2fb12f578..08e52fd10d06 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js @@ -6,7 +6,6 @@ import withLocalize, {withLocalizePropTypes} from '../../../../../components/wit import Text from '../../../../../components/Text'; import Icon from '../../../../../components/Icon'; import * as Expensicons from '../../../../../components/Icon/Expensicons'; -import colors from '../../../../../styles/colors'; import * as StyleUtils from '../../../../../styles/StyleUtils'; const propTypes = { @@ -19,6 +18,9 @@ const propTypes = { /** The name of the emoji */ emojiName: PropTypes.string.isRequired, + /** Count of the emoji */ + emojiCount: PropTypes.number.isRequired, + /** * The default size of the reaction bubble is defined * by the styles in styles.js. This scale factor can be used @@ -40,8 +42,11 @@ const HeaderReactionList = props => ( {props.emojiCodes.join('')} + + {props.emojiCount} + - {`:${props.emojiName}:`} + {`:${props.emojiName}:`} 0; - const onReactionListOpen = (event, reactionUsers, emojiName, emojiCode, hasUserReacted) => { + const onReactionListOpen = (event, reactionUsers, emojiName, emojiCode, emojiCount, hasUserReacted) => { const users = getPersonalDetailsByIDs(reactionUsers); ReactionList.showReactionList( event, @@ -214,6 +214,7 @@ class ReportActionItem extends Component { users, emojiName, emojiCode, + emojiCount, hasUserReacted, ); }; diff --git a/src/styles/styles.js b/src/styles/styles.js index 90d7599c198a..b1f464c80923 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2951,6 +2951,11 @@ const styles = { paddingVertical: 12, paddingHorizontal: 20, }, + reactionListHeaderText: { + color: colors.greenSupportingText, + marginTop: 1, + marginLeft: 8, + }, emojiReactionText: { fontSize: 12, From 02806361dc6380bfb4f8f08ef7bc73cc0d15fa1c Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 7 Mar 2023 16:50:28 +0100 Subject: [PATCH 05/24] typo --- src/pages/home/report/ReactionList/PopoverReactionList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index 4cd5279ab860..d69705bcb301 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -92,12 +92,12 @@ class PopoverReactionList extends React.Component { * Show the ReportActionContextMenu modal popover. * * @param {Object} [event] - A press event. - * @param {Element} contextMenuAnchor - opoverAnchor + * @param {Element} contextMenuAnchor - contextMenuAnchor * @param {Array} users - Array of users id * @param {String} emojiName - Name of emoji * @param {Array} emojiCodes - The emoji codes to display in the bubble. * @param {Number} emojiCount - Count of emoji - * @param {Boolean} [hasUserReacted] - whether the current user has reacted to this emoji + * @param {Boolean} hasUserReacted - whether the current user has reacted to this emoji */ showReactionList( From c66388b1aaff6118a47d06531268f9c0a9e5e745 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 10 Mar 2023 19:58:05 +0100 Subject: [PATCH 06/24] import getPersonalDetailsByIDs --- src/pages/home/report/ReportActionItem.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e655811810bc..a914bf09ee50 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -38,7 +38,7 @@ import focusTextInputAfterAnimation from '../../../libs/focusTextInputAfterAnima import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions'; import * as ReactionList from './ReactionList/ReactionList'; import * as Report from '../../../libs/actions/Report'; -import getPersonalDetailsByIDs from '../../../libs/getPersonalDetailsByIDs'; +import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils'; const propTypes = { /** Report for this action */ @@ -207,7 +207,7 @@ class ReportActionItem extends Component { const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); const hasReactions = reactions.length > 0; const onReactionListOpen = (event, reactionUsers, emojiName, emojiCode, emojiCount, hasUserReacted) => { - const users = getPersonalDetailsByIDs(reactionUsers); + const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers); ReactionList.showReactionList( event, this.popoverReactionListAnchor, From 0317bd7a89f65e3c357dac205feeb1d9b62e1441 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 10 Mar 2023 20:23:03 +0100 Subject: [PATCH 07/24] take hasUserReacted from Report --- src/components/Reactions/EmojiReactionBubble.js | 2 +- src/components/Reactions/ReportActionItemReactions.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 7c0a69e4386d..67371e2a716f 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -37,7 +37,7 @@ const propTypes = { /** * The account ids of the users who reacted. */ - reactionUsers: PropTypes.arrayOf(PropTypes.string), + reactionUsers: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])), /** * The default size of the reaction bubble is defined diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index a0ae30c6603b..39e4ec537d72 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -8,6 +8,7 @@ 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'; /** * Given an emoji object and a list of senders it will return an @@ -79,7 +80,7 @@ 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 = _.includes(reactionUsers, `${props.currentUserPersonalDetails.accountID}`); + const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); const onPress = () => { props.toggleReaction(emoji); From e36ba937e36cd204ad7e92dc5daddcb8b51a7d38 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 10 Mar 2023 20:35:16 +0100 Subject: [PATCH 08/24] Resolving a merge conflict --- src/pages/home/report/ReportActionsView.js | 39 ++++++++++------------ 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 454fd739dc4b..837a6df28cfb 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -23,7 +23,6 @@ import EmojiPicker from '../../../components/EmojiPicker/EmojiPicker'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; import * as ReportUtils from '../../../libs/ReportUtils'; import reportPropTypes from '../../reportPropTypes'; - import * as ReactionList from './ReactionList/ReactionList'; import PopoverReactionList from './ReactionList/PopoverReactionList'; @@ -349,27 +348,23 @@ class ReportActionsView extends React.Component { } return ( <> - {!this.props.isComposerFullSize && ( - <> - - - - - )} + + + From 502920e90da5cfd0f1152bdce09f4ca77b95c045 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 Mar 2023 17:31:22 +0100 Subject: [PATCH 09/24] remove container --- .../Reactions/EmojiReactionBubble.js | 3 +- .../report/ReactionList/BaseReactionList.js | 5 ++-- .../ReactionList/HeaderReactionList/index.js | 20 ++++++------- .../ReactionList/PopoverReactionList.js | 30 +++++++++---------- .../home/report/ReactionList/ReactionList.js | 20 ++++++------- 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index b468fc3cfec3..cc1a7e8ffa22 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; - import styles from '../../styles/styles'; import Text from '../Text'; import * as StyleUtils from '../../styles/StyleUtils'; @@ -37,7 +36,7 @@ const propTypes = { /** * The account ids of the users who reacted. */ - reactionUsers: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])), + reactionUsers: PropTypes.arrayOf(PropTypes.string), /** * The default size of the reaction bubble is defined diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index 42bfc68ee311..6ab92141581f 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -14,6 +14,7 @@ const defaultProps = { }; const renderItem = ({item}) => ; +const keyExtractor = item => `${item.accountID}`; const BaseReactionList = props => (props.isVisible) && ( @@ -28,8 +29,8 @@ const BaseReactionList = props => (props.isVisible) && ( `${item.accountID}`} - style={[styles.mb3, styles.mt2]} + keyExtractor={keyExtractor} + style={[styles.pb3, styles.pt2]} /> ); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.js index 979a220424d4..380eb02ce08c 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.js @@ -32,18 +32,16 @@ const defaultProps = { }; const HeaderReactionList = props => ( - - - - - {props.emojiCodes.join('')} - - - {props.emojiCount} - - - {`:${props.emojiName}:`} + + + + {props.emojiCodes.join('')} + + + {props.emojiCount} + + {`:${props.emojiName}:`} ); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index d69705bcb301..158986367096 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -36,13 +36,13 @@ class PopoverReactionList extends React.Component { }; this.onPopoverHideActionCallback = () => {}; - this.contextMenuAnchor = undefined; + this.reactionListAnchor = undefined; this.showReactionList = this.showReactionList.bind(this); this.hideReactionList = this.hideReactionList.bind(this); this.measureContent = this.measureContent.bind(this); this.measureReactionListPosition = this.measureReactionListPosition.bind(this); - this.getContextMenuMeasuredLocation = this.getContextMenuMeasuredLocation.bind(this); + this.getReactionListMeasuredLocation = this.getReactionListMeasuredLocation.bind(this); this.dimensionsEventListener = null; @@ -73,15 +73,15 @@ class PopoverReactionList extends React.Component { } /** - * Get the Context menu anchor position + * Get the PopoverReactionList anchor position * We calculate the achor coordinates from measureInWindow async method * * @returns {Promise} */ - getContextMenuMeasuredLocation() { + getReactionListMeasuredLocation() { return new Promise((resolve) => { - if (this.contextMenuAnchor) { - this.contextMenuAnchor.measureInWindow((x, y) => resolve({x, y})); + if (this.reactionListAnchor) { + this.reactionListAnchor.measureInWindow((x, y) => resolve({x, y})); } else { resolve({x: 0, y: 0}); } @@ -89,10 +89,10 @@ class PopoverReactionList extends React.Component { } /** - * Show the ReportActionContextMenu modal popover. + * Show the ReactionList modal popover. * * @param {Object} [event] - A press event. - * @param {Element} contextMenuAnchor - contextMenuAnchor + * @param {Element} reactionListAnchor - reactionListAnchor * @param {Array} users - Array of users id * @param {String} emojiName - Name of emoji * @param {Array} emojiCodes - The emoji codes to display in the bubble. @@ -102,7 +102,7 @@ class PopoverReactionList extends React.Component { */ showReactionList( event, - contextMenuAnchor, + reactionListAnchor, users, emojiName, emojiCodes, @@ -111,12 +111,12 @@ class PopoverReactionList extends React.Component { ) { const nativeEvent = event.nativeEvent || {}; - this.contextMenuAnchor = contextMenuAnchor; + this.reactionListAnchor = reactionListAnchor; - // Singleton behaviour of ContextMenu creates race conditions when user requests multiple contextMenus. + // Singleton behaviour of ReactionListPopover creates race conditions when user requests multiple ReactionListPopover. // But it is possible that every new request registers new callbacks thus instanceID is used to corelate those callbacks this.instanceID = Math.random().toString(36).substr(2, 5); - this.getContextMenuMeasuredLocation().then(({x, y}) => { + this.getReactionListMeasuredLocation().then(({x, y}) => { this.setState({ cursorRelativePosition: { horizontal: nativeEvent.pageX - x, @@ -137,13 +137,13 @@ class PopoverReactionList extends React.Component { } /** - * This gets called on Dimensions change to find the anchor coordinates for the action context menu. + * This gets called on Dimensions change to find the anchor coordinates for the action PopoverReactionList. */ measureReactionListPosition() { if (!this.state.isPopoverVisible) { return; } - this.getContextMenuMeasuredLocation().then(({x, y}) => { + this.getReactionListMeasuredLocation().then(({x, y}) => { if (!x || !y) { return; } @@ -166,7 +166,7 @@ class PopoverReactionList extends React.Component { } /** - * Used to calculate the Context Menu Dimensions + * Used to calculate the PopoverReactionList Dimensions * * @returns {JSX} */ diff --git a/src/pages/home/report/ReactionList/ReactionList.js b/src/pages/home/report/ReactionList/ReactionList.js index e204e831eb04..3451c74fb0c1 100644 --- a/src/pages/home/report/ReactionList/ReactionList.js +++ b/src/pages/home/report/ReactionList/ReactionList.js @@ -3,19 +3,19 @@ import React from 'react'; const reactionListRef = React.createRef(); /** - * Show the ReportActionContextMenu modal popover. + * Show the ReactionList popover modal popover. * - * @param {Object} [event] - A press event. - * @param {Element} contextMenuAnchor - popoverAnchor + * @param {Object} [event] - a press event. + * @param {Element} reactionListPopoverAnchor - popoverAnchor * @param {Array} users - array of users id - * @param {String} emojiName - he emoji codes to display near the bubble. + * @param {String} emojiName - the emoji codes to display near the bubble. * @param {String} emojiCodes - the emoji codes to display in the bubble. - * @param {Boolean} hasUserReacted - Show if user has reacted - * @param {Number} sizeScale - Set the sizeScale of emoji + * @param {Boolean} hasUserReacted - show if user has reacted + * @param {Number} sizeScale - set the sizeScale of emoji */ function showReactionList( event, - contextMenuAnchor, + reactionListPopoverAnchor, users, emojiName, emojiCodes, @@ -27,7 +27,7 @@ function showReactionList( } reactionListRef.current.showReactionList( event, - contextMenuAnchor, + reactionListPopoverAnchor, users, emojiName, emojiCodes, @@ -37,10 +37,10 @@ function showReactionList( } /** - * Hide the ReportActionContextMenu modal popover. + * Hide the ReactionList popover. * Hides the popover menu with an optional delay * @param {Boolean} shouldDelay - whether the menu should close after a delay - * @param {Function} [onHideCallback=() => {}] - Callback to be called after Context Menu is completely hidden + * @param {Function} [onHideCallback=() => {}] - Callback to be called after popover is completely hidden */ function hideReactionList(shouldDelay, onHideCallback = () => {}) { if (!reactionListRef.current) { From 6bc54ceb9d8a5adb58a0ead273d24074570105ee Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 20 Mar 2023 13:02:27 +0100 Subject: [PATCH 10/24] update propTypes --- src/components/Reactions/EmojiReactionBubble.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index cc1a7e8ffa22..a0aeb50d92e6 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -7,7 +7,7 @@ import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetai import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; -import withWindowDimensions from '../withWindowDimensions'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; const propTypes = { emojiName: PropTypes.string.isRequired, @@ -45,7 +45,13 @@ const propTypes = { */ sizeScale: PropTypes.number, + /** + * Returns true if the current account has reacted to the report action (with the given skin tone). + */ + hasUserReacted: PropTypes.bool, + ...withCurrentUserPersonalDetailsPropTypes, + ...windowDimensionsPropTypes, }; const defaultProps = { @@ -53,6 +59,7 @@ const defaultProps = { onReactionListOpen: () => {}, reactionUsers: [], sizeScale: 1, + hasUserReacted: false, ...withCurrentUserPersonalDetailsDefaultProps, }; @@ -82,7 +89,6 @@ const EmojiReactionBubble = props => ( styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale), ]} - > {props.emojiCodes.join('')} From e96d82b0881d2c6093370b6a3bfb745df1474bc7 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 20 Mar 2023 17:45:36 +0100 Subject: [PATCH 11/24] changes after the review --- src/CONST.js | 1 + .../Reactions/EmojiReactionBubble.js | 2 +- .../Reactions/ReportActionItemReactions.js | 7 +- .../report/ReactionList/BaseReactionList.js | 69 ++++++++++++++----- .../ReactionList/HeaderReactionList/index.js | 29 ++------ .../HeaderReactionList/index.native.js | 27 ++------ .../HeaderReactionList/reactionPropTypes.js | 28 ++++++++ .../ReactionList/PopoverReactionList.js | 6 +- .../report/ReactionList/ReactionListItem.js | 51 +++++++------- src/styles/StyleUtils.js | 15 ++++ 10 files changed, 136 insertions(+), 99 deletions(-) create mode 100644 src/pages/home/report/ReactionList/HeaderReactionList/reactionPropTypes.js diff --git a/src/CONST.js b/src/CONST.js index a99bb0cfd713..e58caff0b6a5 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -576,6 +576,7 @@ const CONST = { }, NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT: 256, EMOJI_PICKER_ITEM_HEIGHT: 32, + REACTION_LIST_ITEM_HEIGHT: 64, EMOJI_PICKER_HEADER_HEIGHT: 32, RECIPIENT_LOCAL_TIME_HEIGHT: 25, EMOJI_SUGGESTER: { diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index a0aeb50d92e6..71cb8bbadb17 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -82,7 +82,7 @@ const EmojiReactionBubble = props => ( onPress={props.onPress} onLongPress={props.onReactionListOpen} onSecondaryInteraction={props.onReactionListOpen} - ref={props.forwardedRef} + ref={props.forwardRef} isLongPressEnabledWithHover={props.isSmallScreenWidth} > { return emojiCodes; }; -const defaultProps = { - ...withCurrentUserPersonalDetailsDefaultProps, -}; - const propTypes = { /** * An array of objects containing the reaction data. @@ -67,7 +63,10 @@ const propTypes = { onReactionListOpen: PropTypes.func.isRequired, ...withCurrentUserPersonalDetailsPropTypes, +}; +const defaultProps = { + ...withCurrentUserPersonalDetailsDefaultProps, }; const ReportActionItemReactions = (props) => { diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index 6ab92141581f..938efbe12696 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -1,39 +1,70 @@ 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, + + ...reactionPropTypes, ...withLocalizePropTypes, }; const defaultProps = { - contentRef: null, + ...reactionDefaultProps, }; const renderItem = ({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) && ( - - - - -); +// const BaseReactionList = props => (props.isVisible) && ( +const BaseReactionList = (props) => { + if (!props.isVisible) { + return null; + } + return ( + + + + + ); +}; BaseReactionList.propTypes = propTypes; BaseReactionList.defaultProps = defaultProps; diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.js index 380eb02ce08c..03692f3f0b62 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.js @@ -1,34 +1,19 @@ import React from 'react'; import {View} from 'react-native'; -import PropTypes from 'prop-types'; import styles from '../../../../../styles/styles'; -import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; import Text from '../../../../../components/Text'; import * as StyleUtils from '../../../../../styles/StyleUtils'; +import { + propTypes as reactionPropTypes, + defaultProps as reactionDefaultProps, +} from './reactionPropTypes'; const propTypes = { - /** Selected emoji */ - /** The emoji codes */ - emojiCodes: PropTypes.arrayOf(PropTypes.string).isRequired, - - /** The name of the emoji */ - emojiName: PropTypes.string.isRequired, - - /** Count of the emoji */ - emojiCount: PropTypes.number.isRequired, - - /** - * The default size of the reaction bubble is defined - * by the styles in styles.js. This scale factor can be used - * to make the bubble bigger or smaller. - */ - sizeScale: PropTypes.number, - - ...withLocalizePropTypes, + ...reactionPropTypes, }; const defaultProps = { - sizeScale: 1, + ...reactionDefaultProps, }; const HeaderReactionList = props => ( @@ -49,5 +34,5 @@ HeaderReactionList.propTypes = propTypes; HeaderReactionList.defaultProps = defaultProps; HeaderReactionList.displayName = 'HeaderReactionList'; -export default withLocalize(HeaderReactionList); +export default HeaderReactionList; diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js index 08e52fd10d06..b1d6f9da6e79 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js @@ -1,38 +1,23 @@ import React from 'react'; import {View, TouchableOpacity} from 'react-native'; -import PropTypes from 'prop-types'; 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 = { - /** Children view component for this action item */ - onClose: PropTypes.func.isRequired, - - /** The emoji codes */ - emojiCodes: PropTypes.arrayOf(PropTypes.string).isRequired, - - /** The name of the emoji */ - emojiName: PropTypes.string.isRequired, - - /** Count of the emoji */ - emojiCount: PropTypes.number.isRequired, - - /** - * The default size of the reaction bubble is defined - * by the styles in styles.js. This scale factor can be used - * to make the bubble bigger or smaller. - */ - sizeScale: PropTypes.number, - + ...reactionPropTypes, ...withLocalizePropTypes, }; const defaultProps = { - sizeScale: 1, + ...reactionDefaultProps, }; const HeaderReactionList = props => ( diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/reactionPropTypes.js b/src/pages/home/report/ReactionList/HeaderReactionList/reactionPropTypes.js new file mode 100644 index 000000000000..472497eea845 --- /dev/null +++ b/src/pages/home/report/ReactionList/HeaderReactionList/reactionPropTypes.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** Hide the ReactionList modal popover */ + onClose: PropTypes.func, + + /** The emoji codes */ + emojiCodes: PropTypes.arrayOf(PropTypes.string).isRequired, + + /** The name of the emoji */ + emojiName: PropTypes.string.isRequired, + + /** Count of the emoji */ + emojiCount: PropTypes.number.isRequired, + + /** + * The default size of the reaction bubble is defined + * by the styles in styles.js. This scale factor can be used + * to make the bubble bigger or smaller. + */ + sizeScale: PropTypes.number, +}; + +const defaultProps = { + sizeScale: 1, +}; + +export {propTypes, defaultProps}; diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index 158986367096..689508efeb87 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -47,10 +47,6 @@ class PopoverReactionList extends React.Component { this.dimensionsEventListener = null; this.contentRef = React.createRef(); - this.setContentRef = (ref) => { - this.contentRef.current = ref; - }; - this.setContentRef = this.setContentRef.bind(this); } componentDidMount() { @@ -93,7 +89,7 @@ class PopoverReactionList extends React.Component { * * @param {Object} [event] - A press event. * @param {Element} reactionListAnchor - reactionListAnchor - * @param {Array} users - Array of users id + * @param {Array} users - Array of personal detail objects * @param {String} emojiName - Name of emoji * @param {Array} emojiCodes - The emoji codes to display in the bubble. * @param {Number} emojiCount - Count of emoji diff --git a/src/pages/home/report/ReactionList/ReactionListItem.js b/src/pages/home/report/ReactionList/ReactionListItem.js index 06d3047581a9..e7d242d130f7 100644 --- a/src/pages/home/report/ReactionList/ReactionListItem.js +++ b/src/pages/home/report/ReactionList/ReactionListItem.js @@ -9,6 +9,8 @@ import withLocalize, {withLocalizePropTypes} from '../../../../components/withLo import {withPersonalDetails} from '../../../../components/OnyxProvider'; import ControlSelection from '../../../../libs/ControlSelection'; import Text from '../../../../components/Text'; +import participantPropTypes from '../../../../components/participantPropTypes'; +import * as ReportUtils from '../../../../libs/ReportUtils'; const propTypes = { /** Styles for the outermost View */ @@ -16,16 +18,7 @@ const propTypes = { wrapperStyles: PropTypes.arrayOf(PropTypes.object), /** Personal details of the user */ - item: PropTypes.shape({ - // Display Name of participant - displayName: PropTypes.string, - - // Avatar url of participant - avatar: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), - - /** First Name of the participant */ - firstName: PropTypes.string, - }).isRequired, + item: participantPropTypes.isRequired, ...withLocalizePropTypes, }; @@ -34,24 +27,28 @@ const defaultProps = { wrapperStyles: [styles.reactionListItem], }; -const ReactionListItem = props => ( - - - - - - {props.item.displayName} - {props.item.login} +const ReactionListItem = (props) => { + const avatarSource = ReportUtils.getAvatar(props.item.avatar, props.item.login); + + return ( + + + + + + {props.item.displayName} + {props.item.login} + - -); + ); +}; ReactionListItem.propTypes = propTypes; ReactionListItem.defaultProps = defaultProps; diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js index 83ad6f95415f..f12f38b9fe5f 100644 --- a/src/styles/StyleUtils.js +++ b/src/styles/StyleUtils.js @@ -911,6 +911,20 @@ function getEmojiReactionCounterTextStyle(hasUserReacted, sizeScale = 1) { return sizeStyles; } +/** + * Returns a style object with a rotation transformation applied based on the provided direction prop. + * + * @param {string} direction - The direction of the rotation (CONST.DIRECTION.LEFT or CONST.DIRECTION.RIGHT). + * @returns {Object} + */ +function getDirectionStyle(direction) { + if (direction === CONST.DIRECTION.LEFT) { + return {transform: [{rotate: '180deg'}]}; + } + + return {}; +} + export { getAvatarSize, getAvatarStyle, @@ -961,4 +975,5 @@ export { getEmojiReactionTextStyle, getEmojiReactionCounterTextStyle, getEmojiReactionListHeaderBubbleStyle, + getDirectionStyle, }; From e52808706e9a234a4cb48bcdb0ea162748929eb7 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 24 Mar 2023 19:11:50 +0100 Subject: [PATCH 12/24] remove unnecessary check for instance ID --- .../ReactionList/PopoverReactionList.js | 3 --- .../home/report/ReactionList/ReactionList.js | 21 ++----------------- .../report/ReactionList/ReactionListItem.js | 8 ++----- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index 689508efeb87..0644b249e11a 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -109,9 +109,6 @@ class PopoverReactionList extends React.Component { this.reactionListAnchor = reactionListAnchor; - // Singleton behaviour of ReactionListPopover creates race conditions when user requests multiple ReactionListPopover. - // But it is possible that every new request registers new callbacks thus instanceID is used to corelate those callbacks - this.instanceID = Math.random().toString(36).substr(2, 5); this.getReactionListMeasuredLocation().then(({x, y}) => { this.setState({ cursorRelativePosition: { diff --git a/src/pages/home/report/ReactionList/ReactionList.js b/src/pages/home/report/ReactionList/ReactionList.js index 3451c74fb0c1..fdff886b3ccb 100644 --- a/src/pages/home/report/ReactionList/ReactionList.js +++ b/src/pages/home/report/ReactionList/ReactionList.js @@ -38,31 +38,14 @@ function showReactionList( /** * Hide the ReactionList popover. - * Hides the popover menu with an optional delay - * @param {Boolean} shouldDelay - whether the menu should close after a delay * @param {Function} [onHideCallback=() => {}] - Callback to be called after popover is completely hidden */ -function hideReactionList(shouldDelay, onHideCallback = () => {}) { +function hideReactionList(onHideCallback = () => {}) { if (!reactionListRef.current) { return; } - if (!shouldDelay) { - reactionListRef.current.hideReactionList(onHideCallback); - return; - } - - // Save the active instanceID for which hide action was called. - // If menu is being closed with a delay, check that whether the same instance exists or a new was created. - // If instance is not same, cancel the hide action - const instanceID = reactionListRef.current.instanceID; - setTimeout(() => { - if (reactionListRef.current.instanceID !== instanceID) { - return; - } - - reactionListRef.current.hideReactionList(onHideCallback); - }, 800); + reactionListRef.current.hideReactionList(onHideCallback); } export { diff --git a/src/pages/home/report/ReactionList/ReactionListItem.js b/src/pages/home/report/ReactionList/ReactionListItem.js index e7d242d130f7..b698363504b0 100644 --- a/src/pages/home/report/ReactionList/ReactionListItem.js +++ b/src/pages/home/report/ReactionList/ReactionListItem.js @@ -32,16 +32,12 @@ const ReactionListItem = (props) => { return ( - + - + {props.item.displayName} {props.item.login} From b91f52c3620267021681074260ec01d3108785be Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 24 Mar 2023 19:34:00 +0100 Subject: [PATCH 13/24] remove space --- src/components/Reactions/EmojiReactionBubble.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 01bc5c26862d..835b57d7bae6 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import Text from '../Text'; import * as StyleUtils from '../../styles/StyleUtils'; - import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; From 524546bfebd1a2b3c2f8e7d44f34f56f605698ef Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 27 Mar 2023 11:27:47 +0200 Subject: [PATCH 14/24] removed unused eslint rules which appeared after merge --- src/components/createOnyxContext.js | 1 - src/pages/home/report/ReactionList/ReactionListItem.js | 3 +-- src/pages/home/sidebar/SidebarLinks.js | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index a64d17e9c55b..7b8d8b689830 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -20,7 +20,6 @@ export default (onyxKeyName) => { Provider.propTypes = propTypes; Provider.displayName = `${Str.UCFirst(onyxKeyName)}Provider`; - // eslint-disable-next-line rulesdir/onyx-props-must-have-default const ProviderWithOnyx = withOnyx({ [onyxKeyName]: { key: onyxKeyName, diff --git a/src/pages/home/report/ReactionList/ReactionListItem.js b/src/pages/home/report/ReactionList/ReactionListItem.js index b698363504b0..3352ae89486e 100644 --- a/src/pages/home/report/ReactionList/ReactionListItem.js +++ b/src/pages/home/report/ReactionList/ReactionListItem.js @@ -1,5 +1,5 @@ import React from 'react'; -import {View, Pressable} from 'react-native'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../../../styles/styles'; import colors from '../../../../styles/colors'; @@ -7,7 +7,6 @@ import Avatar from '../../../../components/Avatar'; import compose from '../../../../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; import {withPersonalDetails} from '../../../../components/OnyxProvider'; -import ControlSelection from '../../../../libs/ControlSelection'; import Text from '../../../../components/Text'; import participantPropTypes from '../../../../components/participantPropTypes'; import * as ReportUtils from '../../../../libs/ReportUtils'; diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index 20e67cd4d56c..8efd0412c9f9 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -1,4 +1,3 @@ -/* eslint-disable rulesdir/onyx-props-must-have-default */ import lodashGet from 'lodash/get'; import React from 'react'; import {View, TouchableOpacity} from 'react-native'; From b4343a8dc1806dc1fc9a3ad8924196e17eeca329 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 27 Mar 2023 11:39:18 +0200 Subject: [PATCH 15/24] add onyx eslint rules --- src/components/createOnyxContext.js | 1 + src/pages/home/sidebar/SidebarLinks.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index 7b8d8b689830..a64d17e9c55b 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -20,6 +20,7 @@ export default (onyxKeyName) => { Provider.propTypes = propTypes; Provider.displayName = `${Str.UCFirst(onyxKeyName)}Provider`; + // eslint-disable-next-line rulesdir/onyx-props-must-have-default const ProviderWithOnyx = withOnyx({ [onyxKeyName]: { key: onyxKeyName, diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index 8efd0412c9f9..20e67cd4d56c 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -1,3 +1,4 @@ +/* eslint-disable rulesdir/onyx-props-must-have-default */ import lodashGet from 'lodash/get'; import React from 'react'; import {View, TouchableOpacity} from 'react-native'; From 6ebe8acf902c79a6dacb31ca32ed8a8e72fcefd6 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 30 Mar 2023 10:52:34 +0200 Subject: [PATCH 16/24] update keyExtractor key --- src/pages/home/report/ReactionList/BaseReactionList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index 938efbe12696..058712a95729 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -33,7 +33,7 @@ const defaultProps = { }; const renderItem = ({item}) => ; -const keyExtractor = item => `${item.accountID}`; +const keyExtractor = (item, index) => `${item.login}+${index}`; const getItemLayout = (_, index) => ({ index, length: CONST.REACTION_LIST_ITEM_HEIGHT, From 308f6735b29597559c6e864db2900e759fc9bb5b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 30 Mar 2023 13:04:10 +0200 Subject: [PATCH 17/24] remove mobile HeaderReactionList --- .../report/ReactionList/BaseReactionList.js | 2 +- .../index.native.js => HeaderReactionList.js} | 23 +++++++---- .../ReactionList/HeaderReactionList/index.js | 38 ------------------- .../reactionPropTypes.js | 0 4 files changed, 16 insertions(+), 47 deletions(-) rename src/pages/home/report/ReactionList/{HeaderReactionList/index.native.js => HeaderReactionList.js} (67%) delete mode 100644 src/pages/home/report/ReactionList/HeaderReactionList/index.js rename src/pages/home/report/ReactionList/{HeaderReactionList => }/reactionPropTypes.js (100%) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index 058712a95729..b59835310cde 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -10,7 +10,7 @@ import participantPropTypes from '../../../../components/participantPropTypes'; import { propTypes as reactionPropTypes, defaultProps as reactionDefaultProps, -} from './HeaderReactionList/reactionPropTypes'; +} from './reactionPropTypes'; const propTypes = { diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js b/src/pages/home/report/ReactionList/HeaderReactionList.js similarity index 67% rename from src/pages/home/report/ReactionList/HeaderReactionList/index.native.js rename to src/pages/home/report/ReactionList/HeaderReactionList.js index b1d6f9da6e79..c49cce4f1c91 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.native.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList.js @@ -1,15 +1,17 @@ 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 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'; +import compose from '../../../../libs/compose'; +import withWindowDimensions from '../../../../components/withWindowDimensions'; const propTypes = { ...reactionPropTypes, @@ -21,7 +23,7 @@ const defaultProps = { }; const HeaderReactionList = props => ( - + @@ -34,6 +36,7 @@ const HeaderReactionList = props => ( {`:${props.emojiName}:`} + {props.isSmallScreenWidth && ( ( > + )} ); @@ -48,5 +52,8 @@ HeaderReactionList.propTypes = propTypes; HeaderReactionList.defaultProps = defaultProps; HeaderReactionList.displayName = 'HeaderReactionList'; -export default withLocalize(HeaderReactionList); +export default compose( + withWindowDimensions, + withLocalize, +)(HeaderReactionList); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/index.js b/src/pages/home/report/ReactionList/HeaderReactionList/index.js deleted file mode 100644 index 03692f3f0b62..000000000000 --- a/src/pages/home/report/ReactionList/HeaderReactionList/index.js +++ /dev/null @@ -1,38 +0,0 @@ -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 => ( - - - - {props.emojiCodes.join('')} - - - {props.emojiCount} - - - {`:${props.emojiName}:`} - -); - -HeaderReactionList.propTypes = propTypes; -HeaderReactionList.defaultProps = defaultProps; -HeaderReactionList.displayName = 'HeaderReactionList'; - -export default HeaderReactionList; - diff --git a/src/pages/home/report/ReactionList/HeaderReactionList/reactionPropTypes.js b/src/pages/home/report/ReactionList/reactionPropTypes.js similarity index 100% rename from src/pages/home/report/ReactionList/HeaderReactionList/reactionPropTypes.js rename to src/pages/home/report/ReactionList/reactionPropTypes.js From b2a1c657a0403b1e3f43f5e79cda4d083604ba32 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Apr 2023 11:54:04 +0200 Subject: [PATCH 18/24] style --- .../home/report/ReactionList/BaseReactionList.js | 2 +- .../home/report/ReactionList/HeaderReactionList.js | 14 +++++++------- src/styles/styles.js | 3 --- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index b59835310cde..0245b54a78bc 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -60,7 +60,7 @@ const BaseReactionList = (props) => { renderItem={renderItem} keyExtractor={keyExtractor} getItemLayout={getItemLayout} - style={[styles.pb3, styles.pt2]} + contentContainerStyle={styles.pv2} /> ); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.js b/src/pages/home/report/ReactionList/HeaderReactionList.js index c49cce4f1c91..0d1ca0826e51 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList.js @@ -37,13 +37,13 @@ const HeaderReactionList = props => ( {props.isSmallScreenWidth && ( - - - + + + )} ); diff --git a/src/styles/styles.js b/src/styles/styles.js index 1b8e2fa820d9..12e239262903 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3040,9 +3040,6 @@ const styles = { }, reactionListContainer: { - width: '100%', - minWidth: 350, - minHeight: 100, maxHeight: 450, }, From 4ffd2ff9276558a03eec72c0630094010b0eeb0b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 13 Apr 2023 13:47:35 +0200 Subject: [PATCH 19/24] Improvements following review --- src/CONST.js | 1 - .../Reactions/EmojiReactionBubble.js | 8 +-- .../Reactions/ReportActionItemReactions.js | 37 +++++----- .../report/ReactionList/BaseReactionList.js | 69 ++++++++++++++++--- .../report/ReactionList/HeaderReactionList.js | 12 +++- .../report/ReactionList/ReactionListItem.js | 55 --------------- src/pages/home/report/ReportActionItem.js | 17 ----- src/pages/home/report/ReportActionsView.js | 4 +- src/styles/StyleUtils.js | 14 ---- src/styles/styles.js | 9 ++- src/styles/variables.js | 1 + 11 files changed, 95 insertions(+), 132 deletions(-) delete mode 100644 src/pages/home/report/ReactionList/ReactionListItem.js diff --git a/src/CONST.js b/src/CONST.js index 9f456a149201..20f2ff43a632 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -657,7 +657,6 @@ const CONST = { }, NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT: 256, EMOJI_PICKER_ITEM_HEIGHT: 32, - REACTION_LIST_ITEM_HEIGHT: 64, EMOJI_PICKER_HEADER_HEIGHT: 32, RECIPIENT_LOCAL_TIME_HEIGHT: 25, EMOJI_SUGGESTER: { diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 02e262021e4f..48a8f9ab13fe 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import Text from '../Text'; import * as StyleUtils from '../../styles/StyleUtils'; -import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; @@ -41,7 +40,6 @@ const propTypes = { */ hasUserReacted: PropTypes.bool, - ...withCurrentUserPersonalDetailsPropTypes, ...windowDimensionsPropTypes, }; @@ -50,8 +48,6 @@ const defaultProps = { onReactionListOpen: () => {}, sizeScale: 1, hasUserReacted: false, - - ...withCurrentUserPersonalDetailsDefaultProps, }; const EmojiReactionBubble = props => ( @@ -63,7 +59,7 @@ const EmojiReactionBubble = props => ( onPress={props.onPress} onLongPress={props.onReactionListOpen} onSecondaryInteraction={props.onReactionListOpen} - ref={props.forwardRef} + ref={props.forwardedRef} isLongPressEnabledWithHover={props.isSmallScreenWidth} // Prevent text input blur when emoji reaction is clicked @@ -95,5 +91,5 @@ EmojiReactionBubble.displayName = 'EmojiReactionBubble'; export default withWindowDimensions(React.forwardRef((props, ref) => ( /* eslint-disable-next-line react/jsx-props-no-spreading */ - + ))); diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 3c01ffdc8745..84aa33410d8c 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useRef} from 'react'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -8,9 +8,9 @@ import emojis from '../../../assets/emojis'; import AddReactionBubble from './AddReactionBubble'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; import getPreferredEmojiCode from './getPreferredEmojiCode'; - +import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; import * as Report from '../../libs/actions/Report'; - +import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; @@ -57,15 +57,6 @@ const propTypes = { */ 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, }; @@ -74,10 +65,14 @@ const defaultProps = { }; const ReportActionItemReactions = (props) => { + const popoverReactionListAnchor = useRef(null); const reactionsWithCount = _.filter(props.reactions, reaction => reaction.users.length > 0); return ( - + {_.map(reactionsWithCount, (reaction) => { const reactionCount = reaction.users.length; const reactionUsers = _.map(reaction.users, sender => sender.accountID.toString()); @@ -89,7 +84,16 @@ const ReportActionItemReactions = (props) => { props.toggleReaction(emoji); }; const onReactionListOpen = (event) => { - props.onReactionListOpen(event, reactionUsers, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); + const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers); + ReactionList.showReactionList( + event, + popoverReactionListAnchor.current, + users, + reaction.emoji, + emojiCodes, + reactionCount, + hasUserReacted, + ); }; return ( @@ -124,8 +128,5 @@ const ReportActionItemReactions = (props) => { ReportActionItemReactions.displayName = 'ReportActionItemReactions'; ReportActionItemReactions.propTypes = propTypes; ReportActionItemReactions.defaultProps = defaultProps; -export default withCurrentUserPersonalDetails(React.forwardRef((props, ref) => ( - // eslint-disable-next-line react/jsx-props-no-spreading - -))); +export default withCurrentUserPersonalDetails(ReportActionItemReactions); diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index 0245b54a78bc..2715c7415dfe 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -1,16 +1,19 @@ +/* eslint-disable rulesdir/onyx-props-must-have-default */ import React from 'react'; -import {View, FlatList} from 'react-native'; +import {FlatList} from 'react-native'; import PropTypes from 'prop-types'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import ReactionListItem from './ReactionListItem'; +import Str from 'expensify-common/lib/str'; import styles from '../../../../styles/styles'; import HeaderReactionList from './HeaderReactionList'; +import * as ReportUtils from '../../../../libs/ReportUtils'; import CONST from '../../../../CONST'; import participantPropTypes from '../../../../components/participantPropTypes'; import { propTypes as reactionPropTypes, defaultProps as reactionDefaultProps, } from './reactionPropTypes'; +import OptionRow from '../../../../components/OptionRow'; +import variables from '../../../../styles/variables'; const propTypes = { @@ -25,28 +28,71 @@ const propTypes = { hasUserReacted: PropTypes.bool, ...reactionPropTypes, - ...withLocalizePropTypes, }; const defaultProps = { ...reactionDefaultProps, + + hasUserReacted: false, }; -const renderItem = ({item}) => ; +/** + * Given an emoji item object, render a component based on its type. + * Items with the code "SPACER" return nothing and are used to fill rows up to 8 + * so that the sticky headers function properly + * + * @param {Object} params + * @param {Object} params.item + * @return {React.Component} + */ +const renderItem = ({item}) => ( + +); + +/** + * Create a unique key for each action in the FlatList. + * @param {Object} item + * @param {Number} index + * @return {String} + */ const keyExtractor = (item, index) => `${item.login}+${index}`; + +/** + * This function will be used with FlatList getItemLayout property for optimization purpose that allows skipping + * the measurement of dynamic content if we know the size (height or width) of items ahead of time. + * Generate and return an object with properties length(height of each individual row), + * offset(distance of the current row from the top of the FlatList), index(current row index) + * + * @param {*} _ FlatList item + * @param {Number} index row index + * @returns {Object} + */ const getItemLayout = (_, index) => ({ index, - length: CONST.REACTION_LIST_ITEM_HEIGHT, - offset: CONST.REACTION_LIST_ITEM_HEIGHT * index, + length: variables.listItemHeightNormal, + offset: variables.listItemHeightNormal * index, }); -// const BaseReactionList = props => (props.isVisible) && ( const BaseReactionList = (props) => { if (!props.isVisible) { return null; } return ( - + <> { keyExtractor={keyExtractor} getItemLayout={getItemLayout} contentContainerStyle={styles.pv2} + style={styles.reactionListContainer} /> - + ); }; @@ -70,4 +117,4 @@ BaseReactionList.propTypes = propTypes; BaseReactionList.defaultProps = defaultProps; BaseReactionList.displayName = 'BaseReactionList'; -export default withLocalize(BaseReactionList); +export default BaseReactionList; diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.js b/src/pages/home/report/ReactionList/HeaderReactionList.js index 0d1ca0826e51..8d574417f36c 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList.js +++ b/src/pages/home/report/ReactionList/HeaderReactionList.js @@ -1,5 +1,6 @@ import React from 'react'; import {View, TouchableOpacity} from 'react-native'; +import PropTypes from 'prop-types'; import styles from '../../../../styles/styles'; import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; import Text from '../../../../components/Text'; @@ -11,21 +12,28 @@ import { defaultProps as reactionDefaultProps, } from './reactionPropTypes'; import compose from '../../../../libs/compose'; -import withWindowDimensions from '../../../../components/withWindowDimensions'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; const propTypes = { ...reactionPropTypes, ...withLocalizePropTypes, + ...windowDimensionsPropTypes, + + /** + * Returns true if the current account has reacted to the report action (with the given skin tone). + */ + hasUserReacted: PropTypes.bool, }; const defaultProps = { ...reactionDefaultProps, + hasUserReacted: false, }; const HeaderReactionList = props => ( - + {props.emojiCodes.join('')} diff --git a/src/pages/home/report/ReactionList/ReactionListItem.js b/src/pages/home/report/ReactionList/ReactionListItem.js deleted file mode 100644 index 3352ae89486e..000000000000 --- a/src/pages/home/report/ReactionList/ReactionListItem.js +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import styles from '../../../../styles/styles'; -import colors from '../../../../styles/colors'; -import Avatar from '../../../../components/Avatar'; -import compose from '../../../../libs/compose'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import {withPersonalDetails} from '../../../../components/OnyxProvider'; -import Text from '../../../../components/Text'; -import participantPropTypes from '../../../../components/participantPropTypes'; -import * as ReportUtils from '../../../../libs/ReportUtils'; - -const propTypes = { - /** Styles for the outermost View */ - // eslint-disable-next-line react/forbid-prop-types - wrapperStyles: PropTypes.arrayOf(PropTypes.object), - - /** Personal details of the user */ - item: participantPropTypes.isRequired, - - ...withLocalizePropTypes, -}; - -const defaultProps = { - wrapperStyles: [styles.reactionListItem], -}; - -const ReactionListItem = (props) => { - const avatarSource = ReportUtils.getAvatar(props.item.avatar, props.item.login); - - return ( - - - - - - {props.item.displayName} - {props.item.login} - - - ); -}; - -ReactionListItem.propTypes = propTypes; -ReactionListItem.defaultProps = defaultProps; -ReactionListItem.displayName = 'ReactionListItem'; - -export default compose( - withLocalize, - withPersonalDetails(), -)(ReactionListItem); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index dff2b6b651eb..59ee05e67f57 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -38,9 +38,7 @@ import {ShowContextMenuContext} from '../../../components/ShowContextMenuContext import focusTextInputAfterAnimation from '../../../libs/focusTextInputAfterAnimation'; import ChronosOOOListActions from '../../../components/ReportActionItem/ChronosOOOListActions'; import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions'; -import * as ReactionList from './ReactionList/ReactionList'; import * as Report from '../../../libs/actions/Report'; -import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils'; import withLocalize from '../../../components/withLocalize'; const propTypes = { @@ -84,7 +82,6 @@ class ReportActionItem extends Component { constructor(props) { super(props); this.popoverAnchor = undefined; - this.popoverReactionListAnchor = undefined; this.state = { isContextMenuActive: ReportActionContextMenu.isActiveReportAction(props.action.reportActionID), }; @@ -216,27 +213,13 @@ class ReportActionItem extends Component { const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); const hasReactions = reactions.length > 0; - const onReactionListOpen = (event, reactionUsers, emojiName, emojiCode, emojiCount, hasUserReacted) => { - const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers); - ReactionList.showReactionList( - event, - this.popoverReactionListAnchor, - users, - emojiName, - emojiCode, - emojiCount, - hasUserReacted, - ); - }; return ( <> {children} {hasReactions && ( this.popoverReactionListAnchor = el} reactions={reactions} toggleReaction={this.toggleReaction} - onReactionListOpen={onReactionListOpen} /> )} diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 5f898703cd07..98f48e7c839a 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -351,9 +351,7 @@ class ReportActionsView extends React.Component { loadMoreChats={this.loadMoreChats} newMarkerReportActionID={this.state.newMarkerReportActionID} /> - + ); diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js index 766a59984378..17893cb94c85 100644 --- a/src/styles/StyleUtils.js +++ b/src/styles/StyleUtils.js @@ -890,19 +890,6 @@ function getEmojiReactionBubbleStyle(isHovered, hasUserReacted, sizeScale = 1) { return sizeStyles; } -/** - * Select the correct color for emoji reaction bubble. - * @param {Boolean} hasUserReacted - * @returns {Object} - */ -function getEmojiReactionListHeaderBubbleStyle(hasUserReacted) { - if (hasUserReacted) { - return {backgroundColor: themeColors.reactionActive}; - } - - return {backgroundColor: themeColors.border}; -} - function getEmojiReactionTextStyle(sizeScale = 1) { return { fontSize: styles.emojiReactionText.fontSize * sizeScale, @@ -988,6 +975,5 @@ export { getEmojiReactionBubbleStyle, getEmojiReactionTextStyle, getEmojiReactionCounterTextStyle, - getEmojiReactionListHeaderBubbleStyle, getDirectionStyle, }; diff --git a/src/styles/styles.js b/src/styles/styles.js index 12e239262903..37a2720c1eb8 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -19,7 +19,6 @@ import pointerEventsNone from './pointerEventsNone'; import pointerEventsAuto from './pointerEventsAuto'; import overflowXHidden from './overflowXHidden'; import CONST from '../CONST'; -import colors from './colors'; const picker = { backgroundColor: themeColors.transparent, @@ -2974,7 +2973,7 @@ const styles = { emojiReactionListHeader: { marginTop: 8, paddingBottom: 20, - borderBottomColor: colors.greenBorders, + borderBottomColor: themeColors.border, borderBottomWidth: 1, marginHorizontal: 20, }, @@ -2990,13 +2989,12 @@ const styles = { marginRight: 4, }, reactionListItem: { - display: 'flex', flexDirection: 'row', paddingVertical: 12, paddingHorizontal: 20, }, reactionListHeaderText: { - color: colors.greenSupportingText, + color: themeColors.textSupporting, marginTop: 1, marginLeft: 8, }, @@ -3040,7 +3038,8 @@ const styles = { }, reactionListContainer: { - maxHeight: 450, + maxHeight: variables.listItemHeightNormal * 5.75, + ...spacing.pv2, }, validateCodeDigits: { diff --git a/src/styles/variables.js b/src/styles/variables.js index c82d8286446a..1040a478668f 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -104,4 +104,5 @@ export default { verticalLogoWidth: 111, badgeMaxWidth: 180, modalContentMaxWidth: 360, + listItemHeightNormal: 64, }; From 907fd1619406a44d8b49576fdbc008e4822f2b1f Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 13 Apr 2023 15:26:36 +0200 Subject: [PATCH 20/24] removed sizeScale --- .../home/report/ReactionList/BaseReactionList.js | 11 +++-------- .../home/report/ReactionList/HeaderReactionList.js | 10 +++------- .../home/report/ReactionList/PopoverReactionList.js | 3 --- src/pages/home/report/ReactionList/ReactionList.js | 3 --- .../home/report/ReactionList/reactionPropTypes.js | 12 +----------- 5 files changed, 7 insertions(+), 32 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index 2715c7415dfe..a8e5ca91b676 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -8,10 +8,7 @@ import HeaderReactionList from './HeaderReactionList'; import * as ReportUtils from '../../../../libs/ReportUtils'; import CONST from '../../../../CONST'; import participantPropTypes from '../../../../components/participantPropTypes'; -import { - propTypes as reactionPropTypes, - defaultProps as reactionDefaultProps, -} from './reactionPropTypes'; +import reactionPropTypes from './reactionPropTypes'; import OptionRow from '../../../../components/OptionRow'; import variables from '../../../../styles/variables'; @@ -20,7 +17,7 @@ const propTypes = { /** * Array of personal detail objects */ - users: PropTypes.arrayOf(participantPropTypes), + users: PropTypes.arrayOf(participantPropTypes).isRequired, /** * Returns true if the current account has reacted to the report action (with the given skin tone). @@ -31,8 +28,6 @@ const propTypes = { }; const defaultProps = { - ...reactionDefaultProps, - hasUserReacted: false, }; @@ -49,6 +44,7 @@ const renderItem = ({item}) => ( { emojiCodes={props.emojiCodes} emojiCount={props.emojiCount} hasUserReacted={props.hasUserReacted} - sizeScale={props.sizeScale} /> ( - + {props.emojiCodes.join('')} - + {props.emojiCount} diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index 0644b249e11a..c1a827de1e61 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -30,7 +30,6 @@ class PopoverReactionList extends React.Component { users: [], emojiCodes: [], emojiName: '', - sizeScale: 1, emojiCount: 0, hasUserReacted: false, }; @@ -173,7 +172,6 @@ class PopoverReactionList extends React.Component { emojiCodes={this.state.emojiCodes} emojiCount={this.state.emojiCount} onClose={this.hideReactionList} - sizeScale={this.state.sizeScale} hasUserReacted={this.state.hasUserReacted} /> ); @@ -201,7 +199,6 @@ class PopoverReactionList extends React.Component { emojiCodes={this.state.emojiCodes} emojiCount={this.state.emojiCount} onClose={this.hideReactionList} - sizeScale={this.state.sizeScale} hasUserReacted={this.state.hasUserReacted} /> diff --git a/src/pages/home/report/ReactionList/ReactionList.js b/src/pages/home/report/ReactionList/ReactionList.js index fdff886b3ccb..f5a1e97d9b06 100644 --- a/src/pages/home/report/ReactionList/ReactionList.js +++ b/src/pages/home/report/ReactionList/ReactionList.js @@ -11,7 +11,6 @@ const reactionListRef = React.createRef(); * @param {String} emojiName - the emoji codes to display near the bubble. * @param {String} emojiCodes - the emoji codes to display in the bubble. * @param {Boolean} hasUserReacted - show if user has reacted - * @param {Number} sizeScale - set the sizeScale of emoji */ function showReactionList( event, @@ -20,7 +19,6 @@ function showReactionList( emojiName, emojiCodes, hasUserReacted, - sizeScale, ) { if (!reactionListRef.current) { return; @@ -32,7 +30,6 @@ function showReactionList( emojiName, emojiCodes, hasUserReacted, - sizeScale, ); } diff --git a/src/pages/home/report/ReactionList/reactionPropTypes.js b/src/pages/home/report/ReactionList/reactionPropTypes.js index 472497eea845..c8605ac39b41 100644 --- a/src/pages/home/report/ReactionList/reactionPropTypes.js +++ b/src/pages/home/report/ReactionList/reactionPropTypes.js @@ -13,16 +13,6 @@ const propTypes = { /** Count of the emoji */ emojiCount: PropTypes.number.isRequired, - /** - * The default size of the reaction bubble is defined - * by the styles in styles.js. This scale factor can be used - * to make the bubble bigger or smaller. - */ - sizeScale: PropTypes.number, }; -const defaultProps = { - sizeScale: 1, -}; - -export {propTypes, defaultProps}; +export default propTypes; From a09ad6b936d0f2950ded80d9cedc71668a77134e Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 28 Apr 2023 11:06:25 +0200 Subject: [PATCH 21/24] revert, rename enableLongPressWithHover --- src/components/PressableWithSecondaryInteraction/index.js | 2 +- .../pressableWithSecondaryInteractionPropTypes.js | 2 +- src/components/Reactions/EmojiReactionBubble.js | 2 +- src/pages/home/report/ReactionList/BaseReactionList.js | 5 +++-- src/styles/styles.js | 4 ++++ 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js index 05bc4822b2a5..7f236ee54beb 100644 --- a/src/components/PressableWithSecondaryInteraction/index.js +++ b/src/components/PressableWithSecondaryInteraction/index.js @@ -57,7 +57,7 @@ class PressableWithSecondaryInteraction extends Component { style={this.props.inline && styles.dInline} onPressIn={this.props.onPressIn} onLongPress={(e) => { - if (DeviceCapabilities.hasHoverSupport() && !this.props.isLongPressEnabledWithHover) { + if (DeviceCapabilities.hasHoverSupport() && !this.props.enableLongPressWithHover) { return; } if (this.props.withoutFocusOnSecondaryInteraction && this.pressableRef) { diff --git a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js index 0a2d887affcf..b338e2bfc817 100644 --- a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js +++ b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js @@ -43,7 +43,7 @@ const defaultProps = { preventDefaultContentMenu: true, inline: false, withoutFocusOnSecondaryInteraction: false, - isLongPressEnabledWithHover: false, + enableLongPressWithHover: false, }; export {propTypes, defaultProps}; diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js index 501da808b4c7..dda149e90c21 100644 --- a/src/components/Reactions/EmojiReactionBubble.js +++ b/src/components/Reactions/EmojiReactionBubble.js @@ -58,7 +58,7 @@ const EmojiReactionBubble = props => ( onLongPress={props.onReactionListOpen} onSecondaryInteraction={props.onReactionListOpen} ref={props.forwardedRef} - isLongPressEnabledWithHover={props.isSmallScreenWidth} + enableLongPressWithHover={props.isSmallScreenWidth} // Prevent text input blur when emoji reaction is clicked onMouseDown={e => e.preventDefault()} diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index a8e5ca91b676..fa24dcc9707c 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -11,6 +11,7 @@ import participantPropTypes from '../../../../components/participantPropTypes'; import reactionPropTypes from './reactionPropTypes'; import OptionRow from '../../../../components/OptionRow'; import variables from '../../../../styles/variables'; +import withWindowDimensions from '../../../../components/withWindowDimensions'; const propTypes = { @@ -102,7 +103,7 @@ const BaseReactionList = (props) => { keyExtractor={keyExtractor} getItemLayout={getItemLayout} contentContainerStyle={styles.pv2} - style={styles.reactionListContainer} + style={[styles.reactionListContainer, !props.isSmallScreenWidth && styles.reactionListContainerFixedWidth]} /> ); @@ -112,4 +113,4 @@ BaseReactionList.propTypes = propTypes; BaseReactionList.defaultProps = defaultProps; BaseReactionList.displayName = 'BaseReactionList'; -export default BaseReactionList; +export default withWindowDimensions(BaseReactionList); diff --git a/src/styles/styles.js b/src/styles/styles.js index f44956f18764..0a6366087818 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3052,6 +3052,10 @@ const styles = { ...spacing.pv2, }, + reactionListContainerFixedWidth: { + maxWidth: variables.popoverWidth, + }, + validateCodeDigits: { color: themeColors.text, fontFamily: fontFamily.EXP_NEUE, From cb148075dde7eb9cac568fdff8b7bcc7e0c5b1f1 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 28 Apr 2023 13:11:48 +0200 Subject: [PATCH 22/24] clean --- src/styles/styles.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 0a6366087818..ef55f6c2ad99 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3004,7 +3004,6 @@ const styles = { }, reactionListHeaderText: { color: themeColors.textSupporting, - marginBottom: 4, marginLeft: 8, alignSelf: 'center', }, From a9891925083750abc054826a6c77d4464eaf4d36 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 28 Apr 2023 13:35:04 +0200 Subject: [PATCH 23/24] popoverWidth --- src/styles/variables.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/variables.js b/src/styles/variables.js index 9ed9fd1c1135..f6bc4b2fe16c 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -129,6 +129,7 @@ export default { signInLogoWidthLargeScreenPill: 162, modalContentMaxWidth: 360, listItemHeightNormal: 64, + popoverWidth: 375, // The height of the empty list is 14px (2px for borders and 12px for vertical padding) // This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility From 3604015f6d1bb1e01f24b9f489aab28a35d75f3e Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 1 May 2023 13:15:45 +0200 Subject: [PATCH 24/24] disable touch response for item list --- src/pages/home/report/ReactionList/BaseReactionList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js index fa24dcc9707c..0fbf8d8fbb51 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.js @@ -45,6 +45,7 @@ const renderItem = ({item}) => (