Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/components/LHNOptionsList/LHNOptionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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});

@QichenZhu QichenZhu Jun 4, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is causing a "useOnyx returned no data for key with canBeMissing set to false" error.

1000000410-2025-06-04 12_59_23 357

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});
Expand Down Expand Up @@ -184,6 +185,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
<OptionRowLHNData
reportID={reportID}
fullReport={itemFullReport}
reportAttributes={reportAttributes ?? {}}
oneTransactionThreadReport={itemOneTransactionThreadReport}
reportNameValuePairs={itemReportNameValuePairs}
reportActions={itemReportActions}
Expand Down Expand Up @@ -215,6 +217,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
preferredLocale,
reportActions,
reports,
reportAttributes,
reportNameValuePairs,
shouldDisableFocusOptions,
transactions,
Expand All @@ -228,6 +231,22 @@ 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,
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/components/LHNOptionsList/OptionRowLHNData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {OptionRowLHNDataProps} from './types';
function OptionRowLHNData({
isFocused = false,
fullReport,
reportAttributes,
oneTransactionThreadReport,
reportNameValuePairs,
reportActions,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -74,6 +76,7 @@ function OptionRowLHNData({
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [
fullReport,
reportAttributes,
oneTransactionThreadReport,
reportNameValuePairs,
lastReportActionTransaction,
Expand Down
16 changes: 15 additions & 1 deletion src/components/LHNOptionsList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof CONST.OPTION_MODE>;

Expand Down Expand Up @@ -47,6 +58,9 @@ type OptionRowLHNDataProps = {
/** The full data of the report */
fullReport: OnyxEntry<Report>;

/** The report derived attributes */
reportAttributes: ReportAttributesDerivedValue['reports'];

/** The transaction thread report associated with the current report, if any */
oneTransactionThreadReport: OnyxEntry<Report>;

Expand Down
8 changes: 5 additions & 3 deletions src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,20 @@ 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);
const isInvoiceRoom = isInvoiceRoomReportUtils(report);
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);
Expand Down
20 changes: 14 additions & 6 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2700,9 +2700,15 @@ function getDisplayNameForParticipant({
return shouldUseShortForm ? shortName : longName;
}

function getParticipantsAccountIDsForDisplay(report: OnyxEntry<Report>, shouldExcludeHidden = false, shouldExcludeDeleted = false, shouldForceExcludeCurrentUser = false): number[] {
function getParticipantsAccountIDsForDisplay(
report: OnyxEntry<Report>,
shouldExcludeHidden = false,
shouldExcludeDeleted = false,
shouldForceExcludeCurrentUser = false,
reportMetadataParam?: OnyxEntry<ReportMetadata>,
): 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
Expand Down Expand Up @@ -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<Report>): string | undefined {
function getGroupChatName(participants?: SelectedParticipant[], shouldApplyLimit = false, report?: OnyxEntry<Report>, reportMetadataParam?: OnyxEntry<ReportMetadata>): 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),
Expand Down Expand Up @@ -4595,12 +4601,14 @@ function getReportName(
parentReportActionParam?: OnyxInputOrEntry<ReportAction>,
personalDetails?: Partial<PersonalDetailsList>,
invoiceReceiverPolicy?: OnyxEntry<Policy>,
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});
}
Expand Down
6 changes: 4 additions & 2 deletions src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -403,6 +403,7 @@ function shouldShowRedBrickRoad(report: Report, reportActions: OnyxEntry<ReportA
*/
function getOptionData({
report,
reportAttributes,
oneTransactionThreadReport,
reportNameValuePairs,
reportActions,
Expand All @@ -416,6 +417,7 @@ function getOptionData({
invoiceReceiverPolicy,
}: {
report: OnyxEntry<Report>;
reportAttributes: ReportAttributesDerivedValue['reports'];
oneTransactionThreadReport: OnyxEntry<Report>;
reportNameValuePairs: OnyxEntry<ReportNameValuePairs>;
reportActions: OnyxEntry<ReportActions>;
Expand Down Expand Up @@ -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;
Expand Down
21 changes: 15 additions & 6 deletions src/libs/actions/OnyxDerived/configs/reportAttributes.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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);
Expand All @@ -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<ReportAttributesDerivedValue['reports']>((acc, reportID) => {
// source value sends partial data, so we need an entire report object to do computations
const report = reports[reportID];
let dataToIterate: Record<string, Report | ReportMetadata | undefined> = reports;
if (isFullyComputed) {
if (reportUpdates) {
dataToIterate = reportUpdates;
} else if (reportMetadataUpdates) {
dataToIterate = reportMetadataUpdates;
}
}

const reportAttributes = Object.keys(dataToIterate).reduce<ReportAttributesDerivedValue['reports']>((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;
}
Expand Down
13 changes: 7 additions & 6 deletions src/pages/ReportParticipantsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) {
const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout();
const selectionListRef = useRef<SelectionListHandle>(null);
const textInputRef = useRef<TextInput>(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]);
Expand Down Expand Up @@ -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))}
/>
<View style={[styles.pl5, styles.pr5]}>{headerButtons}</View>
<ConfirmModal
Expand Down
3 changes: 2 additions & 1 deletion src/pages/home/HeaderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL, {canBeMissing: true});
const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true});
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true});
const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report?.reportID}`, {canBeMissing: true});
const [isDismissedDiscountBanner, setIsDismissedDiscountBanner] = useState(false);

const {translate} = useLocalize();
Expand All @@ -126,7 +127,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
const {canUseTalkToAISales, canUseLeftHandBar} = usePermissions();
const shouldShowTalkToSales = !!canUseTalkToAISales && isAdminRoom(report);

const allParticipants = getParticipantsAccountIDsForDisplay(report, false, true);
const allParticipants = getParticipantsAccountIDsForDisplay(report, false, true, undefined, reportMetadata);
const shouldAddEllipsis = allParticipants?.length > CONST.DISPLAY_PARTICIPANTS_LIMIT;
const participants = allParticipants.slice(0, CONST.DISPLAY_PARTICIPANTS_LIMIT);
const isMultipleParticipant = participants.length > 1;
Expand Down
1 change: 1 addition & 0 deletions tests/perf-test/SidebarUtils.perf-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ describe('SidebarUtils', () => {
await measureFunction(() =>
SidebarUtils.getOptionData({
report,
reportAttributes: {},
reportNameValuePairs,
reportActions,
personalDetails,
Expand Down
Loading