diff --git a/src/components/SelectionList/ChatListItem.tsx b/src/components/SelectionList/ChatListItem.tsx index d6ce930d0ec7..164b315b07a1 100644 --- a/src/components/SelectionList/ChatListItem.tsx +++ b/src/components/SelectionList/ChatListItem.tsx @@ -1,18 +1,11 @@ -import React, {useMemo} from 'react'; -import {View} from 'react-native'; -import {AttachmentContext} from '@components/AttachmentContext'; -import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; -import MultipleAvatars from '@components/MultipleAvatars'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; -import TextWithTooltip from '@components/TextWithTooltip'; +import React from 'react'; import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; -import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import ReportActionItemDate from '@pages/home/report/ReportActionItemDate'; -import ReportActionItemFragment from '@pages/home/report/ReportActionItemFragment'; +import PureReportActionItem from '@pages/home/report/PureReportActionItem'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import type {SearchPersonalDetails} from '@src/types/onyx/SearchResults'; import BaseListItem from './BaseListItem'; import type {ChatListItemProps, ListItem, ReportActionListItemType} from './types'; @@ -30,34 +23,9 @@ function ChatListItem({ }: ChatListItemProps) { const reportActionItem = item as unknown as ReportActionListItemType; const from = reportActionItem.from; - const icons = [ - { - type: CONST.ICON_TYPE_AVATAR, - source: from.avatar, - name: reportActionItem.formattedFrom, - id: from.accountID, - }, - ]; const styles = useThemeStyles(); const theme = useTheme(); - const StyleUtils = useStyleUtils(); - - const attachmentContextValue = {type: CONST.ATTACHMENT_TYPE.SEARCH}; - - const contextValue = { - anchor: null, - report: undefined, - reportNameValuePairs: undefined, - action: undefined, - transactionThreadReport: undefined, - checkIfContextMenuActive: () => {}, - isDisabled: true, - }; - const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; - const hoveredBackgroundColor = styles.sidebarLinkHover?.backgroundColor ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; - - const mentionReportContextValue = useMemo(() => ({currentReportID: item?.reportID ?? '-1'}), [item.reportID]); const animatedHighlightStyle = useAnimatedHighlightStyle({ borderRadius: variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, @@ -66,6 +34,7 @@ function ChatListItem({ }); const pressableStyle = [ styles.selectionListPressableItemWrapper, + styles.p0, styles.textAlignLeft, styles.overflowHidden, // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle @@ -74,11 +43,16 @@ function ChatListItem({ styles.mh0, item.cursorStyle, ]; + + const personalDetails: Record = { + [from.accountID]: from, + }; + return ( ({ pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} hoverStyle={item.isSelected && styles.activeComponentBG} > - {(hovered) => ( - - - - - - - - - - - - - {reportActionItem.message.map((fragment, index) => ( - - ))} - - - - - - )} + onSelectRow(item)} + report={undefined} + reportActions={[]} + parentReportAction={undefined} + displayAsGroup={false} + isMostRecentIOUReportAction={false} + shouldDisplayNewMarker={false} + index={item.index ?? 0} + isFirstVisibleReportAction={false} + personalDetails={personalDetails} + shouldDisplayContextMenu={false} + attachmentContextValueType={CONST.ATTACHMENT_TYPE.SEARCH} + /> ); } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 027b11c516f1..602e492de7b4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -872,7 +872,7 @@ const unavailableTranslation = Localize.translateLocal('workspace.common.unavail */ function getPolicyName(report: OnyxInputOrEntry, returnEmptyIfNotFound = false, policy?: OnyxInputOrEntry): string { const noPolicyFound = returnEmptyIfNotFound ? '' : unavailableTranslation; - if (isEmptyObject(report) || (isEmptyObject(allPolicies) && !report?.policyName)) { + if (isEmptyObject(report) || (isEmptyObject(allPolicies) && !report?.policyName && !policy)) { return noPolicyFound; } diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 34dd2a9d1350..7d6c54bd2e8c 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -240,6 +240,9 @@ type PureReportActionItemProps = { /** A message related to a report action that has been automatically forwarded */ reportAutomaticallyForwardedMessage?: string; + + /** Type of attachment context value */ + attachmentContextValueType?: ValueOf; }; /** @@ -293,10 +296,11 @@ function PureReportActionItem({ dismissTrackExpenseActionableWhisper = () => {}, userBillingFundID, reportAutomaticallyForwardedMessage, + attachmentContextValueType = CONST.ATTACHMENT_TYPE.REPORT, }: PureReportActionItemProps) { const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const reportID = report?.reportID ?? ''; + const reportID = report?.reportID ?? action?.reportID ?? ''; const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -517,9 +521,14 @@ function PureReportActionItem({ [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, reportNameValuePairs], ); - const attachmentContextValue = useMemo(() => ({reportID, type: CONST.ATTACHMENT_TYPE.REPORT}), [reportID]); + const attachmentContextValue = useMemo(() => { + if (attachmentContextValueType === CONST.ATTACHMENT_TYPE.SEARCH) { + return {type: attachmentContextValueType}; + } + return {reportID, type: attachmentContextValueType}; + }, [reportID, attachmentContextValueType]); - const mentionReportContextValue = useMemo(() => ({currentReportID: report?.reportID ?? '-1'}), [report?.reportID]); + const mentionReportContextValue = useMemo(() => ({currentReportID: reportID ?? '-1'}), [reportID]); const actionableItemButtons: ActionableItem[] = useMemo(() => { if (ReportActionsUtils.isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { @@ -999,6 +1008,7 @@ function PureReportActionItem({ ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !ReportActionsUtils.isPendingRemove(action) } + personalDetails={personalDetails} > {content} diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 7c42991d2852..d791d977e9b2 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -7,7 +7,6 @@ import Avatar from '@components/Avatar'; import {FallbackAvatar} from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {usePersonalDetails} from '@components/OnyxProvider'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import SubscriptAvatar from '@components/SubscriptAvatar'; import Text from '@components/Text'; @@ -24,11 +23,13 @@ import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getReportActionMessage} from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import type {AvatarSource} from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Report, ReportAction} from '@src/types/onyx'; -import type {Icon} from '@src/types/onyx/OnyxCommon'; +import type {PersonalDetailsList, Policy, Report, ReportAction} from '@src/types/onyx'; +import type {Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; +import type {SearchPersonalDetails} from '@src/types/onyx/SearchResults'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import ReportActionItemDate from './ReportActionItemDate'; import ReportActionItemFragment from './ReportActionItemFragment'; @@ -57,6 +58,12 @@ type ReportActionItemSingleProps = Partial & { /** If the action is being hovered */ isHovered?: boolean; + + /** Personal details list */ + personalDetails?: PersonalDetailsList | Record; + + /** Current connected policy */ + policy?: OnyxEntry; }; const showUserDetails = (accountID: string) => { @@ -77,13 +84,15 @@ function ReportActionItemSingle({ report, iouReport, isHovered = false, + personalDetails, + policy, }: ReportActionItemSingleProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); - const personalDetails = usePersonalDetails(); - const policy = usePolicy(report?.policyID); + const onyxPolicy = usePolicy(report?.policyID); + const reportPolicy = policy ?? onyxPolicy; const delegatePersonalDetails = personalDetails?.[action?.delegateAccountID ?? '']; const ownerAccountID = iouReport?.ownerAccountID ?? action?.childOwnerAccountID; const isReportPreviewAction = action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW; @@ -91,7 +100,11 @@ function ReportActionItemSingle({ const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); - const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails?.[actorAccountID ?? -1] ?? {}; + const {avatar, login} = personalDetails?.[actorAccountID ?? -1] ?? {}; + const pendingFields = personalDetails && 'pendingFields' in personalDetails ? personalDetails.pendingFields : undefined; + const status = personalDetails && 'status' in personalDetails ? personalDetails.status : undefined; + const fallbackIcon = personalDetails && 'fallbackIcon' in personalDetails && personalDetails.fallbackIcon !== null ? personalDetails.fallbackIcon : undefined; + const accountOwnerDetails = getPersonalDetailByEmail(login ?? ''); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing let actorHint = (login || (displayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); @@ -104,9 +117,9 @@ function ReportActionItemSingle({ let avatarId: number | string | undefined = actorAccountID; if (isWorkspaceActor) { - displayName = ReportUtils.getPolicyName(report, undefined, policy); + displayName = ReportUtils.getPolicyName(report, undefined, reportPolicy); actorHint = displayName; - avatarSource = ReportUtils.getWorkspaceIcon(report, policy).source; + avatarSource = ReportUtils.getWorkspaceIcon(report, reportPolicy).source; avatarId = report?.policyID; } else if (action?.delegateAccountID && personalDetails?.[action?.delegateAccountID]) { displayName = delegatePersonalDetails?.displayName ?? ''; @@ -145,7 +158,7 @@ function ReportActionItemSingle({ } else if (!isWorkspaceActor) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const avatarIconIndex = report?.isOwnPolicyExpenseChat || ReportUtils.isPolicyExpenseChat(report) ? 0 : 1; - const reportIcons = ReportUtils.getIcons(report, {}); + const reportIcons = ReportUtils.getIcons(report, personalDetails, null, undefined, undefined, reportPolicy); secondaryAvatar = reportIcons.at(avatarIconIndex) ?? {name: '', source: '', type: CONST.ICON_TYPE_AVATAR}; } else if (ReportUtils.isInvoiceReport(iouReport)) { @@ -237,17 +250,17 @@ function ReportActionItemSingle({ type={icon.type} name={icon.name} avatarID={icon.id} - fallbackIcon={fallbackIcon} + fallbackIcon={fallbackIcon as AvatarSource} /> ); }; - const hasEmojiStatus = !displayAllActors && status?.emojiCode; - const formattedDate = DateUtils.getStatusUntilDate(status?.clearAfter ?? ''); - const statusText = status?.text ?? ''; + const hasEmojiStatus = !displayAllActors && status && 'emojiCode' in status && status?.emojiCode; + const statusClearAfter = status && 'clearAfter' in status ? String(status?.clearAfter) ?? '' : ''; + const formattedDate = DateUtils.getStatusUntilDate(statusClearAfter); + const statusText = status && 'text' in status ? String(status?.text) ?? '' : ''; const statusTooltipText = formattedDate ? `${statusText ? `${statusText} ` : ''}(${formattedDate})` : statusText; - return ( - {getAvatar()} + {getAvatar()} {showHeader ? ( @@ -291,7 +304,7 @@ function ReportActionItemSingle({ {`${status?.emojiCode}`} + >{`${String(status?.emojiCode ?? '')}`} )} diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index 0784813127be..1d0f757730d2 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -86,6 +86,7 @@ jest.mock('@src/components/withNavigationFocus', () => (Component: ComponentType return WithNavigationFocus; }); +jest.mock('@src/components/ConfirmedRoute.tsx'); const getMockedReports = (length = 100) => createCollection( diff --git a/tests/perf-test/SelectionList.perf-test.tsx b/tests/perf-test/SelectionList.perf-test.tsx index fcd714129536..759ab1a8da22 100644 --- a/tests/perf-test/SelectionList.perf-test.tsx +++ b/tests/perf-test/SelectionList.perf-test.tsx @@ -84,6 +84,8 @@ jest.mock('../../src/hooks/useScreenWrapperTransitionStatus', () => ({ })), })); +jest.mock('@src/components/ConfirmedRoute.tsx'); + function SelectionListWrapper({canSelectMultiple}: SelectionListWrapperProps) { const [selectedIds, setSelectedIds] = useState([]); diff --git a/tests/ui/ResizeScreenTests.tsx b/tests/ui/ResizeScreenTests.tsx index 5bd86ad152b2..275680e3f345 100644 --- a/tests/ui/ResizeScreenTests.tsx +++ b/tests/ui/ResizeScreenTests.tsx @@ -21,6 +21,7 @@ jest.mock('@libs/getIsNarrowLayout', () => jest.fn()); jest.mock('@pages/settings/InitialSettingsPage'); jest.mock('@pages/settings/Profile/ProfilePage'); jest.mock('@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'); +jest.mock('@src/components/ConfirmedRoute.tsx'); const DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE: ResponsiveLayoutResult = { shouldUseNarrowLayout: true, diff --git a/tests/ui/WorkspaceSwitcherTest.tsx b/tests/ui/WorkspaceSwitcherTest.tsx index 614ed4e5ab70..dde43d141cc9 100644 --- a/tests/ui/WorkspaceSwitcherTest.tsx +++ b/tests/ui/WorkspaceSwitcherTest.tsx @@ -26,6 +26,9 @@ jest.mock('@react-navigation/native', () => { triggerTransitionEnd: jest.fn(), }; }); + +jest.mock('@src/components/ConfirmedRoute.tsx'); + TestHelper.setupApp(); async function signInAndGetApp(): Promise { diff --git a/tests/unit/CalendarPickerTest.tsx b/tests/unit/CalendarPickerTest.tsx index 5cf02409ac23..65e9216f3d8f 100644 --- a/tests/unit/CalendarPickerTest.tsx +++ b/tests/unit/CalendarPickerTest.tsx @@ -36,6 +36,8 @@ jest.mock('../../src/hooks/useLocalize', () => })), ); +jest.mock('@src/components/ConfirmedRoute.tsx'); + describe('CalendarPicker', () => { test('renders calendar component', () => { render(); diff --git a/tests/unit/GoogleTagManagerTest.tsx b/tests/unit/GoogleTagManagerTest.tsx index dcb6bdea0eec..7076775644ff 100644 --- a/tests/unit/GoogleTagManagerTest.tsx +++ b/tests/unit/GoogleTagManagerTest.tsx @@ -14,6 +14,7 @@ jest.mock('@libs/GoogleTagManager'); // Mock the Overlay since it doesn't work in tests jest.mock('@libs/Navigation/AppNavigator/Navigators/Overlay'); +jest.mock('@src/components/ConfirmedRoute.tsx'); describe('GoogleTagManagerTest', () => { const accountID = 123456; diff --git a/tests/unit/ReportActionItemSingleTest.ts b/tests/unit/ReportActionItemSingleTest.ts index e0bf06be1609..925e548389d5 100644 --- a/tests/unit/ReportActionItemSingleTest.ts +++ b/tests/unit/ReportActionItemSingleTest.ts @@ -1,7 +1,6 @@ import {screen, waitFor} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; import type {PersonalDetailsList} from '@src/types/onyx'; -import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -54,16 +53,8 @@ describe('ReportActionItemSingle', () => { }; function setup() { - LHNTestUtils.getDefaultRenderedReportActionItemSingle(shouldShowSubscriptAvatar, fakeReport, fakeReportAction); - const policyCollectionDataSet = toCollectionDataSet(ONYXKEYS.COLLECTION.POLICY, [fakePolicy], (item) => item.id); - - return waitForBatchedUpdates().then(() => - Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS_LIST]: fakePersonalDetails, - [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - ...policyCollectionDataSet, - }), - ); + LHNTestUtils.getDefaultRenderedReportActionItemSingle(shouldShowSubscriptAvatar, fakeReport, fakeReportAction, fakePersonalDetails, fakePolicy); + return waitForBatchedUpdates().then(() => Onyx.set(ONYXKEYS.IS_LOADING_REPORT_DATA, false)); } it('renders secondary Avatar properly', async () => { diff --git a/tests/unit/Search/handleActionButtonPressTest.ts b/tests/unit/Search/handleActionButtonPressTest.ts index 69af0e83849a..5082913a3889 100644 --- a/tests/unit/Search/handleActionButtonPressTest.ts +++ b/tests/unit/Search/handleActionButtonPressTest.ts @@ -2,6 +2,8 @@ import type {ReportListItemType} from '@components/SelectionList/types'; import {handleActionButtonPress} from '@libs/actions/Search'; +jest.mock('@src/components/ConfirmedRoute.tsx'); + const mockReportItemWithHold = { shouldAnimateInHighlight: false, accountID: 1206, diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx index 1a3fc3d07f28..2b89392cd424 100644 --- a/tests/utils/LHNTestUtils.tsx +++ b/tests/utils/LHNTestUtils.tsx @@ -16,6 +16,7 @@ import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData'; import CONST from '@src/CONST'; import type {PersonalDetailsList, Policy, Report, ReportAction, TransactionViolation, ViolationName} from '@src/types/onyx'; import type ReportActionName from '@src/types/onyx/ReportActionName'; +import type {SearchPersonalDetails} from '@src/types/onyx/SearchResults'; import waitForBatchedUpdatesWithAct from './waitForBatchedUpdatesWithAct'; type MockedReportActionItemSingleProps = { @@ -27,6 +28,12 @@ type MockedReportActionItemSingleProps = { /** All the data of the action */ reportAction: ReportAction; + + /** Personal details list */ + personalDetails?: PersonalDetailsList | Record; + + /** Current connected policy */ + policy?: Policy; }; type MockedSidebarLinksProps = { @@ -331,7 +338,7 @@ function internalRender(component: ReactElement) { } } -function MockedReportActionItemSingle({shouldShowSubscriptAvatar = true, report, reportAction}: MockedReportActionItemSingleProps) { +function MockedReportActionItemSingle({shouldShowSubscriptAvatar = true, report, reportAction, personalDetails, policy}: MockedReportActionItemSingleProps) { return ( ); } -function getDefaultRenderedReportActionItemSingle(shouldShowSubscriptAvatar = true, report?: Report, reportAction?: ReportAction) { +function getDefaultRenderedReportActionItemSingle( + shouldShowSubscriptAvatar = true, + report?: Report, + reportAction?: ReportAction, + personalDetails?: PersonalDetailsList | Record, + policy?: Policy, +) { const currentReport = report ?? getFakeReport(); const currentReportAction = reportAction ?? getFakeAdvancedReportAction(); @@ -356,6 +371,8 @@ function getDefaultRenderedReportActionItemSingle(shouldShowSubscriptAvatar = tr shouldShowSubscriptAvatar={shouldShowSubscriptAvatar} report={currentReport} reportAction={currentReportAction} + personalDetails={personalDetails} + policy={policy} />, ); }