From f6279eaa17d910eefdd9a7bf3fdca285cdc01f25 Mon Sep 17 00:00:00 2001
From: Taras Perun <terry.perun@gmail.com>
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) => {
                 />
             )}
         >
-            <Pressable
+            <PressableWithSecondaryInteraction
                 style={({hovered}) => [
                     styles.emojiReactionBubble,
                     StyleUtils.getEmojiReactionBubbleStyle(hovered, hasUserReacted, props.sizeScale),
                 ]}
                 onPress={props.onPress}
                 onLongPress={props.onReactionListOpen}
+                onSecondaryInteraction={props.onReactionListOpen}
+                ref={props.forwardedRef}
             >
                 <Text style={[
                     styles.emojiReactionText,
@@ -97,7 +99,7 @@ const EmojiReactionBubble = (props) => {
                     {props.count}
                 </Text>
                 )}
-            </Pressable>
+            </PressableWithSecondaryInteraction>
         </Tooltip>
     );
 };
@@ -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 */
+    <EmojiReactionBubble {...props} forwardRef={ref} />
+)));
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 (
                 <EmojiReactionBubble
+                    ref={props.forwardedRef}
                     key={reaction.emoji}
                     count={reactionCount}
                     emojiName={reaction.emoji}
                     emojiCodes={emojiCodes}
                     onPress={onPress}
                     reactionUsers={reactionUsers}
+                    onReactionListOpen={onReactionListOpen}
                 />
             );
         })}
@@ -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
+    <ReportActionItemReactions {...props} forwardedRef={ref} />
+));
+
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 <ReactionListItem item={item} />;
+    }
+
+    render() {
+        return (this.props.isVisible || this.state.keepOpen) && (
+            <View
+                ref={this.props.contentRef}
+                style={[{
+                    width: '100%', minWidth: 400, minHeight: 100, maxHeight: 500,
+                }]}
+            >
+                <HeaderReactionList onClose={this.props.onClose} />
+                <Text style={[styles.ph5, styles.pv4, {color: colors.greenSupportingText}]}>{`:${this.props.emojiName}:`}</Text>
+                <FlatList
+                    data={this.props.users}
+                    renderItem={this.renderItem}
+                    keyExtractor={item => item.accountID}
+                    style={styles.mb3}
+                />
+            </View>
+        );
+    }
+}
+
+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 => (
+    <View style={[styles.flexRow, styles.ph5, styles.pt4, styles.justifyContentBetween, styles.alignItemsCenter]}>
+        <Text numberOfLines={2} style={[styles.headerText, styles.textLarge]}>
+            Emoji reactions
+        </Text>
+        <TouchableOpacity
+            onPress={props.onClose}
+            accessibilityRole="button"
+            accessibilityLabel={props.translate('common.close')}
+        >
+            <Icon src={Expensicons.Close} />
+        </TouchableOpacity>
+    </View>
+);
+
+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<Object>}
+     */
+    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 (
+            <BaseReactionList
+                type={this.state.type}
+                isVisible
+                users={this.state.users}
+                emojiName={this.state.emojiName}
+                reportID={this.state.reportID}
+                reportAction={this.state.reportAction}
+                anchor={this.contextMenuTargetNode}
+                contentRef={this.setContentRef}
+                onClose={this.hideReactionList}
+            />
+        );
+    }
+
+    /**
+     * Run the callback and return a noop function to reset it
+     * @param {Function} callback
+     * @returns {Function}
+     */
+    runAndResetCallback(callback) {
+        callback();
+        return () => {};
+    }
+
+    render() {
+        return (
+            <>
+                <PopoverWithMeasuredContent
+                    isVisible={this.state.isPopoverVisible}
+                    onClose={this.hideReactionList}
+                    onModalShow={this.runAndResetOnPopoverShow}
+                    onModalHide={this.runAndResetOnPopoverHide}
+                    anchorPosition={this.state.popoverAnchorPosition}
+                    animationIn="fadeIn"
+                    disableAnimation={false}
+                    animationOutTiming={1}
+                    measureContent={this.measureContent}
+                    shouldSetModalVisibility={false}
+                    fullscreen
+                >
+                    <BaseReactionList
+                        type={this.state.type}
+                        isVisible
+                        users={this.state.users}
+                        emojiName={this.state.emojiName}
+                        reportID={this.state.reportID}
+                        reportAction={this.state.reportAction}
+                        anchor={this.contextMenuTargetNode}
+                        contentRef={this.setContentRef}
+                        onClose={this.hideReactionList}
+                    />
+                </PopoverWithMeasuredContent>
+            </>
+        );
+    }
+}
+
+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 => (
+    <View style={props.wrapperStyles}>
+        <Pressable
+            style={[styles.alignSelfStart, styles.mr3]}
+            onPressIn={ControlSelection.block}
+            onPressOut={ControlSelection.unblock}
+        >
+            <Avatar
+                containerStyles={[styles.actionAvatar]}
+                source={props.item.avatar}
+            />
+        </Pressable>
+        <View style={[styles.chatItemRight]}>
+            <Text style={styles.h3}>{props.item.displayName}</Text>
+            <Text style={{color: colors.greenSupportingText}}>{props.item.login}</Text>
+        </View>
+    </View>
+);
+
+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 && (
                     <ReportActionItemReactions
+                        ref={el => 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}
                         />
+                        <PopoverReactionList
+                            ref={ReactionList.reactionListRef}
+                        />
                     </>
                 )}
                 <EmojiPicker ref={EmojiPickerAction.emojiPickerRef} />

From bc5a5170e0812c78e7b9dc3ab5b34e2af593cc5b Mon Sep 17 00:00:00 2001
From: Taras Perun <terry.perun@gmail.com>
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 (
-        <Tooltip
-            renderTooltipContent={() => (
-                <ReactionTooltipContent
-                    emojiName={props.emojiName}
-                    emojiCodes={props.emojiCodes}
-                    accountIDs={props.reactionUsers}
-                />
-            )}
+const EmojiReactionBubble = props => (
+    <Tooltip
+        renderTooltipContent={() => (
+            <ReactionTooltipContent
+                emojiName={props.emojiName}
+                emojiCodes={props.emojiCodes}
+                accountIDs={props.reactionUsers}
+            />
+        )}
+    >
+        <PressableWithSecondaryInteraction
+            style={({hovered}) => [
+                styles.emojiReactionBubble,
+                StyleUtils.getEmojiReactionBubbleStyle(hovered, props.hasUserReacted, props.sizeScale),
+            ]}
+            onPress={props.onPress}
+            onLongPress={props.onReactionListOpen}
+            onSecondaryInteraction={props.onReactionListOpen}
+            ref={props.forwardedRef}
         >
-            <PressableWithSecondaryInteraction
-                style={({hovered}) => [
-                    styles.emojiReactionBubble,
-                    StyleUtils.getEmojiReactionBubbleStyle(hovered, hasUserReacted, props.sizeScale),
-                ]}
-                onPress={props.onPress}
-                onLongPress={props.onReactionListOpen}
-                onSecondaryInteraction={props.onReactionListOpen}
-                ref={props.forwardedRef}
+            <Text style={[
+                styles.emojiReactionText,
+                StyleUtils.getEmojiReactionTextStyle(props.sizeScale),
+            ]}
             >
-                <Text style={[
-                    styles.emojiReactionText,
-                    StyleUtils.getEmojiReactionTextStyle(props.sizeScale),
-                ]}
-                >
-                    {props.emojiCodes.join('')}
-                </Text>
-                {props.count > 0 && (
+                {props.emojiCodes.join('')}
+            </Text>
+            {props.count > 0 && (
                 <Text style={[
                     styles.reactionCounterText,
-                    StyleUtils.getEmojiReactionCounterTextStyle(hasUserReacted, props.sizeScale),
+                    StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale),
                 ]}
                 >
                     {props.count}
                 </Text>
-                )}
-            </PressableWithSecondaryInteraction>
-        </Tooltip>
-    );
-};
-
+            )}
+        </PressableWithSecondaryInteraction>
+    </Tooltip>
+);
 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 */
     <EmojiReactionBubble {...props} forwardRef={ref} />
-)));
+));
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
     <ReportActionItemReactions {...props} forwardedRef={ref} />
-));
+)));
 
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 <ReactionListItem item={item} />;
-    }
 
-    render() {
-        return (this.props.isVisible || this.state.keepOpen) && (
-            <View
-                ref={this.props.contentRef}
-                style={[{
-                    width: '100%', minWidth: 400, minHeight: 100, maxHeight: 500,
-                }]}
-            >
-                <HeaderReactionList onClose={this.props.onClose} />
-                <Text style={[styles.ph5, styles.pv4, {color: colors.greenSupportingText}]}>{`:${this.props.emojiName}:`}</Text>
-                <FlatList
-                    data={this.props.users}
-                    renderItem={this.renderItem}
-                    keyExtractor={item => item.accountID}
-                    style={styles.mb3}
-                />
-            </View>
-        );
-    }
-}
+const renderItem = ({item}) => <ReactionListItem item={item} />;
+
+const BaseReactionList = props => (props.isVisible) && (
+    <View style={styles.reactionListContainer}>
+        <HeaderReactionList
+            onClose={props.onClose}
+            emojiName={props.emojiName}
+            emojiCodes={props.emojiCodes}
+            hasUserReacted={props.hasUserReacted}
+            sizeScale={props.sizeScale}
+        />
+        <FlatList
+            data={props.users}
+            renderItem={renderItem}
+            keyExtractor={item => `${item.accountID}`}
+            style={[styles.mb3, styles.mt2]}
+        />
+    </View>
+);
 
 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 => (
+    <View style={[styles.pt4, styles.mh5, styles.emojiReactionListHeader]}>
+        <View style={[styles.flexRow]}>
+            <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
+                <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
+                    {props.emojiCodes.join('')}
+                </Text>
+            </View>
+            <Text style={[{color: colors.greenSupportingText}, styles.ml2]}>{`:${props.emojiName}:`}</Text>
+        </View>
+    </View>
+);
+
+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 => (
-    <View style={[styles.flexRow, styles.ph5, styles.pt4, styles.justifyContentBetween, styles.alignItemsCenter]}>
-        <Text numberOfLines={2} style={[styles.headerText, styles.textLarge]}>
-            Emoji reactions
-        </Text>
+    <View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.emojiReactionListHeader]}>
+        <View style={styles.flexRow}>
+            <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
+                <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
+                    {props.emojiCodes.join('')}
+                </Text>
+            </View>
+            <Text style={[{color: colors.greenSupportingText}, styles.ml2]}>{`:${props.emojiName}:`}</Text>
+        </View>
+
         <TouchableOpacity
             onPress={props.onClose}
             accessibilityRole="button"
@@ -32,10 +55,8 @@ const HeaderReactionList = props => (
 );
 
 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 (
             <>
                 <PopoverWithMeasuredContent
                     isVisible={this.state.isPopoverVisible}
                     onClose={this.hideReactionList}
-                    onModalShow={this.runAndResetOnPopoverShow}
-                    onModalHide={this.runAndResetOnPopoverHide}
                     anchorPosition={this.state.popoverAnchorPosition}
                     animationIn="fadeIn"
                     disableAnimation={false}
@@ -245,11 +200,10 @@ 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}
                     />
                 </PopoverWithMeasuredContent>
             </>
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 <terry.perun@gmail.com>
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}
         >
             <Text style={[
                 styles.emojiReactionText,
@@ -99,7 +101,7 @@ EmojiReactionBubble.propTypes = propTypes;
 EmojiReactionBubble.defaultProps = defaultProps;
 EmojiReactionBubble.displayName = 'EmojiReactionBubble';
 
-export default React.forwardRef((props, ref) => (
+export default withWindowDimensions(React.forwardRef((props, ref) => (
     /* eslint-disable-next-line react/jsx-props-no-spreading */
     <EmojiReactionBubble {...props} forwardRef={ref} />
-));
+)));

From 72d53e953d2740d46759d923641327184b896c73 Mon Sep 17 00:00:00 2001
From: Taras Perun <terry.perun@gmail.com>
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 => (
                 <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
                     {props.emojiCodes.join('')}
                 </Text>
+                <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}>
+                    {props.emojiCount}
+                </Text>
             </View>
-            <Text style={[{color: colors.greenSupportingText}, styles.ml2]}>{`:${props.emojiName}:`}</Text>
+            <Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text>
         </View>
     </View>
 );
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 => (
                 <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
                     {props.emojiCodes.join('')}
                 </Text>
+                <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}>
+                    {props.emojiCount}
+                </Text>
             </View>
-            <Text style={[{color: colors.greenSupportingText}, styles.ml2]}>{`:${props.emojiName}:`}</Text>
+            <Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text>
         </View>
 
         <TouchableOpacity
diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js
index 0578398f906f..4cd5279ab860 100644
--- a/src/pages/home/report/ReactionList/PopoverReactionList.js
+++ b/src/pages/home/report/ReactionList/PopoverReactionList.js
@@ -31,6 +31,7 @@ class PopoverReactionList extends React.Component {
             emojiCodes: [],
             emojiName: '',
             sizeScale: 1,
+            emojiCount: 0,
             hasUserReacted: false,
         };
 
@@ -91,10 +92,11 @@ class PopoverReactionList extends React.Component {
      * 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 {Element} contextMenuAnchor - opoverAnchor
+     * @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
 
      */
@@ -104,6 +106,7 @@ class PopoverReactionList extends React.Component {
         users,
         emojiName,
         emojiCodes,
+        emojiCount,
         hasUserReacted,
     ) {
         const nativeEvent = event.nativeEvent || {};
@@ -126,6 +129,7 @@ class PopoverReactionList extends React.Component {
                 users,
                 emojiName,
                 emojiCodes,
+                emojiCount,
                 isPopoverVisible: true,
                 hasUserReacted,
             });
@@ -174,6 +178,7 @@ class PopoverReactionList extends React.Component {
                 users={this.state.users}
                 emojiName={this.state.emojiName}
                 emojiCodes={this.state.emojiCodes}
+                emojiCount={this.state.emojiCount}
                 onClose={this.hideReactionList}
                 sizeScale={this.state.sizeScale}
                 hasUserReacted={this.state.hasUserReacted}
@@ -201,6 +206,7 @@ class PopoverReactionList extends React.Component {
                         users={this.state.users}
                         emojiName={this.state.emojiName}
                         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/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 8e43b0ad0305..e655811810bc 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -206,7 +206,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, 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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 && (
-                    <>
-                        <FloatingMessageCounter
-                            isActive={this.state.isFloatingMessageCounterVisible && !_.isEmpty(this.state.newMarkerReportActionID)}
-                            onClick={this.scrollToBottomAndMarkReportAsRead}
-                        />
-                        <ReportActionsList
-                            report={this.props.report}
-                            onScroll={this.trackScroll}
-                            onLayout={this.recordTimeToMeasureItemLayout}
-                            sortedReportActions={this.props.reportActions}
-                            mostRecentIOUReportActionID={this.mostRecentIOUReportActionID}
-                            isLoadingMoreReportActions={this.props.report.isLoadingMoreReportActions}
-                            loadMoreChats={this.loadMoreChats}
-                            newMarkerReportActionID={this.state.newMarkerReportActionID}
-                        />
-                        <PopoverReactionList
-                            ref={ReactionList.reactionListRef}
-                        />
-                    </>
-                )}
+                <FloatingMessageCounter
+                    isActive={this.state.isFloatingMessageCounterVisible && !_.isEmpty(this.state.newMarkerReportActionID)}
+                    onClick={this.scrollToBottomAndMarkReportAsRead}
+                />
+                <ReportActionsList
+                    report={this.props.report}
+                    onScroll={this.trackScroll}
+                    onLayout={this.recordTimeToMeasureItemLayout}
+                    sortedReportActions={this.props.reportActions}
+                    mostRecentIOUReportActionID={this.mostRecentIOUReportActionID}
+                    isLoadingMoreReportActions={this.props.report.isLoadingMoreReportActions}
+                    loadMoreChats={this.loadMoreChats}
+                    newMarkerReportActionID={this.state.newMarkerReportActionID}
+                />
+                <PopoverReactionList
+                    ref={ReactionList.reactionListRef}
+                />
                 <EmojiPicker ref={EmojiPickerAction.emojiPickerRef} />
                 <CopySelectionHelper />
             </>

From 502920e90da5cfd0f1152bdce09f4ca77b95c045 Mon Sep 17 00:00:00 2001
From: Taras Perun <terry.perun@gmail.com>
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}) => <ReactionListItem item={item} />;
+const keyExtractor = item => `${item.accountID}`;
 
 const BaseReactionList = props => (props.isVisible) && (
     <View style={styles.reactionListContainer}>
@@ -28,8 +29,8 @@ const BaseReactionList = props => (props.isVisible) && (
         <FlatList
             data={props.users}
             renderItem={renderItem}
-            keyExtractor={item => `${item.accountID}`}
-            style={[styles.mb3, styles.mt2]}
+            keyExtractor={keyExtractor}
+            style={[styles.pb3, styles.pt2]}
         />
     </View>
 );
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 => (
-    <View style={[styles.pt4, styles.mh5, styles.emojiReactionListHeader]}>
-        <View style={[styles.flexRow]}>
-            <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
-                <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
-                    {props.emojiCodes.join('')}
-                </Text>
-                <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}>
-                    {props.emojiCount}
-                </Text>
-            </View>
-            <Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text>
+    <View style={[styles.pt4, styles.mh5, styles.emojiReactionListHeader, styles.flexRow]}>
+        <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
+            <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
+                {props.emojiCodes.join('')}
+            </Text>
+            <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}>
+                {props.emojiCount}
+            </Text>
         </View>
+        <Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text>
     </View>
 );
 
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<Object>}
      */
-    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 <terry.perun@gmail.com>
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('')}
             </Text>

From e96d82b0881d2c6093370b6a3bfb745df1474bc7 Mon Sep 17 00:00:00 2001
From: Taras Perun <terry.perun@gmail.com>
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}
         >
             <Text style={[
diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js
index 39e4ec537d72..de6a358e690b 100644
--- a/src/components/Reactions/ReportActionItemReactions.js
+++ b/src/components/Reactions/ReportActionItemReactions.js
@@ -30,10 +30,6 @@ const getUniqueEmojiCodes = (emoji, users) => {
     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}) => <ReactionListItem item={item} />;
 const keyExtractor = item => `${item.accountID}`;
+const getItemLayout = (_, index) => ({
+    index,
+    length: CONST.REACTION_LIST_ITEM_HEIGHT,
+    offset: CONST.REACTION_LIST_ITEM_HEIGHT * index,
+});
 
-const BaseReactionList = props => (props.isVisible) && (
-    <View style={styles.reactionListContainer}>
-        <HeaderReactionList
-            onClose={props.onClose}
-            emojiName={props.emojiName}
-            emojiCodes={props.emojiCodes}
-            emojiCount={props.emojiCount}
-            hasUserReacted={props.hasUserReacted}
-            sizeScale={props.sizeScale}
-        />
-        <FlatList
-            data={props.users}
-            renderItem={renderItem}
-            keyExtractor={keyExtractor}
-            style={[styles.pb3, styles.pt2]}
-        />
-    </View>
-);
+// const BaseReactionList = props => (props.isVisible) && (
+const BaseReactionList = (props) => {
+    if (!props.isVisible) {
+        return null;
+    }
+    return (
+        <View style={styles.reactionListContainer}>
+            <HeaderReactionList
+                onClose={props.onClose}
+                emojiName={props.emojiName}
+                emojiCodes={props.emojiCodes}
+                emojiCount={props.emojiCount}
+                hasUserReacted={props.hasUserReacted}
+                sizeScale={props.sizeScale}
+            />
+            <FlatList
+                data={props.users}
+                renderItem={renderItem}
+                keyExtractor={keyExtractor}
+                getItemLayout={getItemLayout}
+                style={[styles.pb3, styles.pt2]}
+            />
+        </View>
+    );
+};
 
 BaseReactionList.propTypes = propTypes;
 BaseReactionList.defaultProps = defaultProps;
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 => (
-    <View style={props.wrapperStyles}>
-        <Pressable
-            style={[styles.alignSelfStart, styles.mr3]}
-            onPressIn={ControlSelection.block}
-            onPressOut={ControlSelection.unblock}
-        >
-            <Avatar
-                containerStyles={[styles.actionAvatar]}
-                source={props.item.avatar}
-            />
-        </Pressable>
-        <View style={[styles.chatItemRight]}>
-            <Text style={styles.h3}>{props.item.displayName}</Text>
-            <Text style={{color: colors.greenSupportingText}}>{props.item.login}</Text>
+const ReactionListItem = (props) => {
+    const avatarSource = ReportUtils.getAvatar(props.item.avatar, props.item.login);
+
+    return (
+        <View style={props.wrapperStyles}>
+            <Pressable
+                style={[styles.alignSelfStart, styles.mr3]}
+                onPressIn={ControlSelection.block}
+                onPressOut={ControlSelection.unblock}
+            >
+                <Avatar
+                    containerStyles={[styles.actionAvatar]}
+                    source={avatarSource}
+                />
+            </Pressable>
+            <View style={[styles.chatItemRight]}>
+                <Text numberOfLines={1} style={styles.h3}>{props.item.displayName}</Text>
+                <Text style={{color: colors.greenSupportingText}}>{props.item.login}</Text>
+            </View>
         </View>
-    </View>
-);
+    );
+};
 
 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 <terry.perun@gmail.com>
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 (
         <View style={props.wrapperStyles}>
-            <Pressable
-                style={[styles.alignSelfStart, styles.mr3]}
-                onPressIn={ControlSelection.block}
-                onPressOut={ControlSelection.unblock}
-            >
+            <View style={[styles.alignSelfStart, styles.mr3]}>
                 <Avatar
                     containerStyles={[styles.actionAvatar]}
                     source={avatarSource}
                 />
-            </Pressable>
+            </View>
             <View style={[styles.chatItemRight]}>
                 <Text numberOfLines={1} style={styles.h3}>{props.item.displayName}</Text>
                 <Text style={{color: colors.greenSupportingText}}>{props.item.login}</Text>

From b91f52c3620267021681074260ec01d3108785be Mon Sep 17 00:00:00 2001
From: Taras Perun <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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}) => <ReactionListItem item={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 <terry.perun@gmail.com>
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 => (
-    <View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.emojiReactionListHeader]}>
+    <View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.emojiReactionListHeader, !props.isSmallScreenWidth && styles.pt4]}>
         <View style={styles.flexRow}>
             <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
                 <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
@@ -34,6 +36,7 @@ const HeaderReactionList = props => (
             <Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text>
         </View>
 
+        {props.isSmallScreenWidth && (
         <TouchableOpacity
             onPress={props.onClose}
             accessibilityRole="button"
@@ -41,6 +44,7 @@ const HeaderReactionList = props => (
         >
             <Icon src={Expensicons.Close} />
         </TouchableOpacity>
+        )}
     </View>
 );
 
@@ -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 => (
-    <View style={[styles.pt4, styles.mh5, styles.emojiReactionListHeader, styles.flexRow]}>
-        <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
-            <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
-                {props.emojiCodes.join('')}
-            </Text>
-            <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}>
-                {props.emojiCount}
-            </Text>
-        </View>
-        <Text style={styles.reactionListHeaderText}>{`:${props.emojiName}:`}</Text>
-    </View>
-);
-
-HeaderReactionList.propTypes = propTypes;
-HeaderReactionList.defaultProps = defaultProps;
-HeaderReactionList.displayName = 'HeaderReactionList';
-
-export default HeaderReactionList;
-
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 <terry.perun@gmail.com>
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}
             />
         </View>
     );
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 => (
         </View>
 
         {props.isSmallScreenWidth && (
-        <TouchableOpacity
-            onPress={props.onClose}
-            accessibilityRole="button"
-            accessibilityLabel={props.translate('common.close')}
-        >
-            <Icon src={Expensicons.Close} />
-        </TouchableOpacity>
+            <TouchableOpacity
+                onPress={props.onClose}
+                accessibilityRole="button"
+                accessibilityLabel={props.translate('common.close')}
+            >
+                <Icon src={Expensicons.Close} />
+            </TouchableOpacity>
         )}
     </View>
 );
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 <terry.perun@gmail.com>
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 */
-    <EmojiReactionBubble {...props} forwardRef={ref} />
+    <EmojiReactionBubble {...props} forwardedRef={ref} />
 )));
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 (
-        <View style={[styles.flexRow, styles.flexWrap]}>
+        <View
+            ref={popoverReactionListAnchor}
+            style={[styles.flexRow, styles.flexWrap]}
+        >
             {_.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
-    <ReportActionItemReactions {...props} forwardedRef={ref} />
-)));
+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}) => <ReactionListItem item={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}) => (
+    <OptionRow
+        item={item}
+        boldStyle
+        option={{
+            text: Str.removeSMSDomain(item.displayName),
+            alternateText: Str.removeSMSDomain(item.login),
+            participantsList: [item],
+            icons: [{
+                source: ReportUtils.getAvatar(item.avatar, item.login),
+                name: item.login,
+                type: CONST.ICON_TYPE_AVATAR,
+            }],
+            keyForList: item.login,
+        }}
+    />
+);
+
+/**
+ * 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 (
-        <View style={styles.reactionListContainer}>
+        <>
             <HeaderReactionList
                 onClose={props.onClose}
                 emojiName={props.emojiName}
@@ -61,8 +107,9 @@ const BaseReactionList = (props) => {
                 keyExtractor={keyExtractor}
                 getItemLayout={getItemLayout}
                 contentContainerStyle={styles.pv2}
+                style={styles.reactionListContainer}
             />
-        </View>
+        </>
     );
 };
 
@@ -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 => (
     <View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.emojiReactionListHeader, !props.isSmallScreenWidth && styles.pt4]}>
         <View style={styles.flexRow}>
-            <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionListHeaderBubbleStyle(props.hasUserReacted)]}>
+            <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionBubbleStyle(false, props.hasUserReacted)]}>
                 <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
                     {props.emojiCodes.join('')}
                 </Text>
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 (
-        <View style={props.wrapperStyles}>
-            <View style={[styles.alignSelfStart, styles.mr3]}>
-                <Avatar
-                    containerStyles={[styles.actionAvatar]}
-                    source={avatarSource}
-                />
-            </View>
-            <View style={[styles.chatItemRight]}>
-                <Text numberOfLines={1} style={styles.h3}>{props.item.displayName}</Text>
-                <Text style={{color: colors.greenSupportingText}}>{props.item.login}</Text>
-            </View>
-        </View>
-    );
-};
-
-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 && (
                     <ReportActionItemReactions
-                        ref={el => 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}
                 />
-                <PopoverReactionList
-                    ref={ReactionList.reactionListRef}
-                />
+                <PopoverReactionList ref={ReactionList.reactionListRef} />
                 <CopySelectionHelper />
             </>
         );
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 <terry.perun@gmail.com>
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}) => (
     <OptionRow
         item={item}
         boldStyle
+        style={{maxWidth: variables.mobileResponsiveWidthBreakpoint}}
         option={{
             text: Str.removeSMSDomain(item.displayName),
             alternateText: Str.removeSMSDomain(item.login),
@@ -99,7 +95,6 @@ const BaseReactionList = (props) => {
                 emojiCodes={props.emojiCodes}
                 emojiCount={props.emojiCount}
                 hasUserReacted={props.hasUserReacted}
-                sizeScale={props.sizeScale}
             />
             <FlatList
                 data={props.users}
diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.js b/src/pages/home/report/ReactionList/HeaderReactionList.js
index 8d574417f36c..b3551aff4f60 100644
--- a/src/pages/home/report/ReactionList/HeaderReactionList.js
+++ b/src/pages/home/report/ReactionList/HeaderReactionList.js
@@ -7,10 +7,7 @@ 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 reactionPropTypes from './reactionPropTypes';
 import compose from '../../../../libs/compose';
 import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions';
 
@@ -26,7 +23,6 @@ const propTypes = {
 };
 
 const defaultProps = {
-    ...reactionDefaultProps,
     hasUserReacted: false,
 };
 
@@ -34,10 +30,10 @@ const HeaderReactionList = props => (
     <View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.emojiReactionListHeader, !props.isSmallScreenWidth && styles.pt4]}>
         <View style={styles.flexRow}>
             <View style={[styles.emojiReactionListHeaderBubble, StyleUtils.getEmojiReactionBubbleStyle(false, props.hasUserReacted)]}>
-                <Text style={[styles.emojiReactionText, StyleUtils.getEmojiReactionTextStyle(props.sizeScale)]}>
+                <Text style={[styles.miniQuickEmojiReactionText, StyleUtils.getEmojiReactionBubbleTextStyle(true)]}>
                     {props.emojiCodes.join('')}
                 </Text>
-                <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted, props.sizeScale)]}>
+                <Text style={[styles.reactionCounterText, StyleUtils.getEmojiReactionCounterTextStyle(props.hasUserReacted)]}>
                     {props.emojiCount}
                 </Text>
             </View>
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}
                     />
                 </PopoverWithMeasuredContent>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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 <terry.perun@gmail.com>
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}) => (
     <OptionRow
         item={item}
         boldStyle
+        isDisabled
         style={{maxWidth: variables.mobileResponsiveWidthBreakpoint}}
         option={{
             text: Str.removeSMSDomain(item.displayName),
