Fix trending swipe gesture (#7417)

This commit is contained in:
dan
2025-01-10 00:32:08 +00:00
committed by GitHub
parent 72dc508cb7
commit 02dbcc134e
3 changed files with 105 additions and 79 deletions
+87 -77
View File
@@ -1,6 +1,6 @@
import React from 'react' import React, {useContext} from 'react'
import {View} from 'react-native' import {ScrollView, View} from 'react-native'
import {ScrollView} from 'react-native-gesture-handler' import {GestureDetector} from 'react-native-gesture-handler'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -12,6 +12,7 @@ import {
import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics' import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
import {useTrendingConfig} from '#/state/trending-config' import {useTrendingConfig} from '#/state/trending-config'
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {TrendingGestureContext} from '#/view/shell/TrendingGestureContext'
import {atoms as a, useGutters, useTheme} from '#/alf' import {atoms as a, useGutters, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button' import {Button, ButtonIcon} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
@@ -40,83 +41,92 @@ export function Inner() {
setTrendingDisabled(true) setTrendingDisabled(true)
}, [setTrendingDisabled]) }, [setTrendingDisabled])
// This is coordinated to take precedence over the drawer pan gesture.
const trendingScrollGesture = useContext(TrendingGestureContext)
return error || noTopics ? null : ( return error || noTopics ? null : (
<View style={[t.atoms.border_contrast_low, a.border_t]}> <View style={[t.atoms.border_contrast_low, a.border_t]}>
<ScrollView <GestureDetector gesture={trendingScrollGesture}>
horizontal <ScrollView
showsHorizontalScrollIndicator={false} horizontal
decelerationRate="fast"> showsHorizontalScrollIndicator={false}
<View style={[gutters, a.flex_row, a.align_center, a.gap_lg]}> decelerationRate="fast">
<View style={{paddingLeft: 4, paddingRight: 2}}> <View style={[gutters, a.flex_row, a.align_center, a.gap_lg]}>
<Graph size="sm" /> <View style={{paddingLeft: 4, paddingRight: 2}}>
</View> <Graph size="sm" />
{isLoading ? (
<View style={[a.py_lg, a.flex_row, a.gap_lg, a.align_center]}>
<LoadingPlaceholder
width={80}
height={undefined}
style={{alignSelf: 'stretch'}}
/>
<LoadingPlaceholder
width={50}
height={undefined}
style={{alignSelf: 'stretch'}}
/>
<LoadingPlaceholder
width={120}
height={undefined}
style={{alignSelf: 'stretch'}}
/>
<LoadingPlaceholder
width={30}
height={undefined}
style={{alignSelf: 'stretch'}}
/>
<LoadingPlaceholder
width={180}
height={undefined}
style={{alignSelf: 'stretch'}}
/>
<Text
style={[t.atoms.text_contrast_medium, a.text_sm, a.font_bold]}>
{' '}
</Text>
</View> </View>
) : !trending?.topics ? null : ( {isLoading ? (
<> <View style={[a.py_lg, a.flex_row, a.gap_lg, a.align_center]}>
{trending.topics.map(topic => ( <LoadingPlaceholder
<TrendingTopicLink width={80}
key={topic.link} height={undefined}
topic={topic} style={{alignSelf: 'stretch'}}
onPress={() => { />
logEvent('trendingTopic:click', {context: 'interstitial'}) <LoadingPlaceholder
}}> width={50}
<View style={[a.py_lg]}> height={undefined}
<Text style={{alignSelf: 'stretch'}}
style={[ />
t.atoms.text, <LoadingPlaceholder
a.text_sm, width={120}
a.font_bold, height={undefined}
{opacity: 0.7}, // NOTE: we use opacity 0.7 instead of a color to match the color of the home pager tab bar style={{alignSelf: 'stretch'}}
]}> />
{topic.topic} <LoadingPlaceholder
</Text> width={30}
</View> height={undefined}
</TrendingTopicLink> style={{alignSelf: 'stretch'}}
))} />
<Button <LoadingPlaceholder
label={_(msg`Hide trending topics`)} width={180}
size="tiny" height={undefined}
variant="ghost" style={{alignSelf: 'stretch'}}
color="secondary" />
shape="round" <Text
onPress={() => trendingPrompt.open()}> style={[
<ButtonIcon icon={X} /> t.atoms.text_contrast_medium,
</Button> a.text_sm,
</> a.font_bold,
)} ]}>
</View> {' '}
</ScrollView> </Text>
</View>
) : !trending?.topics ? null : (
<>
{trending.topics.map(topic => (
<TrendingTopicLink
key={topic.link}
topic={topic}
onPress={() => {
logEvent('trendingTopic:click', {context: 'interstitial'})
}}>
<View style={[a.py_lg]}>
<Text
style={[
t.atoms.text,
a.text_sm,
a.font_bold,
{opacity: 0.7}, // NOTE: we use opacity 0.7 instead of a color to match the color of the home pager tab bar
]}>
{topic.topic}
</Text>
</View>
</TrendingTopicLink>
))}
<Button
label={_(msg`Hide trending topics`)}
size="tiny"
variant="ghost"
color="secondary"
shape="round"
onPress={() => trendingPrompt.open()}>
<ButtonIcon icon={X} />
</Button>
</>
)}
</View>
</ScrollView>
</GestureDetector>
<Prompt.Basic <Prompt.Basic
control={trendingPrompt} control={trendingPrompt}
@@ -0,0 +1,7 @@
import {createContext} from 'react'
import {Gesture} from 'react-native-gesture-handler'
// Not really used but serves as a fallback for types.
const noopGesture = Gesture.Native()
export const TrendingGestureContext = createContext(noopGesture)
+11 -2
View File
@@ -1,6 +1,7 @@
import {useCallback, useEffect} from 'react' import {useCallback, useEffect, useState} from 'react'
import {BackHandler, useWindowDimensions, View} from 'react-native' import {BackHandler, useWindowDimensions, View} from 'react-native'
import {Drawer} from 'react-native-drawer-layout' import {Drawer} from 'react-native-drawer-layout'
import {Gesture} from 'react-native-gesture-handler'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {StatusBar} from 'expo-status-bar' import {StatusBar} from 'expo-status-bar'
import {useNavigation, useNavigationState} from '@react-navigation/native' import {useNavigation, useNavigationState} from '@react-navigation/native'
@@ -33,6 +34,7 @@ import {BottomSheetOutlet} from '../../../modules/bottom-sheet'
import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView' import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView'
import {Composer} from './Composer' import {Composer} from './Composer'
import {DrawerContent} from './Drawer' import {DrawerContent} from './Drawer'
import {TrendingGestureContext} from './TrendingGestureContext'
function ShellInner() { function ShellInner() {
const t = useTheme() const t = useTheme()
@@ -92,6 +94,7 @@ function ShellInner() {
}, [dedupe, navigation]) }, [dedupe, navigation])
const swipeEnabled = !canGoBack && hasSession && !isDrawerSwipeDisabled const swipeEnabled = !canGoBack && hasSession && !isDrawerSwipeDisabled
const [trendingScrollGesture] = useState(() => Gesture.Native())
return ( return (
<> <>
<View style={[a.h_full]}> <View style={[a.h_full]}>
@@ -101,6 +104,10 @@ function ShellInner() {
renderDrawerContent={renderDrawerContent} renderDrawerContent={renderDrawerContent}
drawerStyle={{width: Math.min(400, winDim.width * 0.8)}} drawerStyle={{width: Math.min(400, winDim.width * 0.8)}}
configureGestureHandler={handler => { configureGestureHandler={handler => {
handler = handler.requireExternalGestureToFail(
trendingScrollGesture,
)
if (swipeEnabled) { if (swipeEnabled) {
if (isDrawerOpen) { if (isDrawerOpen) {
return handler.activeOffsetX([-1, 1]) return handler.activeOffsetX([-1, 1])
@@ -138,7 +145,9 @@ function ShellInner() {
dim: 'rgba(10, 13, 16, 0.8)', dim: 'rgba(10, 13, 16, 0.8)',
}), }),
}}> }}>
<TabsNavigator /> <TrendingGestureContext.Provider value={trendingScrollGesture}>
<TabsNavigator />
</TrendingGestureContext.Provider>
</Drawer> </Drawer>
</ErrorBoundary> </ErrorBoundary>
</View> </View>