|
1 | | -import { useEffect, useMemo, useRef } from 'react'; |
| 1 | +import { useCallback, useEffect, useMemo, useRef } from 'react'; |
2 | 2 | import { BackHandler, NativeEventSubscription } from 'react-native'; |
| 3 | +import { useFocusEffect, useNavigation } from '@react-navigation/native'; |
3 | 4 | import { |
4 | 5 | useSafeAreaFrame, |
5 | 6 | useSafeAreaInsets, |
6 | 7 | } from 'react-native-safe-area-context'; |
7 | 8 |
|
8 | 9 | import { useAppDispatch, useAppSelector } from './redux'; |
9 | | -import { closeSheet } from '../store/slices/ui'; |
10 | | -import { viewControllerIsOpenSelector } from '../store/reselect/ui'; |
| 10 | +import { objectKeys } from '../utils/objectKeys'; |
11 | 11 | import { TViewController } from '../store/types/ui'; |
| 12 | +import { closeAllSheets, closeSheet } from '../store/slices/ui'; |
| 13 | +import { |
| 14 | + viewControllerIsOpenSelector, |
| 15 | + viewControllersSelector, |
| 16 | +} from '../store/reselect/ui'; |
12 | 17 |
|
13 | 18 | export const useSnapPoints = ( |
14 | 19 | size: 'small' | 'medium' | 'large' | 'calendar', |
@@ -44,6 +49,10 @@ export const useSnapPoints = ( |
44 | 49 | return snapPoints; |
45 | 50 | }; |
46 | 51 |
|
| 52 | +/** |
| 53 | + * Hook to handle hardware back press (Android) when bottom sheet is open |
| 54 | + * for simple one-sheet screens |
| 55 | + */ |
47 | 56 | export const useBottomSheetBackPress = ( |
48 | 57 | viewController: TViewController, |
49 | 58 | ): void => { |
@@ -75,3 +84,47 @@ export const useBottomSheetBackPress = ( |
75 | 84 | }; |
76 | 85 | }, [isBottomSheetOpen, viewController, dispatch]); |
77 | 86 | }; |
| 87 | + |
| 88 | +/** |
| 89 | + * Hook to handle hardware back press (Android) when bottom sheet is open |
| 90 | + * for screens that are part of a navigator nested in a bottom sheet |
| 91 | + */ |
| 92 | +export const useBottomSheetScreenBackPress = (): void => { |
| 93 | + const dispatch = useAppDispatch(); |
| 94 | + const navigation = useNavigation(); |
| 95 | + const viewControllers = useAppSelector(viewControllersSelector); |
| 96 | + |
| 97 | + const isBottomSheetOpen = useMemo(() => { |
| 98 | + const viewControllerKeys = objectKeys(viewControllers); |
| 99 | + return viewControllerKeys.some((view) => viewControllers[view].isOpen); |
| 100 | + }, [viewControllers]); |
| 101 | + |
| 102 | + const backHandlerSubscriptionRef = useRef<NativeEventSubscription | null>( |
| 103 | + null, |
| 104 | + ); |
| 105 | + |
| 106 | + useFocusEffect( |
| 107 | + useCallback(() => { |
| 108 | + if (!isBottomSheetOpen) { |
| 109 | + return; |
| 110 | + } |
| 111 | + |
| 112 | + backHandlerSubscriptionRef.current = BackHandler.addEventListener( |
| 113 | + 'hardwareBackPress', |
| 114 | + () => { |
| 115 | + if (navigation.canGoBack()) { |
| 116 | + navigation.goBack(); |
| 117 | + } else { |
| 118 | + dispatch(closeAllSheets()); |
| 119 | + } |
| 120 | + return true; |
| 121 | + }, |
| 122 | + ); |
| 123 | + |
| 124 | + return (): void => { |
| 125 | + backHandlerSubscriptionRef.current?.remove(); |
| 126 | + backHandlerSubscriptionRef.current = null; |
| 127 | + }; |
| 128 | + }, [dispatch, isBottomSheetOpen, navigation]), |
| 129 | + ); |
| 130 | +}; |
0 commit comments