From c4b7c3d97225359e850d085b947676802c523cb5 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 13:22:47 -0400
Subject: [PATCH 01/18] Add only visible label

---
 src/languages/en.js                           |  1 +
 src/languages/es.js                           |  1 +
 .../home/report/ReportActionItemSingle.js     | 93 +++++++++++--------
 3 files changed, 55 insertions(+), 40 deletions(-)

diff --git a/src/languages/en.js b/src/languages/en.js
index e50e86aba693..55148e2dc2ad 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -242,6 +242,7 @@ export default {
         editComment: 'Edit comment',
         deleteComment: 'Delete comment',
         deleteConfirmation: 'Are you sure you want to delete this comment?',
+        onlyVisible: 'Only visible to you',
     },
     emojiReactions: {
         addReactionTooltip: 'Add reaction',
diff --git a/src/languages/es.js b/src/languages/es.js
index c3fa8eb065a2..eadec3feb827 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -241,6 +241,7 @@ export default {
         editComment: 'Editar comentario',
         deleteComment: 'Eliminar comentario',
         deleteConfirmation: '¿Estás seguro de que quieres eliminar este comentario?',
+        onlyVisible: 'Only visible to you',
     },
     emojiReactions: {
         addReactionTooltip: 'Añadir una reacción',
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 19d2cc05f58c..0cf74192ff03 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -15,11 +15,14 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal
 import Navigation from '../../../libs/Navigation/Navigation';
 import ROUTES from '../../../ROUTES';
 import {withPersonalDetails} from '../../../components/OnyxProvider';
+import Text from '../../../components/Text';
 import Tooltip from '../../../components/Tooltip';
 import ControlSelection from '../../../libs/ControlSelection';
 import * as ReportUtils from '../../../libs/ReportUtils';
 import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
 import CONST from '../../../CONST';
+import * as Expensicons from "../../../components/Icon/Expensicons";
+import Icon from "../../../components/Icon";
 
 const propTypes = {
     /** All the data of the action */
@@ -70,47 +73,57 @@ const ReportActionItemSingle = (props) => {
 
     return (
         <View style={props.wrapperStyles}>
-            <Pressable
-                style={[styles.alignSelfStart, styles.mr3]}
-                onPressIn={ControlSelection.block}
-                onPressOut={ControlSelection.unblock}
-                onPress={() => showUserDetails(actorEmail)}
-            >
-                <Tooltip text={actorEmail}>
-                    <OfflineWithFeedback
-                        pendingAction={lodashGet(pendingFields, 'avatar', null)}
-                    >
-                        <Avatar
-                            containerStyles={[styles.actionAvatar]}
-                            source={avatarSource}
-                        />
-                    </OfflineWithFeedback>
-                </Tooltip>
-            </Pressable>
-            <View style={[styles.chatItemRight]}>
-                {props.showHeader ? (
-                    <View style={[styles.chatItemMessageHeader]}>
-                        <Pressable
-                            style={[styles.flexShrink1, styles.mr1]}
-                            onPressIn={ControlSelection.block}
-                            onPressOut={ControlSelection.unblock}
-                            onPress={() => showUserDetails(actorEmail)}
+            <View style={[styles.chatItem]}>
+                <View style={[styles.chatItemRight, styles.mr3]}>
+                    <Icon src={Expensicons.Eye} />
+                </View>
+                <Text style={[styles.chatItemMessageHeaderTimestamp]}>
+                    {props.translate('reportActionContextMenu.onlyVisible')}
+                </Text>
+            </View>
+            <View style={[styles.chatItem]}>
+                <Pressable
+                    style={[styles.alignSelfStart, styles.mr3]}
+                    onPressIn={ControlSelection.block}
+                    onPressOut={ControlSelection.unblock}
+                    onPress={() => showUserDetails(actorEmail)}
+                >
+                    <Tooltip text={actorEmail}>
+                        <OfflineWithFeedback
+                            pendingAction={lodashGet(pendingFields, 'avatar', null)}
                         >
-                            {_.map(personArray, (fragment, index) => (
-                                <ReportActionItemFragment
-                                    key={`person-${props.action.reportActionID}-${index}`}
-                                    fragment={fragment}
-                                    tooltipText={actorEmail}
-                                    isAttachment={props.action.isAttachment}
-                                    isLoading={props.action.isLoading}
-                                    isSingleLine
-                                />
-                            ))}
-                        </Pressable>
-                        <ReportActionItemDate created={props.action.created} />
-                    </View>
-                ) : null}
-                {props.children}
+                            <Avatar
+                                containerStyles={[styles.actionAvatar]}
+                                source={avatarSource}
+                            />
+                        </OfflineWithFeedback>
+                    </Tooltip>
+                </Pressable>
+                <View style={[styles.chatItemRight]}>
+                    {props.showHeader ? (
+                        <View style={[styles.chatItemMessageHeader]}>
+                            <Pressable
+                                style={[styles.flexShrink1, styles.mr1]}
+                                onPressIn={ControlSelection.block}
+                                onPressOut={ControlSelection.unblock}
+                                onPress={() => showUserDetails(actorEmail)}
+                            >
+                                {_.map(personArray, (fragment, index) => (
+                                    <ReportActionItemFragment
+                                        key={`person-${props.action.reportActionID}-${index}`}
+                                        fragment={fragment}
+                                        tooltipText={actorEmail}
+                                        isAttachment={props.action.isAttachment}
+                                        isLoading={props.action.isLoading}
+                                        isSingleLine
+                                    />
+                                ))}
+                            </Pressable>
+                            <ReportActionItemDate created={props.action.created} />
+                        </View>
+                    ) : null}
+                    {props.children}
+                </View>
             </View>
         </View>
     );

From 8d59ce9d1fc919156da276e9116ac746f35adc27 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 13:46:08 -0400
Subject: [PATCH 02/18] Add whisperTo functionality

---
 .../home/report/ReportActionItemSingle.js     | 23 +++++++++++--------
 .../report/reportActionFragmentPropTypes.js   |  3 +++
 src/styles/styles.js                          |  4 ++++
 3 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 0cf74192ff03..3d0668228dde 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -21,8 +21,9 @@ import ControlSelection from '../../../libs/ControlSelection';
 import * as ReportUtils from '../../../libs/ReportUtils';
 import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
 import CONST from '../../../CONST';
-import * as Expensicons from "../../../components/Icon/Expensicons";
-import Icon from "../../../components/Icon";
+import * as Expensicons from '../../../components/Icon/Expensicons';
+import Icon from '../../../components/Icon';
+import * as EmojiUtils from "../../../libs/EmojiUtils";
 
 const propTypes = {
     /** All the data of the action */
@@ -72,15 +73,17 @@ const ReportActionItemSingle = (props) => {
         : props.action.person;
 
     return (
-        <View style={props.wrapperStyles}>
-            <View style={[styles.chatItem]}>
-                <View style={[styles.chatItemRight, styles.mr3]}>
-                    <Icon src={Expensicons.Eye} />
+        <View style={[props.wrapperStyles, props.action.isWhisper ? styles.whisper : undefined]}>
+            {props.action.isWhisper && (
+                <View style={[styles.chatItem]}>
+                    <View style={[styles.chatItemRight, styles.mr3]}>
+                        <Icon src={Expensicons.Eye} />
+                    </View>
+                    <Text style={[styles.chatItemMessageHeaderTimestamp]}>
+                        {props.translate('reportActionContextMenu.onlyVisible')}
+                    </Text>
                 </View>
-                <Text style={[styles.chatItemMessageHeaderTimestamp]}>
-                    {props.translate('reportActionContextMenu.onlyVisible')}
-                </Text>
-            </View>
+            )}
             <View style={[styles.chatItem]}>
                 <Pressable
                     style={[styles.alignSelfStart, styles.mr3]}
diff --git a/src/pages/home/report/reportActionFragmentPropTypes.js b/src/pages/home/report/reportActionFragmentPropTypes.js
index 0e30deae5ed1..42c98a273fd1 100644
--- a/src/pages/home/report/reportActionFragmentPropTypes.js
+++ b/src/pages/home/report/reportActionFragmentPropTypes.js
@@ -29,4 +29,7 @@ export default PropTypes.shape({
 
     /** Fragment edited flag */
     isEdited: PropTypes.bool,
+
+    /** Flag to signal that the message is not visible to all participants */
+    isWhisper: PropTypes.bool,
 });
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 3f983b4579d2..43887da31ec4 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -3094,6 +3094,10 @@ const styles = {
         width: variables.modalContentMaxWidth,
         textAlign: 'center',
     },
+
+    whisper: {
+        backgroundColor: themeColors.cardBG,
+    },
 };
 
 export default styles;

From 51fb7e4f5a2ae1bb2c17df8ec47871067ff62567 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 14:31:58 -0400
Subject: [PATCH 03/18] Display names

---
 src/languages/en.js                            |  3 ++-
 src/languages/es.js                            |  3 ++-
 src/libs/ReportUtils.js                        | 18 ++++++++++++++++++
 .../home/report/ReportActionItemSingle.js      | 14 +++++++++++++-
 .../report/reportActionFragmentPropTypes.js    |  4 ++++
 5 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/src/languages/en.js b/src/languages/en.js
index 55148e2dc2ad..ca4ce72ae97c 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -121,6 +121,7 @@ export default {
         message: 'Message ',
         leaveRoom: 'Leave room',
         you: 'You',
+        youAfterPreposition: 'you',
         your: 'your',
         conciergeHelp: 'Please reach out to Concierge for help.',
         maxParticipantsReached: ({count}) => `You've selected the maximum number (${count}) of participants.`,
@@ -242,7 +243,7 @@ export default {
         editComment: 'Edit comment',
         deleteComment: 'Delete comment',
         deleteConfirmation: 'Are you sure you want to delete this comment?',
-        onlyVisible: 'Only visible to you',
+        onlyVisible: ({participants}) => `Only visible to ${participants}`,
     },
     emojiReactions: {
         addReactionTooltip: 'Add reaction',
diff --git a/src/languages/es.js b/src/languages/es.js
index eadec3feb827..d43e948eae20 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -120,6 +120,7 @@ export default {
         message: 'Chatear con ',
         leaveRoom: 'Salir de la sala de chat',
         you: 'Tú',
+        youAfterPreposition: 'ti',
         your: 'tu',
         conciergeHelp: 'Por favor, contacta con Concierge para obtener ayuda.',
         maxParticipantsReached: ({count}) => `Has seleccionado el número máximo (${count}) de participantes.`,
@@ -241,7 +242,7 @@ export default {
         editComment: 'Editar comentario',
         deleteComment: 'Eliminar comentario',
         deleteConfirmation: '¿Estás seguro de que quieres eliminar este comentario?',
-        onlyVisible: 'Only visible to you',
+        onlyVisible: ({participants}) => `Visible sólo para ${participants}`,
     },
     emojiReactions: {
         addReactionTooltip: 'Añadir una reacción',
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index f35f2ceb7784..d13aa75e3485 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -1707,6 +1707,23 @@ function canLeaveRoom(report, isPolicyMember) {
     return true;
 }
 
+function getWhisperDisplayNames(participants) {
+    const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
+    const isMultipleParticipantReport = participantsWithoutCurrentUser.length > 1;
+
+    // If we removed the user seeing the whispers, it means it's only visible to "you"
+    if (!isMultipleParticipantReport) {
+        return Localize.translateLocal('common.youAfterPreposition');
+    }
+
+    const displayNames = [];
+    for (let i = 0; i < participantsWithoutCurrentUser.length; i++) {
+        const login = participantsWithoutCurrentUser[i];
+        displayNames.push(getDisplayNameForParticipant(login, isMultipleParticipantReport));
+    }
+    return displayNames.join(', ');
+}
+
 export {
     getReportParticipantsTitle,
     isReportMessageAttachment,
@@ -1775,4 +1792,5 @@ export {
     getSmallSizeAvatar,
     getMoneyRequestOptions,
     canRequestMoney,
+    getWhisperDisplayNames,
 };
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 3d0668228dde..bfa823286a8c 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -23,7 +23,7 @@ import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
 import CONST from '../../../CONST';
 import * as Expensicons from '../../../components/Icon/Expensicons';
 import Icon from '../../../components/Icon';
-import * as EmojiUtils from "../../../libs/EmojiUtils";
+import DisplayNames from "../../../components/DisplayNames";
 
 const propTypes = {
     /** All the data of the action */
@@ -72,6 +72,10 @@ const ReportActionItemSingle = (props) => {
         ? [{type: 'TEXT', text: Str.isSMSLogin(login) ? props.toLocalPhone(displayName) : displayName}]
         : props.action.person;
 
+    // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
+    const isMultipleParticipant = lodashGet(props.action, 'whisperedTo', 0) > 1;
+    const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips((props.action.whisperedTo || []).slice(0, 10), isMultipleParticipant);
+
     return (
         <View style={[props.wrapperStyles, props.action.isWhisper ? styles.whisper : undefined]}>
             {props.action.isWhisper && (
@@ -82,6 +86,14 @@ const ReportActionItemSingle = (props) => {
                     <Text style={[styles.chatItemMessageHeaderTimestamp]}>
                         {props.translate('reportActionContextMenu.onlyVisible')}
                     </Text>
+                    <DisplayNames
+                        fullTitle={ReportUtils.getWhisperDisplayNames(props.action.participants || [])}
+                        displayNamesWithTooltips={displayNamesWithTooltips}
+                        tooltipEnabled
+                        numberOfLines={1}
+                        textStyles={[styles.chatItemMessageHeaderTimestamp, styles.pre]}
+                        shouldUseFullTitle={false}
+                    />
                 </View>
             )}
             <View style={[styles.chatItem]}>
diff --git a/src/pages/home/report/reportActionFragmentPropTypes.js b/src/pages/home/report/reportActionFragmentPropTypes.js
index 42c98a273fd1..906fba073a35 100644
--- a/src/pages/home/report/reportActionFragmentPropTypes.js
+++ b/src/pages/home/report/reportActionFragmentPropTypes.js
@@ -1,4 +1,5 @@
 import PropTypes from 'prop-types';
+import participantPropTypes from "../../../components/participantPropTypes";
 
 export default PropTypes.shape({
     /** The type of the action item fragment. Used to render a corresponding component */
@@ -32,4 +33,7 @@ export default PropTypes.shape({
 
     /** Flag to signal that the message is not visible to all participants */
     isWhisper: PropTypes.bool,
+
+    /** Display names of the people to which the whisper was sent to */
+    whisperedTo: PropTypes.arrayOf(participantPropTypes),
 });

From 1ed10466fd1aea97d7e915b808d14abe7b3038da Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 14:42:14 -0400
Subject: [PATCH 04/18] Fix errors

---
 src/pages/home/report/ReportActionItemSingle.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index bfa823286a8c..407611a434d3 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -23,7 +23,7 @@ import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
 import CONST from '../../../CONST';
 import * as Expensicons from '../../../components/Icon/Expensicons';
 import Icon from '../../../components/Icon';
-import DisplayNames from "../../../components/DisplayNames";
+import DisplayNames from '../../../components/DisplayNames';
 
 const propTypes = {
     /** All the data of the action */
@@ -87,11 +87,11 @@ const ReportActionItemSingle = (props) => {
                         {props.translate('reportActionContextMenu.onlyVisible')}
                     </Text>
                     <DisplayNames
-                        fullTitle={ReportUtils.getWhisperDisplayNames(props.action.participants || [])}
+                        fullTitle={ReportUtils.getWhisperDisplayNames(props.action.whisperedTo || [])}
                         displayNamesWithTooltips={displayNamesWithTooltips}
                         tooltipEnabled
                         numberOfLines={1}
-                        textStyles={[styles.chatItemMessageHeaderTimestamp, styles.pre]}
+                        textStyles={[styles.chatItemMessageHeaderTimestamp]}
                         shouldUseFullTitle={false}
                     />
                 </View>

From 621d79f4f894b1bec4a065e34d6a189ecdd924a7 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 15:47:50 -0400
Subject: [PATCH 05/18] Use action

---
 src/pages/home/report/ReportActionItemSingle.js        | 6 +++---
 src/pages/home/report/reportActionFragmentPropTypes.js | 7 -------
 src/pages/home/report/reportActionPropTypes.js         | 4 ++++
 3 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 407611a434d3..d84085526a74 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -75,10 +75,10 @@ const ReportActionItemSingle = (props) => {
     // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
     const isMultipleParticipant = lodashGet(props.action, 'whisperedTo', 0) > 1;
     const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips((props.action.whisperedTo || []).slice(0, 10), isMultipleParticipant);
-
+    const isWhisper = (props.action.whisperedTo || []).length > 0;
     return (
-        <View style={[props.wrapperStyles, props.action.isWhisper ? styles.whisper : undefined]}>
-            {props.action.isWhisper && (
+        <View style={[props.wrapperStyles, isWhisper ? styles.whisper : undefined]}>
+            {isWhisper && (
                 <View style={[styles.chatItem]}>
                     <View style={[styles.chatItemRight, styles.mr3]}>
                         <Icon src={Expensicons.Eye} />
diff --git a/src/pages/home/report/reportActionFragmentPropTypes.js b/src/pages/home/report/reportActionFragmentPropTypes.js
index 906fba073a35..0e30deae5ed1 100644
--- a/src/pages/home/report/reportActionFragmentPropTypes.js
+++ b/src/pages/home/report/reportActionFragmentPropTypes.js
@@ -1,5 +1,4 @@
 import PropTypes from 'prop-types';
-import participantPropTypes from "../../../components/participantPropTypes";
 
 export default PropTypes.shape({
     /** The type of the action item fragment. Used to render a corresponding component */
@@ -30,10 +29,4 @@ export default PropTypes.shape({
 
     /** Fragment edited flag */
     isEdited: PropTypes.bool,
-
-    /** Flag to signal that the message is not visible to all participants */
-    isWhisper: PropTypes.bool,
-
-    /** Display names of the people to which the whisper was sent to */
-    whisperedTo: PropTypes.arrayOf(participantPropTypes),
 });
diff --git a/src/pages/home/report/reportActionPropTypes.js b/src/pages/home/report/reportActionPropTypes.js
index f5821dfb9323..abd8509d8a84 100644
--- a/src/pages/home/report/reportActionPropTypes.js
+++ b/src/pages/home/report/reportActionPropTypes.js
@@ -1,6 +1,7 @@
 import PropTypes from 'prop-types';
 
 import reportActionFragmentPropTypes from './reportActionFragmentPropTypes';
+import participantPropTypes from '../../../components/participantPropTypes';
 
 export default {
     /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */
@@ -29,4 +30,7 @@ export default {
 
     /** Error message that's come back from the server. */
     error: PropTypes.string,
+
+    /** Display names of the people to which the whisper was sent to (if any) */
+    whisperedTo: PropTypes.arrayOf(participantPropTypes),
 };

From 12f3c99ec5d0f73274182411048d47c4d608a9f1 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 20:05:08 -0400
Subject: [PATCH 06/18] Refactor code

---
 src/pages/home/report/ReportActionItem.js     | 29 +++++++++++++++++++
 .../home/report/ReportActionItemSingle.js     | 28 +-----------------
 .../home/report/reportActionPropTypes.js      |  6 ++--
 3 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index c4e7d6c5fe69..60aaeb62b413 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -40,6 +40,11 @@ import ChronosOOOListActions from '../../../components/ReportActionItem/ChronosO
 import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions';
 import * as Report from '../../../libs/actions/Report';
 import withLocalize from '../../../components/withLocalize';
+import Icon from "../../../components/Icon";
+import * as Expensicons from "../../../components/Icon/Expensicons";
+import Text from "../../../components/Text";
+import DisplayNames from "../../../components/DisplayNames";
+import * as OptionsListUtils from "../../../libs/OptionsListUtils";
 
 const propTypes = {
     /** Report for this action */
@@ -239,6 +244,12 @@ class ReportActionItem extends Component {
         if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.CHRONOSOOOLIST) {
             return <ChronosOOOListActions action={this.props.action} reportID={this.props.report.reportID} />;
         }
+
+        const whisperedTo = lodashGet(this.props.action, 'whisperedTo', []);
+        const isWhisper = _.size(whisperedTo) > 0;
+        const isMultipleParticipant = _.size(whisperedTo) > 1;
+        const participantPersonalDetailList = isWhisper ? _.values(OptionsListUtils.getPersonalDetailsForLogins(_.keys(whisperedTo), whisperedTo)) : [];
+        const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetailList, isMultipleParticipant) : [];
         return (
             <PressableWithSecondaryInteraction
                 pointerEvents={this.props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ? 'none' : 'auto'}
@@ -276,6 +287,24 @@ class ReportActionItem extends Component {
                                     errorRowStyles={[styles.ml10, styles.mr2]}
                                     needsOffscreenAlphaCompositing={this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU}
                                 >
+                                    {isWhisper && (
+                                        <View style={[styles.chatItem]}>
+                                            <View style={[styles.chatItemRight, styles.mr3]}>
+                                                <Icon src={Expensicons.Eye} />
+                                            </View>
+                                            <Text style={[styles.chatItemMessageHeaderTimestamp]}>
+                                                {this.props.translate('reportActionContextMenu.onlyVisible')}
+                                            </Text>
+                                            <DisplayNames
+                                                fullTitle={ReportUtils.getWhisperDisplayNames(participantPersonalDetailList)}
+                                                displayNamesWithTooltips={displayNamesWithTooltips}
+                                                tooltipEnabled
+                                                numberOfLines={1}
+                                                textStyles={[styles.chatItemMessageHeaderTimestamp]}
+                                                shouldUseFullTitle={false}
+                                            />
+                                        </View>
+                                    )}
                                     {!this.props.displayAsGroup
                                         ? (
                                             <ReportActionItemSingle action={this.props.action} showHeader={!this.props.draftMessage}>
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index d84085526a74..4ceb4b13dadb 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -15,15 +15,11 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal
 import Navigation from '../../../libs/Navigation/Navigation';
 import ROUTES from '../../../ROUTES';
 import {withPersonalDetails} from '../../../components/OnyxProvider';
-import Text from '../../../components/Text';
 import Tooltip from '../../../components/Tooltip';
 import ControlSelection from '../../../libs/ControlSelection';
 import * as ReportUtils from '../../../libs/ReportUtils';
 import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
 import CONST from '../../../CONST';
-import * as Expensicons from '../../../components/Icon/Expensicons';
-import Icon from '../../../components/Icon';
-import DisplayNames from '../../../components/DisplayNames';
 
 const propTypes = {
     /** All the data of the action */
@@ -72,30 +68,8 @@ const ReportActionItemSingle = (props) => {
         ? [{type: 'TEXT', text: Str.isSMSLogin(login) ? props.toLocalPhone(displayName) : displayName}]
         : props.action.person;
 
-    // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
-    const isMultipleParticipant = lodashGet(props.action, 'whisperedTo', 0) > 1;
-    const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips((props.action.whisperedTo || []).slice(0, 10), isMultipleParticipant);
-    const isWhisper = (props.action.whisperedTo || []).length > 0;
     return (
-        <View style={[props.wrapperStyles, isWhisper ? styles.whisper : undefined]}>
-            {isWhisper && (
-                <View style={[styles.chatItem]}>
-                    <View style={[styles.chatItemRight, styles.mr3]}>
-                        <Icon src={Expensicons.Eye} />
-                    </View>
-                    <Text style={[styles.chatItemMessageHeaderTimestamp]}>
-                        {props.translate('reportActionContextMenu.onlyVisible')}
-                    </Text>
-                    <DisplayNames
-                        fullTitle={ReportUtils.getWhisperDisplayNames(props.action.whisperedTo || [])}
-                        displayNamesWithTooltips={displayNamesWithTooltips}
-                        tooltipEnabled
-                        numberOfLines={1}
-                        textStyles={[styles.chatItemMessageHeaderTimestamp]}
-                        shouldUseFullTitle={false}
-                    />
-                </View>
-            )}
+        <View style={[props.wrapperStyles]}>
             <View style={[styles.chatItem]}>
                 <Pressable
                     style={[styles.alignSelfStart, styles.mr3]}
diff --git a/src/pages/home/report/reportActionPropTypes.js b/src/pages/home/report/reportActionPropTypes.js
index abd8509d8a84..6c18c172e592 100644
--- a/src/pages/home/report/reportActionPropTypes.js
+++ b/src/pages/home/report/reportActionPropTypes.js
@@ -1,4 +1,4 @@
-import PropTypes from 'prop-types';
+import PropTypes, {array} from 'prop-types';
 
 import reportActionFragmentPropTypes from './reportActionFragmentPropTypes';
 import participantPropTypes from '../../../components/participantPropTypes';
@@ -31,6 +31,6 @@ export default {
     /** Error message that's come back from the server. */
     error: PropTypes.string,
 
-    /** Display names of the people to which the whisper was sent to (if any) */
-    whisperedTo: PropTypes.arrayOf(participantPropTypes),
+    /** Display names of the people to which the whisper was sent to (if any). Returns empty array if it is not a whisper */
+    whisperedTo: PropTypes.oneOfType([PropTypes.array, PropTypes.objectOf(participantPropTypes)]),
 };

From 73919c756cca6636942010090a299489c3de21ac Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 21:01:45 -0400
Subject: [PATCH 07/18] Whispers are showing

---
 src/pages/home/report/ReportActionItem.js     |  5 +-
 .../home/report/ReportActionItemSingle.js     | 84 +++++++++----------
 2 files changed, 43 insertions(+), 46 deletions(-)

diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 60aaeb62b413..0fc726c11c21 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -248,8 +248,7 @@ class ReportActionItem extends Component {
         const whisperedTo = lodashGet(this.props.action, 'whisperedTo', []);
         const isWhisper = _.size(whisperedTo) > 0;
         const isMultipleParticipant = _.size(whisperedTo) > 1;
-        const participantPersonalDetailList = isWhisper ? _.values(OptionsListUtils.getPersonalDetailsForLogins(_.keys(whisperedTo), whisperedTo)) : [];
-        const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetailList, isMultipleParticipant) : [];
+        const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedTo, isMultipleParticipant) : [];
         return (
             <PressableWithSecondaryInteraction
                 pointerEvents={this.props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ? 'none' : 'auto'}
@@ -296,7 +295,7 @@ class ReportActionItem extends Component {
                                                 {this.props.translate('reportActionContextMenu.onlyVisible')}
                                             </Text>
                                             <DisplayNames
-                                                fullTitle={ReportUtils.getWhisperDisplayNames(participantPersonalDetailList)}
+                                                fullTitle={ReportUtils.getWhisperDisplayNames(whisperedTo)}
                                                 displayNamesWithTooltips={displayNamesWithTooltips}
                                                 tooltipEnabled
                                                 numberOfLines={1}
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 4ceb4b13dadb..19d2cc05f58c 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -69,50 +69,48 @@ const ReportActionItemSingle = (props) => {
         : props.action.person;
 
     return (
-        <View style={[props.wrapperStyles]}>
-            <View style={[styles.chatItem]}>
-                <Pressable
-                    style={[styles.alignSelfStart, styles.mr3]}
-                    onPressIn={ControlSelection.block}
-                    onPressOut={ControlSelection.unblock}
-                    onPress={() => showUserDetails(actorEmail)}
-                >
-                    <Tooltip text={actorEmail}>
-                        <OfflineWithFeedback
-                            pendingAction={lodashGet(pendingFields, 'avatar', null)}
+        <View style={props.wrapperStyles}>
+            <Pressable
+                style={[styles.alignSelfStart, styles.mr3]}
+                onPressIn={ControlSelection.block}
+                onPressOut={ControlSelection.unblock}
+                onPress={() => showUserDetails(actorEmail)}
+            >
+                <Tooltip text={actorEmail}>
+                    <OfflineWithFeedback
+                        pendingAction={lodashGet(pendingFields, 'avatar', null)}
+                    >
+                        <Avatar
+                            containerStyles={[styles.actionAvatar]}
+                            source={avatarSource}
+                        />
+                    </OfflineWithFeedback>
+                </Tooltip>
+            </Pressable>
+            <View style={[styles.chatItemRight]}>
+                {props.showHeader ? (
+                    <View style={[styles.chatItemMessageHeader]}>
+                        <Pressable
+                            style={[styles.flexShrink1, styles.mr1]}
+                            onPressIn={ControlSelection.block}
+                            onPressOut={ControlSelection.unblock}
+                            onPress={() => showUserDetails(actorEmail)}
                         >
-                            <Avatar
-                                containerStyles={[styles.actionAvatar]}
-                                source={avatarSource}
-                            />
-                        </OfflineWithFeedback>
-                    </Tooltip>
-                </Pressable>
-                <View style={[styles.chatItemRight]}>
-                    {props.showHeader ? (
-                        <View style={[styles.chatItemMessageHeader]}>
-                            <Pressable
-                                style={[styles.flexShrink1, styles.mr1]}
-                                onPressIn={ControlSelection.block}
-                                onPressOut={ControlSelection.unblock}
-                                onPress={() => showUserDetails(actorEmail)}
-                            >
-                                {_.map(personArray, (fragment, index) => (
-                                    <ReportActionItemFragment
-                                        key={`person-${props.action.reportActionID}-${index}`}
-                                        fragment={fragment}
-                                        tooltipText={actorEmail}
-                                        isAttachment={props.action.isAttachment}
-                                        isLoading={props.action.isLoading}
-                                        isSingleLine
-                                    />
-                                ))}
-                            </Pressable>
-                            <ReportActionItemDate created={props.action.created} />
-                        </View>
-                    ) : null}
-                    {props.children}
-                </View>
+                            {_.map(personArray, (fragment, index) => (
+                                <ReportActionItemFragment
+                                    key={`person-${props.action.reportActionID}-${index}`}
+                                    fragment={fragment}
+                                    tooltipText={actorEmail}
+                                    isAttachment={props.action.isAttachment}
+                                    isLoading={props.action.isLoading}
+                                    isSingleLine
+                                />
+                            ))}
+                        </Pressable>
+                        <ReportActionItemDate created={props.action.created} />
+                    </View>
+                ) : null}
+                {props.children}
             </View>
         </View>
     );

From e5aa976ee115815728f93dd23e6e92d2bd5e573d Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 21:41:16 -0400
Subject: [PATCH 08/18] Handle only you

---
 src/languages/en.js                       |  2 +-
 src/languages/es.js                       |  2 +-
 src/libs/ReportUtils.js                   | 31 ++++++++++++++++++-----
 src/pages/home/report/ReportActionItem.js |  6 +++--
 4 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/src/languages/en.js b/src/languages/en.js
index ca4ce72ae97c..0dde540c4a2c 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -243,7 +243,7 @@ export default {
         editComment: 'Edit comment',
         deleteComment: 'Delete comment',
         deleteConfirmation: 'Are you sure you want to delete this comment?',
-        onlyVisible: ({participants}) => `Only visible to ${participants}`,
+        onlyVisible: `Only visible to`,
     },
     emojiReactions: {
         addReactionTooltip: 'Add reaction',
diff --git a/src/languages/es.js b/src/languages/es.js
index d43e948eae20..fcad9e54698c 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -242,7 +242,7 @@ export default {
         editComment: 'Editar comentario',
         deleteComment: 'Eliminar comentario',
         deleteConfirmation: '¿Estás seguro de que quieres eliminar este comentario?',
-        onlyVisible: ({participants}) => `Visible sólo para ${participants}`,
+        onlyVisible: `Visible sólo para`,
     },
     emojiReactions: {
         addReactionTooltip: 'Añadir una reacción',
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index d13aa75e3485..c1ca5d5046b9 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -1707,23 +1707,39 @@ function canLeaveRoom(report, isPolicyMember) {
     return true;
 }
 
+/**
+ * Returns display names for those that can see the whisper.
+ * However, it returns "you" if only the current user can see it besides the person that sent it.
+ *
+ * @param {string[]} participants
+ * @returns {Boolean}
+ */
 function getWhisperDisplayNames(participants) {
-    const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
-    const isMultipleParticipantReport = participantsWithoutCurrentUser.length > 1;
+    const isOneOnOneWhisper = this.isOnlyVisibleByCurrentUser(participants);
 
-    // If we removed the user seeing the whispers, it means it's only visible to "you"
-    if (!isMultipleParticipantReport) {
+    // If we removed all users, it means it's only visible to "you"
+    if (!isOneOnOneWhisper) {
         return Localize.translateLocal('common.youAfterPreposition');
     }
 
     const displayNames = [];
-    for (let i = 0; i < participantsWithoutCurrentUser.length; i++) {
-        const login = participantsWithoutCurrentUser[i];
-        displayNames.push(getDisplayNameForParticipant(login, isMultipleParticipantReport));
+    for (let i = 0; i < participants.length; i++) {
+        const login = participants[i];
+        displayNames.push(getDisplayNameForParticipant(login, !isOneOnOneWhisper));
     }
     return displayNames.join(', ');
 }
 
+/**
+ * Returns whether a whisper is only visible by the current user and whoever sent it.
+ * @param {string[]} participants
+ * @returns {Boolean}
+ */
+function isOnlyVisibleByCurrentUser(participants) {
+    const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
+    return participantsWithoutCurrentUser.length > 0;
+}
+
 export {
     getReportParticipantsTitle,
     isReportMessageAttachment,
@@ -1778,6 +1794,7 @@ export {
     getDisplayNameForParticipant,
     isExpenseReport,
     isIOUReport,
+    isOnlyVisibleByCurrentUser,
     chatIncludesChronos,
     getAvatar,
     isDefaultAvatar,
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 0fc726c11c21..eb796c357aee 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -248,6 +248,7 @@ class ReportActionItem extends Component {
         const whisperedTo = lodashGet(this.props.action, 'whisperedTo', []);
         const isWhisper = _.size(whisperedTo) > 0;
         const isMultipleParticipant = _.size(whisperedTo) > 1;
+        const isOnlyVisibleByUser = ReportUtils.isOnlyVisibleByCurrentUser(_.keys(whisperedTo));
         const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedTo, isMultipleParticipant) : [];
         return (
             <PressableWithSecondaryInteraction
@@ -293,14 +294,15 @@ class ReportActionItem extends Component {
                                             </View>
                                             <Text style={[styles.chatItemMessageHeaderTimestamp]}>
                                                 {this.props.translate('reportActionContextMenu.onlyVisible')}
+                                                &nbsp;
                                             </Text>
                                             <DisplayNames
-                                                fullTitle={ReportUtils.getWhisperDisplayNames(whisperedTo)}
+                                                fullTitle={ReportUtils.getWhisperDisplayNames(_.keys(whisperedTo))}
                                                 displayNamesWithTooltips={displayNamesWithTooltips}
                                                 tooltipEnabled
                                                 numberOfLines={1}
                                                 textStyles={[styles.chatItemMessageHeaderTimestamp]}
-                                                shouldUseFullTitle={false}
+                                                shouldUseFullTitle={!isOnlyVisibleByUser}
                                             />
                                         </View>
                                     )}

From ebc353473418fb36f682c1d9090b4b283098e787 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Mon, 17 Apr 2023 23:24:38 -0400
Subject: [PATCH 09/18] Style

---
 src/languages/en.js                            | 2 +-
 src/languages/es.js                            | 2 +-
 src/pages/home/report/ReportActionItem.js      | 9 ++++-----
 src/pages/home/report/reportActionPropTypes.js | 2 +-
 4 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/languages/en.js b/src/languages/en.js
index a75a87405dd0..3b69d9dd8c5b 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -251,7 +251,7 @@ export default {
         editComment: 'Edit comment',
         deleteComment: 'Delete comment',
         deleteConfirmation: 'Are you sure you want to delete this comment?',
-        onlyVisible: `Only visible to`,
+        onlyVisible: 'Only visible to',
     },
     emojiReactions: {
         addReactionTooltip: 'Add reaction',
diff --git a/src/languages/es.js b/src/languages/es.js
index 90ee71f389a3..27999a1ef8b9 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -250,7 +250,7 @@ export default {
         editComment: 'Editar comentario',
         deleteComment: 'Eliminar comentario',
         deleteConfirmation: '¿Estás seguro de que quieres eliminar este comentario?',
-        onlyVisible: `Visible sólo para`,
+        onlyVisible: 'Visible sólo para',
     },
     emojiReactions: {
         addReactionTooltip: 'Añadir una reacción',
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index eb796c357aee..2c4414b424a1 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -40,11 +40,10 @@ import ChronosOOOListActions from '../../../components/ReportActionItem/ChronosO
 import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions';
 import * as Report from '../../../libs/actions/Report';
 import withLocalize from '../../../components/withLocalize';
-import Icon from "../../../components/Icon";
-import * as Expensicons from "../../../components/Icon/Expensicons";
-import Text from "../../../components/Text";
-import DisplayNames from "../../../components/DisplayNames";
-import * as OptionsListUtils from "../../../libs/OptionsListUtils";
+import Icon from '../../../components/Icon';
+import * as Expensicons from '../../../components/Icon/Expensicons';
+import Text from '../../../components/Text';
+import DisplayNames from '../../../components/DisplayNames';
 
 const propTypes = {
     /** Report for this action */
diff --git a/src/pages/home/report/reportActionPropTypes.js b/src/pages/home/report/reportActionPropTypes.js
index 6c18c172e592..77296d328d34 100644
--- a/src/pages/home/report/reportActionPropTypes.js
+++ b/src/pages/home/report/reportActionPropTypes.js
@@ -1,4 +1,4 @@
-import PropTypes, {array} from 'prop-types';
+import PropTypes from 'prop-types';
 
 import reportActionFragmentPropTypes from './reportActionFragmentPropTypes';
 import participantPropTypes from '../../../components/participantPropTypes';

From d6e54ccbca163a339772c8944f382e500d0ea37b Mon Sep 17 00:00:00 2001
From: Georgia Monahan <georgia@expensify.com>
Date: Tue, 18 Apr 2023 14:35:29 +0100
Subject: [PATCH 10/18] Whisper styles

---
 src/pages/home/report/ReportActionItem.js        | 15 ++++++++++-----
 src/pages/home/report/ReportActionItemGrouped.js | 11 ++++++++++-
 src/styles/utilities/spacing.js                  |  4 ++++
 3 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 2c4414b424a1..6aa9d6053474 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -268,6 +268,7 @@ class ReportActionItem extends Component {
                             <View
                                 style={StyleUtils.getReportActionItemStyle(
                                     hovered
+                                    || isWhisper
                                     || this.state.isContextMenuActive
                                     || this.props.draftMessage,
                                     (this.props.network.isOffline && this.props.action.isLoading) || this.props.action.error,
@@ -287,9 +288,9 @@ class ReportActionItem extends Component {
                                     needsOffscreenAlphaCompositing={this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU}
                                 >
                                     {isWhisper && (
-                                        <View style={[styles.chatItem]}>
-                                            <View style={[styles.chatItemRight, styles.mr3]}>
-                                                <Icon src={Expensicons.Eye} />
+                                        <View style={[styles.flexRow, styles.pl5, styles.pt2]}>
+                                            <View style={[styles.pl6, styles.mr3]}>
+                                                <Icon src={Expensicons.Eye} small />
                                             </View>
                                             <Text style={[styles.chatItemMessageHeaderTimestamp]}>
                                                 {this.props.translate('reportActionContextMenu.onlyVisible')}
@@ -307,12 +308,16 @@ class ReportActionItem extends Component {
                                     )}
                                     {!this.props.displayAsGroup
                                         ? (
-                                            <ReportActionItemSingle action={this.props.action} showHeader={!this.props.draftMessage}>
+                                            <ReportActionItemSingle
+                                                action={this.props.action}
+                                                showHeader={!this.props.draftMessage}
+                                                wrapperStyles={[styles.chatItem, isWhisper && styles.pt1]}
+                                            >
                                                 {this.renderItemContent(hovered || this.state.isContextMenuActive)}
                                             </ReportActionItemSingle>
                                         )
                                         : (
-                                            <ReportActionItemGrouped>
+                                            <ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper && styles.pt1]}>
                                                 {this.renderItemContent(hovered || this.state.isContextMenuActive)}
                                             </ReportActionItemGrouped>
                                         )}
diff --git a/src/pages/home/report/ReportActionItemGrouped.js b/src/pages/home/report/ReportActionItemGrouped.js
index e811144ccaad..9c65afd9b6f3 100644
--- a/src/pages/home/report/ReportActionItemGrouped.js
+++ b/src/pages/home/report/ReportActionItemGrouped.js
@@ -6,10 +6,18 @@ import styles from '../../../styles/styles';
 const propTypes = {
     /** Children view component for this action item */
     children: PropTypes.node.isRequired,
+
+    /** Styles for the outermost View */
+    // eslint-disable-next-line react/forbid-prop-types
+    wrapperStyles: PropTypes.arrayOf(PropTypes.object),
+};
+
+const defaultProps = {
+    wrapperStyles: [styles.chatItem],
 };
 
 const ReportActionItemGrouped = props => (
-    <View style={[styles.chatItem]}>
+    <View style={props.wrapperStyles}>
         <View style={[styles.chatItemRightGrouped]}>
             {props.children}
         </View>
@@ -17,5 +25,6 @@ const ReportActionItemGrouped = props => (
 );
 
 ReportActionItemGrouped.propTypes = propTypes;
+ReportActionItemGrouped.defaultProps = defaultProps;
 ReportActionItemGrouped.displayName = 'ReportActionItemGrouped';
 export default ReportActionItemGrouped;
diff --git a/src/styles/utilities/spacing.js b/src/styles/utilities/spacing.js
index 92445523120e..d56344c74bcf 100644
--- a/src/styles/utilities/spacing.js
+++ b/src/styles/utilities/spacing.js
@@ -370,6 +370,10 @@ export default {
         paddingLeft: 20,
     },
 
+    pl6: {
+        paddingLeft: 24,
+    },
+
     pl10: {
         paddingLeft: 40,
     },

From 8c2644052ac55aff8ca9c74f85558455823a2381 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Tue, 18 Apr 2023 14:39:31 -0400
Subject: [PATCH 11/18] Use emails for whispers

---
 src/pages/home/report/ReportActionItem.js      | 16 ++++++++++++++--
 src/pages/home/report/reportActionPropTypes.js |  5 ++---
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 2c4414b424a1..2adc3666a853 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -24,7 +24,12 @@ import * as DeviceCapabilities from '../../../libs/DeviceCapabilities';
 import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
 import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu';
 import * as ContextMenuActions from './ContextMenu/ContextMenuActions';
-import {withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider';
+import {
+    withBlockedFromConcierge,
+    withNetwork,
+    withPersonalDetails,
+    withReportActionsDrafts
+} from '../../../components/OnyxProvider';
 import RenameAction from '../../../components/ReportActionItem/RenameAction';
 import InlineSystemMessage from '../../../components/InlineSystemMessage';
 import styles from '../../../styles/styles';
@@ -44,6 +49,7 @@ import Icon from '../../../components/Icon';
 import * as Expensicons from '../../../components/Icon/Expensicons';
 import Text from '../../../components/Text';
 import DisplayNames from '../../../components/DisplayNames';
+import personalDetailsPropType from '../../personalDetailsPropType';
 
 const propTypes = {
     /** Report for this action */
@@ -73,6 +79,9 @@ const propTypes = {
     /** Stores user's preferred skin tone */
     preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
 
+    /** All of the personalDetails */
+    personalDetails: PropTypes.objectOf(personalDetailsPropType),
+
     ...windowDimensionsPropTypes,
 };
 
@@ -80,6 +89,7 @@ const defaultProps = {
     draftMessage: '',
     hasOutstandingIOU: false,
     preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE,
+    personalDetails: {},
 };
 
 class ReportActionItem extends Component {
@@ -248,7 +258,8 @@ class ReportActionItem extends Component {
         const isWhisper = _.size(whisperedTo) > 0;
         const isMultipleParticipant = _.size(whisperedTo) > 1;
         const isOnlyVisibleByUser = ReportUtils.isOnlyVisibleByCurrentUser(_.keys(whisperedTo));
-        const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedTo, isMultipleParticipant) : [];
+        const whisperedToPersonalDetails = _.filter(this.props.personalDetails, details => _.includes(whisperedTo, details.login));
+        const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : [];
         return (
             <PressableWithSecondaryInteraction
                 pointerEvents={this.props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ? 'none' : 'auto'}
@@ -347,6 +358,7 @@ export default compose(
     withWindowDimensions,
     withLocalize,
     withNetwork(),
+    withPersonalDetails(),
     withBlockedFromConcierge({propName: 'blockedFromConcierge'}),
     withReportActionsDrafts({
         propName: 'draftMessage',
diff --git a/src/pages/home/report/reportActionPropTypes.js b/src/pages/home/report/reportActionPropTypes.js
index 77296d328d34..46cba2b5c365 100644
--- a/src/pages/home/report/reportActionPropTypes.js
+++ b/src/pages/home/report/reportActionPropTypes.js
@@ -1,7 +1,6 @@
 import PropTypes from 'prop-types';
 
 import reportActionFragmentPropTypes from './reportActionFragmentPropTypes';
-import participantPropTypes from '../../../components/participantPropTypes';
 
 export default {
     /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */
@@ -31,6 +30,6 @@ export default {
     /** Error message that's come back from the server. */
     error: PropTypes.string,
 
-    /** Display names of the people to which the whisper was sent to (if any). Returns empty array if it is not a whisper */
-    whisperedTo: PropTypes.oneOfType([PropTypes.array, PropTypes.objectOf(participantPropTypes)]),
+    /** Emails of the people to which the whisper was sent to (if any). Returns empty array if it is not a whisper */
+    whisperedTo: PropTypes.arrayOf(PropTypes.string),
 };

From 2d4a524fc857b98b1734f195380e9fe7233be46a Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Tue, 18 Apr 2023 15:02:32 -0400
Subject: [PATCH 12/18] Style

---
 src/pages/home/report/ReportActionItem.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 3368cf6dc1fc..694a70ffe3cb 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -28,7 +28,7 @@ import {
     withBlockedFromConcierge,
     withNetwork,
     withPersonalDetails,
-    withReportActionsDrafts
+    withReportActionsDrafts,
 } from '../../../components/OnyxProvider';
 import RenameAction from '../../../components/ReportActionItem/RenameAction';
 import InlineSystemMessage from '../../../components/InlineSystemMessage';
@@ -322,13 +322,13 @@ class ReportActionItem extends Component {
                                             <ReportActionItemSingle
                                                 action={this.props.action}
                                                 showHeader={!this.props.draftMessage}
-                                                wrapperStyles={[styles.chatItem, isWhisper && styles.pt1]}
+                                                wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : null]}
                                             >
                                                 {this.renderItemContent(hovered || this.state.isContextMenuActive)}
                                             </ReportActionItemSingle>
                                         )
                                         : (
-                                            <ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper && styles.pt1]}>
+                                            <ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : null]}>
                                                 {this.renderItemContent(hovered || this.state.isContextMenuActive)}
                                             </ReportActionItemGrouped>
                                         )}

From 21745bedd864bacc71cd5b5d9a2c5f919ba3e3c5 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Tue, 18 Apr 2023 17:24:32 -0400
Subject: [PATCH 13/18] Fix conditions

---
 src/libs/ReportUtils.js                   | 6 +++---
 src/pages/home/report/ReportActionItem.js | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index c1ca5d5046b9..cd1ee3300b62 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -1712,13 +1712,13 @@ function canLeaveRoom(report, isPolicyMember) {
  * However, it returns "you" if only the current user can see it besides the person that sent it.
  *
  * @param {string[]} participants
- * @returns {Boolean}
+ * @returns {string}
  */
 function getWhisperDisplayNames(participants) {
     const isOneOnOneWhisper = this.isOnlyVisibleByCurrentUser(participants);
 
     // If we removed all users, it means it's only visible to "you"
-    if (!isOneOnOneWhisper) {
+    if (isOneOnOneWhisper) {
         return Localize.translateLocal('common.youAfterPreposition');
     }
 
@@ -1737,7 +1737,7 @@ function getWhisperDisplayNames(participants) {
  */
 function isOnlyVisibleByCurrentUser(participants) {
     const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
-    return participantsWithoutCurrentUser.length > 0;
+    return participantsWithoutCurrentUser.length === 0;
 }
 
 export {
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 694a70ffe3cb..013a724e6ce1 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -257,8 +257,8 @@ class ReportActionItem extends Component {
         const whisperedTo = lodashGet(this.props.action, 'whisperedTo', []);
         const isWhisper = _.size(whisperedTo) > 0;
         const isMultipleParticipant = _.size(whisperedTo) > 1;
-        const isOnlyVisibleByUser = ReportUtils.isOnlyVisibleByCurrentUser(_.keys(whisperedTo));
-        const whisperedToPersonalDetails = _.filter(this.props.personalDetails, details => _.includes(whisperedTo, details.login));
+        const isOnlyVisibleByUser = isWhisper ? ReportUtils.isOnlyVisibleByCurrentUser(whisperedTo) : false;
+        const whisperedToPersonalDetails = isWhisper ? _.filter(this.props.personalDetails, details => _.includes(whisperedTo, details.login)) : [];
         const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : [];
         return (
             <PressableWithSecondaryInteraction
@@ -308,12 +308,12 @@ class ReportActionItem extends Component {
                                                 &nbsp;
                                             </Text>
                                             <DisplayNames
-                                                fullTitle={ReportUtils.getWhisperDisplayNames(_.keys(whisperedTo))}
+                                                fullTitle={ReportUtils.getWhisperDisplayNames(whisperedTo)}
                                                 displayNamesWithTooltips={displayNamesWithTooltips}
                                                 tooltipEnabled
                                                 numberOfLines={1}
                                                 textStyles={[styles.chatItemMessageHeaderTimestamp]}
-                                                shouldUseFullTitle={!isOnlyVisibleByUser}
+                                                shouldUseFullTitle={isOnlyVisibleByUser}
                                             />
                                         </View>
                                     )}

From a7b28fd13157d946c37459ebd2e4e288e48c0e16 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <pecanoro@users.noreply.github.com>
Date: Tue, 18 Apr 2023 23:37:19 -0400
Subject: [PATCH 14/18] Apply suggestions from code review

Co-authored-by: Tim Golen <tgolen@gmail.com>
---
 src/libs/ReportUtils.js                   | 19 ++++++-------------
 src/pages/home/report/ReportActionItem.js |  6 +++---
 2 files changed, 9 insertions(+), 16 deletions(-)

diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index cd1ee3300b62..6a07f695474a 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -1715,29 +1715,22 @@ function canLeaveRoom(report, isPolicyMember) {
  * @returns {string}
  */
 function getWhisperDisplayNames(participants) {
-    const isOneOnOneWhisper = this.isOnlyVisibleByCurrentUser(participants);
+    const isCurrentUserTheOnlyParticipant = this.isCurrentUserTheOnlyParticipant(participants);
 
-    // If we removed all users, it means it's only visible to "you"
-    if (isOneOnOneWhisper) {
+    // When the current user is the only participant, the display name needs to be "you" because that's the only person reading it
+    if (isCurrentUserTheOnlyParticipant) {
         return Localize.translateLocal('common.youAfterPreposition');
     }
 
-    const displayNames = [];
-    for (let i = 0; i < participants.length; i++) {
-        const login = participants[i];
-        displayNames.push(getDisplayNameForParticipant(login, !isOneOnOneWhisper));
-    }
-    return displayNames.join(', ');
+    return _.map(participants, login => getDisplayNameForParticipant(login, ! isCurrentUserTheOnlyParticipant)).join(', ');
 }
 
 /**
- * Returns whether a whisper is only visible by the current user and whoever sent it.
  * @param {string[]} participants
  * @returns {Boolean}
  */
-function isOnlyVisibleByCurrentUser(participants) {
-    const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
-    return participantsWithoutCurrentUser.length === 0;
+function isCurrentUserTheOnlyParticipant(participants) {
+    return participants && participants.length === 1 && participants[0] === sessionEmail;
 }
 
 export {
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 013a724e6ce1..05ba0f7e9b53 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -255,9 +255,9 @@ class ReportActionItem extends Component {
         }
 
         const whisperedTo = lodashGet(this.props.action, 'whisperedTo', []);
-        const isWhisper = _.size(whisperedTo) > 0;
-        const isMultipleParticipant = _.size(whisperedTo) > 1;
-        const isOnlyVisibleByUser = isWhisper ? ReportUtils.isOnlyVisibleByCurrentUser(whisperedTo) : false;
+        const isWhisper = whisperedTo.length > 0;
+        const isMultipleParticipant = whisperedTo.length > 1;
+        const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedTo);
         const whisperedToPersonalDetails = isWhisper ? _.filter(this.props.personalDetails, details => _.includes(whisperedTo, details.login)) : [];
         const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : [];
         return (

From e7d4dad3cc22ba56b2510df47adf8180adf2aa2d Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Wed, 19 Apr 2023 00:26:17 -0400
Subject: [PATCH 15/18] Clean up code'

---
 src/libs/ReportUtils.js                   | 17 ++++++-----------
 src/pages/home/report/ReportActionItem.js |  2 +-
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index 6a07f695474a..ba0e94f0fa11 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -860,12 +860,7 @@ function getReportName(report, policies = {}) {
     const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
     const isMultipleParticipantReport = participantsWithoutCurrentUser.length > 1;
 
-    const displayNames = [];
-    for (let i = 0; i < participantsWithoutCurrentUser.length; i++) {
-        const login = participantsWithoutCurrentUser[i];
-        displayNames.push(getDisplayNameForParticipant(login, isMultipleParticipantReport));
-    }
-    return displayNames.join(', ');
+    return _.map(participants, login => getDisplayNameForParticipant(login, isMultipleParticipantReport)).join(', ');
 }
 
 /**
@@ -1709,20 +1704,20 @@ function canLeaveRoom(report, isPolicyMember) {
 
 /**
  * Returns display names for those that can see the whisper.
- * However, it returns "you" if only the current user can see it besides the person that sent it.
+ * However, it returns "you" if the current user is the only one who can see it besides the person that sent it.
  *
  * @param {string[]} participants
  * @returns {string}
  */
 function getWhisperDisplayNames(participants) {
-    const isCurrentUserTheOnlyParticipant = this.isCurrentUserTheOnlyParticipant(participants);
+    const isWhisperOnlyVisibleToCurrentUSer = this.isCurrentUserTheOnlyParticipant(participants);
 
     // When the current user is the only participant, the display name needs to be "you" because that's the only person reading it
-    if (isCurrentUserTheOnlyParticipant) {
+    if (isWhisperOnlyVisibleToCurrentUSer) {
         return Localize.translateLocal('common.youAfterPreposition');
     }
 
-    return _.map(participants, login => getDisplayNameForParticipant(login, ! isCurrentUserTheOnlyParticipant)).join(', ');
+    return _.map(participants, login => getDisplayNameForParticipant(login, !isWhisperOnlyVisibleToCurrentUSer)).join(', ');
 }
 
 /**
@@ -1753,6 +1748,7 @@ export {
     isPolicyExpenseChatAdmin,
     isPublicRoom,
     isConciergeChatReport,
+    isCurrentUserTheOnlyParticipant,
     hasAutomatedExpensifyEmails,
     hasExpensifyGuidesEmails,
     hasOutstandingIOU,
@@ -1787,7 +1783,6 @@ export {
     getDisplayNameForParticipant,
     isExpenseReport,
     isIOUReport,
-    isOnlyVisibleByCurrentUser,
     chatIncludesChronos,
     getAvatar,
     isDefaultAvatar,
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 05ba0f7e9b53..8ea57649d8b5 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -313,7 +313,7 @@ class ReportActionItem extends Component {
                                                 tooltipEnabled
                                                 numberOfLines={1}
                                                 textStyles={[styles.chatItemMessageHeaderTimestamp]}
-                                                shouldUseFullTitle={isOnlyVisibleByUser}
+                                                shouldUseFullTitle={isWhisperOnlyVisibleByUser}
                                             />
                                         </View>
                                     )}

From b916bb67e8c4d8e7601336b589246b3003926cdb Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Wed, 19 Apr 2023 00:58:24 -0400
Subject: [PATCH 16/18] Fix error

---
 src/libs/ReportUtils.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index ba0e94f0fa11..ad3272accab6 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -860,7 +860,7 @@ function getReportName(report, policies = {}) {
     const participantsWithoutCurrentUser = _.without(participants, sessionEmail);
     const isMultipleParticipantReport = participantsWithoutCurrentUser.length > 1;
 
-    return _.map(participants, login => getDisplayNameForParticipant(login, isMultipleParticipantReport)).join(', ');
+    return _.map(participantsWithoutCurrentUser, login => getDisplayNameForParticipant(login, isMultipleParticipantReport)).join(', ');
 }
 
 /**

From c405a2fafc08f0e7167b81aacb856fc8ad1313dc Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Thu, 20 Apr 2023 13:05:02 -0400
Subject: [PATCH 17/18] Update shouldComponentUpdate

---
 src/pages/home/report/ReportActionItem.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 8ea57649d8b5..f4617a110f23 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -114,7 +114,8 @@ class ReportActionItem extends Component {
             || !_.isEqual(this.props.action, nextProps.action)
             || this.state.isContextMenuActive !== nextState.isContextMenuActive
             || lodashGet(this.props.report, 'statusNum') !== lodashGet(nextProps.report, 'statusNum')
-            || lodashGet(this.props.report, 'stateNum') !== lodashGet(nextProps.report, 'stateNum');
+            || lodashGet(this.props.report, 'stateNum') !== lodashGet(nextProps.report, 'stateNum')
+            || this.props.translate !== nextProps.translate;
     }
 
     componentDidUpdate(prevProps) {

From 64e5b0473b473ac47c337ea8e79bf0673b627ad8 Mon Sep 17 00:00:00 2001
From: Rocio Perez-Cano <rocio@expensify.com>
Date: Thu, 20 Apr 2023 13:07:24 -0400
Subject: [PATCH 18/18] {} style

---
 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 f4617a110f23..31bfb49bfbe5 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -323,13 +323,13 @@ class ReportActionItem extends Component {
                                             <ReportActionItemSingle
                                                 action={this.props.action}
                                                 showHeader={!this.props.draftMessage}
-                                                wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : null]}
+                                                wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]}
                                             >
                                                 {this.renderItemContent(hovered || this.state.isContextMenuActive)}
                                             </ReportActionItemSingle>
                                         )
                                         : (
-                                            <ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : null]}>
+                                            <ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]}>
                                                 {this.renderItemContent(hovered || this.state.isContextMenuActive)}
                                             </ReportActionItemGrouped>
                                         )}
