From e322104c71a058c74cd35cb82df9df78703d655e Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Wed, 3 Jul 2024 05:54:49 +0900 Subject: [PATCH 01/13] bugfix: unmount inbox component on navigation away --- src/pages/home/sidebar/SidebarScreen/index.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pages/home/sidebar/SidebarScreen/index.tsx b/src/pages/home/sidebar/SidebarScreen/index.tsx index 625491674cd8..42567cceeb94 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.tsx +++ b/src/pages/home/sidebar/SidebarScreen/index.tsx @@ -1,8 +1,19 @@ import React from 'react'; +import useActiveRoute from '@hooks/useActiveRoute'; import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; import BaseSidebarScreen from './BaseSidebarScreen'; function SidebarScreen() { + /** + * In web useActiveRoute reports tab change immediately. + */ + const route = useActiveRoute(); + /** + * Immediately unmount component on tab change to avoid blocking js thread + */ + if (route?.name !== 'Report' && !!route?.name) { + return null; + } return ( From 40d1378c51aeafed8a5280e81412a3b64d2c99dc Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Wed, 3 Jul 2024 05:55:49 +0900 Subject: [PATCH 02/13] bugfix: useActiveRoute in web bottom tabs --- .../BottomTabBar/index.website.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 90a7a0df056f..1fa08c9fd947 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -7,6 +7,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; +import useActiveRoute from '@hooks/useActiveRoute'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; @@ -69,6 +70,11 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return topmostBottomTabRoute?.name ?? SCREENS.HOME; }); + /** + * Use active route context in web because useNavigationState events aren't being fired immediately on tab change. + */ + const activeRoute = useActiveRoute(); + const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); const navigateToChats = useCallback(() => { @@ -92,7 +98,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps @@ -118,14 +124,14 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps - + From 176471ec862eadfa95400bfcb9979b521a8201e7 Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Wed, 3 Jul 2024 06:40:20 +0900 Subject: [PATCH 03/13] bugfix: check whether route name is defined --- .../BottomTabBar/index.website.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 1fa08c9fd947..871f4b232ebb 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -98,7 +98,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps From 1c05f2d53872403c135475def4d5b59586a3f3dd Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Wed, 3 Jul 2024 16:29:05 +0900 Subject: [PATCH 04/13] bugfix: bottom tab regression --- src/hooks/useActiveBottomTabRoute.ts | 8 ++++ src/hooks/useActiveRoute.ts | 9 ---- .../Navigators/ActiveBottomTabRouteContext.ts | 6 +++ .../Navigators/ActiveRouteContext.ts | 6 --- .../Navigators/BottomTabNavigator.tsx | 28 +++++++++--- .../BottomTabBar/index.website.tsx | 13 +++--- src/libs/Navigation/types.ts | 13 ++++++ src/libs/NavigationUtils.ts | 44 ++++++++++++++++++- src/libs/flattenObject.ts | 24 ++++++++++ src/pages/Search/SearchPageBottomTab.tsx | 4 +- .../home/sidebar/SidebarScreen/index.tsx | 11 ----- src/pages/settings/InitialSettingsPage.tsx | 6 +-- 12 files changed, 127 insertions(+), 45 deletions(-) create mode 100644 src/hooks/useActiveBottomTabRoute.ts delete mode 100644 src/hooks/useActiveRoute.ts create mode 100644 src/libs/Navigation/AppNavigator/Navigators/ActiveBottomTabRouteContext.ts delete mode 100644 src/libs/Navigation/AppNavigator/Navigators/ActiveRouteContext.ts create mode 100644 src/libs/flattenObject.ts diff --git a/src/hooks/useActiveBottomTabRoute.ts b/src/hooks/useActiveBottomTabRoute.ts new file mode 100644 index 000000000000..434cca0cd815 --- /dev/null +++ b/src/hooks/useActiveBottomTabRoute.ts @@ -0,0 +1,8 @@ +import {useContext} from 'react'; +import ActiveBottomTabRouteContext from '@libs/Navigation/AppNavigator/Navigators/ActiveBottomTabRouteContext'; + +function useActiveBottomTabRoute() { + return useContext(ActiveBottomTabRouteContext); +} + +export default useActiveBottomTabRoute; diff --git a/src/hooks/useActiveRoute.ts b/src/hooks/useActiveRoute.ts deleted file mode 100644 index 812e7c634ee8..000000000000 --- a/src/hooks/useActiveRoute.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {useContext} from 'react'; -import ActiveRouteContext from '@libs/Navigation/AppNavigator/Navigators/ActiveRouteContext'; -import type {AuthScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types'; - -function useActiveRoute(): NavigationPartialRoute | undefined { - return useContext(ActiveRouteContext); -} - -export default useActiveRoute; diff --git a/src/libs/Navigation/AppNavigator/Navigators/ActiveBottomTabRouteContext.ts b/src/libs/Navigation/AppNavigator/Navigators/ActiveBottomTabRouteContext.ts new file mode 100644 index 000000000000..ce55da8e4bde --- /dev/null +++ b/src/libs/Navigation/AppNavigator/Navigators/ActiveBottomTabRouteContext.ts @@ -0,0 +1,6 @@ +import React from 'react'; +import type {BottomTabScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types'; + +const ActiveBottomTabRouteContext = React.createContext | undefined>(undefined); + +export default ActiveBottomTabRouteContext; diff --git a/src/libs/Navigation/AppNavigator/Navigators/ActiveRouteContext.ts b/src/libs/Navigation/AppNavigator/Navigators/ActiveRouteContext.ts deleted file mode 100644 index c319aeca3e04..000000000000 --- a/src/libs/Navigation/AppNavigator/Navigators/ActiveRouteContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import type {AuthScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types'; - -const ActiveRouteContext = React.createContext | undefined>(undefined); - -export default ActiveRouteContext; diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx index 46212f3bc41f..82b9e3032c7c 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx @@ -2,13 +2,15 @@ import {useNavigationState} from '@react-navigation/native'; import type {StackNavigationOptions} from '@react-navigation/stack'; import React from 'react'; import createCustomBottomTabNavigator from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator'; +import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import type {BottomTabNavigatorParamList, CentralPaneName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import type {BottomTabNavigatorParamList, BottomTabScreensParamList, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import {isBottomTabName} from '@libs/NavigationUtils'; import SidebarScreen from '@pages/home/sidebar/SidebarScreen'; import SearchPageBottomTab from '@pages/Search/SearchPageBottomTab'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; -import ActiveRouteContext from './ActiveRouteContext'; +import ActiveBottomTabRouteContext from './ActiveBottomTabRouteContext'; const loadInitialSettingsPage = () => require('../../../../pages/settings/InitialSettingsPage').default; const Tab = createCustomBottomTabNavigator(); @@ -19,10 +21,26 @@ const screenOptions: StackNavigationOptions = { }; function BottomTabNavigator() { - const activeRoute = useNavigationState | undefined>(getTopmostCentralPaneRoute); + const activeRoute = useNavigationState | undefined>((state) => { + if (!state) { + return undefined; + } + console.log(state); + let route = state.routes.filter((route) => isBottomTabName(route.name)).at(-1) as NavigationPartialRoute; + if (!route) { + for (let selector of [getTopmostBottomTabRoute, getTopmostCentralPaneRoute]) { + const selectedRoute = selector(state); + if (isBottomTabName(selectedRoute?.name)) { + route = selectedRoute as NavigationPartialRoute; + } + } + } + + return route; + }); return ( - + - + ); } diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 871f4b232ebb..995be1483465 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -7,7 +7,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; -import useActiveRoute from '@hooks/useActiveRoute'; +import useActiveRoute from '@hooks/useActiveBottomTabRoute'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; @@ -18,7 +18,7 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute' import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import {isCentralPaneName} from '@libs/NavigationUtils'; +import {isCentralPaneName, isHomeTabName, isSearchTabName, isSettingTabName} from '@libs/NavigationUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; @@ -74,7 +74,6 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps * Use active route context in web because useNavigationState events aren't being fired immediately on tab change. */ const activeRoute = useActiveRoute(); - const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); const navigateToChats = useCallback(() => { @@ -98,7 +97,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps @@ -111,7 +110,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps { - if (currentTabName === SCREENS.SEARCH.BOTTOM_TAB || currentTabName === SCREENS.SEARCH.CENTRAL_PANE) { + if (isSearchTabName(activeRoute?.name)) { return; } interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL))); @@ -124,14 +123,14 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps - + diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 0a2809d97208..7f9fe8cfcf5b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -78,6 +78,12 @@ type CentralPaneScreensParamList = { [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined; }; +type SearchNavigatorParamList = { + [SCREENS.SEARCH.BOTTOM_TAB]: undefined; + [SCREENS.SEARCH.CENTRAL_PANE]: undefined; + [SCREENS.SEARCH.REPORT_RHP]: undefined; +}; + type SettingsNavigatorParamList = { [SCREENS.SETTINGS.SHARE_CODE]: undefined; [SCREENS.SETTINGS.PROFILE.ROOT]: undefined; @@ -938,6 +944,8 @@ type ExplanationModalNavigatorParamList = { [SCREENS.EXPLANATION_MODAL.ROOT]: undefined; }; +type BottomTabScreensParamList = {[SCREENS.HOME]: undefined; [SCREENS.REPORT]: undefined} & SearchNavigatorParamList & SettingsNavigatorParamList; + type BottomTabNavigatorParamList = { [SCREENS.HOME]: {policyID?: string}; [SCREENS.SEARCH.BOTTOM_TAB]: { @@ -1036,6 +1044,8 @@ type RootStackParamList = PublicScreensParamList & AuthScreensParamList & LeftMo type BottomTabName = keyof BottomTabNavigatorParamList; +type BottomTabScreenName = keyof BottomTabScreensParamList; + type FullScreenName = keyof FullScreenNavigatorParamList; type CentralPaneName = keyof CentralPaneScreensParamList; @@ -1053,6 +1063,8 @@ export type { CentralPaneName, BackToParams, BottomTabName, + BottomTabScreenName, + BottomTabScreensParamList, BottomTabNavigatorParamList, DetailsNavigatorParamList, EditRequestNavigatorParamList, @@ -1083,6 +1095,7 @@ export type { RoomInviteNavigatorParamList, RoomMembersNavigatorParamList, RootStackParamList, + SearchNavigatorParamList, SettingsNavigatorParamList, SignInNavigatorParamList, FeatureTrainingNavigatorParamList, diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index 34fc0b971ef6..8a9e2f9bab8d 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -1,7 +1,8 @@ import cloneDeep from 'lodash/cloneDeep'; import SCREENS from '@src/SCREENS'; +import {flattenObject} from './flattenObject'; import getTopmostBottomTabRoute from './Navigation/getTopmostBottomTabRoute'; -import type {CentralPaneName, RootStackParamList, State} from './Navigation/types'; +import type {BottomTabScreenName, CentralPaneName, RootStackParamList, State} from './Navigation/types'; const CENTRAL_PANE_SCREEN_NAMES = new Set([ SCREENS.SETTINGS.WORKSPACES, @@ -34,4 +35,43 @@ const removePolicyIDParamFromState = (state: State) => { return stateCopy; }; -export {isCentralPaneName, removePolicyIDParamFromState}; +const SETTINGS_SCREENS = Object.values(flattenObject(SCREENS.SETTINGS)); +const SEARCH_SCREENS = Object.values(flattenObject(SCREENS.SEARCH)); +const HOME_SCREENS = [SCREENS.HOME, SCREENS.REPORT]; +const BOTTOM_TAB_SCREEN_NAMES = new Set([...SETTINGS_SCREENS, ...SEARCH_SCREENS, ...HOME_SCREENS]); + +const SETTINGS_TAB_SCREEN_NAMES = new Set(SETTINGS_SCREENS); + +const SEARCH_TAB_SCREEN_NAMES = new Set(SEARCH_SCREENS); + +const HOME_SCREEN_NAMES = new Set(HOME_SCREENS); + +function isBottomTabName(screen: string | undefined): screen is BottomTabScreenName { + if (!screen) { + return false; + } + return BOTTOM_TAB_SCREEN_NAMES.has(screen as any); +} + +function isSettingTabName(screen: string | undefined): screen is any { + if (!screen) { + return false; + } + return SETTINGS_TAB_SCREEN_NAMES.has(screen as any); +} + +function isSearchTabName(screen: string | undefined): screen is (typeof SEARCH_SCREENS)[0] { + if (!screen) { + return false; + } + return SEARCH_TAB_SCREEN_NAMES.has(screen as (typeof SEARCH_SCREENS)[0]); +} + +function isHomeTabName(screen: string | undefined): screen is (typeof HOME_SCREENS)[0] { + if (!screen) { + return false; + } + return HOME_SCREEN_NAMES.has(screen as (typeof HOME_SCREENS)[0]); +} + +export {isCentralPaneName, isBottomTabName, isSearchTabName, isSettingTabName, isHomeTabName, removePolicyIDParamFromState}; diff --git a/src/libs/flattenObject.ts b/src/libs/flattenObject.ts new file mode 100644 index 000000000000..d5195adb91a5 --- /dev/null +++ b/src/libs/flattenObject.ts @@ -0,0 +1,24 @@ +type Flatten = { + [K in keyof T]: T[K] extends object ? Flatten : T[K]; +}; + +export function flattenObject(object: T): Flatten { + const result = {} as any; + + const flattenHelper = (obj: any, prefix = ''): void => { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + const newKey = prefix ? `${prefix}.${key}` : key; + + if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) { + flattenHelper(obj[key], newKey); + } else { + result[newKey] = obj[key]; + } + } + } + }; + + flattenHelper(object); + return result as Flatten; +} diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index eb662fd49046..58a8c54c0c14 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -3,7 +3,7 @@ import React, {useMemo} from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; -import useActiveRoute from '@hooks/useActiveRoute'; +import useActiveBottomTabRoute from '@hooks/useActiveBottomTabRoute'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -27,7 +27,7 @@ const defaultSearchProps = { function SearchPageBottomTab() { const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); - const activeRoute = useActiveRoute(); + const activeRoute = useActiveBottomTabRoute(); const styles = useThemeStyles(); const { diff --git a/src/pages/home/sidebar/SidebarScreen/index.tsx b/src/pages/home/sidebar/SidebarScreen/index.tsx index 42567cceeb94..625491674cd8 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.tsx +++ b/src/pages/home/sidebar/SidebarScreen/index.tsx @@ -1,19 +1,8 @@ import React from 'react'; -import useActiveRoute from '@hooks/useActiveRoute'; import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; import BaseSidebarScreen from './BaseSidebarScreen'; function SidebarScreen() { - /** - * In web useActiveRoute reports tab change immediately. - */ - const route = useActiveRoute(); - /** - * Immediately unmount component on tab change to avoid blocking js thread - */ - if (route?.name !== 'Report' && !!route?.name) { - return null; - } return ( diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 65acd3660f7b..d3b9e28fe619 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,11 +1,11 @@ import {useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; -// eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; import {NativeModules, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +// eslint-disable-next-line no-restricted-imports import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import ConfirmModal from '@components/ConfirmModal'; import CurrentUserPersonalDetailsSkeletonView from '@components/CurrentUserPersonalDetailsSkeletonView'; @@ -20,7 +20,7 @@ import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useActiveRoute from '@hooks/useActiveRoute'; +import useActiveBottomTabRoute from '@hooks/useActiveBottomTabRoute'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useSingleExecution from '@hooks/useSingleExecution'; @@ -105,7 +105,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa const waitForNavigate = useWaitForNavigation(); const popoverAnchor = useRef(null); const {translate, formatPhoneNumber} = useLocalize(); - const activeRoute = useActiveRoute(); + const activeRoute = useActiveBottomTabRoute(); const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? ''; const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); From 5af3b4bf16674b744bb3add0853a34ffde242e3f Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Wed, 3 Jul 2024 16:44:40 +0900 Subject: [PATCH 05/13] bugfix: impliment search tab fix by @luacmartins --- src/components/Search/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 6414501fb06d..1c409a36528f 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -88,7 +88,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [hash, isOffline]); - const isLoadingItems = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined; + const isLoadingItems = !isOffline && searchResults?.data === undefined; const isLoadingMoreItems = !isLoadingItems && searchResults?.search?.isLoading && searchResults?.search?.offset > 0; const shouldShowEmptyState = !isLoadingItems && isEmptyObject(searchResults?.data); From c7e6e358719de16bab5f00fd1942428c3ea2c1cf Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Wed, 3 Jul 2024 17:13:29 +0900 Subject: [PATCH 06/13] improvement: removed unneeded selector --- .../AppNavigator/Navigators/BottomTabNavigator.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx index 82b9e3032c7c..24d8afca10fc 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx @@ -25,15 +25,11 @@ function BottomTabNavigator() { if (!state) { return undefined; } - console.log(state); - let route = state.routes.filter((route) => isBottomTabName(route.name)).at(-1) as NavigationPartialRoute; - - if (!route) { - for (let selector of [getTopmostBottomTabRoute, getTopmostCentralPaneRoute]) { - const selectedRoute = selector(state); - if (isBottomTabName(selectedRoute?.name)) { - route = selectedRoute as NavigationPartialRoute; - } + let route: NavigationPartialRoute | undefined; + for (let selector of [getTopmostBottomTabRoute, getTopmostCentralPaneRoute]) { + const selectedRoute = selector(state); + if (isBottomTabName(selectedRoute?.name)) { + route = selectedRoute as NavigationPartialRoute; } } From 3d6cd890727960c08ec76a87bb886b519271ed51 Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Thu, 4 Jul 2024 06:18:13 +0900 Subject: [PATCH 07/13] chore: remove redudant flatten object function --- src/libs/flattenObject.ts | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 src/libs/flattenObject.ts diff --git a/src/libs/flattenObject.ts b/src/libs/flattenObject.ts deleted file mode 100644 index d5195adb91a5..000000000000 --- a/src/libs/flattenObject.ts +++ /dev/null @@ -1,24 +0,0 @@ -type Flatten = { - [K in keyof T]: T[K] extends object ? Flatten : T[K]; -}; - -export function flattenObject(object: T): Flatten { - const result = {} as any; - - const flattenHelper = (obj: any, prefix = ''): void => { - for (const key in obj) { - if (obj.hasOwnProperty(key)) { - const newKey = prefix ? `${prefix}.${key}` : key; - - if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) { - flattenHelper(obj[key], newKey); - } else { - result[newKey] = obj[key]; - } - } - } - }; - - flattenHelper(object); - return result as Flatten; -} From d88c2fb29d603cd469c7b431af575b6aa5fb32d7 Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Thu, 4 Jul 2024 06:19:21 +0900 Subject: [PATCH 08/13] improvement: fix type errors --- .../BottomTabBar/index.website.tsx | 17 +++++++------ src/libs/NavigationUtils.ts | 24 ++++++++++++------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 995be1483465..b27c5bc9df0d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -3,11 +3,12 @@ import React, {useCallback, useEffect} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import type {TupleToUnion} from 'type-fest'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; -import useActiveRoute from '@hooks/useActiveBottomTabRoute'; +import useActiveBottomTabRoute from '@hooks/useActiveBottomTabRoute'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; @@ -41,6 +42,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const styles = useThemeStyles(); const {translate} = useLocalize(); const navigation = useNavigation(); + const HOME_SCREENS = [SCREENS.HOME, SCREENS.REPORT]; const {activeWorkspaceID: contextActiveWorkspaceID} = useActiveWorkspace(); const activeWorkspaceID = sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID) ?? contextActiveWorkspaceID; @@ -70,10 +72,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return topmostBottomTabRoute?.name ?? SCREENS.HOME; }); - /** - * Use active route context in web because useNavigationState events aren't being fired immediately on tab change. - */ - const activeRoute = useActiveRoute(); + const activeBottomTabRoute = useActiveBottomTabRoute(); const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); const navigateToChats = useCallback(() => { @@ -97,7 +96,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps ) ? theme.iconMenu : theme.icon} width={variables.iconBottomBar} height={variables.iconBottomBar} /> @@ -110,7 +109,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps { - if (isSearchTabName(activeRoute?.name)) { + if (isSearchTabName(activeBottomTabRoute?.name)) { return; } interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL))); @@ -123,14 +122,14 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps - + diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index 8a9e2f9bab8d..66e9434848ac 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -1,8 +1,9 @@ import cloneDeep from 'lodash/cloneDeep'; +import type {TupleToUnion} from 'type-fest'; +import {flattenObject} from '@src/languages/translations'; import SCREENS from '@src/SCREENS'; -import {flattenObject} from './flattenObject'; import getTopmostBottomTabRoute from './Navigation/getTopmostBottomTabRoute'; -import type {BottomTabScreenName, CentralPaneName, RootStackParamList, State} from './Navigation/types'; +import type {CentralPaneName, RootStackParamList, State} from './Navigation/types'; const CENTRAL_PANE_SCREEN_NAMES = new Set([ SCREENS.SETTINGS.WORKSPACES, @@ -46,11 +47,11 @@ const SEARCH_TAB_SCREEN_NAMES = new Set(SEARCH_SCREENS); const HOME_SCREEN_NAMES = new Set(HOME_SCREENS); -function isBottomTabName(screen: string | undefined): screen is BottomTabScreenName { +function isBottomTabName(screen: TupleToUnion | undefined) { if (!screen) { return false; } - return BOTTOM_TAB_SCREEN_NAMES.has(screen as any); + return BOTTOM_TAB_SCREEN_NAMES.has(screen); } function isSettingTabName(screen: string | undefined): screen is any { @@ -60,18 +61,25 @@ function isSettingTabName(screen: string | undefined): screen is any { return SETTINGS_TAB_SCREEN_NAMES.has(screen as any); } -function isSearchTabName(screen: string | undefined): screen is (typeof SEARCH_SCREENS)[0] { ++function isSettingTabName(screen: TupleToUnion | undefined) { if (!screen) { return false; } - return SEARCH_TAB_SCREEN_NAMES.has(screen as (typeof SEARCH_SCREENS)[0]); + return SETTINGS_TAB_SCREEN_NAMES.has(screen); +}; + +function isSearchTabName(screen: TupleToUnion | undefined) { + if (!screen) { + return false; + } + return SEARCH_TAB_SCREEN_NAMES.has(screen); } -function isHomeTabName(screen: string | undefined): screen is (typeof HOME_SCREENS)[0] { +function isHomeTabName(screen: TupleToUnion | undefined) { if (!screen) { return false; } - return HOME_SCREEN_NAMES.has(screen as (typeof HOME_SCREENS)[0]); + return HOME_SCREEN_NAMES.has(screen); } export {isCentralPaneName, isBottomTabName, isSearchTabName, isSettingTabName, isHomeTabName, removePolicyIDParamFromState}; From ca188c10105ab67b32e86a3b9eb95db8e6f0a014 Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Thu, 4 Jul 2024 06:19:34 +0900 Subject: [PATCH 09/13] improvement: use const vs let --- .../Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx index 24d8afca10fc..372c1ce478cc 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx @@ -26,7 +26,7 @@ function BottomTabNavigator() { return undefined; } let route: NavigationPartialRoute | undefined; - for (let selector of [getTopmostBottomTabRoute, getTopmostCentralPaneRoute]) { + for (const selector of [getTopmostBottomTabRoute, getTopmostCentralPaneRoute]) { const selectedRoute = selector(state); if (isBottomTabName(selectedRoute?.name)) { route = selectedRoute as NavigationPartialRoute; From 60ed14bb87d58c3f9b45fb9397ea2cb0e97695db Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Thu, 4 Jul 2024 06:20:12 +0900 Subject: [PATCH 10/13] improvement: renamed variables --- src/pages/Search/SearchPageBottomTab.tsx | 8 ++++---- src/pages/settings/InitialSettingsPage.tsx | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 58a8c54c0c14..ad95824fa6a9 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -27,7 +27,7 @@ const defaultSearchProps = { function SearchPageBottomTab() { const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); - const activeRoute = useActiveBottomTabRoute(); + const activeBottomTabRoute = useActiveBottomTabRoute(); const styles = useThemeStyles(); const { @@ -36,11 +36,11 @@ function SearchPageBottomTab() { sortBy, sortOrder, } = useMemo(() => { - if (activeRoute?.name !== SCREENS.SEARCH.CENTRAL_PANE || !activeRoute.params) { + if (activeBottomTabRoute?.name !== SCREENS.SEARCH.CENTRAL_PANE || !activeBottomTabRoute.params) { return defaultSearchProps; } - return {...defaultSearchProps, ...activeRoute.params} as SearchPageProps['route']['params']; - }, [activeRoute]); + return {...defaultSearchProps, ...activeBottomTabRoute.params} as SearchPageProps['route']['params']; + }, [activeBottomTabRoute]); const query = rawQuery as SearchQuery; diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index d3b9e28fe619..ef781d781301 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,11 +1,11 @@ import {useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; +// eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; import {NativeModules, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -// eslint-disable-next-line no-restricted-imports import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import ConfirmModal from '@components/ConfirmModal'; import CurrentUserPersonalDetailsSkeletonView from '@components/CurrentUserPersonalDetailsSkeletonView'; @@ -105,7 +105,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa const waitForNavigate = useWaitForNavigation(); const popoverAnchor = useRef(null); const {translate, formatPhoneNumber} = useLocalize(); - const activeRoute = useActiveBottomTabRoute(); + const activeBottomTabRoute = useActiveBottomTabRoute(); const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? ''; const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); @@ -332,7 +332,11 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa hoverAndPressStyle={styles.hoveredComponentBG} shouldBlockSelection={!!item.link} onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} - focused={!!activeRoute && !!item.routeName && !!(activeRoute.name.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} + focused={ + !!activeBottomTabRoute && + !!item.routeName && + !!(activeBottomTabRoute.name.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')) + } isPaneMenu iconRight={item.iconRight} shouldShowRightIcon={item.shouldShowRightIcon} @@ -352,7 +356,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa userWallet?.currentBalance, isExecuting, singleExecution, - activeRoute, + activeBottomTabRoute, waitForNavigate, ], ); From 42b0bfa7655e7b503563a4540ffac151b7f98669 Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Fri, 5 Jul 2024 08:11:38 +0900 Subject: [PATCH 11/13] chore: corrected linting errors --- src/components/Search/index.tsx | 3 +-- src/libs/NavigationUtils.ts | 11 ++--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 1c409a36528f..c03fe960a23a 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -25,7 +25,6 @@ import ROUTES from '@src/ROUTES'; import type SearchResults from '@src/types/onyx/SearchResults'; import type {SearchDataTypes, SearchQuery} from '@src/types/onyx/SearchResults'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import SearchListWithHeader from './SearchListWithHeader'; import SearchPageHeader from './SearchPageHeader'; @@ -70,7 +69,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { ); const hash = SearchUtils.getQueryHash(query, policyIDs, sortBy, sortOrder); - const [currentSearchResults, searchResultsMeta] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); + const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); // save last non-empty search results to avoid ugly flash of loading screen when hash changes and onyx returns empty data if (currentSearchResults?.data && currentSearchResults !== lastSearchResultsRef.current) { diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index 66e9434848ac..34e9df954688 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -54,19 +54,12 @@ function isBottomTabName(screen: TupleToUnion | undefin return BOTTOM_TAB_SCREEN_NAMES.has(screen); } -function isSettingTabName(screen: string | undefined): screen is any { - if (!screen) { - return false; - } - return SETTINGS_TAB_SCREEN_NAMES.has(screen as any); -} - -+function isSettingTabName(screen: TupleToUnion | undefined) { +function isSettingTabName(screen: TupleToUnion | undefined) { if (!screen) { return false; } return SETTINGS_TAB_SCREEN_NAMES.has(screen); -}; +} function isSearchTabName(screen: TupleToUnion | undefined) { if (!screen) { From 0eae14cb9e7767f60e3ddf1ac1dbd34e6f69ae0b Mon Sep 17 00:00:00 2001 From: Zakhary Oliver Date: Thu, 11 Jul 2024 11:11:32 +0900 Subject: [PATCH 12/13] chore: fix type errors --- src/components/Search/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 907325984571..845fd0225f31 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -102,7 +102,6 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { const isLoadingItems = !isOffline && searchResults?.data === undefined; const isLoadingMoreItems = !isLoadingItems && searchResults?.search?.isLoading && searchResults?.search?.offset > 0; - const shouldShowEmptyState = !isLoadingItems && SearchUtils.isSearchResultsEmpty(searchResults); if (isLoadingItems) { return ( @@ -116,7 +115,9 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { ); } - if (shouldShowEmptyState) { + const shouldShowEmptyState = searchResults && SearchUtils.isSearchResultsEmpty(searchResults); + + if (shouldShowEmptyState || !searchResults) { return ( <> Date: Fri, 12 Jul 2024 00:31:47 +0900 Subject: [PATCH 13/13] chore: fix linting errors --- src/components/Search/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 845fd0225f31..10804e709bc4 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -51,7 +51,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { const {setCurrentSearchHash} = useSearchContext(); const hash = SearchUtils.getQueryHash(query, policyIDs, sortBy, sortOrder); - const [currentSearchResults, searchResultsMeta] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); + const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); const getItemHeight = useCallback( (item: TransactionListItemType | ReportListItemType) => { @@ -117,7 +117,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { const shouldShowEmptyState = searchResults && SearchUtils.isSearchResultsEmpty(searchResults); - if (shouldShowEmptyState || !searchResults) { + if (shouldShowEmptyState ?? !searchResults) { return ( <>