From 545696166d620fc7bb9f415caa21581140d622d6 Mon Sep 17 00:00:00 2001 From: yuldashevyusuf Date: Tue, 9 Jun 2026 16:12:03 +0500 Subject: [PATCH 1/6] fix: keep public room in LHN for anonymous users An anonymous session can only access public rooms, whose notification preference defaults to `hidden`. Previously a public room only survived the LHN filter while it was the focused report, so opening a thread inside the room (which steals focus) dropped the room from the Inbox, leaving the anon user unable to return to it. Add an override in `shouldDisplayReportInLHN` so a public room stays in the LHN for anonymous users regardless of focus, mirroring how ReportUtils already gates anon-only public-room behaviour (e.g. canLeaveChat). Fixes #92672 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/libs/SidebarUtils.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 8f28be104bd6..ec49bcfc3680 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -31,6 +31,7 @@ import {Str} from 'expensify-common'; import type {OptionData} from './ReportUtils'; +import {isAnonymousUser} from './actions/Session'; import {formatPhoneNumber as formatPhoneNumberPhoneUtils} from './LocalePhoneNumber'; import {formatList} from './Localize'; import { @@ -196,6 +197,7 @@ import { isOneOnOneChat, isOneTransactionThread, isPolicyExpenseChat, + isPublicRoom, isSelfDM, isSystemChat as isSystemChatUtil, isTaskReport, @@ -343,6 +345,10 @@ function shouldDisplayReportInLHN({ !!draftComment || hasErrorsOtherThanFailedReceipt || isFocused || + // An anonymous user can only access public rooms, and such a room's notification preference + // defaults to `hidden`. Without this, opening a thread inside the room (which steals focus) + // drops the room from the LHN, leaving the anon user unable to return to it. See #92672. + (isPublicRoom(report) && isAnonymousUser()) || isSystemChat || !!report.isPinned || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing From c54e7e212202feb06f456d013667339c9bb3f47b Mon Sep 17 00:00:00 2001 From: yuldashevyusuf Date: Fri, 12 Jun 2026 16:18:49 +0500 Subject: [PATCH 2/6] fix: keep public room in LHN for anonymous users on cold start --- src/DeepLinkHandler.tsx | 35 ++++++++++++++++++- .../Navigation/linkingConfig/subscribe.ts | 8 +++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/DeepLinkHandler.tsx b/src/DeepLinkHandler.tsx index a97974d2cd15..a81e85d5aac8 100644 --- a/src/DeepLinkHandler.tsx +++ b/src/DeepLinkHandler.tsx @@ -10,8 +10,9 @@ import useIsAuthenticated from './hooks/useIsAuthenticated'; import useOnyx from './hooks/useOnyx'; import {openReportFromDeepLink} from './libs/actions/Link'; import * as Report from './libs/actions/Report'; -import {hasAuthToken} from './libs/actions/Session'; +import {hasAuthToken, isAnonymousUser} from './libs/actions/Session'; import Log from './libs/Log'; +import {getReportIDFromLink} from './libs/ReportUtils'; import {endSpan} from './libs/telemetry/activeSpans'; import ONYXKEYS from './ONYXKEYS'; import {hasSeenTourSelector} from './selectors/Onboarding'; @@ -30,8 +31,11 @@ type DeepLinkHandlerProps = { function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { const linkingChangeListener = useRef(null); const initialUrlProcessed = useRef(false); + const pendingPublicRoomReportID = useRef(''); + const hasRefetchedPublicRoom = useRef(false); const [allReports, allReportsMetadata] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const [, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); const [conciergeReportID, conciergeReportIDMetadata] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [introSelected, introSelectedMetadata] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); @@ -89,6 +93,11 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); } openReportFromDeepLink(url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas); + const deepLinkReportID = getReportIDFromLink(url); + if (deepLinkReportID && !isCurrentlyAuthenticated) { + pendingPublicRoomReportID.current = deepLinkReportID; + hasRefetchedPublicRoom.current = false; + } } else { Report.doneCheckingPublicRoom(); } @@ -116,6 +125,11 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { } const isCurrentlyAuthenticated = hasAuthToken(); openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas); + const deepLinkReportID = getReportIDFromLink(state.url); + if (deepLinkReportID && !isCurrentlyAuthenticated) { + pendingPublicRoomReportID.current = deepLinkReportID; + hasRefetchedPublicRoom.current = false; + } }); return () => { @@ -149,6 +163,25 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { Report.doneCheckingPublicRoom(); }, [isAuthenticated]); + // An anonymous user opening a public room via a cold deep link loads the room (OpenReport), but the + // OpenApp that follows anonymous session creation drops it from Onyx, so it never reaches the LHN. + // Once OpenApp settles, re-fetch the room if it went missing so it shows up in the LHN. See #92672. + useEffect(() => { + const reportID = pendingPublicRoomReportID.current; + if (!reportID || isLoadingApp || !isAnonymousUser()) { + return; + } + if (allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportID) { + hasRefetchedPublicRoom.current = false; + return; + } + if (hasRefetchedPublicRoom.current) { + return; + } + hasRefetchedPublicRoom.current = true; + Report.openReport({reportID, introSelected, betas}); + }, [isLoadingApp, allReports, introSelected, betas]); + return null; } diff --git a/src/libs/Navigation/linkingConfig/subscribe.ts b/src/libs/Navigation/linkingConfig/subscribe.ts index d1407a0cdba2..85f77ed3f41b 100644 --- a/src/libs/Navigation/linkingConfig/subscribe.ts +++ b/src/libs/Navigation/linkingConfig/subscribe.ts @@ -1,3 +1,4 @@ +import {hasAuthToken} from '@libs/actions/Session'; import continuePlaidOAuth from '@libs/continuePlaidOAuth'; import navigationRef from '@libs/Navigation/navigationRef'; import type {RootNavigatorParamList} from '@libs/Navigation/types'; @@ -34,6 +35,13 @@ const subscribe: LinkingOptions['subscribe'] = (listener continuePlaidOAuth(url); return; } + // For an unauthenticated session, a report deep link (`/r/`) targets the Report screen, + // which lives in AuthScreens and is not mounted while PublicScreens is showing. Dispatching it here + // throws "NAVIGATE ... was not handled by any navigator". openReportFromDeepLink() already opens the + // public room as an anonymous user and handles navigation, so defer to it instead. See #92672. + if (!hasAuthToken() && url.includes(`/${ROUTES.REPORT}/`)) { + return; + } listener(url); }); return () => subscription.remove(); From 17b02bc7423aa4081318828cc910c0e3edba685d Mon Sep 17 00:00:00 2001 From: yuldashevyusuf Date: Sat, 13 Jun 2026 14:25:36 +0500 Subject: [PATCH 3/6] test: add unit tests for anonymous public room LHN visibility and deep link refetch --- tests/unit/DeepLinkHandlerTest.tsx | 117 +++++++++++++++++++++++++++++ tests/unit/SidebarUtilsTest.ts | 109 +++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 tests/unit/DeepLinkHandlerTest.tsx diff --git a/tests/unit/DeepLinkHandlerTest.tsx b/tests/unit/DeepLinkHandlerTest.tsx new file mode 100644 index 000000000000..2aad57b5179c --- /dev/null +++ b/tests/unit/DeepLinkHandlerTest.tsx @@ -0,0 +1,117 @@ +import {act, render} from '@testing-library/react-native'; +import {Linking} from 'react-native'; +import Onyx from 'react-native-onyx'; +import type * as Link from '@libs/actions/Link'; +import * as Report from '@libs/actions/Report'; +import CONST from '@src/CONST'; +import DeepLinkHandler from '@src/DeepLinkHandler'; +import ONYXKEYS from '@src/ONYXKEYS'; +import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; + +jest.mock('@libs/actions/Link', () => ({ + ...jest.requireActual('@libs/actions/Link'), + openReportFromDeepLink: jest.fn(), +})); + +jest.mock('@libs/actions/Report', () => ({ + ...jest.requireActual('@libs/actions/Report'), + openReport: jest.fn(), + doneCheckingPublicRoom: jest.fn(), +})); + +const PUBLIC_ROOM_ID = '987654321'; +const DEFAULT_URL = 'https://new.expensify.com/'; + +describe('DeepLinkHandler', () => { + beforeAll(() => { + Onyx.init({keys: ONYXKEYS}); + }); + + beforeEach(async () => { + await act(async () => { + await Onyx.clear(); + }); + jest.clearAllMocks(); + Linking.setInitialURL(DEFAULT_URL); + }); + + it('re-fetches a public room that is missing from Onyx after OpenApp settles for an anonymous user (#92672)', async () => { + // An anonymous session has no authToken, so hasAuthToken() is false and isAnonymousUser() is true. + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: {authTokenType: CONST.AUTH_TOKEN_TYPES.ANONYMOUS}, + [ONYXKEYS.IS_LOADING_APP]: true, + [ONYXKEYS.CONCIERGE_REPORT_ID]: '', + [ONYXKEYS.NVP_INTRO_SELECTED]: {}, + [ONYXKEYS.BETAS]: [], + }); + }); + + // Cold deep link straight into the public room. + Linking.setInitialURL(`new-expensify://r/${PUBLIC_ROOM_ID}`); + + render(); + await waitForBatchedUpdatesWithAct(); + + // While OpenApp is still loading, we should not refetch yet. + expect(Report.openReport).not.toHaveBeenCalled(); + + // OpenApp settles but the public room is still absent from Onyx -> refetch it so it reaches the LHN. + await act(async () => { + await Onyx.merge(ONYXKEYS.IS_LOADING_APP, false); + }); + await waitForBatchedUpdatesWithAct(); + + expect(Report.openReport).toHaveBeenCalledWith(expect.objectContaining({reportID: PUBLIC_ROOM_ID})); + }); + + it('does not refetch when the public room is already present in Onyx', async () => { + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: {authTokenType: CONST.AUTH_TOKEN_TYPES.ANONYMOUS}, + [ONYXKEYS.IS_LOADING_APP]: true, + [ONYXKEYS.CONCIERGE_REPORT_ID]: '', + [ONYXKEYS.NVP_INTRO_SELECTED]: {}, + [ONYXKEYS.BETAS]: [], + [`${ONYXKEYS.COLLECTION.REPORT}${PUBLIC_ROOM_ID}`]: {reportID: PUBLIC_ROOM_ID}, + }); + }); + + Linking.setInitialURL(`new-expensify://r/${PUBLIC_ROOM_ID}`); + + render(); + await waitForBatchedUpdatesWithAct(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.IS_LOADING_APP, false); + }); + await waitForBatchedUpdatesWithAct(); + + expect(Report.openReport).not.toHaveBeenCalled(); + }); + + it('does not refetch for an authenticated user', async () => { + await act(async () => { + await Onyx.multiSet({ + // An authToken means hasAuthToken() is true, so the deep link is not tracked as a pending public room. + [ONYXKEYS.SESSION]: {authToken: 'token', accountID: 1}, + [ONYXKEYS.IS_LOADING_APP]: true, + [ONYXKEYS.CONCIERGE_REPORT_ID]: '', + [ONYXKEYS.NVP_INTRO_SELECTED]: {}, + [ONYXKEYS.BETAS]: [], + }); + }); + + Linking.setInitialURL(`new-expensify://r/${PUBLIC_ROOM_ID}`); + + render(); + await waitForBatchedUpdatesWithAct(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.IS_LOADING_APP, false); + }); + await waitForBatchedUpdatesWithAct(); + + expect(Report.openReport).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index f18474089139..1a397e540690 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -1110,6 +1110,115 @@ describe('SidebarUtils', () => { expect(result).toBeDefined(); expect(typeof result.shouldDisplay).toBe('boolean'); }); + + describe('anonymous user public room (#92672)', () => { + const PUBLIC_ROOM_ID = '92672'; + const OTHER_FOCUSED_REPORT_ID = '92673'; + + // The room has no notification preference for the current user, so `isHiddenForCurrentUser` + // treats it as `hidden` - exactly the state an anonymous user lands in for a public room. + const buildPublicRoom = (visibility: Report['visibility']): Report => ({ + reportID: PUBLIC_ROOM_ID, + type: CONST.REPORT.TYPE.CHAT, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, + visibility, + reportName: '#publicroom', + policyID: '1', + lastMessageText: 'hello', + lastVisibleActionCreated: DateUtils.getDBTime(), + participants: {}, + }); + + it('keeps a hidden public room in the LHN when it is not the focused report', async () => { + const report = buildPublicRoom(CONST.REPORT.VISIBILITY.PUBLIC); + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}` as const; + const reports: OnyxCollection = {[reportKey]: report}; + + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: {authTokenType: CONST.AUTH_TOKEN_TYPES.ANONYMOUS}, + [reportKey]: report, + }); + }); + await waitForBatchedUpdates(); + + const result = SidebarUtils.shouldDisplayReportInLHN({ + report, + reports, + currentReportId: OTHER_FOCUSED_REPORT_ID, + isInFocusMode: false, + betas: [], + transactionViolations: {}, + draftComment: undefined, + transactions: {}, + isOffline: false, + currentUserLogin: CURRENT_USER_LOGIN, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); + + expect(result.shouldDisplay).toBe(true); + }); + + it('drops the same hidden public room for an authenticated (non-anonymous) user', async () => { + const report = buildPublicRoom(CONST.REPORT.VISIBILITY.PUBLIC); + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}` as const; + const reports: OnyxCollection = {[reportKey]: report}; + + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: {accountID: CURRENT_USER_ACCOUNT_ID, email: CURRENT_USER_LOGIN}, + [reportKey]: report, + }); + }); + await waitForBatchedUpdates(); + + const result = SidebarUtils.shouldDisplayReportInLHN({ + report, + reports, + currentReportId: OTHER_FOCUSED_REPORT_ID, + isInFocusMode: false, + betas: [], + transactionViolations: {}, + draftComment: undefined, + transactions: {}, + isOffline: false, + currentUserLogin: CURRENT_USER_LOGIN, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); + + expect(result.shouldDisplay).toBe(false); + }); + + it('does not apply the override to a non-public hidden room for an anonymous user', async () => { + const report = buildPublicRoom(undefined); + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}` as const; + const reports: OnyxCollection = {[reportKey]: report}; + + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: {authTokenType: CONST.AUTH_TOKEN_TYPES.ANONYMOUS}, + [reportKey]: report, + }); + }); + await waitForBatchedUpdates(); + + const result = SidebarUtils.shouldDisplayReportInLHN({ + report, + reports, + currentReportId: OTHER_FOCUSED_REPORT_ID, + isInFocusMode: false, + betas: [], + transactionViolations: {}, + draftComment: undefined, + transactions: {}, + isOffline: false, + currentUserLogin: CURRENT_USER_LOGIN, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); + + expect(result.shouldDisplay).toBe(false); + }); + }); }); describe('getWelcomeMessage', () => { From 89e00a6babd9f788ac156cdf5836ef6146da5a29 Mon Sep 17 00:00:00 2001 From: yuldashevyusuf Date: Sat, 13 Jun 2026 16:29:34 +0500 Subject: [PATCH 4/6] fix spellcheck and typecheck CI failures in public room tests --- cspell.json | 3 ++- tests/unit/DeepLinkHandlerTest.tsx | 2 +- tests/unit/SidebarUtilsTest.ts | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cspell.json b/cspell.json index fc11acf9656e..ab21181d0608 100644 --- a/cspell.json +++ b/cspell.json @@ -1045,7 +1045,8 @@ "Prefetch", "Prefetcher", "knip", - "lottiefiles" + "lottiefiles", + "Refetched" ], "ignorePaths": [ ".gitignore", diff --git a/tests/unit/DeepLinkHandlerTest.tsx b/tests/unit/DeepLinkHandlerTest.tsx index 2aad57b5179c..2ecf5b34dea9 100644 --- a/tests/unit/DeepLinkHandlerTest.tsx +++ b/tests/unit/DeepLinkHandlerTest.tsx @@ -73,7 +73,7 @@ describe('DeepLinkHandler', () => { [ONYXKEYS.CONCIERGE_REPORT_ID]: '', [ONYXKEYS.NVP_INTRO_SELECTED]: {}, [ONYXKEYS.BETAS]: [], - [`${ONYXKEYS.COLLECTION.REPORT}${PUBLIC_ROOM_ID}`]: {reportID: PUBLIC_ROOM_ID}, + [`${ONYXKEYS.COLLECTION.REPORT}${PUBLIC_ROOM_ID}` as const]: {reportID: PUBLIC_ROOM_ID}, }); }); diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 1a397e540690..b72d5ad45102 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -1122,7 +1122,7 @@ describe('SidebarUtils', () => { type: CONST.REPORT.TYPE.CHAT, chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, visibility, - reportName: '#publicroom', + reportName: '#public-room', policyID: '1', lastMessageText: 'hello', lastVisibleActionCreated: DateUtils.getDBTime(), @@ -1131,7 +1131,7 @@ describe('SidebarUtils', () => { it('keeps a hidden public room in the LHN when it is not the focused report', async () => { const report = buildPublicRoom(CONST.REPORT.VISIBILITY.PUBLIC); - const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}` as const; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${PUBLIC_ROOM_ID}` as const; const reports: OnyxCollection = {[reportKey]: report}; await act(async () => { @@ -1161,7 +1161,7 @@ describe('SidebarUtils', () => { it('drops the same hidden public room for an authenticated (non-anonymous) user', async () => { const report = buildPublicRoom(CONST.REPORT.VISIBILITY.PUBLIC); - const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}` as const; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${PUBLIC_ROOM_ID}` as const; const reports: OnyxCollection = {[reportKey]: report}; await act(async () => { @@ -1191,7 +1191,7 @@ describe('SidebarUtils', () => { it('does not apply the override to a non-public hidden room for an anonymous user', async () => { const report = buildPublicRoom(undefined); - const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}` as const; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${PUBLIC_ROOM_ID}` as const; const reports: OnyxCollection = {[reportKey]: report}; await act(async () => { From aba48a93368119abb0b3b7a8f6695cb5a1fd378e Mon Sep 17 00:00:00 2001 From: yuldashevyusuf Date: Sun, 21 Jun 2026 14:09:25 +0500 Subject: [PATCH 5/6] Extract pending public room helper and clear ref after refetch --- src/DeepLinkHandler.tsx | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/DeepLinkHandler.tsx b/src/DeepLinkHandler.tsx index a81e85d5aac8..8c7ebbdb80a3 100644 --- a/src/DeepLinkHandler.tsx +++ b/src/DeepLinkHandler.tsx @@ -1,6 +1,6 @@ import type {NativeEventSubscription} from 'react-native'; -import {useEffect, useRef} from 'react'; +import {useCallback, useEffect, useRef} from 'react'; import {Linking} from 'react-native'; import type {Route} from './ROUTES'; @@ -43,6 +43,17 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { const [betas, betasMetadata] = useOnyx(ONYXKEYS.BETAS); const isAuthenticated = useIsAuthenticated(); + // An anonymous deep link into a public room needs to be re-fetched after OpenApp settles (see the effect + // below). Track the pending reportID so both the initial-URL and the url-change paths stay in sync. + const trackPendingPublicRoomFromDeepLink = useCallback((url: string, isCurrentlyAuthenticated: boolean) => { + const deepLinkReportID = getReportIDFromLink(url); + if (!deepLinkReportID || isCurrentlyAuthenticated) { + return; + } + pendingPublicRoomReportID.current = deepLinkReportID; + hasRefetchedPublicRoom.current = false; + }, []); + useEffect(() => { if (isLoadingOnyxValue(allReportsMetadata, sessionMetadata, conciergeReportIDMetadata, introSelectedMetadata, isSelfTourViewedMetadata, betasMetadata)) { return; @@ -93,11 +104,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); } openReportFromDeepLink(url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas); - const deepLinkReportID = getReportIDFromLink(url); - if (deepLinkReportID && !isCurrentlyAuthenticated) { - pendingPublicRoomReportID.current = deepLinkReportID; - hasRefetchedPublicRoom.current = false; - } + trackPendingPublicRoomFromDeepLink(url, isCurrentlyAuthenticated); } else { Report.doneCheckingPublicRoom(); } @@ -125,11 +132,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { } const isCurrentlyAuthenticated = hasAuthToken(); openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas); - const deepLinkReportID = getReportIDFromLink(state.url); - if (deepLinkReportID && !isCurrentlyAuthenticated) { - pendingPublicRoomReportID.current = deepLinkReportID; - hasRefetchedPublicRoom.current = false; - } + trackPendingPublicRoomFromDeepLink(state.url, isCurrentlyAuthenticated); }); return () => { @@ -171,7 +174,9 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { if (!reportID || isLoadingApp || !isAnonymousUser()) { return; } + // The room made it into Onyx, so the cold-start race is over - stop tracking it. if (allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportID) { + pendingPublicRoomReportID.current = ''; hasRefetchedPublicRoom.current = false; return; } From e43c5adca5e84d979d2fb2e15c545e7578e950d1 Mon Sep 17 00:00:00 2001 From: yuldashevyusuf Date: Fri, 3 Jul 2026 16:13:11 +0500 Subject: [PATCH 6/6] Format DeepLinkHandlerTest with oxfmt --- tests/unit/DeepLinkHandlerTest.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/unit/DeepLinkHandlerTest.tsx b/tests/unit/DeepLinkHandlerTest.tsx index 2ecf5b34dea9..77ab38769ca5 100644 --- a/tests/unit/DeepLinkHandlerTest.tsx +++ b/tests/unit/DeepLinkHandlerTest.tsx @@ -1,11 +1,15 @@ import {act, render} from '@testing-library/react-native'; -import {Linking} from 'react-native'; -import Onyx from 'react-native-onyx'; + import type * as Link from '@libs/actions/Link'; import * as Report from '@libs/actions/Report'; + import CONST from '@src/CONST'; import DeepLinkHandler from '@src/DeepLinkHandler'; import ONYXKEYS from '@src/ONYXKEYS'; + +import {Linking} from 'react-native'; +import Onyx from 'react-native-onyx'; + import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; jest.mock('@libs/actions/Link', () => ({