diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/screens/Hashtag.tsx | 6 | ||||
-rw-r--r-- | src/view/com/pager/Pager.tsx | 39 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 12 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 12 | ||||
-rw-r--r-- | src/view/screens/Search/Search.tsx | 6 | ||||
-rw-r--r-- | src/view/shell/index.tsx | 28 |
6 files changed, 65 insertions, 38 deletions
diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx index a0fc3707c..a87487150 100644 --- a/src/screens/Hashtag.tsx +++ b/src/screens/Hashtag.tsx @@ -14,7 +14,7 @@ import {cleanError} from '#/lib/strings/errors' import {sanitizeHandle} from '#/lib/strings/handles' import {enforceLen} from '#/lib/strings/helpers' import {useSearchPostsQuery} from '#/state/queries/search-posts' -import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import {useSetMinimalShellMode} from '#/state/shell' import {Pager} from '#/view/com/pager/Pager' import {TabBar} from '#/view/com/pager/TabBar' import {Post} from '#/view/com/post/Post' @@ -63,7 +63,6 @@ export default function HashtagScreen({ const [activeTab, setActiveTab] = React.useState(0) const setMinimalShellMode = useSetMinimalShellMode() - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() useFocusEffect( React.useCallback(() => { @@ -74,10 +73,9 @@ export default function HashtagScreen({ const onPageSelected = React.useCallback( (index: number) => { setMinimalShellMode(false) - setDrawerSwipeDisabled(index > 0) setActiveTab(index) }, - [setDrawerSwipeDisabled, setMinimalShellMode], + [setMinimalShellMode], ) const sections = React.useMemo(() => { diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx index da7fd1e93..2c0bbee52 100644 --- a/src/view/com/pager/Pager.tsx +++ b/src/view/com/pager/Pager.tsx @@ -1,5 +1,7 @@ -import React, {forwardRef} from 'react' +import React, {forwardRef, useCallback, useContext} from 'react' import {View} from 'react-native' +import {DrawerGestureContext} from 'react-native-drawer-layout' +import {Gesture, GestureDetector} from 'react-native-gesture-handler' import PagerView, { PagerViewOnPageScrollEventData, PagerViewOnPageSelectedEvent, @@ -13,7 +15,9 @@ import Animated, { useHandler, useSharedValue, } from 'react-native-reanimated' +import {useFocusEffect} from '@react-navigation/native' +import {useSetDrawerSwipeDisabled} from '#/state/shell' import {atoms as a, native} from '#/alf' export type PageSelectedEvent = PagerViewOnPageSelectedEvent @@ -58,6 +62,18 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>( const [selectedPage, setSelectedPage] = React.useState(initialPage) const pagerView = React.useRef<PagerView>(null) + const [isIdle, setIsIdle] = React.useState(true) + const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() + useFocusEffect( + useCallback(() => { + const canSwipeDrawer = selectedPage === 0 && isIdle + setDrawerSwipeDisabled(!canSwipeDrawer) + return () => { + setDrawerSwipeDisabled(false) + } + }, [setDrawerSwipeDisabled, selectedPage, isIdle]), + ) + React.useImperativeHandle(ref, () => ({ setPage: (index: number) => { pagerView.current?.setPage(index) @@ -96,6 +112,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>( }, onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) { 'worklet' + runOnJS(setIsIdle)(e.pageScrollState === 'idle') if (dragState.get() === 'idle' && e.pageScrollState === 'settling') { // This is a programmatic scroll on Android. // Stay "idle" to match iOS and avoid confusing downstream code. @@ -113,6 +130,10 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>( [parentOnPageScrollStateChanged], ) + const drawerGesture = useContext(DrawerGestureContext)! + const nativeGesture = + Gesture.Native().requireExternalGestureToFail(drawerGesture) + return ( <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}> {renderTabBar({ @@ -121,13 +142,15 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>( dragProgress, dragState, })} - <AnimatedPagerView - ref={pagerView} - style={[a.flex_1]} - initialPage={initialPage} - onPageScroll={handlePageScroll}> - {children} - </AnimatedPagerView> + <GestureDetector gesture={nativeGesture}> + <AnimatedPagerView + ref={pagerView} + style={[a.flex_1]} + initialPage={initialPage} + onPageScroll={handlePageScroll}> + {children} + </AnimatedPagerView> + </GestureDetector> </View> ) }, diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 1218a5ba0..59b296730 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -19,7 +19,7 @@ import {FeedParams} from '#/state/queries/post-feed' import {usePreferencesQuery} from '#/state/queries/preferences' import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types' import {useSession} from '#/state/session' -import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import {useSetMinimalShellMode} from '#/state/shell' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed' import {FeedPage} from '#/view/com/feeds/FeedPage' @@ -127,15 +127,10 @@ function HomeScreenReady({ const {hasSession} = useSession() const setMinimalShellMode = useSetMinimalShellMode() - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() useFocusEffect( React.useCallback(() => { setMinimalShellMode(false) - setDrawerSwipeDisabled(selectedIndex > 0) - return () => { - setDrawerSwipeDisabled(false) - } - }, [setDrawerSwipeDisabled, selectedIndex, setMinimalShellMode]), + }, [setMinimalShellMode]), ) useFocusEffect( @@ -154,7 +149,6 @@ function HomeScreenReady({ const onPageSelected = React.useCallback( (index: number) => { setMinimalShellMode(false) - setDrawerSwipeDisabled(index > 0) const feed = allFeeds[index] // Mutate the ref before setting state to avoid the imperative syncing effect // above from starting a loop on Android when swiping back and forth. @@ -166,7 +160,7 @@ function HomeScreenReady({ feedUrl: feed, }) }, - [setDrawerSwipeDisabled, setSelectedFeed, setMinimalShellMode, allFeeds], + [setSelectedFeed, setMinimalShellMode, allFeeds], ) const onPressSelected = React.useCallback(() => { diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 6a9b6f7f2..782e9b9c8 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -32,7 +32,7 @@ import {resetProfilePostsQueries} from '#/state/queries/post-feed' import {useProfileQuery} from '#/state/queries/profile' import {useResolveDidQuery} from '#/state/queries/resolve-uri' import {useAgent, useSession} from '#/state/session' -import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import {useSetMinimalShellMode} from '#/state/shell' import {useComposerControls} from '#/state/shell/composer' import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens' import {ProfileLists} from '#/view/com/lists/ProfileLists' @@ -183,7 +183,6 @@ function ProfileScreenLoaded({ }) const [currentPage, setCurrentPage] = React.useState(0) const {_} = useLingui() - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() const [scrollViewTag, setScrollViewTag] = React.useState<number | null>(null) @@ -307,15 +306,6 @@ function ProfileScreenLoaded({ }, [setMinimalShellMode, currentPage, scrollSectionToTop]), ) - useFocusEffect( - React.useCallback(() => { - setDrawerSwipeDisabled(currentPage > 0) - return () => { - setDrawerSwipeDisabled(false) - } - }, [setDrawerSwipeDisabled, currentPage]), - ) - // events // = diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 0871458c9..ed62c5a51 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -47,7 +47,7 @@ import {usePopularFeedsSearch} from '#/state/queries/feed' import {useSearchPostsQuery} from '#/state/queries/search-posts' import {useSession} from '#/state/session' import {useSetDrawerOpen} from '#/state/shell' -import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import {useSetMinimalShellMode} from '#/state/shell' import {Pager} from '#/view/com/pager/Pager' import {TabBar} from '#/view/com/pager/TabBar' import {Post} from '#/view/com/post/Post' @@ -471,7 +471,6 @@ let SearchScreenInner = ({ }): React.ReactNode => { const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() const {hasSession} = useSession() const {isDesktop} = useWebMediaQueries() const [activeTab, setActiveTab] = React.useState(0) @@ -480,10 +479,9 @@ let SearchScreenInner = ({ const onPageSelected = React.useCallback( (index: number) => { setMinimalShellMode(false) - setDrawerSwipeDisabled(index > 0) setActiveTab(index) }, - [setDrawerSwipeDisabled, setMinimalShellMode], + [setMinimalShellMode], ) const sections = React.useMemo(() => { diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 8dbbbea6f..179e8858e 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -90,6 +90,7 @@ function ShellInner() { } }, [dedupe, navigation]) + const swipeEnabled = !canGoBack && hasSession && !isDrawerSwipeDisabled return ( <> <View style={[a.h_full]}> @@ -98,12 +99,35 @@ function ShellInner() { <Drawer renderDrawerContent={renderDrawerContent} drawerStyle={{width: Math.min(400, winDim.width * 0.8)}} + configureGestureHandler={handler => { + if (swipeEnabled) { + if (isDrawerOpen) { + return handler.activeOffsetX([-1, 1]) + } else { + return ( + handler + // Any movement to the left is a pager swipe + // so fail the drawer gesture immediately. + .failOffsetX(-1) + // Don't rush declaring that a movement to the right + // is a drawer swipe. It could be a vertical scroll. + .activeOffsetX(5) + ) + } + } else { + // Fail the gesture immediately. + // This seems more reliable than the `swipeEnabled` prop. + // With `swipeEnabled` alone, the gesture may freeze after toggling off/on. + return handler.failOffsetX([0, 0]).failOffsetY([0, 0]) + } + }} open={isDrawerOpen} onOpen={onOpenDrawer} onClose={onCloseDrawer} - swipeEdgeWidth={winDim.width / 2} + swipeEdgeWidth={winDim.width} + swipeMinVelocity={100} + swipeMinDistance={10} drawerType={isIOS ? 'slide' : 'front'} - swipeEnabled={!canGoBack && hasSession && !isDrawerSwipeDisabled} overlayStyle={{ backgroundColor: select(t.name, { light: 'rgba(0, 57, 117, 0.1)', |