From fc8cb33016ebd30f1dbac289bf8bd30be327bed8 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Thu, 9 Jan 2025 15:20:11 +0100 Subject: [PATCH 01/11] Prevent app from crash when putting app in background from ND --- .../setupCustomAndroidBackHandler/index.android.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts index d31c3693d495e..bdea8c1574256 100644 --- a/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts +++ b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts @@ -1,5 +1,5 @@ import {findFocusedRoute, StackActions} from '@react-navigation/native'; -import {BackHandler, NativeModules} from 'react-native'; +import {BackHandler} from 'react-native'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import getTopmostCentralPaneRoute from '@navigation/getTopmostCentralPaneRoute'; import navigationRef from '@navigation/navigationRef'; @@ -22,12 +22,6 @@ function setupCustomAndroidBackHandler() { return false; } - const isLastScreenOnStack = bottomTabRoutes.length === 1 && rootState?.routes?.length === 1; - - if (NativeModules.HybridAppModule && isLastScreenOnStack) { - NativeModules.HybridAppModule.exitApp(); - } - // Handle back press on the search page. // We need to pop two screens, from the central pane and from the bottom tab. if (bottomTabRoutes[bottomTabRoutes.length - 1].name === SCREENS.SEARCH.BOTTOM_TAB && focusedRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { From cef80e40b307e6dfc86af78949a6741bc48fe44f Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 10 Jan 2025 14:26:37 +0100 Subject: [PATCH 02/11] remove pendingIntent functionality & clean exitApp method --- src/types/modules/react-native.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index c72d4bf2a653b..4804d7e713418 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -9,7 +9,6 @@ type HybridAppModule = { closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void; completeOnboarding: (status: boolean) => void; switchAccount: (newDotCurrentAccount: string) => void; - exitApp: () => void; }; type RNTextInputResetModule = { From 7f16473295f4ec7ddce147cf8bc5ca08c77e93bd Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Tue, 14 Jan 2025 12:37:38 +0100 Subject: [PATCH 03/11] Do not duplicate isNavigationReady call --- .../subscribePushNotification/index.ts | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts index 237a615b570ab..59f1384c0ce99 100644 --- a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts +++ b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts @@ -116,37 +116,35 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; const reportBelongsToWorkspace = policyID && !isEmptyObject(report) && doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID); - Navigation.isNavigationReady() - .then(Navigation.waitForProtectedRoutes) - .then(() => { - // The attachment modal remains open when navigating to the report so we need to close it - Modal.close(() => { - try { - // Get rid of the transition screen, if it is on the top of the stack - if (NativeModules.HybridAppModule && Navigation.getActiveRoute().includes(ROUTES.TRANSITION_BETWEEN_APPS)) { - Navigation.goBack(); - } - // If a chat is visible other than the one we are trying to navigate to, then we need to navigate back - if (Navigation.getActiveRoute().slice(1, 2) === ROUTES.REPORT && !Navigation.isActiveRoute(`r/${reportID}`)) { - Navigation.goBack(); - } - - Log.info('[PushNotification] onSelected() - Navigation is ready. Navigating...', false, {reportID, reportActionID}); - if (!reportBelongsToWorkspace) { - Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); - } - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); - updateLastVisitedPath(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); - } catch (error) { - let errorMessage = String(error); - if (error instanceof Error) { - errorMessage = error.message; - } - - Log.alert('[PushNotification] onSelected() - failed', {reportID, reportActionID, error: errorMessage}); + Navigation.waitForProtectedRoutes().then(() => { + // The attachment modal remains open when navigating to the report so we need to close it + Modal.close(() => { + try { + // Get rid of the transition screen, if it is on the top of the stack + if (NativeModules.HybridAppModule && Navigation.getActiveRoute().includes(ROUTES.TRANSITION_BETWEEN_APPS)) { + Navigation.goBack(); } - }); + // If a chat is visible other than the one we are trying to navigate to, then we need to navigate back + if (Navigation.getActiveRoute().slice(1, 2) === ROUTES.REPORT && !Navigation.isActiveRoute(`r/${reportID}`)) { + Navigation.goBack(); + } + + Log.info('[PushNotification] onSelected() - Navigation is ready. Navigating...', false, {reportID, reportActionID}); + if (!reportBelongsToWorkspace) { + Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); + } + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); + updateLastVisitedPath(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); + } catch (error) { + let errorMessage = String(error); + if (error instanceof Error) { + errorMessage = error.message; + } + + Log.alert('[PushNotification] onSelected() - failed', {reportID, reportActionID, error: errorMessage}); + } }); + }); return Promise.resolve(); } From e92ae2994e502b0b83d5e1b5b57312df193417f2 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 20 Jan 2025 15:39:52 +0100 Subject: [PATCH 04/11] fix crashes (util they're fixed on main) --- .../ReactNativeBackgroundTaskModule.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt index 395f822645b3d..c492a258b20c2 100644 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -13,6 +13,7 @@ import android.content.Intent import android.content.IntentFilter import android.os.PersistableBundle import android.util.Log +import androidx.core.content.ContextCompat class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : ReactNativeBackgroundTaskSpec(context) { @@ -27,7 +28,7 @@ class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplica init { val filter = IntentFilter("com.expensify.reactnativebackgroundtask.TASK_ACTION") - reactApplicationContext.registerReceiver(taskReceiver, filter) + reactApplicationContext.registerReceiver(taskReceiver, filter, ContextCompat.RECEIVER_EXPORTED) } override fun getName(): String { From c739b5ab4e867c79255fbf2b8c1e277ffdb13988 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 21 Jan 2025 18:12:03 +0100 Subject: [PATCH 05/11] fix status bar color when returning to OD --- src/pages/settings/InitialSettingsPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 13d0d1d74802b..e5b269a7be6e7 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -8,6 +8,7 @@ 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 DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -100,6 +101,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? ''; const [allConnectionSyncProgresses] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}`); const {setInitialURL} = useContext(InitialURLContext); + const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION); const subscriptionPlan = useSubscriptionPlan(); @@ -243,6 +245,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr action: () => { NativeModules.HybridAppModule.closeReactNativeApp(false, true); setInitialURL(undefined); + setRootStatusBarEnabled(false); }, } : { @@ -285,7 +288,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr }, ], }; - }, [styles.pt4, signOut, setInitialURL, shouldOpenBookACall, isActingAsDelegate]); + }, [styles.pt4, setInitialURL, setRootStatusBarEnabled, isActingAsDelegate, shouldOpenBookACall, signOut]); /** * Retuns JSX.Element with menu items From 4a19dcde3a39114c4f6769e8fef2bfd9cda5a68c Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 22 Jan 2025 15:58:55 +0100 Subject: [PATCH 06/11] don't show bootsplash when app in background --- src/App.tsx | 5 +++-- src/Expensify.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 40028a10a2dad..e464d0f284582 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,6 +45,7 @@ import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { /** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */ url?: Route; + withoutBootsplash?: boolean; }; LogBox.ignoreLogs([ @@ -60,7 +61,7 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; -function App({url}: AppProps) { +function App({url, withoutBootsplash}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); @@ -103,7 +104,7 @@ function App({url}: AppProps) { - + diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 1d0100add00f5..0e9643aa6f3bc 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -76,7 +76,7 @@ type ExpensifyProps = { /** Last visited path in the app */ lastVisitedPath: OnyxEntry; }; -function Expensify() { +function Expensify({withoutBootsplash = false}) { const appStateChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); const [isOnyxMigrated, setIsOnyxMigrated] = useState(false); @@ -294,7 +294,7 @@ function Expensify() { shouldShowRequire2FAModal={shouldShowRequire2FAModal} /> )} - {shouldHideSplash && } + {shouldHideSplash && !withoutBootsplash && } ); } From ced4cb32b8b4f7e53de0f5113882d571f940d846 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 22 Jan 2025 16:19:40 +0100 Subject: [PATCH 07/11] rename new prop --- src/App.tsx | 10 ++++++---- src/Expensify.tsx | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e464d0f284582..4ee30a6a29520 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -42,10 +42,12 @@ import type {Route} from './ROUTES'; import './setup/backgroundTask'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; +/** Values passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */ type AppProps = { - /** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */ + /** URL containing all necessary data to run React Native app (e.g. login data) */ url?: Route; - withoutBootsplash?: boolean; + /** Specifies if the SplashScreenHider should be mounted */ + withBootsplash?: boolean; }; LogBox.ignoreLogs([ @@ -61,7 +63,7 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; -function App({url, withoutBootsplash}: AppProps) { +function App({url, withBootsplash}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); @@ -104,7 +106,7 @@ function App({url, withoutBootsplash}: AppProps) { - + diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 0e9643aa6f3bc..cfca5c93c0efd 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -76,7 +76,7 @@ type ExpensifyProps = { /** Last visited path in the app */ lastVisitedPath: OnyxEntry; }; -function Expensify({withoutBootsplash = false}) { +function Expensify({withBootsplash = true}) { const appStateChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); const [isOnyxMigrated, setIsOnyxMigrated] = useState(false); @@ -294,7 +294,7 @@ function Expensify({withoutBootsplash = false}) { shouldShowRequire2FAModal={shouldShowRequire2FAModal} /> )} - {shouldHideSplash && !withoutBootsplash && } + {shouldHideSplash && withBootsplash && } ); } From 640a6506b99258a6e889a7478daf30b3d9575266 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 23 Jan 2025 12:51:42 +0100 Subject: [PATCH 08/11] revert bootsplash changes --- src/App.tsx | 6 ++---- src/Expensify.tsx | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 20f54c8a5ad76..4e951f695b0a1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -47,8 +47,6 @@ import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { /** URL containing all necessary data to run React Native app (e.g. login data) */ url?: Route; - /** Specifies if the SplashScreenHider should be mounted */ - withBootsplash?: boolean; }; LogBox.ignoreLogs([ @@ -64,7 +62,7 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; -function App({url, withBootsplash}: AppProps) { +function App({url}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); @@ -108,7 +106,7 @@ function App({url, withBootsplash}: AppProps) { - + diff --git a/src/Expensify.tsx b/src/Expensify.tsx index cfca5c93c0efd..1d0100add00f5 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -76,7 +76,7 @@ type ExpensifyProps = { /** Last visited path in the app */ lastVisitedPath: OnyxEntry; }; -function Expensify({withBootsplash = true}) { +function Expensify() { const appStateChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); const [isOnyxMigrated, setIsOnyxMigrated] = useState(false); @@ -294,7 +294,7 @@ function Expensify({withBootsplash = true}) { shouldShowRequire2FAModal={shouldShowRequire2FAModal} /> )} - {shouldHideSplash && withBootsplash && } + {shouldHideSplash && } ); } From 447038a87ac5898eda1285c864e0e38bce7e57e2 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 27 Jan 2025 17:05:18 +0100 Subject: [PATCH 09/11] fix status bar on travel page --- src/components/ScreenWrapper.tsx | 3 +++ src/libs/TripReservationUtils.ts | 8 +++++++- src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx | 5 ++++- src/pages/Search/EmptySearchView.tsx | 6 ++++-- src/pages/Travel/ManageTrips.tsx | 6 ++++-- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index b22b4eac3fc6e..3bf1990572057 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -5,6 +5,7 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {Keyboard, NativeModules, PanResponder, View} from 'react-native'; import {PickerAvoidingView} from 'react-native-picker-select'; import type {EdgeInsets} from 'react-native-safe-area-context'; +import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import useEnvironment from '@hooks/useEnvironment'; import useInitialDimensions from '@hooks/useInitialWindowDimensions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -152,6 +153,7 @@ function ScreenWrapper( const {windowHeight} = useWindowDimensions(shouldUseCachedViewportHeight); // since Modals are drawn in separate native view hierarchy we should always add paddings const ignoreInsetsConsumption = !useContext(ModalContext).default; + const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout for a case where we want to show the offline indicator only on small screens // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -171,6 +173,7 @@ function ScreenWrapper( UNSTABLE_usePreventRemove(shouldReturnToOldDot, () => { NativeModules.HybridAppModule?.closeReactNativeApp(false, false); + setRootStatusBarEnabled(false); }); const panResponder = useRef( diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index 6d376620deef3..66bc5a344f9bf 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -95,7 +95,12 @@ function getTripEReceiptIcon(transaction?: Transaction): IconAsset | undefined { } } -function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessage: Dispatch>, ctaErrorMessage = ''): void { +function bookATrip( + translate: LocaleContextProps['translate'], + setCtaErrorMessage: Dispatch>, + setRootStatusBarEnabled: (isEnabled: boolean) => void, + ctaErrorMessage = '', +): void { if (Str.isSMSLogin(primaryLogin)) { setCtaErrorMessage(translate('travel.phoneError')); return; @@ -120,6 +125,7 @@ function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessag Log.info('[HybridApp] Returning to OldDot after opening TravelDot'); NativeModules.HybridAppModule.closeReactNativeApp(false, false); + setRootStatusBarEnabled(false); }) ?.catch(() => { setCtaErrorMessage(translate('travel.errorMessage')); diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index 12722e87f05a0..56cf4041c9c34 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -1,7 +1,8 @@ -import React, {useMemo, useState} from 'react'; +import React, {useContext, useMemo, useState} from 'react'; import {NativeModules} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; +import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import FormHelpMessage from '@components/FormHelpMessage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -34,6 +35,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const paidGroupPolicy = Object.values(allPolicies ?? {}).find(PolicyUtils.isPaidGroupPolicy); @@ -106,6 +108,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE } NativeModules.HybridAppModule.closeReactNativeApp(false, true); + setRootStatusBarEnabled(false); }} pressOnEnter /> diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index 7d9550c5ec064..cfa5161adf82e 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -1,8 +1,9 @@ -import React, {useMemo, useState} from 'react'; +import React, {useContext, useMemo, useState} from 'react'; import {Linking, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; +import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; import EmptyStateComponent from '@components/EmptyStateComponent'; import type {FeatureListItem} from '@components/FeatureList'; @@ -60,6 +61,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { const shouldRedirectToExpensifyClassic = useMemo(() => { return PolicyUtils.areAllGroupPoliciesExpenseChatDisabled((allPolicies as OnyxCollection) ?? {}); }, [allPolicies]); + const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const [ctaErrorMessage, setCtaErrorMessage] = useState(''); @@ -133,7 +135,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { buttons: [ { buttonText: translate('search.searchResults.emptyTripResults.buttonText'), - buttonAction: () => TripsResevationUtils.bookATrip(translate, setCtaErrorMessage, ctaErrorMessage), + buttonAction: () => TripsResevationUtils.bookATrip(translate, setCtaErrorMessage, setRootStatusBarEnabled, ctaErrorMessage), success: true, }, ], diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index 9a83468feb14b..925215fd8ebf3 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -1,6 +1,7 @@ -import React, {useState} from 'react'; +import React, {useContext, useState} from 'react'; import {Linking, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import type {FeatureListItem} from '@components/FeatureList'; import FeatureList from '@components/FeatureList'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; @@ -34,6 +35,7 @@ function ManageTrips() { const {translate} = useLocalize(); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const policy = usePolicy(activePolicyID); + const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); const [ctaErrorMessage, setCtaErrorMessage] = useState(''); @@ -55,7 +57,7 @@ function ManageTrips() { ctaText={translate('travel.bookTravel')} ctaAccessibilityLabel={translate('travel.bookTravel')} onCtaPress={() => { - TripsResevationUtils.bookATrip(translate, setCtaErrorMessage, ctaErrorMessage); + TripsResevationUtils.bookATrip(translate, setCtaErrorMessage, setRootStatusBarEnabled, ctaErrorMessage); }} ctaErrorMessage={ctaErrorMessage} illustration={LottieAnimations.TripsEmptyState} From f2525bf5f66570142d915df369d3c0110fa12dd2 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 27 Jan 2025 17:41:37 +0100 Subject: [PATCH 10/11] fix lint --- src/components/ScreenWrapper.tsx | 2 +- .../BaseOnboardingEmployees.tsx | 20 +++---- src/pages/Search/EmptySearchView.tsx | 55 ++++++++++--------- src/pages/Travel/ManageTrips.tsx | 4 +- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 3bf1990572057..638ef0737ed5a 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -5,7 +5,6 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {Keyboard, NativeModules, PanResponder, View} from 'react-native'; import {PickerAvoidingView} from 'react-native-picker-select'; import type {EdgeInsets} from 'react-native-safe-area-context'; -import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import useEnvironment from '@hooks/useEnvironment'; import useInitialDimensions from '@hooks/useInitialWindowDimensions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -20,6 +19,7 @@ import addViewportResizeListener from '@libs/VisualViewport'; import toggleTestToolsModal from '@userActions/TestTool'; import CONST from '@src/CONST'; import CustomDevMenu from './CustomDevMenu'; +import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext'; import FocusTrapForScreens from './FocusTrap/FocusTrapForScreen'; import type FocusTrapForScreenProps from './FocusTrap/FocusTrapForScreen/FocusTrapProps'; import HeaderGap from './HeaderGap'; diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index 56cf4041c9c34..2d951474f4593 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -13,11 +13,11 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import {completeOnboarding} from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as Policy from '@userActions/Policy/Policy'; -import * as Report from '@userActions/Report'; -import * as Welcome from '@userActions/Welcome'; +import {isPaidGroupPolicy} from '@libs/PolicyUtils'; +import {createWorkspace, generatePolicyID} from '@userActions/Policy/Policy'; +import {setOnboardingAdminsChatReportID, setOnboardingCompanySize, setOnboardingPolicyID} from '@userActions/Welcome'; import CONST from '@src/CONST'; import type {OnboardingCompanySize} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -37,7 +37,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); - const paidGroupPolicy = Object.values(allPolicies ?? {}).find(PolicyUtils.isPaidGroupPolicy); + const paidGroupPolicy = Object.values(allPolicies ?? {}).find(isPaidGroupPolicy); const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout(); const [selectedCompanySize, setSelectedCompanySize] = useState(onboardingCompanySize); @@ -71,19 +71,19 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE setError(translate('onboarding.errorSelection')); return; } - Welcome.setOnboardingCompanySize(selectedCompanySize); + setOnboardingCompanySize(selectedCompanySize); const shouldCreateWorkspace = !onboardingPolicyID && !paidGroupPolicy; // We need `adminsChatReportID` for `Report.completeOnboarding`, but at the same time, we don't want to call `Policy.createWorkspace` more than once. // If we have already created a workspace, we want to reuse the `onboardingAdminsChatReportID` and `onboardingPolicyID`. const {adminsChatReportID, policyID} = shouldCreateWorkspace - ? Policy.createWorkspace(undefined, true, '', Policy.generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM) + ? createWorkspace(undefined, true, '', generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM) : {adminsChatReportID: onboardingAdminsChatReportID, policyID: onboardingPolicyID}; if (shouldCreateWorkspace) { - Welcome.setOnboardingAdminsChatReportID(adminsChatReportID); - Welcome.setOnboardingPolicyID(policyID); + setOnboardingAdminsChatReportID(adminsChatReportID); + setOnboardingPolicyID(policyID); } // For MICRO companies (1-10 employees), we want to remain on NewDot. @@ -95,7 +95,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE // For other company sizes we want to complete onboarding here. // At this point `onboardingPurposeSelected` should always exist as we set it in `BaseOnboardingPurpose`. if (onboardingPurposeSelected) { - Report.completeOnboarding( + completeOnboarding( onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], undefined, diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index cfa5161adf82e..1bced846bcd4b 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -21,15 +21,15 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import {hasSeenTourSelector} from '@libs/onboardingSelectors'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {areAllGroupPoliciesExpenseChatDisabled} from '@libs/PolicyUtils'; +import {generateReportID} from '@libs/ReportUtils'; import {getNavatticURL} from '@libs/TourUtils'; -import * as TripsResevationUtils from '@libs/TripReservationUtils'; +import {bookATrip} from '@libs/TripReservationUtils'; import variables from '@styles/variables'; -import * as IOU from '@userActions/IOU'; -import * as Link from '@userActions/Link'; -import * as Task from '@userActions/Task'; -import * as Welcome from '@userActions/Welcome'; +import {startMoneyRequest} from '@userActions/IOU'; +import {openExternalLink, openOldDotLink} from '@userActions/Link'; +import {canActionTask, canModifyTask, completeTask} from '@userActions/Task'; +import {setSelfTourViewed} from '@userActions/Welcome'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -59,7 +59,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { const [modalVisible, setModalVisible] = useState(false); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const shouldRedirectToExpensifyClassic = useMemo(() => { - return PolicyUtils.areAllGroupPoliciesExpenseChatDisabled((allPolicies as OnyxCollection) ?? {}); + return areAllGroupPoliciesExpenseChatDisabled((allPolicies as OnyxCollection) ?? {}); }, [allPolicies]); const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); @@ -120,8 +120,8 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { const viewTourTaskReportID = introSelected?.viewTour; const [viewTourTaskReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${viewTourTaskReportID}`); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const canModifyTask = Task.canModifyTask(viewTourTaskReport, currentUserPersonalDetails.accountID); - const canActionTask = Task.canActionTask(viewTourTaskReport, currentUserPersonalDetails.accountID); + const isTaskModifiable = canModifyTask(viewTourTaskReport, currentUserPersonalDetails.accountID); + const isTaskActionable = canActionTask(viewTourTaskReport, currentUserPersonalDetails.accountID); const content = useMemo(() => { switch (type) { @@ -135,7 +135,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { buttons: [ { buttonText: translate('search.searchResults.emptyTripResults.buttonText'), - buttonAction: () => TripsResevationUtils.bookATrip(translate, setCtaErrorMessage, setRootStatusBarEnabled, ctaErrorMessage), + buttonAction: () => bookATrip(translate, setCtaErrorMessage, setRootStatusBarEnabled, ctaErrorMessage), success: true, }, ], @@ -153,10 +153,10 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { { buttonText: translate('emptySearchView.takeATour'), buttonAction: () => { - Link.openExternalLink(navatticURL); - Welcome.setSelfTourViewed(); - if (viewTourTaskReport && canModifyTask && canActionTask) { - Task.completeTask(viewTourTaskReport); + openExternalLink(navatticURL); + setSelfTourViewed(); + if (viewTourTaskReport && isTaskModifiable && isTaskActionable) { + completeTask(viewTourTaskReport); } }, }, @@ -170,7 +170,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { setModalVisible(true); return; } - IOU.startMoneyRequest(CONST.IOU.TYPE.CREATE, ReportUtils.generateReportID()); + startMoneyRequest(CONST.IOU.TYPE.CREATE, generateReportID()); }), success: true, }, @@ -193,10 +193,10 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { { buttonText: translate('emptySearchView.takeATour'), buttonAction: () => { - Link.openExternalLink(navatticURL); - Welcome.setSelfTourViewed(); - if (viewTourTaskReport && canModifyTask && canActionTask) { - Task.completeTask(viewTourTaskReport); + openExternalLink(navatticURL); + setSelfTourViewed(); + if (viewTourTaskReport && isTaskModifiable && isTaskActionable) { + completeTask(viewTourTaskReport); } }, }, @@ -210,7 +210,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { setModalVisible(true); return; } - IOU.startMoneyRequest(CONST.IOU.TYPE.INVOICE, ReportUtils.generateReportID()); + startMoneyRequest(CONST.IOU.TYPE.INVOICE, generateReportID()); }), success: true, }, @@ -239,14 +239,15 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { styles.textAlignLeft, styles.emptyStateFolderWebStyles, subtitleComponent, - hasSeenTour, + hasResults, + setRootStatusBarEnabled, ctaErrorMessage, + hasSeenTour, navatticURL, - shouldRedirectToExpensifyClassic, - hasResults, viewTourTaskReport, - canModifyTask, - canActionTask, + isTaskModifiable, + isTaskActionable, + shouldRedirectToExpensifyClassic, ]); return ( @@ -269,7 +270,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { isVisible={modalVisible} onConfirm={() => { setModalVisible(false); - Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); + openOldDotLink(CONST.OLDDOT_URLS.INBOX); }} onCancel={() => setModalVisible(false)} title={translate('sidebarScreen.redirectToExpensifyClassicModal.title')} diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index 925215fd8ebf3..5b08bd7c90374 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -12,7 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as TripsResevationUtils from '@libs/TripReservationUtils'; +import {bookATrip} from '@libs/TripReservationUtils'; import colors from '@styles/theme/colors'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -57,7 +57,7 @@ function ManageTrips() { ctaText={translate('travel.bookTravel')} ctaAccessibilityLabel={translate('travel.bookTravel')} onCtaPress={() => { - TripsResevationUtils.bookATrip(translate, setCtaErrorMessage, setRootStatusBarEnabled, ctaErrorMessage); + bookATrip(translate, setCtaErrorMessage, setRootStatusBarEnabled, ctaErrorMessage); }} ctaErrorMessage={ctaErrorMessage} illustration={LottieAnimations.TripsEmptyState} From abdb0001f7f5aa3787fcdbdf3977204c39b24248 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 28 Jan 2025 11:21:58 +0100 Subject: [PATCH 11/11] update bookATrip function after merge --- src/libs/actions/Travel.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts index 1886885587c49..2aeb04b60f1b1 100644 --- a/src/libs/actions/Travel.ts +++ b/src/libs/actions/Travel.ts @@ -114,7 +114,12 @@ function provisionDomain(domain: string) { Navigation.navigate(ROUTES.TRAVEL_TCS.getRoute(domain)); } -function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessage: Dispatch>, ctaErrorMessage = ''): void { +function bookATrip( + translate: LocaleContextProps['translate'], + setCtaErrorMessage: Dispatch>, + setRootStatusBarEnabled: (isEnabled: boolean) => void, + ctaErrorMessage = '', +): void { if (!activePolicyID) { return; } @@ -138,6 +143,7 @@ function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessag Log.info('[HybridApp] Returning to OldDot after opening TravelDot'); NativeModules.HybridAppModule.closeReactNativeApp(false, false); + setRootStatusBarEnabled(false); }) ?.catch(() => { setCtaErrorMessage(translate('travel.errorMessage'));