From 0abda520dca4079e5333d678fbd612cefc3c72a7 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 19 Mar 2026 01:44:45 +0530 Subject: [PATCH 1/6] Part 8 - Pass betas to remaining production callers that don't pass it yet. Signed-off-by: krishna2323 --- src/libs/actions/Report/index.ts | 8 ++++++-- .../actions/replaceOptimisticReportWithActualReport.ts | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index aaa3f7d833034..37e165f6d7ab4 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -1975,6 +1975,7 @@ function createTransactionThreadReport( optimisticSelfDMReport, currentUserLogin, currentUserAccountID, + betas: undefined, }); return optimisticTransactionThread; } @@ -2029,7 +2030,7 @@ function navigateToAndOpenReport( notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, }); // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport({reportID: newChat?.reportID, introSelected, reportActionID: '', participantLoginList: userLogins, newReportObject: newChat, isSelfTourViewed}); + openReport({reportID: newChat?.reportID, introSelected, reportActionID: '', participantLoginList: userLogins, newReportObject: newChat, isSelfTourViewed, betas: undefined}); } const report = isEmptyObject(chat) ? newChat : chat; @@ -2075,6 +2076,7 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[], newReportObject: newChat, parentReportActionID: '0', participantAccountIDList: participantAccountIDs, + betas: undefined, }); } const report = chat ?? newChat; @@ -2141,6 +2143,7 @@ function createChildReport( newReportObject: newChat, parentReportActionID: parentReportAction.reportActionID, isNewThread: true, + betas: undefined, }); } else { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${childReportID}`, newChat); @@ -2950,7 +2953,7 @@ function toggleSubscribeToChildReport( prevNotificationPreference?: NotificationPreference, ) { if (childReportID) { - openReport({reportID: childReportID, introSelected}); + openReport({reportID: childReportID, introSelected, betas: undefined}); const parentReportActionID = parentReportAction.reportActionID; if (!prevNotificationPreference || isHiddenForCurrentUser(prevNotificationPreference)) { updateNotificationPreference( @@ -2991,6 +2994,7 @@ function toggleSubscribeToChildReport( participantLoginList: participantLogins, newReportObject: newChat, parentReportActionID: parentReportAction.reportActionID, + betas: undefined, }); const notificationPreference = isHiddenForCurrentUser(prevNotificationPreference) ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; updateNotificationPreference(newChat.reportID, prevNotificationPreference, notificationPreference, currentUserAccountID, parentReport?.reportID, parentReportAction.reportActionID); diff --git a/src/libs/actions/replaceOptimisticReportWithActualReport.ts b/src/libs/actions/replaceOptimisticReportWithActualReport.ts index bdf52b17aa24c..3d00bc783b3d8 100644 --- a/src/libs/actions/replaceOptimisticReportWithActualReport.ts +++ b/src/libs/actions/replaceOptimisticReportWithActualReport.ts @@ -195,13 +195,13 @@ function replaceOptimisticReportWithActualReport(report: Report, draftReportComm callback(); // We are already on the parent one expense report, so just call the API to fetch report data - openReport({reportID: parentReportID, introSelected: undefined}); + openReport({reportID: parentReportID, introSelected: undefined, betas: undefined}); }); } else { callback(); // We are already on the parent one expense report, so just call the API to fetch report data - openReport({reportID: parentReportID, introSelected: undefined}); + openReport({reportID: parentReportID, introSelected: undefined, betas: undefined}); } return; } From 53a71e6730bb4a4ebe4844be5bf23c0df906b3df Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 19 Mar 2026 02:02:54 +0530 Subject: [PATCH 2/6] Part 8 - Thread betas through navigateToAndOpenReportWithAccountIDs Signed-off-by: krishna2323 --- src/components/PromotedActionsBar.tsx | 6 ++++-- src/libs/actions/Report/index.ts | 4 ++-- src/pages/ProfilePage.tsx | 3 ++- .../workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 3 ++- tests/unit/PromotedActionsBarTest.ts | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx index b25fb2fd1d75e..83dc995c7576a 100644 --- a/src/components/PromotedActionsBar.tsx +++ b/src/components/PromotedActionsBar.tsx @@ -13,6 +13,7 @@ import {joinRoom, navigateToAndOpenReport, navigateToAndOpenReportWithAccountIDs import {callFunctionIfActionIsAllowed} from '@userActions/Session'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import type Beta from '@src/types/onyx/Beta'; import type OnyxReport from '@src/types/onyx/Report'; import Button from './Button'; import type {ThreeDotsMenuItem} from './HeaderWithBackButton/types'; @@ -33,6 +34,7 @@ type PromotedActionsType = Record P currentUserAccountID: number; introSelected: OnyxEntry; isSelfTourViewed: boolean | undefined; + betas: OnyxEntry; }) => PromotedAction; } & { [CONST.PROMOTED_ACTIONS.JOIN]: (report: OnyxReport, currentUserAccountID: number) => PromotedAction; @@ -64,7 +66,7 @@ const PromotedActions = { joinRoom(report, currentUserAccountID); }), }), - message: ({reportID, accountID, login, currentUserAccountID, introSelected, isSelfTourViewed}) => ({ + message: ({reportID, accountID, login, currentUserAccountID, introSelected, isSelfTourViewed, betas}) => ({ key: CONST.PROMOTED_ACTIONS.MESSAGE, icon: 'CommentBubbles', translationKey: 'common.message', @@ -80,7 +82,7 @@ const PromotedActions = { return; } if (accountID) { - navigateToAndOpenReportWithAccountIDs([accountID], currentUserAccountID, introSelected); + navigateToAndOpenReportWithAccountIDs([accountID], currentUserAccountID, introSelected, betas); } }, }), diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 37e165f6d7ab4..cece16e88dd32 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -2062,7 +2062,7 @@ function navigateToAndCreateGroupChat( * * @param participantAccountIDs of user logins to start a chat report with. */ -function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[], currentUserAccountID: number, introSelected: OnyxEntry) { +function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[], currentUserAccountID: number, introSelected: OnyxEntry, betas: OnyxEntry) { let newChat: OptimisticChatReport | undefined; const chat = getChatByParticipants([...participantAccountIDs, currentUserAccountID]); if (!chat) { @@ -2076,7 +2076,7 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[], newReportObject: newChat, parentReportActionID: '0', participantAccountIDList: participantAccountIDs, - betas: undefined, + betas, }); } const report = chat ?? newChat; diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 8446698409876..4d41f4011d98f 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -76,6 +76,7 @@ function ProfilePage({route}: ProfilePageProps) { const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [isDebugModeEnabled = false] = useOnyx(ONYXKEYS.IS_DEBUG_MODE_ENABLED); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const guideCalendarLink = account?.guideDetails?.calendarLink ?? ''; const expensifyIcons = useMemoizedLazyExpensifyIcons(['Bug', 'Pencil', 'Phone']); @@ -165,7 +166,7 @@ function ProfilePage({route}: ProfilePageProps) { // If it's a self DM, we only want to show the Message button if the self DM report exists because we don't want to optimistically create a report for self DM if ((!isCurrentUser || report) && !isAnonymousUserSession()) { - promotedActions.push(PromotedActions.message({reportID: report?.reportID, accountID, login: loginParams, currentUserAccountID, introSelected, isSelfTourViewed})); + promotedActions.push(PromotedActions.message({reportID: report?.reportID, accountID, login: loginParams, currentUserAccountID, introSelected, isSelfTourViewed, betas})); } return ( diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 4e73c62a9f47c..3146268479931 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -67,6 +67,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const bankAccountInfo = bankAccountFromList ?? bankAccountConnectedToWorkspace; const bankAccountID = policyBankAccountID ?? bankAccountInfo?.accountData?.bankAccountID; const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const [betas] = useOnyx(ONYXKEYS.BETAS); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); @@ -357,7 +358,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } setShowErrorModal(false); - navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID, introSelected); + navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID, introSelected, betas); }} html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { admin: selectedPayerDetails?.displayName ?? '', diff --git a/tests/unit/PromotedActionsBarTest.ts b/tests/unit/PromotedActionsBarTest.ts index e48d720dc14ad..794604358ab65 100644 --- a/tests/unit/PromotedActionsBarTest.ts +++ b/tests/unit/PromotedActionsBarTest.ts @@ -50,7 +50,7 @@ describe('PromotedActions.message', () => { action.onSelected(); - expect(mockNavigateToAndOpenReportWithAccountIDs).toHaveBeenCalledWith([42], 1, introSelected); + expect(mockNavigateToAndOpenReportWithAccountIDs).toHaveBeenCalledWith([42], 1, introSelected, undefined); }); it('should pass undefined introSelected when not provided', () => { @@ -63,7 +63,7 @@ describe('PromotedActions.message', () => { action.onSelected(); - expect(mockNavigateToAndOpenReportWithAccountIDs).toHaveBeenCalledWith([42], 1, undefined); + expect(mockNavigateToAndOpenReportWithAccountIDs).toHaveBeenCalledWith([42], 1, undefined, undefined); }); it('should navigate to report directly when reportID is provided', () => { From 8c47222a97256d6a9d440fc85172f7dcc0ad9bbf Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 19 Mar 2026 02:09:59 +0530 Subject: [PATCH 3/6] fix tests. Signed-off-by: krishna2323 --- tests/unit/PromotedActionsBarTest.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/unit/PromotedActionsBarTest.ts b/tests/unit/PromotedActionsBarTest.ts index 794604358ab65..b4a73a0f9a129 100644 --- a/tests/unit/PromotedActionsBarTest.ts +++ b/tests/unit/PromotedActionsBarTest.ts @@ -32,6 +32,7 @@ describe('PromotedActions.message', () => { currentUserAccountID: 1, introSelected, isSelfTourViewed: false, + betas: undefined, }); action.onSelected(); @@ -46,6 +47,7 @@ describe('PromotedActions.message', () => { currentUserAccountID: 1, introSelected, isSelfTourViewed: false, + betas: undefined, }); action.onSelected(); @@ -59,6 +61,7 @@ describe('PromotedActions.message', () => { currentUserAccountID: 1, introSelected: undefined, isSelfTourViewed: undefined, + betas: undefined, }); action.onSelected(); @@ -72,6 +75,7 @@ describe('PromotedActions.message', () => { currentUserAccountID: 1, introSelected: undefined, isSelfTourViewed: undefined, + betas: undefined, }); action.onSelected(); @@ -88,6 +92,7 @@ describe('PromotedActions.message', () => { currentUserAccountID: 1, introSelected, isSelfTourViewed: false, + betas: undefined, }); action.onSelected(); From 01ee2c5d79975f7b3cf8542043a4224ac833372b Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 19 Mar 2026 03:13:59 +0530 Subject: [PATCH 4/6] Remove unnecessary betas: undefined from out-of-scope openReport calls Signed-off-by: krishna2323 --- src/libs/actions/Report/index.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index cece16e88dd32..c11f52339c3c2 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -1975,7 +1975,6 @@ function createTransactionThreadReport( optimisticSelfDMReport, currentUserLogin, currentUserAccountID, - betas: undefined, }); return optimisticTransactionThread; } @@ -2030,7 +2029,7 @@ function navigateToAndOpenReport( notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, }); // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport({reportID: newChat?.reportID, introSelected, reportActionID: '', participantLoginList: userLogins, newReportObject: newChat, isSelfTourViewed, betas: undefined}); + openReport({reportID: newChat?.reportID, introSelected, reportActionID: '', participantLoginList: userLogins, newReportObject: newChat, isSelfTourViewed}); } const report = isEmptyObject(chat) ? newChat : chat; @@ -2143,7 +2142,6 @@ function createChildReport( newReportObject: newChat, parentReportActionID: parentReportAction.reportActionID, isNewThread: true, - betas: undefined, }); } else { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${childReportID}`, newChat); @@ -2953,7 +2951,7 @@ function toggleSubscribeToChildReport( prevNotificationPreference?: NotificationPreference, ) { if (childReportID) { - openReport({reportID: childReportID, introSelected, betas: undefined}); + openReport({reportID: childReportID, introSelected}); const parentReportActionID = parentReportAction.reportActionID; if (!prevNotificationPreference || isHiddenForCurrentUser(prevNotificationPreference)) { updateNotificationPreference( @@ -2994,7 +2992,6 @@ function toggleSubscribeToChildReport( participantLoginList: participantLogins, newReportObject: newChat, parentReportActionID: parentReportAction.reportActionID, - betas: undefined, }); const notificationPreference = isHiddenForCurrentUser(prevNotificationPreference) ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; updateNotificationPreference(newChat.reportID, prevNotificationPreference, notificationPreference, currentUserAccountID, parentReport?.reportID, parentReportAction.reportActionID); From 5e417618ed84ed36155c9493051cfd760dd5652d Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 19 Mar 2026 15:26:37 +0530 Subject: [PATCH 5/6] add tests. Signed-off-by: krishna2323 --- tests/actions/ReportTest.ts | 46 ++++++++++++++++++++++++++++ tests/unit/PromotedActionsBarTest.ts | 16 ++++++++++ 2 files changed, 62 insertions(+) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index b6451e05d7abc..0ec80725d83d8 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -5729,6 +5729,52 @@ describe('actions/Report', () => { }); }); + describe('navigateToAndOpenReportWithAccountIDs', () => { + it('should create new chat and pass betas to openReport when no existing chat', async () => { + const TEST_USER_ACCOUNT_ID = 1; + const TEST_USER_LOGIN = 'test@user.com'; + const PARTICIPANT_ACCOUNT_ID = 2; + + await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN); + await TestHelper.setPersonalDetails(TEST_USER_LOGIN, TEST_USER_ACCOUNT_ID); + + const testIntroSelected: OnyxTypes.IntroSelected = {choice: CONST.ONBOARDING_CHOICES.ADMIN}; + + Report.navigateToAndOpenReportWithAccountIDs([PARTICIPANT_ACCOUNT_ID], TEST_USER_ACCOUNT_ID, testIntroSelected, undefined); + await waitForBatchedUpdates(); + + TestHelper.expectAPICommandToHaveBeenCalled(WRITE_COMMANDS.OPEN_REPORT, 1); + expect(Navigation.navigate).toHaveBeenCalled(); + }); + + it('should not call openReport when chat already exists', async () => { + const TEST_USER_ACCOUNT_ID = 1; + const TEST_USER_LOGIN = 'test@user.com'; + const PARTICIPANT_ACCOUNT_ID = 2; + const EXISTING_REPORT_ID = '456'; + + await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN); + await TestHelper.setPersonalDetails(TEST_USER_LOGIN, TEST_USER_ACCOUNT_ID); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${EXISTING_REPORT_ID}`, { + reportID: EXISTING_REPORT_ID, + type: CONST.REPORT.TYPE.CHAT, + participants: { + [TEST_USER_ACCOUNT_ID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + [PARTICIPANT_ACCOUNT_ID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + }, + }); + await waitForBatchedUpdates(); + + const testIntroSelected: OnyxTypes.IntroSelected = {choice: CONST.ONBOARDING_CHOICES.ADMIN}; + + Report.navigateToAndOpenReportWithAccountIDs([PARTICIPANT_ACCOUNT_ID], TEST_USER_ACCOUNT_ID, testIntroSelected, undefined); + await waitForBatchedUpdates(); + + TestHelper.expectAPICommandToHaveBeenCalled(WRITE_COMMANDS.OPEN_REPORT, 0); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(EXISTING_REPORT_ID)); + }); + }); + describe('getGuidedSetupDataForOpenReport', () => { const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@user.com'; diff --git a/tests/unit/PromotedActionsBarTest.ts b/tests/unit/PromotedActionsBarTest.ts index b4a73a0f9a129..ab8a69fa51086 100644 --- a/tests/unit/PromotedActionsBarTest.ts +++ b/tests/unit/PromotedActionsBarTest.ts @@ -100,4 +100,20 @@ describe('PromotedActions.message', () => { expect(mockNavigateToAndOpenReport).toHaveBeenCalledWith(['test@example.com'], 1, introSelected, false, false); expect(mockNavigateToAndOpenReportWithAccountIDs).not.toHaveBeenCalled(); }); + + it('should pass betas to navigateToAndOpenReportWithAccountIDs when accountID is provided', () => { + const introSelected = {choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM}; + const betas = [CONST.BETAS.ALL]; + const action = PromotedActions.message({ + accountID: 42, + currentUserAccountID: 1, + introSelected, + isSelfTourViewed: false, + betas, + }); + + action.onSelected(); + + expect(mockNavigateToAndOpenReportWithAccountIDs).toHaveBeenCalledWith([42], 1, introSelected, betas); + }); }); From f45d132d11b77aa0ef35d4e09d4e2681a369ef88 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 19 Mar 2026 17:11:55 +0530 Subject: [PATCH 6/6] add comment. Signed-off-by: krishna2323 --- src/libs/actions/replaceOptimisticReportWithActualReport.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/actions/replaceOptimisticReportWithActualReport.ts b/src/libs/actions/replaceOptimisticReportWithActualReport.ts index 3d00bc783b3d8..0084d7a417448 100644 --- a/src/libs/actions/replaceOptimisticReportWithActualReport.ts +++ b/src/libs/actions/replaceOptimisticReportWithActualReport.ts @@ -195,12 +195,18 @@ function replaceOptimisticReportWithActualReport(report: Report, draftReportComm callback(); // We are already on the parent one expense report, so just call the API to fetch report data + // betas is safe to pass as undefined because introSelected is undefined, so the code path + // that uses betas is never reached. Passing it explicitly so the compiler flags this when + // betas becomes required. Refactor issue: https://github.com/Expensify/App/issues/66424 openReport({reportID: parentReportID, introSelected: undefined, betas: undefined}); }); } else { callback(); // We are already on the parent one expense report, so just call the API to fetch report data + // betas is safe to pass as undefined because introSelected is undefined, so the code path + // that uses betas is never reached. Passing it explicitly so the compiler flags this when + // betas becomes required. Refactor issue: https://github.com/Expensify/App/issues/66424 openReport({reportID: parentReportID, introSelected: undefined, betas: undefined}); } return;