diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 45567a80b716..c07b2da5f654 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -11,7 +11,6 @@ import FocusModeNotification from './components/FocusModeNotification'; import GrowlNotification from './components/GrowlNotification'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import SplashScreenHider from './components/SplashScreenHider'; -import TestToolsModal from './components/TestToolsModal'; import UpdateAppModal from './components/UpdateAppModal'; import CONFIG from './CONFIG'; import CONST from './CONST'; @@ -306,7 +305,6 @@ function Expensify() { /> )} {shouldHideSplash && } - ); } diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index 178943ce056f..f184c5896949 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -19,4 +19,5 @@ export default { SEARCH_FULLSCREEN_NAVIGATOR: 'SearchFullscreenNavigator', SHARE_MODAL_NAVIGATOR: 'ShareModalNavigator', PUBLIC_RIGHT_MODAL_NAVIGATOR: 'PublicRightModalNavigator', + TEST_TOOLS_MODAL_NAVIGATOR: 'TestToolsModalNavigator', } as const; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 220d08606040..2868e30e2451 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1948,6 +1948,8 @@ const ROUTES = { }, MIGRATED_USER_WELCOME_MODAL: 'onboarding/migrated-user-welcome', + TEST_TOOLS_MODAL: 'test-tools', + TRANSACTION_RECEIPT: { route: 'r/:reportID/transaction/:transactionID/receipt/:action?/:iouType?', getRoute: (reportID: string | undefined, transactionID: string | undefined, readonly = false, isFromReviewDuplicates = false, action?: IOUAction, iouType?: IOUType) => { diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 504acb4e9e29..928b72c35274 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -671,6 +671,10 @@ const SCREENS = { ROOT: 'TestDrive_Demo_Root', }, + TEST_TOOLS_MODAL: { + ROOT: 'TestToolsModal_Root', + }, + I_KNOW_A_TEACHER: 'I_Know_A_Teacher', INTRO_SCHOOL_PRINCIPAL: 'Intro_School_Principal', I_AM_A_TEACHER: 'I_Am_A_Teacher', diff --git a/src/components/Search/SearchRouter/SearchRouterContext.tsx b/src/components/Search/SearchRouter/SearchRouterContext.tsx index fa60646ec3ee..b7dff8a58a4c 100644 --- a/src/components/Search/SearchRouter/SearchRouterContext.tsx +++ b/src/components/Search/SearchRouter/SearchRouterContext.tsx @@ -1,4 +1,4 @@ -import React, {useContext, useMemo, useRef, useState} from 'react'; +import React, {useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute'; import {navigationRef} from '@libs/Navigation/Navigation'; @@ -16,6 +16,10 @@ type SearchRouterContext = { unregisterSearchPageInput: () => void; }; +type HistoryState = { + isSearchModalOpen?: boolean; +}; + const defaultSearchContext: SearchRouterContext = { isSearchRouterDisplayed: false, openSearchRouter: () => {}, @@ -27,17 +31,43 @@ const defaultSearchContext: SearchRouterContext = { const Context = React.createContext(defaultSearchContext); +const supportsHistoryAPI = typeof window !== 'undefined' && typeof window.history !== 'undefined'; +const canListenPopState = typeof window !== 'undefined' && typeof window.addEventListener === 'function'; + function SearchRouterContextProvider({children}: ChildrenProps) { const [isSearchRouterDisplayed, setIsSearchRouterDisplayed] = useState(false); const searchRouterDisplayedRef = useRef(false); const searchPageInputRef = useRef(undefined); + useEffect(() => { + if (!canListenPopState) { + return; + } + + const handlePopState = (event: PopStateEvent) => { + const state = event.state as HistoryState | null; + if (state?.isSearchModalOpen) { + setIsSearchRouterDisplayed(true); + searchRouterDisplayedRef.current = true; + } else { + setIsSearchRouterDisplayed(false); + searchRouterDisplayedRef.current = false; + } + }; + + window.addEventListener('popstate', handlePopState); + return () => window.removeEventListener('popstate', handlePopState); + }, []); + const routerContext = useMemo(() => { const openSearchRouter = () => { close( () => { setIsSearchRouterDisplayed(true); searchRouterDisplayedRef.current = true; + if (supportsHistoryAPI) { + window.history.pushState({isSearchModalOpen: true} satisfies HistoryState, ''); + } }, false, true, @@ -46,6 +76,12 @@ function SearchRouterContextProvider({children}: ChildrenProps) { const closeSearchRouter = () => { setIsSearchRouterDisplayed(false); searchRouterDisplayedRef.current = false; + if (supportsHistoryAPI) { + const state = window.history.state as HistoryState | null; + if (state?.isSearchModalOpen) { + window.history.back(); + } + } }; // There are callbacks that live outside of React render-loop and interact with SearchRouter diff --git a/src/components/Search/SearchRouter/SearchRouterModal.tsx b/src/components/Search/SearchRouter/SearchRouterModal.tsx index a14513c6731f..68f2edd754e0 100644 --- a/src/components/Search/SearchRouter/SearchRouterModal.tsx +++ b/src/components/Search/SearchRouter/SearchRouterModal.tsx @@ -6,7 +6,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import useViewportOffsetTop from '@hooks/useViewportOffsetTop'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {isMobileChrome, isMobileIOS} from '@libs/Browser'; +import {isMobileIOS} from '@libs/Browser'; import CONST from '@src/CONST'; import SearchRouter from './SearchRouter'; import {useSearchRouterContext} from './SearchRouterContext'; @@ -34,7 +34,6 @@ function SearchRouterModal() { fullscreen propagateSwipe swipeDirection={shouldUseNarrowLayout ? CONST.SWIPE_DIRECTION.RIGHT : undefined} - shouldHandleNavigationBack={isMobileChrome()} onClose={closeSearchRouter} onModalHide={() => setShouldHideInputCaret(isMobileWebIOS)} onModalShow={() => setShouldHideInputCaret(false)} diff --git a/src/components/TestToolsModal.tsx b/src/components/TestToolsModal.tsx deleted file mode 100644 index 43743dc61014..000000000000 --- a/src/components/TestToolsModal.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import {useOnyx} from 'react-native-onyx'; -import useIsAuthenticated from '@hooks/useIsAuthenticated'; -import useLocalize from '@hooks/useLocalize'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import Navigation from '@navigation/Navigation'; -import toggleTestToolsModal, {shouldShowProfileTool} from '@userActions/TestTool'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import Button from './Button'; -import ClientSideLoggingToolMenu from './ClientSideLoggingToolMenu'; -import Modal from './Modal'; -import ProfilingToolMenu from './ProfilingToolMenu'; -import ScrollView from './ScrollView'; -import TestToolMenu from './TestToolMenu'; -import TestToolRow from './TestToolRow'; -import Text from './Text'; - -function getRouteBasedOnAuthStatus(isAuthenticated: boolean, activeRoute: string) { - return isAuthenticated ? ROUTES.SETTINGS_CONSOLE.getRoute(activeRoute) : ROUTES.PUBLIC_CONSOLE_DEBUG.getRoute(activeRoute); -} - -const modalContentMaxHeightPercentage = 0.75; - -function TestToolsModal() { - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const [isTestToolsModalOpen = false] = useOnyx(ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN); - const [shouldStoreLogs = false] = useOnyx(ONYXKEYS.SHOULD_STORE_LOGS); - const {windowWidth, windowHeight} = useWindowDimensions(); - const StyleUtils = useStyleUtils(); - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const activeRoute = Navigation.getActiveRoute(); - const isAuthenticated = useIsAuthenticated(); - const route = getRouteBasedOnAuthStatus(isAuthenticated, activeRoute); - - if (!isTestToolsModalOpen) { - return null; - } - - return ( - - - - {translate('initialSettingsPage.troubleshoot.releaseOptions')} - - {shouldShowProfileTool() && } - - {!!shouldStoreLogs && ( - -