forked from Expensify/App
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #160 from software-mansion-labs/poc/split-tooltips
Fix tooltips for bottom tab bar and sidebar list
- Loading branch information
Showing
12 changed files
with
154 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import {findFocusedRoute, useNavigationState} from '@react-navigation/native'; | ||
import React, {useEffect, useRef, useState} from 'react'; | ||
import {InteractionManager, View} from 'react-native'; | ||
import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import {isFullScreenName, isSplitNavigatorName} from '@libs/Navigation/helpers/isNavigatorName'; | ||
import {FULLSCREEN_TO_TAB, SIDEBAR_TO_SPLIT} from '@libs/Navigation/linkingConfig/RELATIONS'; | ||
import type {FullScreenName} from '@libs/Navigation/types'; | ||
import NAVIGATORS from '@src/NAVIGATORS'; | ||
import SCREENS from '@src/SCREENS'; | ||
import BottomTabBar from './BottomTabBar'; | ||
|
||
const SCREENS_WITH_BOTTOM_TAB_BAR = [...Object.keys(SIDEBAR_TO_SPLIT), SCREENS.SEARCH.ROOT, SCREENS.SETTINGS.WORKSPACES]; | ||
|
||
/** | ||
* TopLevelBottomTabBar is displayed when the user can interact with the bottom tab bar. | ||
* We hide it when: | ||
* 1. The bottom tab bar is not visible. | ||
* 2. There is transition between screens with and without the bottom tab bar. | ||
* 3. The bottom tab bar is under the overlay. | ||
* For cases 2 and 3, local bottom tab bar mounted on the screen will be displayed. | ||
*/ | ||
|
||
function TopLevelBottomTabBar() { | ||
const styles = useThemeStyles(); | ||
const {shouldUseNarrowLayout} = useResponsiveLayout(); | ||
const {paddingBottom} = useStyledSafeAreaInsets(); | ||
const [isAfterClosingTransition, setIsAfterClosingTransition] = useState(false); | ||
const cancelAfterInteractions = useRef<ReturnType<typeof InteractionManager.runAfterInteractions> | undefined>(); | ||
|
||
const selectedTab = useNavigationState((state) => { | ||
const topmostFullScreenRoute = state?.routes.findLast((route) => isFullScreenName(route.name)); | ||
return FULLSCREEN_TO_TAB[(topmostFullScreenRoute?.name as FullScreenName) ?? NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]; | ||
}); | ||
|
||
// There always should be a focused screen. | ||
const isScreenWithBottomTabFocused = useNavigationState((state) => { | ||
const focusedRoute = findFocusedRoute(state); | ||
|
||
// We are checking if the focused route is a split navigator because there may be a brief moment where the navigator don't have state yet. | ||
// That mens we don't have screen with bottom tab focused. This caused glitching. | ||
return SCREENS_WITH_BOTTOM_TAB_BAR.includes(focusedRoute?.name ?? '') || isSplitNavigatorName(focusedRoute?.name); | ||
}); | ||
|
||
// Visible directly means not through the overlay. | ||
const isScreenWithBottomTabVisibleDirectly = useNavigationState((state) => isFullScreenName(state.routes.at(-1)?.name)); | ||
|
||
const shouldDisplayTopLevelBottomTabBar = shouldUseNarrowLayout ? isScreenWithBottomTabFocused : isScreenWithBottomTabVisibleDirectly; | ||
|
||
useEffect(() => { | ||
cancelAfterInteractions.current?.cancel(); | ||
|
||
if (!shouldDisplayTopLevelBottomTabBar) { | ||
// If the bottom tab is not visible, that means there is a screen covering it. | ||
// In that case we need to set the flag to true because there will be a transition for which we need to wait. | ||
setIsAfterClosingTransition(false); | ||
} else { | ||
// If the bottom tab should be visible, we want to wait for transition to finish. | ||
cancelAfterInteractions.current = InteractionManager.runAfterInteractions(() => { | ||
setIsAfterClosingTransition(true); | ||
}); | ||
} | ||
}, [shouldDisplayTopLevelBottomTabBar]); | ||
|
||
return ( | ||
<View style={styles.topLevelBottomTabBar(shouldDisplayTopLevelBottomTabBar && isAfterClosingTransition, shouldUseNarrowLayout, paddingBottom)}> | ||
{/* We are not rendering BottomTabBar conditionally for two reasons | ||
1. It's faster to hide/show it than mount a new when needed. | ||
2. We need to hide tooltips as well if they were displayed. */} | ||
<BottomTabBar | ||
selectedTab={selectedTab} | ||
tooltipAllowed={shouldDisplayTopLevelBottomTabBar} | ||
/> | ||
</View> | ||
); | ||
} | ||
export default TopLevelBottomTabBar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type {NavigationState} from '@react-navigation/routers'; | ||
import {useEffect, useRef, useState} from 'react'; | ||
import navigationRef from '@libs/Navigation/navigationRef'; | ||
|
||
type Selector<T> = (state: NavigationState) => T; | ||
|
||
/** | ||
* Hook to get a value from the current root navigation state using a selector. | ||
* | ||
* @param selector Selector function to get a value from the state. | ||
*/ | ||
function useRootNavigationState<T>(selector: Selector<T>): T { | ||
const [result, setResult] = useState(() => selector(navigationRef.getRootState())); | ||
|
||
// We store the selector in a ref to avoid re-subscribing listeners every render | ||
const selectorRef = useRef(selector); | ||
|
||
useEffect(() => { | ||
selectorRef.current = selector; | ||
}); | ||
|
||
useEffect(() => { | ||
const unsubscribe = navigationRef.addListener('state', (e) => { | ||
setResult(selectorRef.current(e.data.state as NavigationState)); | ||
}); | ||
|
||
return unsubscribe; | ||
}, []); | ||
|
||
return result; | ||
} | ||
|
||
export default useRootNavigationState; |
42 changes: 0 additions & 42 deletions
42
src/libs/Navigation/AppNavigator/createRootStackNavigator/TopLevelBottomTabBar.tsx
This file was deleted.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
src/libs/Navigation/AppNavigator/createRootStackNavigator/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.