diff --git a/src/components/OnyxProvider.tsx b/src/components/OnyxProvider.tsx index 3bd4ca52c3be..8682e832debc 100644 --- a/src/components/OnyxProvider.tsx +++ b/src/components/OnyxProvider.tsx @@ -6,7 +6,7 @@ import ComposeProviders from './ComposeProviders'; // Set up any providers for individual keys. This should only be used in cases where many components will subscribe to // the same key (e.g. FlatList renderItem components) const [withNetwork, NetworkProvider, NetworkContext] = createOnyxContext(ONYXKEYS.NETWORK); -const [withPersonalDetails, PersonalDetailsProvider] = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS_LIST); +const [withPersonalDetails, PersonalDetailsProvider, , usePersonalDetails] = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS_LIST); const [withCurrentDate, CurrentDateProvider] = createOnyxContext(ONYXKEYS.CURRENT_DATE); const [withReportActionsDrafts, ReportActionsDraftsProvider] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS); const [withBlockedFromConcierge, BlockedFromConciergeProvider] = createOnyxContext(ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE); @@ -45,6 +45,7 @@ export default OnyxProvider; export { withNetwork, withPersonalDetails, + usePersonalDetails, withReportActionsDrafts, withCurrentDate, withBlockedFromConcierge, diff --git a/src/components/createOnyxContext.tsx b/src/components/createOnyxContext.tsx index d142e551012f..a0ac5942b098 100644 --- a/src/components/createOnyxContext.tsx +++ b/src/components/createOnyxContext.tsx @@ -1,4 +1,4 @@ -import React, {ComponentType, ForwardRefExoticComponent, ForwardedRef, PropsWithoutRef, ReactNode, RefAttributes, createContext, forwardRef} from 'react'; +import React, {ComponentType, ForwardRefExoticComponent, ForwardedRef, PropsWithoutRef, ReactNode, RefAttributes, createContext, forwardRef, useContext} from 'react'; import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import getComponentDisplayName from '../libs/getComponentDisplayName'; @@ -29,7 +29,12 @@ type WithOnyxKey = WrapComponentWithConsumer; // createOnyxContext return type -type CreateOnyxContext = [WithOnyxKey, ComponentType, TOnyxKey>>, React.Context>]; +type CreateOnyxContext = [ + WithOnyxKey, + ComponentType, TOnyxKey>>, + React.Context>, + () => OnyxValues[TOnyxKey], +]; export default (onyxKeyName: TOnyxKey): CreateOnyxContext => { const Context = createContext>(null); @@ -77,5 +82,13 @@ export default (onyxKeyName: TOnyxKey): CreateOnyxCon }; } - return [withOnyxKey, ProviderWithOnyx, Context]; + const useOnyxContext = () => { + const value = useContext(Context); + if (value === null) { + throw new Error(`useOnyxContext must be used within a OnyxProvider [key: ${onyxKeyName}]`); + } + return value; + }; + + return [withOnyxKey, ProviderWithOnyx, Context, useOnyxContext]; }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 4d73838fc529..2ae8c5c4ccdc 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -28,7 +28,7 @@ import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMe import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu'; import * as ContextMenuActions from './ContextMenu/ContextMenuActions'; import * as EmojiPickerAction from '../../../libs/actions/EmojiPickerAction'; -import {withBlockedFromConcierge, withNetwork, withPersonalDetails, withReportActionsDrafts} from '../../../components/OnyxProvider'; +import {usePersonalDetails, withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider'; import RenameAction from '../../../components/ReportActionItem/RenameAction'; import InlineSystemMessage from '../../../components/InlineSystemMessage'; import styles from '../../../styles/styles'; @@ -49,7 +49,6 @@ 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'; import ReportPreview from '../../../components/ReportActionItem/ReportPreview'; import ReportActionItemDraft from './ReportActionItemDraft'; import TaskPreview from '../../../components/ReportActionItem/TaskPreview'; @@ -111,7 +110,6 @@ const propTypes = { ...windowDimensionsPropTypes, emojiReactions: EmojiReactionsPropTypes, - personalDetailsList: PropTypes.objectOf(personalDetailsPropType), /** IOU report for this action, if any */ iouReport: reportPropTypes, @@ -127,7 +125,6 @@ const defaultProps = { draftMessage: '', preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, emojiReactions: {}, - personalDetailsList: {}, shouldShowSubscriptAvatar: false, hasOutstandingIOU: false, iouReport: undefined, @@ -136,6 +133,7 @@ const defaultProps = { }; function ReportActionItem(props) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const [isContextMenuActive, setIsContextMenuActive] = useState(ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); const [isHidden, setIsHidden] = useState(false); const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED); @@ -362,7 +360,7 @@ function ReportActionItem(props) { /> ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetailsList, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); @@ -508,7 +506,7 @@ function ReportActionItem(props) { numberOfReplies={numberOfThreadReplies} mostRecentReply={`${props.action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, props.personalDetailsList)} + icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, personalDetails)} onSecondaryInteraction={showPopover} /> @@ -640,7 +638,7 @@ function ReportActionItem(props) { const isWhisper = whisperedToAccountIDs.length > 0; const isMultipleParticipant = whisperedToAccountIDs.length > 1; const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedToAccountIDs); - const whisperedToPersonalDetails = isWhisper ? _.filter(props.personalDetailsList, (details) => _.includes(whisperedToAccountIDs, details.accountID)) : []; + const whisperedToPersonalDetails = isWhisper ? _.filter(personalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)) : []; const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; return ( { }; function ReportActionItemSingle(props) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; - let {displayName} = props.personalDetailsList[actorAccountID] || {}; - const {avatar, login, pendingFields, status, fallbackIcon} = props.personalDetailsList[actorAccountID] || {}; + let {displayName} = personalDetails[actorAccountID] || {}; + const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport, [props.action.actionName, props.iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(props.report) && (!actorAccountID || displayAllActors); @@ -100,10 +96,10 @@ function ReportActionItemSingle(props) { displayName = ReportUtils.getPolicyName(props.report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(props.report); - } else if (props.action.delegateAccountID && props.personalDetailsList[props.action.delegateAccountID]) { + } else if (props.action.delegateAccountID && personalDetails[props.action.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. - const delegateDetails = props.personalDetailsList[props.action.delegateAccountID]; + const delegateDetails = personalDetails[props.action.delegateAccountID]; const delegateDisplayName = delegateDetails.displayName; actorHint = `${delegateDisplayName} (${props.translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; @@ -116,7 +112,7 @@ function ReportActionItemSingle(props) { if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; - const secondaryUserDetails = props.personalDetailsList[secondaryAccountId] || {}; + const secondaryUserDetails = personalDetails[secondaryAccountId] || {}; const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { @@ -270,7 +266,6 @@ ReportActionItemSingle.displayName = 'ReportActionItemSingle'; export default compose( withLocalize, - withPersonalDetails(), withOnyx({ betas: { key: ONYXKEYS.BETAS,