diff --git a/.eslintrc.js b/.eslintrc.js index 236cc17d3f8b2..5d798f6db9c82 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -277,6 +277,14 @@ module.exports = { property: 'isHybridApp', message: 'Use CONFIG.IS_HYBRID_APP instead.', }, + // Prevent direct use of HybridAppModule.closeReactNativeApp(). + // Instead, use the `closeReactNativeApp` action from `@userActions/HybridApp`, + // which correctly updates `hybridApp.closingReactNativeApp` when closing NewDot + { + object: 'HybridAppModule', + property: 'closeReactNativeApp', + message: 'Use `closeReactNativeApp` from `@userActions/HybridApp` instead.', + }, ], 'no-restricted-imports': [ 'error', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 89b73fd157b1c..a982a7d2d0b95 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -476,9 +476,6 @@ const ONYXKEYS = { /** Stores recently used currencies */ RECENTLY_USED_CURRENCIES: 'nvp_recentlyUsedCurrencies', - /** States whether we transitioned from OldDot to show only certain group of screens. It should be undefined on pure NewDot. */ - IS_SINGLE_NEW_DOT_ENTRY: 'isSingleNewDotEntry', - /** Company cards custom names */ NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES: 'nvp_expensify_ccCustomNames', @@ -851,6 +848,9 @@ const ONYXKEYS = { REPORT_ATTRIBUTES: 'reportAttributes', REPORT_TRANSACTIONS_AND_VIOLATIONS: 'reportTransactionsAndViolations', }, + + /** Stores HybridApp specific state required to interoperate with OldDot */ + HYBRID_APP: 'hybridApp', } as const; type AllOnyxKeys = DeepValueOf; @@ -1173,7 +1173,6 @@ type OnyxValuesMapping = { [ONYXKEYS.APPROVAL_WORKFLOW]: OnyxTypes.ApprovalWorkflowOnyx; [ONYXKEYS.IMPORTED_SPREADSHEET]: OnyxTypes.ImportedSpreadsheet; [ONYXKEYS.LAST_ROUTE]: string; - [ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY]: boolean | undefined; [ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; [ONYXKEYS.CONCIERGE_REPORT_ID]: string; @@ -1199,6 +1198,7 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_LAST_IPHONE_LOGIN]: string; [ONYXKEYS.NVP_LAST_ANDROID_LOGIN]: string; [ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_REPORT_IDS]: string[]; + [ONYXKEYS.HYBRID_APP]: OnyxTypes.HybridApp; }; type OnyxDerivedValuesMapping = { diff --git a/src/components/BookTravelButton.tsx b/src/components/BookTravelButton.tsx index d2dd7a407233e..388b5896ebc8e 100644 --- a/src/components/BookTravelButton.tsx +++ b/src/components/BookTravelButton.tsx @@ -1,7 +1,6 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; import {Str} from 'expensify-common'; import type {ReactElement} from 'react'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -15,6 +14,7 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import {getActivePolicies, getAdminsPrivateEmailDomains, isPaidGroupPolicy} from '@libs/PolicyUtils'; import colors from '@styles/theme/colors'; +import closeReactNativeApp from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,7 +22,6 @@ import ROUTES from '@src/ROUTES'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; -import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import DotIndicatorMessage from './DotIndicatorMessage'; import {RocketDude} from './Icon/Illustrations'; import Text from './Text'; @@ -62,7 +61,6 @@ function BookTravelButton({text, shouldRenderErrorMessageBelowButton = false, se const [travelSettings] = useOnyx(ONYXKEYS.NVP_TRAVEL_SETTINGS, {canBeMissing: false}); const [sessionEmail] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email, canBeMissing: false}); const primaryContactMethod = primaryLogin ?? sessionEmail ?? ''; - const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const {isBlockedFromSpotnanaTravel, isBetaEnabled} = usePermissions(); const [isPreventionModalVisible, setPreventionModalVisibility] = useState(false); const [isVerificationModalVisible, setVerificationModalVisibility] = useState(false); @@ -72,7 +70,7 @@ function BookTravelButton({text, shouldRenderErrorMessageBelowButton = false, se const groupPaidPolicies = activePolicies.filter((activePolicy) => activePolicy.type !== CONST.POLICY.TYPE.PERSONAL && isPaidGroupPolicy(activePolicy)); // Flag indicating whether NewDot was launched exclusively for Travel, // e.g., when the user selects "Trips" from the Expensify Classic menu in HybridApp. - const [wasNewDotLaunchedJustForTravel] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, {canBeMissing: false}); + const [wasNewDotLaunchedJustForTravel] = useOnyx(ONYXKEYS.HYBRID_APP, {selector: (hybridApp) => hybridApp?.isSingleNewDotEntry, canBeMissing: false}); const hidePreventionModal = () => setPreventionModalVisibility(false); const hideVerificationModal = () => setVerificationModalVisibility(false); @@ -137,8 +135,7 @@ function BookTravelButton({text, shouldRenderErrorMessageBelowButton = false, se // Close NewDot if it was opened only for Travel, as its purpose is now fulfilled. Log.info('[HybridApp] Returning to OldDot after opening TravelDot'); - HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); - setRootStatusBarEnabled(false); + closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); }) ?.catch(() => { setErrorMessage(translate('travel.errorMessage')); @@ -170,17 +167,16 @@ function BookTravelButton({text, shouldRenderErrorMessageBelowButton = false, se isBlockedFromSpotnanaTravel, primaryContactMethod, policy, + groupPaidPolicies.length, travelSettings?.hasAcceptedTerms, + travelSettings?.lastTravelSignupRequestTime, + isBetaEnabled, styles.flexRow, styles.link, StyleUtils, translate, wasNewDotLaunchedJustForTravel, - setRootStatusBarEnabled, isUserValidated, - groupPaidPolicies.length, - isBetaEnabled, - travelSettings?.lastTravelSignupRequestTime, ]); return ( diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index 95ad1fe0f3a80..4533d020d2497 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -1,10 +1,12 @@ import React, {useCallback, useContext, useEffect, useRef, useState} from 'react'; import {interpolateColor, runOnJS, useAnimatedReaction, useSharedValue, withDelay, withTiming} from 'react-native-reanimated'; +import useOnyx from '@hooks/useOnyx'; import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import {navigationRef} from '@libs/Navigation/Navigation'; import StatusBar from '@libs/StatusBar'; import type {StatusBarStyle} from '@styles/index'; +import ONYXKEYS from '@src/ONYXKEYS'; import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackgroundContext'; import updateGlobalBackgroundColor from './updateGlobalBackgroundColor'; import updateStatusBarAppearance from './updateStatusBarAppearance'; @@ -19,8 +21,11 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack const {isRootStatusBarEnabled, setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const theme = useTheme(); const [statusBarStyle, setStatusBarStyle] = useState(); + const [closingReactNativeApp = false] = useOnyx(ONYXKEYS.HYBRID_APP, {selector: (hybridApp) => hybridApp?.closingReactNativeApp, canBeMissing: true}); - const isDisabled = !isNested && !isRootStatusBarEnabled; + // Include `closingReactNativeApp` to disable the StatusBar when switching from HybridApp to OldDot, + // preventing unexpected status bar blinking during the transition + const isDisabled = (!isNested && !isRootStatusBarEnabled) || closingReactNativeApp; // Disable the root status bar when a nested status bar is rendered useEffect(() => { diff --git a/src/components/ScreenWrapper/index.tsx b/src/components/ScreenWrapper/index.tsx index fb73901d25dfa..0ec09710eaf23 100644 --- a/src/components/ScreenWrapper/index.tsx +++ b/src/components/ScreenWrapper/index.tsx @@ -1,4 +1,3 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; import {useIsFocused, useNavigation, usePreventRemove} from '@react-navigation/native'; import type {ForwardedRef, ReactNode} from 'react'; import React, {forwardRef, useContext, useEffect, useMemo, useState} from 'react'; @@ -6,7 +5,6 @@ import type {StyleProp, View, ViewStyle} from 'react-native'; import {Keyboard} from 'react-native'; import type {EdgeInsets} from 'react-native-safe-area-context'; import CustomDevMenu from '@components/CustomDevMenu'; -import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import FocusTrapForScreen from '@components/FocusTrap/FocusTrapForScreen'; import type FocusTrapForScreenProps from '@components/FocusTrap/FocusTrapForScreen/FocusTrapProps'; import HeaderGap from '@components/HeaderGap'; @@ -22,6 +20,7 @@ import NarrowPaneContext from '@libs/Navigation/AppNavigator/Navigators/NarrowPa import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList, RootNavigatorParamList} from '@libs/Navigation/types'; +import closeReactNativeApp from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -170,15 +169,13 @@ function ScreenWrapper( const shouldOffsetMobileOfflineIndicator = displaySmallScreenOfflineIndicator && addSmallScreenOfflineIndicatorBottomSafeAreaPadding && isOffline; const {initialURL} = useContext(InitialURLContext); - const [isSingleNewDotEntry] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, {canBeMissing: true}); - const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); + const [isSingleNewDotEntry = false] = useOnyx(ONYXKEYS.HYBRID_APP, {selector: (hybridApp) => hybridApp?.isSingleNewDotEntry, canBeMissing: true}); - usePreventRemove((isSingleNewDotEntry ?? false) && initialURL === Navigation.getActiveRouteWithoutParams(), () => { + usePreventRemove(isSingleNewDotEntry && initialURL === Navigation.getActiveRouteWithoutParams(), () => { if (!CONFIG.IS_HYBRID_APP) { return; } - HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); - setRootStatusBarEnabled(false); + closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); }); useEffect(() => { diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index b8732ccf10c22..86ea98b3d4b6a 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -32,7 +32,7 @@ function useOnboardingFlowRouter() { const [dismissedProductTraining, dismissedProductTrainingMetadata] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true}); - const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, {canBeMissing: true}); + const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.HYBRID_APP, {selector: (hybridApp) => hybridApp?.isSingleNewDotEntry, canBeMissing: true}); useEffect(() => { // This should delay opening the onboarding modal so it does not interfere with the ongoing ReportScreen params changes diff --git a/src/libs/Notification/PushNotification/subscribeToPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToPushNotifications.ts index 7cc6023be4573..a9bdd4aeabb3d 100644 --- a/src/libs/Notification/PushNotification/subscribeToPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToPushNotifications.ts @@ -41,12 +41,12 @@ Onyx.connect({ let isSingleNewDotEntry: boolean | undefined; Onyx.connect({ - key: ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, + key: ONYXKEYS.HYBRID_APP, callback: (value) => { if (!value) { return; } - isSingleNewDotEntry = value; + isSingleNewDotEntry = value?.isSingleNewDotEntry; }, }); diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts new file mode 100644 index 0000000000000..941d91ada9655 --- /dev/null +++ b/src/libs/actions/HybridApp/index.ts @@ -0,0 +1,14 @@ +import HybridAppModule from '@expensify/react-native-hybrid-app'; +import Onyx from 'react-native-onyx'; +import CONFIG from '@src/CONFIG'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function closeReactNativeApp({shouldSignOut, shouldSetNVP}: {shouldSignOut: boolean; shouldSetNVP: boolean}) { + if (CONFIG.IS_HYBRID_APP) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {closingReactNativeApp: true}); + } + // eslint-disable-next-line no-restricted-properties + HybridAppModule.closeReactNativeApp({shouldSignOut, shouldSetNVP}); +} + +export default closeReactNativeApp; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 741b705a5d229..8a23b8f753148 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -47,6 +47,7 @@ import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContex import {KEYS_TO_PRESERVE, openApp, reconnectApp} from '@userActions/App'; import {KEYS_TO_PRESERVE_DELEGATE_ACCESS} from '@userActions/Delegate'; import * as Device from '@userActions/Device'; +import closeReactNativeApp from '@userActions/HybridApp'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; import * as Welcome from '@userActions/Welcome'; @@ -265,7 +266,7 @@ function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSess // In the HybridApp, we want the Old Dot to handle the sign out process if (CONFIG.IS_HYBRID_APP && shouldKillHybridApp) { - HybridAppModule.closeReactNativeApp({shouldSignOut: true, shouldSetNVP: false}); + closeReactNativeApp({shouldSignOut: true, shouldSetNVP: false}); return; } @@ -616,13 +617,14 @@ function signInAfterTransitionFromOldDot(hybridAppSettings: string) { Onyx.multiSet({ [ONYXKEYS.SESSION]: {email, authToken, encryptedAuthToken: decodeURIComponent(encryptedAuthToken), accountID: Number(accountID)}, [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword}, - [ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY]: isSingleNewDotEntry, [ONYXKEYS.NVP_TRY_NEW_DOT]: { classicRedirect: {completedHybridAppOnboarding}, nudgeMigration: nudgeMigrationTimestamp ? {timestamp: new Date(nudgeMigrationTimestamp)} : undefined, }, [ONYXKEYS.ACCOUNT]: {shouldUseStagingServer: isStaging}, - }).then(() => Onyx.merge(ONYXKEYS.ACCOUNT, {primaryLogin, requiresTwoFactorAuth, needsTwoFactorAuthSetup})), + }) + .then(() => Onyx.merge(ONYXKEYS.ACCOUNT, {primaryLogin, requiresTwoFactorAuth, needsTwoFactorAuthSetup})) + .then(() => Onyx.merge(ONYXKEYS.HYBRID_APP, {isSingleNewDotEntry, closingReactNativeApp: false})), ) .then(() => { if (clearOnyxOnStart) { diff --git a/src/pages/ErrorPage/SessionExpiredPage.tsx b/src/pages/ErrorPage/SessionExpiredPage.tsx index 85dd6724a3bdd..4972b8e6459e6 100644 --- a/src/pages/ErrorPage/SessionExpiredPage.tsx +++ b/src/pages/ErrorPage/SessionExpiredPage.tsx @@ -1,4 +1,3 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; import React from 'react'; import {View} from 'react-native'; import Icon from '@components/Icon'; @@ -10,6 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import closeReactNativeApp from '@userActions/HybridApp'; import {clearSignInData} from '@userActions/Session'; import CONFIG from '@src/CONFIG'; @@ -39,7 +39,7 @@ function SessionExpiredPage() { Navigation.goBack(); return; } - HybridAppModule.closeReactNativeApp({shouldSignOut: true, shouldSetNVP: false}); + closeReactNativeApp({shouldSignOut: true, shouldSetNVP: false}); }} > {translate('deeplinkWrapper.signIn')} diff --git a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx index 1a311b20cb84b..7c59ef5b3a109 100644 --- a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx +++ b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx @@ -1,9 +1,7 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; -import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; import Button from '@components/Button'; -import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import FixedFooter from '@components/FixedFooter'; import FormHelpMessage from '@components/FormHelpMessage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -25,6 +23,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import closeReactNativeApp from '@libs/actions/HybridApp'; import {openOldDotLink} from '@libs/actions/Link'; import {createWorkspace, generatePolicyID} from '@libs/actions/Policy/Policy'; import {completeOnboarding} from '@libs/actions/Report'; @@ -102,7 +101,6 @@ function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccount const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {onboardingMessages} = useOnboardingMessages(); - const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); // We need to use isSmallScreenWidth, see navigateAfterOnboarding function comment // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -139,14 +137,13 @@ function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccount } if (CONFIG.IS_HYBRID_APP) { - HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); - setRootStatusBarEnabled(false); + closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); return; } waitForIdle().then(() => { openOldDotLink(CONST.OLDDOT_URLS.INBOX, true); }); - }, [isLoading, prevIsLoading, setRootStatusBarEnabled]); + }, [isLoading, prevIsLoading]); const accountingOptions: OnboardingListItem[] = useMemo(() => { const createAccountingOption = (integration: Integration): OnboardingListItem => ({ diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index 913aa3cced9c5..7e94548784f5b 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -1,13 +1,11 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; import {useIsFocused} from '@react-navigation/native'; import {Str} from 'expensify-common'; import type {ImageContentFit} from 'expo-image'; import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; -import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; import type {PopoverMenuItem} from '@components/PopoverMenu'; @@ -45,6 +43,7 @@ import {getQuickActionIcon, getQuickActionTitle, isQuickActionAllowed} from '@li import {generateReportID, getDisplayNameForParticipant, getIcons, getReportName, getWorkspaceChats, isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import variables from '@styles/variables'; +import closeReactNativeApp from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -135,8 +134,6 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT const viewTourReportID = introSelected?.viewTour; const [viewTourReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${viewTourReportID}`, {canBeMissing: true}); - const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); - const groupPoliciesWithChatEnabled = getGroupPaidPoliciesWithExpenseChatEnabled(); /** @@ -578,8 +575,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT onConfirm={() => { setModalVisible(false); if (CONFIG.IS_HYBRID_APP) { - HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); - setRootStatusBarEnabled(false); + closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); return; } openOldDotLink(CONST.OLDDOT_URLS.INBOX); diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 845faf9b0bb32..42f29d95588d8 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,4 +1,3 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app/src'; import {findFocusedRoute, useNavigationState, useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports @@ -8,7 +7,6 @@ import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; import AccountSwitcherSkeletonView from '@components/AccountSwitcherSkeletonView'; import ConfirmModal from '@components/ConfirmModal'; -import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; @@ -41,6 +39,7 @@ import type SETTINGS_TO_RHP from '@navigation/linkingConfig/RELATIONS/SETTINGS_T import {showContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import {confirmReadyToOpenApp} from '@userActions/App'; +import closeReactNativeApp from '@userActions/HybridApp'; import {openExternalLink} from '@userActions/Link'; import {hasPaymentMethodError} from '@userActions/PaymentMethods'; import {isSupportAuthToken, signOutAndRedirectToSignIn} from '@userActions/Session'; @@ -102,7 +101,6 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const {translate} = useLocalize(); const focusedRouteName = useNavigationState((state) => findFocusedRoute(state)?.name); const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? ''; - const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const isScreenFocused = useIsSidebarRouteActive(NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, shouldUseNarrowLayout); const hasActivatedWallet = ([CONST.WALLET.TIER_NAME.GOLD, CONST.WALLET.TIER_NAME.PLATINUM] as string[]).includes(userWallet?.tierName ?? ''); @@ -248,10 +246,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr icon: Expensicons.ExpensifyLogoNew, ...(CONFIG.IS_HYBRID_APP ? { - action: () => { - HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); - setRootStatusBarEnabled(false); - }, + action: () => closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}), } : { action() { @@ -292,7 +287,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr }, ], }; - }, [styles.pt4, setRootStatusBarEnabled, shouldOpenSurveyReasonPage, signOut]); + }, [styles.pt4, shouldOpenSurveyReasonPage, signOut]); /** * Return JSX.Element with menu items diff --git a/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx b/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx index c3f9b0453680b..943382f82eb25 100644 --- a/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx +++ b/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx @@ -1,11 +1,9 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; import {useRoute} from '@react-navigation/native'; -import React, {useContext, useEffect, useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; import {InteractionManager} from 'react-native'; import type {ValueOf} from 'type-fest'; import ConfirmationPage from '@components/ConfirmationPage'; import type {ConfirmationPageProps} from '@components/ConfirmationPage'; -import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import LottieAnimations from '@components/LottieAnimations'; @@ -18,6 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import closeReactNativeApp from '@userActions/HybridApp'; import {openOldDotLink} from '@userActions/Link'; import {navigateToConciergeChat} from '@userActions/Report'; import CONFIG from '@src/CONFIG'; @@ -29,7 +28,6 @@ import SCREENS from '@src/SCREENS'; function MergeResultPage() { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const [userEmailOrPhone] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email, canBeMissing: true}); const {params} = useRoute>(); const {result, login} = params; @@ -153,8 +151,7 @@ function MergeResultPage() { secondaryButtonText: translate('mergeAccountsPage.mergePendingSAML.goToExpensifyClassic'), onSecondaryButtonPress: () => { if (CONFIG.IS_HYBRID_APP) { - HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); - setRootStatusBarEnabled(false); + closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); return; } openOldDotLink(CONST.OLDDOT_URLS.INBOX, false); @@ -234,7 +231,7 @@ function MergeResultPage() { illustration: Illustrations.LockClosedOrange, }, }; - }, [setRootStatusBarEnabled, login, translate, userEmailOrPhone, styles]); + }, [login, translate, userEmailOrPhone, styles]); useEffect(() => { /** diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts new file mode 100644 index 0000000000000..1cdac26938d11 --- /dev/null +++ b/src/types/onyx/HybridApp.ts @@ -0,0 +1,10 @@ +/** State and configuration of a HybridApp */ +type HybridApp = { + /** Specifies if the transition from OldDot was made to display a specific subset of screens in NewDot */ + isSingleNewDotEntry?: boolean; + + /** Indicates if NewDot is being closed */ + closingReactNativeApp?: boolean; +}; + +export default HybridApp; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 5157dc67e06fb..811bbb99e99d7 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -32,6 +32,7 @@ import type ExpensifyCardSettings from './ExpensifyCardSettings'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; import type Fund from './Fund'; +import type HybridApp from './HybridApp'; import type ImportedSpreadsheet from './ImportedSpreadsheet'; import type IntroSelected from './IntroSelected'; import type InvitedEmailsToAccountIDs from './InvitedEmailsToAccountIDs'; @@ -264,4 +265,5 @@ export type { ScheduleCallDraft, ValidateUserAndGetAccessiblePolicies, BillingReceiptDetails, + HybridApp, };