diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 0b7d0761139e2..a90f7705e9ad4 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -1743,6 +1743,7 @@ const CONST = { SHOW_HOVER_PREVIEW_DELAY: 270, SHOW_HOVER_PREVIEW_ANIMATION_DURATION: 250, ACTIVITY_INDICATOR_TIMEOUT: 10000, + GET_INITIAL_URL_TIMEOUT: 10000, MIN_SMOOTH_SCROLL_EVENT_THROTTLE: 16, }, TELEMETRY: { diff --git a/src/DeepLinkHandler.tsx b/src/DeepLinkHandler.tsx index e73386b23f086..9b8a88c2feef9 100644 --- a/src/DeepLinkHandler.tsx +++ b/src/DeepLinkHandler.tsx @@ -25,6 +25,7 @@ type DeepLinkHandlerProps = { */ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { const linkingChangeListener = useRef(null); + const initialUrlProcessed = useRef(false); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); @@ -36,24 +37,57 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { if (isLoadingOnyxValue(sessionMetadata)) { return; } - // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report - Linking.getInitialURL().then((url) => { - onInitialUrl(url as Route); - if (url) { - if (conciergeReportID === undefined) { - Log.info('[Deep link] conciergeReportID is undefined when processing initial URL', false, {url}); + // Guard against stale closures: when deps change and the effect re-runs, the previous + // getInitialURL() promise may still be in-flight. Without this guard, its .then() would + // fire with stale conciergeReportID/introSelected values, causing a duplicate + // openReportFromDeepLink() call. + let cancelled = false; + + // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report. + // We race against a timeout to prevent permanently blocking NavigationRoot if getInitialURL() never resolves + // (e.g. in HybridApp when OldDot fails to send the URL via native bridge). + Promise.race([ + Linking.getInitialURL(), + new Promise((resolve) => { + setTimeout(() => resolve(null), CONST.TIMING.GET_INITIAL_URL_TIMEOUT); + }), + ]) + .then((url) => { + if (cancelled) { + return; } - if (introSelected === undefined) { - Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); + + initialUrlProcessed.current = true; + onInitialUrl(url as Route); + + if (url) { + if (conciergeReportID === undefined) { + Log.info('[Deep link] conciergeReportID is undefined when processing initial URL', false, {url}); + } + if (introSelected === undefined) { + Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); + } + // Use hasAuthToken() for the latest auth state at call time, since the isAuthenticated + // closure value may be stale on cold start (useOnyx reports 'loaded' before storage completes). + const isCurrentlyAuthenticated = hasAuthToken(); + openReportFromDeepLink(url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected); + } else { + Report.doneCheckingPublicRoom(); } - openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected); - } else { - Report.doneCheckingPublicRoom(); - } - endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); - }); + endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); + }) + .catch(() => { + if (cancelled) { + return; + } + + initialUrlProcessed.current = true; + onInitialUrl(null); + Report.doneCheckingPublicRoom(); + endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); + }); // Open chat report from a deep link (only mobile native) linkingChangeListener.current = Linking.addEventListener('url', (state) => { @@ -68,11 +102,25 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { }); return () => { + cancelled = true; linkingChangeListener.current?.remove(); }; // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to re-run when conciergeReportID changes }, [sessionMetadata?.status, conciergeReportID, introSelected]); + // Safety net: if getInitialURL() resolves before the session loads, hasAuthToken() may return false + // for an authenticated user, causing openReportFromDeepLink to take the wrong path. Once isAuthenticated + // settles to true, unblock the UI. The initialUrlProcessed guard ensures this doesn't fire before URL + // resolution. In the common case (isAuthenticated settles first), this is a no-op because + // openReportFromDeepLink's own doneCheckingPublicRoom() call handles it. + useEffect(() => { + if (!isAuthenticated || !initialUrlProcessed.current) { + return; + } + + Report.doneCheckingPublicRoom(); + }, [isAuthenticated]); + return null; } diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 0afde4a09222b..6e468851e34a9 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -65,9 +65,9 @@ function Expensify() { const {preferredLocale} = useLocalize(); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector}); const [lastRoute] = useOnyx(ONYXKEYS.LAST_ROUTE); - const [isCheckingPublicRoom = true] = useOnyx(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, {initWithStoredValues: false}); - const [updateAvailable] = useOnyx(ONYXKEYS.UPDATE_AVAILABLE, {initWithStoredValues: false}); - const [updateRequired] = useOnyx(ONYXKEYS.UPDATE_REQUIRED, {initWithStoredValues: false}); + const [isCheckingPublicRoom = true] = useOnyx(ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM); + const [updateAvailable] = useOnyx(ONYXKEYS.RAM_ONLY_UPDATE_AVAILABLE); + const [updateRequired] = useOnyx(ONYXKEYS.RAM_ONLY_UPDATE_REQUIRED); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); useDebugShortcut(); @@ -81,7 +81,7 @@ function Expensify() { const bootsplashSpan = useRef(null); - const [initialUrl, setInitialUrl] = useState(null); + const [initialUrl, setInitialUrl] = useState(undefined); const {setIsAuthenticatedAtStartup} = useInitialURLActions(); useEffect(() => { @@ -302,7 +302,7 @@ function Expensify() { - {hasAttemptedToOpenPublicRoom && ( + {hasAttemptedToOpenPublicRoom && initialUrl !== undefined && ( ; [ONYXKEYS.TASK]: OnyxTypes.Task; [ONYXKEYS.CURRENCY_LIST]: OnyxTypes.CurrencyList; - [ONYXKEYS.UPDATE_AVAILABLE]: boolean; + [ONYXKEYS.RAM_ONLY_UPDATE_AVAILABLE]: boolean; [ONYXKEYS.SCREEN_SHARE_REQUEST]: OnyxTypes.ScreenShareRequest; [ONYXKEYS.COUNTRY_CODE]: number; [ONYXKEYS.COUNTRY]: string; @@ -1321,7 +1321,7 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING]: number; [ONYXKEYS.NVP_PRIVATE_BILLING_STATUS]: OnyxTypes.BillingStatus; [ONYXKEYS.USER_WALLET]: OnyxTypes.UserWallet; - [ONYXKEYS.WALLET_ONFIDO]: OnyxTypes.WalletOnfido; + [ONYXKEYS.RAM_ONLY_WALLET_ONFIDO]: OnyxTypes.WalletOnfido; [ONYXKEYS.WALLET_ADDITIONAL_DETAILS]: OnyxTypes.WalletAdditionalDetails; [ONYXKEYS.WALLET_TERMS]: OnyxTypes.WalletTerms; [ONYXKEYS.BANK_ACCOUNT_LIST]: OnyxTypes.BankAccountList; @@ -1350,7 +1350,7 @@ type OnyxValuesMapping = { [ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string; [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean; [ONYXKEYS.IS_BETA]: boolean; - [ONYXKEYS.IS_CHECKING_PUBLIC_ROOM]: boolean; + [ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM]: boolean; [ONYXKEYS.MY_DOMAIN_SECURITY_GROUPS]: Record; [ONYXKEYS.VERIFY_3DS_SUBSCRIPTION]: string; [ONYXKEYS.PREFERRED_THEME]: ValueOf; @@ -1367,10 +1367,10 @@ type OnyxValuesMapping = { [ONYXKEYS.ONBOARDING_POLICY_ID]: string; [ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID]: string; [ONYXKEYS.ONBOARDING_LAST_VISITED_PATH]: string; - [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: boolean; + [ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS]: boolean; [ONYXKEYS.LAST_VISITED_PATH]: string | undefined; [ONYXKEYS.RECENTLY_USED_REPORT_FIELDS]: OnyxTypes.RecentlyUsedReportFields; - [ONYXKEYS.UPDATE_REQUIRED]: boolean; + [ONYXKEYS.RAM_ONLY_UPDATE_REQUIRED]: boolean; [ONYXKEYS.SUPPORTAL_PERMISSION_DENIED]: OnyxTypes.SupportalPermissionDenied | null; [ONYXKEYS.RESET_REQUIRED]: boolean; [ONYXKEYS.PLAID_CURRENT_EVENT]: string; diff --git a/src/components/BaseVacationDelegateSelectionComponent.tsx b/src/components/BaseVacationDelegateSelectionComponent.tsx index f95dc2420c5a8..937dbbff73291 100644 --- a/src/components/BaseVacationDelegateSelectionComponent.tsx +++ b/src/components/BaseVacationDelegateSelectionComponent.tsx @@ -54,7 +54,7 @@ function BaseVacationDelegateSelectionComponent({ const styles = useThemeStyles(); const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar']); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const currentVacationDelegate = vacationDelegate?.delegate ?? ''; const delegatePersonalDetails = getPersonalDetailByEmail(currentVacationDelegate); diff --git a/src/components/Search/FilterDropdowns/UserSelectPopup.tsx b/src/components/Search/FilterDropdowns/UserSelectPopup.tsx index 86594fbc3beeb..22d795924f306 100644 --- a/src/components/Search/FilterDropdowns/UserSelectPopup.tsx +++ b/src/components/Search/FilterDropdowns/UserSelectPopup.tsx @@ -54,7 +54,7 @@ function UserSelectPopup({value, closeOverlay, onChange, isSearchable}: UserSele const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [searchTerm, setSearchTerm] = useState(''); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const getInitialSelectedIDs = useCallback(() => { return value.reduce>((acc, id) => { diff --git a/src/components/Search/SearchFiltersChatsSelector.tsx b/src/components/Search/SearchFiltersChatsSelector.tsx index 2ef2a6999e748..9d4c0857b5bd4 100644 --- a/src/components/Search/SearchFiltersChatsSelector.tsx +++ b/src/components/Search/SearchFiltersChatsSelector.tsx @@ -56,7 +56,7 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen const currentUserAccountID = currentUserPersonalDetails.accountID; const currentUserEmail = currentUserPersonalDetails.email ?? ''; - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const reportAttributesDerived = useReportAttributes(); const [selectedReportIDs, setSelectedReportIDs] = useState(initialReportIDs); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); diff --git a/src/components/Search/SearchFiltersParticipantsSelector.tsx b/src/components/Search/SearchFiltersParticipantsSelector.tsx index 9458f6b0cedea..6726c8f8a4bd3 100644 --- a/src/components/Search/SearchFiltersParticipantsSelector.tsx +++ b/src/components/Search/SearchFiltersParticipantsSelector.tsx @@ -83,7 +83,7 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate, const {options, areOptionsInitialized} = useOptionsList({ shouldInitialize: didScreenTransitionEnd, }); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const reportAttributesDerived = useReportAttributes(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index a04fdfe81f9c4..11bac2d055f5a 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -61,7 +61,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla const {setShouldResetSearchQuery} = useSearchActionsContext(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const currentUserAccountID = currentUserPersonalDetails.accountID; - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const personalDetails = usePersonalDetails(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 08285a7a98fd4..baf7da632a6e5 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -121,7 +121,7 @@ Onyx.connectWithoutView({ const KEYS_TO_PRESERVE: OnyxKey[] = [ ONYXKEYS.ACCOUNT, - ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, + ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, ONYXKEYS.IS_LOADING_APP, ONYXKEYS.IS_SIDEBAR_LOADED, ONYXKEYS.MODAL, diff --git a/src/libs/actions/AppUpdate/index.ts b/src/libs/actions/AppUpdate/index.ts index 69c80a0898315..0c4f88d2dde2a 100644 --- a/src/libs/actions/AppUpdate/index.ts +++ b/src/libs/actions/AppUpdate/index.ts @@ -3,7 +3,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import updateApp from './updateApp'; function triggerUpdateAvailable() { - Onyx.set(ONYXKEYS.UPDATE_AVAILABLE, true); + Onyx.set(ONYXKEYS.RAM_ONLY_UPDATE_AVAILABLE, true); } function setIsAppInBeta(isBeta: boolean) { diff --git a/src/libs/actions/QueuedOnyxUpdates.ts b/src/libs/actions/QueuedOnyxUpdates.ts index be2a1480ccec1..cbb735e4dc309 100644 --- a/src/libs/actions/QueuedOnyxUpdates.ts +++ b/src/libs/actions/QueuedOnyxUpdates.ts @@ -45,7 +45,7 @@ function flushQueue(): Promise { ONYXKEYS.CREDENTIALS, ONYXKEYS.IS_SIDEBAR_LOADED, ONYXKEYS.ACCOUNT, - ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, + ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, ONYXKEYS.MODAL, ONYXKEYS.NETWORK, ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 410d95d1e7dbe..8f22acb103826 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -1347,7 +1347,7 @@ function openReport(params: OpenReportActionParams) { }); } - const finallyData: Array> = []; + const finallyData: Array> = []; const parameters: OpenReportParams = { reportID, @@ -1614,7 +1614,7 @@ function openReport(params: OpenReportActionParams) { if (isFromDeepLink) { finallyData.push({ onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, + key: ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, value: false, }); @@ -4145,7 +4145,7 @@ function toggleEmojiReaction( } function doneCheckingPublicRoom() { - Onyx.set(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, false); + Onyx.set(ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, false); } function navigateToMostRecentReport(currentReport: OnyxEntry, conciergeReportID: string | undefined, currentUserAccountID: number, introSelected: OnyxEntry) { @@ -5001,22 +5001,22 @@ function savePrivateNotesDraft(reportID: string, note: string) { function searchForReports(isOffline: boolean, searchInput: string, policyID?: string, isUserSearch = false) { // We do not try to make this request while offline because it sets a loading indicator optimistically if (isOffline) { - Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); + Onyx.set(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS, false); return; } - const successData: Array> = [ + const successData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + key: ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS, value: false, }, ]; - const failureData: Array> = [ + const failureData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + key: ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS, value: false, }, ]; @@ -5040,14 +5040,14 @@ function performServerSearch(searchInput: string, policyID?: string, isUserSearc // We are not getting isOffline from components as useEffect change will re-trigger the search on network change const isOffline = NetworkStore.isOffline(); if (isOffline || !searchInput.trim().length) { - Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); + Onyx.set(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS, false); return; } // Why not set this in optimistic data? It won't run until the API request happens and while the API request is debounced // we want to show the loading state right away. Otherwise, we will see a flashing UI where the client options are sorted and // tell the user there are no options, then we start searching, and tell them there are no options again. - Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, true); + Onyx.set(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS, true); searchForReports(isOffline, searchInput, policyID, isUserSearch); } diff --git a/src/libs/actions/UpdateRequired.ts b/src/libs/actions/UpdateRequired.ts index 078bcc41fd714..50573f62da242 100644 --- a/src/libs/actions/UpdateRequired.ts +++ b/src/libs/actions/UpdateRequired.ts @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; function alertUser() { - Onyx.set(ONYXKEYS.UPDATE_REQUIRED, true); + Onyx.set(ONYXKEYS.RAM_ONLY_UPDATE_REQUIRED, true); } export { diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index d19bfb759688b..5b15531bcd3bd 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -23,21 +23,21 @@ type WalletQuestionAnswer = { * identity check. Note: This happens in Web-Secure when we call Activate_Wallet during the OnfidoStep. */ function openOnfidoFlow() { - const optimisticData: Array> = [ + const optimisticData: Array> = [ { // Use Onyx.set() since we are resetting the Onfido flow completely. onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.WALLET_ONFIDO, + key: ONYXKEYS.RAM_ONLY_WALLET_ONFIDO, value: { isLoading: true, }, }, ]; - const finallyData: Array> = [ + const finallyData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.WALLET_ONFIDO, + key: ONYXKEYS.RAM_ONLY_WALLET_ONFIDO, value: { isLoading: false, }, @@ -97,10 +97,10 @@ function updatePersonalDetails(personalDetails: UpdatePersonalDetailsForWalletPa * API request to fetch the userWallet after we call VerifyIdentity */ function verifyIdentity(parameters: VerifyIdentityParams) { - const optimisticData: Array> = [ + const optimisticData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.WALLET_ONFIDO, + key: ONYXKEYS.RAM_ONLY_WALLET_ONFIDO, value: { isLoading: true, errors: null, @@ -116,10 +116,10 @@ function verifyIdentity(parameters: VerifyIdentityParams) { }, ]; - const successData: Array> = [ + const successData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.WALLET_ONFIDO, + key: ONYXKEYS.RAM_ONLY_WALLET_ONFIDO, value: { isLoading: false, errors: null, @@ -127,10 +127,10 @@ function verifyIdentity(parameters: VerifyIdentityParams) { }, ]; - const failureData: Array> = [ + const failureData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.WALLET_ONFIDO, + key: ONYXKEYS.RAM_ONLY_WALLET_ONFIDO, value: { isLoading: false, hasAcceptedPrivacyPolicy: false, diff --git a/src/pages/EnablePayments/OnfidoStep.tsx b/src/pages/EnablePayments/OnfidoStep.tsx index 94002641a84f2..3a0e4c46a08db 100644 --- a/src/pages/EnablePayments/OnfidoStep.tsx +++ b/src/pages/EnablePayments/OnfidoStep.tsx @@ -15,10 +15,7 @@ import OnfidoPrivacy from './OnfidoPrivacy'; function OnfidoStep() { const {translate} = useLocalize(); - const [walletOnfidoData] = useOnyx(ONYXKEYS.WALLET_ONFIDO, { - // Let's get a new onfido token each time the user hits this flow (as it should only be once) - initWithStoredValues: false, - }); + const [walletOnfidoData] = useOnyx(ONYXKEYS.RAM_ONLY_WALLET_ONFIDO); const shouldShowOnfido = walletOnfidoData?.hasAcceptedPrivacyPolicy && !walletOnfidoData?.isLoading && !walletOnfidoData?.errors && walletOnfidoData?.sdkToken; diff --git a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx index b2b45a3a4625d..6540e2305d2f2 100644 --- a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx @@ -28,7 +28,7 @@ function VerifyIdentity() { const styles = useThemeStyles(); const {translate} = useLocalize(); const illustrations = useMemoizedLazyIllustrations(['ToddBehindCloud']); - const [walletOnfidoData] = useOnyx(ONYXKEYS.WALLET_ONFIDO, {initWithStoredValues: false}); + const [walletOnfidoData] = useOnyx(ONYXKEYS.RAM_ONLY_WALLET_ONFIDO); const handleOnfidoSuccess = useCallback( (onfidoData: OnfidoData) => { diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index bd66a42cf757c..b02e6d0bc02ec 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -238,7 +238,7 @@ function NewChatPage({ref}: NewChatPageProps) { const personalData = useCurrentUserPersonalDetails(); const currentUserAccountID = personalData.accountID; const {top} = useSafeAreaInsets(); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [reportAttributesDerivedFull] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES); const privateIsArchivedMap = usePrivateIsArchivedMap(); diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index f488545ae8465..1fb4dea154c2f 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -54,7 +54,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [account] = useOnyx(ONYXKEYS.ACCOUNT); diff --git a/src/pages/RoomInvitePage.tsx b/src/pages/RoomInvitePage.tsx index 0c76ae5c5cdb5..4f71fe6af22dc 100644 --- a/src/pages/RoomInvitePage.tsx +++ b/src/pages/RoomInvitePage.tsx @@ -72,7 +72,7 @@ function RoomInvitePage({ const currentUserEmail = currentUserPersonalDetails.email ?? ''; const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(userSearchPhrase ?? ''); const [selectedOptions, setSelectedOptions] = useState([]); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const isReportArchived = useReportIsArchived(report.reportID); const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING); diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 8039de13c85d3..a14040db02b45 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -64,7 +64,7 @@ function ShareTab({ref}: ShareTabProps) { const {options, areOptionsInitialized} = useOptionsList(); const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus(); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; const shouldShowLoadingPlaceholder = !areOptionsInitialized || !didScreenTransitionEnd; diff --git a/src/pages/domain/Admins/DomainAddAdminPage.tsx b/src/pages/domain/Admins/DomainAddAdminPage.tsx index 4854fa1c26f03..8af631497a511 100644 --- a/src/pages/domain/Admins/DomainAddAdminPage.tsx +++ b/src/pages/domain/Admins/DomainAddAdminPage.tsx @@ -36,7 +36,7 @@ function DomainAddAdminPage({route}: DomainAddAdminProps) { const {translate} = useLocalize(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [domainEmail] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, { selector: domainEmailSelector, }); diff --git a/src/pages/iou/request/MoneyRequestAccountantSelector.tsx b/src/pages/iou/request/MoneyRequestAccountantSelector.tsx index 84b66d40a9216..cc7acf5f35772 100644 --- a/src/pages/iou/request/MoneyRequestAccountantSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAccountantSelector.tsx @@ -59,7 +59,7 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const [betas] = useOnyx(ONYXKEYS.BETAS); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const {options, areOptionsInitialized} = useOptionsList({ shouldInitialize: didScreenTransitionEnd, }); diff --git a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx index 460225f0f055a..fdf959768012d 100644 --- a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx @@ -68,7 +68,7 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [recentAttendees] = useOnyx(ONYXKEYS.NVP_RECENT_ATTENDEES); const policy = usePolicy(activePolicyID); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const reportAttributesDerived = useReportAttributes(); const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 7d5838bed75bd..cd0e7f3c729c5 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -120,7 +120,7 @@ function MoneyRequestParticipantsSelector({ const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`]; const [ownerBillingGraceEndPeriod] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const currentUserLogin = currentUserPersonalDetails.login; const currentUserEmail = currentUserPersonalDetails.email ?? ''; diff --git a/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx b/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx index 4adec3d12e248..91a2a28464e0e 100644 --- a/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx +++ b/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx @@ -19,7 +19,7 @@ import ROUTES from '@src/ROUTES'; function AddDelegatePage() { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const existingDelegates = diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 5af569e88b12f..74f39b007b032 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -41,7 +41,7 @@ function TaskAssigneeSelectorModal() { const backTo = route.params?.backTo; const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [task] = useOnyx(ONYXKEYS.TASK); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const currentUserEmail = currentUserPersonalDetails.email ?? ''; diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 197dc9c6c9570..73dea1eb5b28c 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -52,7 +52,7 @@ function TaskShareDestinationSelectorModal() { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const {searchTerm, debouncedSearchTerm, setSearchTerm, availableOptions, areOptionsInitialized, onListEndReached} = useSearchSelector({ diff --git a/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx b/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx index 318a972beb1d5..cc7888ec1ab9b 100644 --- a/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx +++ b/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx @@ -56,7 +56,7 @@ function WorkspaceConfirmationOwnerSelectorPage() { const {login: currentUserLogin} = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [draftValues] = useOnyx(ONYXKEYS.FORMS.WORKSPACE_CONFIRMATION_FORM_DRAFT); const currentOwner = draftValues?.owner ?? currentUserLogin ?? ''; const ownerPersonalDetails = getPersonalDetailByEmail(currentOwner); diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index d018d0609cfcc..60b12c5c6d73f 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -45,7 +45,7 @@ function WorkspaceInvitePage({route, policy}: WorkspaceInvitePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const [invitedEmailsToAccountIDsDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID}`); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index c102b4336a8a3..08562711e7bac 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -47,7 +47,7 @@ function AssigneeStep({route}: AssigneeStepProps) { const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const ineligibleInvites = getIneligibleInvitees(policy?.employeeList); const excludedUsers: Record = {}; diff --git a/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx b/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx index 89e667d6e28af..815e4e82228b8 100644 --- a/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx @@ -51,7 +51,7 @@ function AssigneeStep({policy, stepNames, startStepIndex, route}: AssigneeStepPr const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const ineligibleInvites = getIneligibleInvitees(policy?.employeeList); const excludedUsers: Record = {}; diff --git a/src/setup/index.ts b/src/setup/index.ts index 6f8aec7bb1e8f..898d0c8d9b4f1 100644 --- a/src/setup/index.ts +++ b/src/setup/index.ts @@ -60,6 +60,13 @@ export default function () { }, skippableCollectionMemberIDs: CONST.SKIPPABLE_COLLECTION_MEMBER_IDS, snapshotMergeKeys: ['pendingAction', 'pendingFields'], + ramOnlyKeys: [ + ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, + ONYXKEYS.RAM_ONLY_UPDATE_AVAILABLE, + ONYXKEYS.RAM_ONLY_UPDATE_REQUIRED, + ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS, + ONYXKEYS.RAM_ONLY_WALLET_ONFIDO, + ], }); // Must be imported after Onyx.init() and outside the React lifecycle so that push notification diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index b9320a1087ed3..ea02915723163 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -167,7 +167,7 @@ test('[SearchRouter] should render list with cached options', async () => { ...mockedReports, [ONYXKEYS.PERSONAL_DETAILS_LIST]: mockedPersonalDetails, [ONYXKEYS.BETAS]: mockedBetas, - [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: true, + [ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS]: true, }), ) .then(() => measureRenders(, {scenario})); @@ -187,7 +187,7 @@ test('[SearchRouter] should react to text input changes', async () => { ...mockedReports, [ONYXKEYS.PERSONAL_DETAILS_LIST]: mockedPersonalDetails, [ONYXKEYS.BETAS]: mockedBetas, - [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: true, + [ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS]: true, }), ) .then(() => measureRenders(, {scenario})); diff --git a/tests/ui/SessionTest.tsx b/tests/ui/SessionTest.tsx index 6f3b64b28b4a5..2c1af88404b2f 100644 --- a/tests/ui/SessionTest.tsx +++ b/tests/ui/SessionTest.tsx @@ -155,6 +155,13 @@ describe('Deep linking', () => { await waitForBatchedUpdatesWithAct(); + // In production, RAM-only keys don't exist on a fresh app start because they're never persisted to storage. + // In tests, however, unmounting and remounting doesn't restart the JS process, + // so RAM-only keys retain their values from the previous mount. We need to manually + // remove them to simulate a real app restart. + await Onyx.set(ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, null); + await waitForBatchedUpdates(); + const url = getInitialURL(); // User signs in automatically when the app is remounted because of the deep link. // This overrides the previous sign-in. diff --git a/tests/unit/APITest.ts b/tests/unit/APITest.ts index 8e81e4bfd42aa..1b011574482f1 100644 --- a/tests/unit/APITest.ts +++ b/tests/unit/APITest.ts @@ -874,8 +874,8 @@ describe('API.write() persistence guarantees', () => { // persisted-requests queue. This avoids spy-ordering issues that caused // false passes on CI. const updateMock = jest.spyOn(Onyx, 'update').mockImplementation((data) => { - // We use ONYXKEYS.IS_CHECKING_PUBLIC_ROOM as a sample key to identify the marker - const hasMarker = data.some((entry) => entry.key === ONYXKEYS.IS_CHECKING_PUBLIC_ROOM); + // We use ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM as a sample key to identify the marker + const hasMarker = data.some((entry) => entry.key === ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM); if (hasMarker) { optimisticDataApplied = true; // Note: getAll() checks the in-memory queue, not durable (disk) state. @@ -892,7 +892,7 @@ describe('API.write() persistence guarantees', () => { optimisticData: [ { onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, + key: ONYXKEYS.RAM_ONLY_IS_CHECKING_PUBLIC_ROOM, value: true, }, ],