diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 9aef35003e30..d042e49d53ef 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -41,6 +41,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio const route = useRoute(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (attributes) => attributes?.reports, canBeMissing: false}); const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: false}); const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, {canBeMissing: false}); const [policy] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: false}); @@ -184,6 +185,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio [ reportActions, reports, + reportAttributes, + reportNameValuePairs, + transactionViolations, + policy, + personalDetails, + data.length, + draftComments, + optionMode, + preferredLocale, + transactions, + isOffline, + ], + [ + reportActions, + reports, + reportAttributes, reportNameValuePairs, transactionViolations, policy, @@ -239,7 +258,6 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio transactions, isOffline, ], - [reportActions, reports, reportNameValuePairs, transactionViolations, policy, personalDetails, data.length, draftComments, optionMode, preferredLocale, transactions, isOffline], ); const previousOptionMode = usePrevious(optionMode); diff --git a/src/components/LHNOptionsList/OptionRowLHNData.tsx b/src/components/LHNOptionsList/OptionRowLHNData.tsx index 27bf69a58159..9b67dd5624df 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.tsx +++ b/src/components/LHNOptionsList/OptionRowLHNData.tsx @@ -17,6 +17,7 @@ import type {OptionRowLHNDataProps} from './types'; function OptionRowLHNData({ isFocused = false, fullReport, + reportAttributes, oneTransactionThreadReport, reportNameValuePairs, reportActions, @@ -47,6 +48,7 @@ function OptionRowLHNData({ // Note: ideally we'd have this as a dependent selector in onyx! const item = SidebarUtils.getOptionData({ report: fullReport, + reportAttributes, oneTransactionThreadReport, reportNameValuePairs, reportActions, @@ -74,6 +76,7 @@ function OptionRowLHNData({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [ fullReport, + reportAttributes, oneTransactionThreadReport, reportNameValuePairs, lastReportActionTransaction, diff --git a/src/components/LHNOptionsList/types.ts b/src/components/LHNOptionsList/types.ts index 597523dd522a..6e294ff2d845 100644 --- a/src/components/LHNOptionsList/types.ts +++ b/src/components/LHNOptionsList/types.ts @@ -5,7 +5,18 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {OptionData} from '@src/libs/ReportUtils'; -import type {Locale, PersonalDetailsList, Policy, Report, ReportAction, ReportActions, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx'; +import type { + Locale, + PersonalDetailsList, + Policy, + Report, + ReportAction, + ReportActions, + ReportAttributesDerivedValue, + ReportNameValuePairs, + Transaction, + TransactionViolation, +} from '@src/types/onyx'; type OptionMode = ValueOf; @@ -47,6 +58,9 @@ type OptionRowLHNDataProps = { /** The full data of the report */ fullReport: OnyxEntry; + /** The report derived attributes */ + reportAttributes: ReportAttributesDerivedValue['reports']; + /** The transaction thread report associated with the current report, if any */ oneTransactionThreadReport: OnyxEntry; diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index 51bf45ea3cb5..0cca8b293c12 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -45,10 +45,12 @@ type ReportWelcomeTextProps = { function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(report); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID || undefined}`); + const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID || undefined}`, {canBeMissing: false}); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report?.reportID || undefined}`, {canBeMissing: false}); const isArchivedRoom = isArchivedNonExpenseReport(report, reportNameValuePairs); const isChatRoom = isChatRoomReportUtils(report); const isSelfDM = isSelfDMReportUtils(report); @@ -56,7 +58,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { const isSystemChat = isSystemChatReportUtils(report); const isAdminRoom = isAdminRoomReportUtils(report); const isDefault = !(isChatRoom || isPolicyExpenseChat || isSelfDM || isInvoiceRoom || isSystemChat); - const participantAccountIDs = getParticipantsAccountIDsForDisplay(report, undefined, true, true); + const participantAccountIDs = getParticipantsAccountIDsForDisplay(report, undefined, true, true, reportMetadata); const isMultipleParticipant = participantAccountIDs.length > 1; const displayNamesWithTooltips = getDisplayNamesWithTooltips(getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails), isMultipleParticipant); const welcomeMessage = SidebarUtils.getWelcomeMessage(report, policy); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4f3a0cf32927..76cf95a9aa2a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2700,9 +2700,15 @@ function getDisplayNameForParticipant({ return shouldUseShortForm ? shortName : longName; } -function getParticipantsAccountIDsForDisplay(report: OnyxEntry, shouldExcludeHidden = false, shouldExcludeDeleted = false, shouldForceExcludeCurrentUser = false): number[] { +function getParticipantsAccountIDsForDisplay( + report: OnyxEntry, + shouldExcludeHidden = false, + shouldExcludeDeleted = false, + shouldForceExcludeCurrentUser = false, + reportMetadataParam?: OnyxEntry, +): number[] { const reportParticipants = report?.participants ?? {}; - const reportMetadata = getReportMetadata(report?.reportID); + const reportMetadata = reportMetadataParam ?? getReportMetadata(report?.reportID); let participantsEntries = Object.entries(reportParticipants); // We should not show participants that have an optimistic entry with the same login in the personal details @@ -2797,13 +2803,13 @@ function buildParticipantsFromAccountIDs(accountIDs: number[]): Participants { /** * Returns the report name if the report is a group chat */ -function getGroupChatName(participants?: SelectedParticipant[], shouldApplyLimit = false, report?: OnyxEntry): string | undefined { +function getGroupChatName(participants?: SelectedParticipant[], shouldApplyLimit = false, report?: OnyxEntry, reportMetadataParam?: OnyxEntry): string | undefined { // If we have a report always try to get the name from the report. if (report?.reportName) { return report.reportName; } - const reportMetadata = getReportMetadata(report?.reportID); + const reportMetadata = reportMetadataParam ?? getReportMetadata(report?.reportID); const pendingMemberAccountIDs = new Set( reportMetadata?.pendingChatMembers?.filter((member) => member.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE).map((member) => member.accountID), @@ -4595,12 +4601,14 @@ function getReportName( parentReportActionParam?: OnyxInputOrEntry, personalDetails?: Partial, invoiceReceiverPolicy?: OnyxEntry, + reportAttributesParam?: ReportAttributesDerivedValue['reports'], ): string { // Check if we can use report name in derived values - only when we have report but no other params const canUseDerivedValue = report && policy === undefined && parentReportActionParam === undefined && personalDetails === undefined && invoiceReceiverPolicy === undefined; - const derivedNameExists = report && !!reportAttributes?.[report.reportID]?.reportName; + const attributes = reportAttributesParam ?? reportAttributes; + const derivedNameExists = report && !!attributes?.[report.reportID]?.reportName; if (canUseDerivedValue && derivedNameExists) { - return reportAttributes[report.reportID].reportName; + return attributes[report.reportID].reportName; } return getReportNameInternal({report, policy, parentReportActionParam, personalDetails, invoiceReceiverPolicy}); } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index e4b1ba6ef8c7..1b1ff0407c73 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import type {PartialPolicyForSidebar} from '@hooks/useSidebarOrderedReportIDs'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportAttributesDerivedValue, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; import type Beta from '@src/types/onyx/Beta'; import type Policy from '@src/types/onyx/Policy'; import type PriorityMode from '@src/types/onyx/PriorityMode'; @@ -403,6 +403,7 @@ function shouldShowRedBrickRoad(report: Report, reportActions: OnyxEntry; + reportAttributes: ReportAttributesDerivedValue['reports']; oneTransactionThreadReport: OnyxEntry; reportNameValuePairs: OnyxEntry; reportActions: OnyxEntry; @@ -688,7 +690,7 @@ function getOptionData({ result.phoneNumber = personalDetail?.phoneNumber ?? ''; } - const reportName = getReportName(report, policy, undefined, undefined, invoiceReceiverPolicy); + const reportName = getReportName(report, policy, undefined, undefined, invoiceReceiverPolicy, reportAttributes); result.text = reportName; result.subtitle = subtitle; diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 538fed759f36..1d2210590a19 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -1,7 +1,7 @@ import {generateReportName, isValidReport} from '@libs/ReportUtils'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportAttributesDerivedValue} from '@src/types/onyx'; +import type {Report, ReportAttributesDerivedValue, ReportMetadata} from '@src/types/onyx'; let isFullyComputed = false; @@ -19,6 +19,7 @@ export default createOnyxDerivedValueConfig({ ONYXKEYS.COLLECTION.REPORT_ACTIONS, ONYXKEYS.COLLECTION.POLICY, ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, + ONYXKEYS.COLLECTION.REPORT_METADATA, ], compute: (dependencies, {currentValue, sourceValues}) => { const areAllDependenciesSet = [...dependencies].every((dependency) => dependency !== undefined); @@ -36,17 +37,25 @@ export default createOnyxDerivedValueConfig({ } const reportUpdates = sourceValues?.[ONYXKEYS.COLLECTION.REPORT]; + const reportMetadataUpdates = sourceValues?.[ONYXKEYS.COLLECTION.REPORT_METADATA]; // if we already computed the report attributes and there is no new reports data, return the current value - if ((isFullyComputed && reportUpdates === undefined) || !reports) { + if ((isFullyComputed && reportUpdates === undefined && reportMetadataUpdates === undefined) || !reports) { return currentValue ?? {reports: {}, locale: null}; } - const dataToIterate = isFullyComputed && reportUpdates !== undefined ? reportUpdates : reports ?? {}; - const reportAttributes = Object.keys(dataToIterate).reduce((acc, reportID) => { - // source value sends partial data, so we need an entire report object to do computations - const report = reports[reportID]; + let dataToIterate: Record = reports; + if (isFullyComputed) { + if (reportUpdates) { + dataToIterate = reportUpdates; + } else if (reportMetadataUpdates) { + dataToIterate = reportMetadataUpdates; + } + } + const reportAttributes = Object.keys(dataToIterate).reduce((acc, key) => { + // source value sends partial data, so we need an entire report object to do computations + const report = reports[`${ONYXKEYS.COLLECTION.REPORT}${key.replace(ONYXKEYS.COLLECTION.REPORT, '').replace(ONYXKEYS.COLLECTION.REPORT_METADATA, '')}`]; if (!report || !isValidReport(report)) { return acc; } diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 1b296dcfc993..ef8b0c317a19 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -69,12 +69,13 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout(); const selectionListRef = useRef(null); const textInputRef = useRef(null); - const [userSearchPhrase] = useOnyx(ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE); - const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); - const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report?.reportID}`); + const [userSearchPhrase] = useOnyx(ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE, {canBeMissing: true}); + const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: false}); + const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report?.reportID}`, {canBeMissing: false}); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (attributes) => attributes?.reports, canBeMissing: false}); const {selectionMode} = useMobileSelectionMode(); - const [session] = useOnyx(ONYXKEYS.SESSION); - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const currentUserAccountID = Number(session?.accountID); const isCurrentUserAdmin = isGroupChatAdmin(report, currentUserAccountID); const isGroupChat = useMemo(() => isGroupChatUtils(report), [report]); @@ -397,7 +398,7 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID, backTo)); } }} - subtitle={StringUtils.lineBreaksToSpaces(getReportName(report))} + subtitle={StringUtils.lineBreaksToSpaces(getReportName(report, undefined, undefined, undefined, undefined, reportAttributes))} /> {headerButtons} CONST.DISPLAY_PARTICIPANTS_LIMIT; const participants = allParticipants.slice(0, CONST.DISPLAY_PARTICIPANTS_LIMIT); const isMultipleParticipant = participants.length > 1; diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index 9dc93803b776..456f1cd51f6a 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -83,6 +83,7 @@ describe('SidebarUtils', () => { await measureFunction(() => SidebarUtils.getOptionData({ report, + reportAttributes: {}, reportNameValuePairs, reportActions, personalDetails, diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 4c2aea25a8f8..e3a8595cbd93 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -212,6 +212,7 @@ describe('SidebarUtils', () => { const optionDataPinned = SidebarUtils.getOptionData({ report: MOCK_REPORT_PINNED, + reportAttributes: {}, reportNameValuePairs: {}, reportActions: {}, personalDetails: {}, @@ -223,6 +224,7 @@ describe('SidebarUtils', () => { }); const optionDataUnpinned = SidebarUtils.getOptionData({ report: MOCK_REPORT_UNPINNED, + reportAttributes: {}, reportNameValuePairs: {}, reportActions: {}, personalDetails: {}, @@ -564,6 +566,7 @@ describe('SidebarUtils', () => { const result = SidebarUtils.getOptionData({ report, reportActions, + reportAttributes: {}, reportNameValuePairs: {}, hasViolations: false, personalDetails: {}, @@ -604,6 +607,7 @@ describe('SidebarUtils', () => { const optionData = SidebarUtils.getOptionData({ report, + reportAttributes: {}, reportNameValuePairs, reportActions: {}, personalDetails: {}, @@ -640,6 +644,7 @@ describe('SidebarUtils', () => { const optionData = SidebarUtils.getOptionData({ report, + reportAttributes: {}, reportNameValuePairs, reportActions: {}, personalDetails: LHNTestUtils.fakePersonalDetails, @@ -673,6 +678,7 @@ describe('SidebarUtils', () => { const optionData = SidebarUtils.getOptionData({ report, + reportAttributes: {}, reportNameValuePairs, reportActions: {}, personalDetails: {}, @@ -710,6 +716,7 @@ describe('SidebarUtils', () => { const optionData = SidebarUtils.getOptionData({ report, + reportAttributes: {}, reportNameValuePairs, reportActions: {}, personalDetails: {}, @@ -777,6 +784,7 @@ describe('SidebarUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}1`, policy); const result = SidebarUtils.getOptionData({ report, + reportAttributes: {}, reportActions, reportNameValuePairs: {}, hasViolations: false,