This commit is contained in:
Dan Abramov
2024-01-19 05:00:45 +00:00
parent 296ab76535
commit 34d38573b1
3 changed files with 310 additions and 245 deletions
+18 -1
View File
@@ -126,6 +126,23 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
[scrollRefs], [scrollRefs],
) )
const pagerRef = React.useRef(null)
React.useImperativeHandle(ref, () => ({
setPage: (index: number) => pagerRef.current?.setPage(index),
scrollTo: dy => {
'worklet'
const forcedScrollY = scrollY.value + dy
scrollY.value = forcedScrollY
lastForcedScrollY.value = forcedScrollY
const refs = scrollRefs.value
for (let i = 0; i < refs.length; i++) {
if (refs[i] != null) {
scrollTo(refs[i], 0, forcedScrollY, false)
}
}
},
}))
const lastForcedScrollY = useSharedValue(0) const lastForcedScrollY = useSharedValue(0)
const adjustScrollForOtherPages = () => { const adjustScrollForOtherPages = () => {
'worklet' 'worklet'
@@ -178,7 +195,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
return ( return (
<Pager <Pager
ref={ref} ref={pagerRef}
testID={testID} testID={testID}
initialPage={initialPage} initialPage={initialPage}
onPageSelected={onPageSelectedInner} onPageSelected={onPageSelectedInner}
+30 -3
View File
@@ -5,6 +5,7 @@ import {
TouchableWithoutFeedback, TouchableWithoutFeedback,
View, View,
} from 'react-native' } from 'react-native'
import {GestureDetector, Gesture} from 'react-native-gesture-handler'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -68,6 +69,7 @@ export function ProfileHeader({
moderation, moderation,
hideBackButton = false, hideBackButton = false,
isProfilePreview, isProfilePreview,
onPan,
}: Props) { }: Props) {
const pal = usePalette('default') const pal = usePalette('default')
@@ -98,6 +100,7 @@ export function ProfileHeader({
moderation={moderation} moderation={moderation}
hideBackButton={hideBackButton} hideBackButton={hideBackButton}
isProfilePreview={isProfilePreview} isProfilePreview={isProfilePreview}
onPan={onPan}
/> />
) )
} }
@@ -114,6 +117,7 @@ let ProfileHeaderLoaded = ({
moderation, moderation,
hideBackButton = false, hideBackButton = false,
isProfilePreview, isProfilePreview,
onPan,
}: LoadedProps): React.ReactNode => { }: LoadedProps): React.ReactNode => {
const pal = usePalette('default') const pal = usePalette('default')
const palInverted = usePalette('inverted') const palInverted = usePalette('inverted')
@@ -441,7 +445,19 @@ let ProfileHeaderLoaded = ({
const followers = formatCount(profile.followersCount || 0) const followers = formatCount(profile.followersCount || 0)
const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower') const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower')
const pan = Gesture.Pan()
.activeOffsetY([-10, 10])
.failOffsetX([-10, 10])
.maxPointers(1)
.onUpdate(e => {
onPan(e)
})
.onEnd(e => {
onPan(null)
})
return ( return (
<GestureDetector gesture={pan}>
<View style={pal.view}> <View style={pal.view}>
<UserBanner banner={profile.banner} moderation={moderation.avatar} /> <UserBanner banner={profile.banner} moderation={moderation.avatar} />
<View style={styles.content}> <View style={styles.content}>
@@ -479,7 +495,9 @@ let ProfileHeaderLoaded = ({
{!isProfilePreview && hasSession && ( {!isProfilePreview && hasSession && (
<TouchableOpacity <TouchableOpacity
testID="suggestedFollowsBtn" testID="suggestedFollowsBtn"
onPress={() => setShowSuggestedFollows(!showSuggestedFollows)} onPress={() =>
setShowSuggestedFollows(!showSuggestedFollows)
}
style={[ style={[
styles.btn, styles.btn,
styles.mainBtn, styles.mainBtn,
@@ -560,7 +578,11 @@ let ProfileHeaderLoaded = ({
accessibilityLabel={_(msg`More options`)} accessibilityLabel={_(msg`More options`)}
accessibilityHint=""> accessibilityHint="">
<View style={[styles.btn, styles.secondaryBtn, pal.btn]}> <View style={[styles.btn, styles.secondaryBtn, pal.btn]}>
<FontAwesomeIcon icon="ellipsis" size={20} style={[pal.text]} /> <FontAwesomeIcon
icon="ellipsis"
size={20}
style={[pal.text]}
/>
</View> </View>
</NativeDropdown> </NativeDropdown>
) : undefined} ) : undefined}
@@ -699,7 +721,11 @@ let ProfileHeaderLoaded = ({
accessibilityLabel={_(msg`View ${profile.handle}'s avatar`)} accessibilityLabel={_(msg`View ${profile.handle}'s avatar`)}
accessibilityHint=""> accessibilityHint="">
<View <View
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}> style={[
pal.view,
{borderColor: pal.colors.background},
styles.avi,
]}>
<UserAvatar <UserAvatar
size={80} size={80}
avatar={profile.avatar} avatar={profile.avatar}
@@ -708,6 +734,7 @@ let ProfileHeaderLoaded = ({
</View> </View>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
</View> </View>
</GestureDetector>
) )
} }
ProfileHeaderLoaded = memo(ProfileHeaderLoaded) ProfileHeaderLoaded = memo(ProfileHeaderLoaded)
+21
View File
@@ -40,6 +40,7 @@ import {Text} from '#/view/com/util/text/Text'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {isInvalidHandle} from '#/lib/strings/handles' import {isInvalidHandle} from '#/lib/strings/handles'
import {useSharedValue} from 'react-native-reanimated'
interface SectionRef { interface SectionRef {
scrollToTop: () => void scrollToTop: () => void
@@ -254,17 +255,36 @@ function ProfileScreenLoaded({
// rendering // rendering
// = // =
let scrollHeader = () => {
'worklet'
}
React.useEffect(() => {
scrollHeader = pageRef.current.scrollTo
})
const lastTranslationY = useSharedValue(0)
const renderHeader = React.useCallback(() => { const renderHeader = React.useCallback(() => {
return ( return (
<ProfileHeader <ProfileHeader
profile={profile} profile={profile}
moderation={moderation} moderation={moderation}
hideBackButton={hideBackButton} hideBackButton={hideBackButton}
onPan={e => {
'worklet'
// console.log()
if (e) {
scrollHeader(lastTranslationY.value - e.translationY)
lastTranslationY.value = e.translationY
} else {
lastTranslationY.value = 0
}
}}
/> />
) )
}, [profile, moderation, hideBackButton]) }, [profile, moderation, hideBackButton])
const pageRef = React.useRef(null)
return ( return (
<ScreenHider <ScreenHider
testID="profileView" testID="profileView"
@@ -272,6 +292,7 @@ function ProfileScreenLoaded({
screenDescription="profile" screenDescription="profile"
moderation={moderation.account}> moderation={moderation.account}>
<PagerWithHeader <PagerWithHeader
ref={pageRef}
testID="profilePager" testID="profilePager"
isHeaderReady={true} isHeaderReady={true}
items={sectionTitles} items={sectionTitles}