diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx index 22fd16aa0e..4c2c1ff920 100644 --- a/src/view/com/pager/PagerWithHeader.tsx +++ b/src/view/com/pager/PagerWithHeader.tsx @@ -126,6 +126,23 @@ export const PagerWithHeader = React.forwardRef( [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 adjustScrollForOtherPages = () => { 'worklet' @@ -178,7 +195,7 @@ export const PagerWithHeader = React.forwardRef( return ( ) } @@ -114,6 +117,7 @@ let ProfileHeaderLoaded = ({ moderation, hideBackButton = false, isProfilePreview, + onPan, }: LoadedProps): React.ReactNode => { const pal = usePalette('default') const palInverted = usePalette('inverted') @@ -441,273 +445,296 @@ let ProfileHeaderLoaded = ({ const followers = formatCount(profile.followersCount || 0) 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 ( - - - - - {isMe ? ( - - - Edit Profile - - - ) : profile.viewer?.blocking ? ( - profile.viewer?.blockingByList ? null : ( + + + + + + {isMe ? ( - - Unblock + accessibilityLabel={_(msg`Edit profile`)} + accessibilityHint={_( + msg`Opens editor for profile display name, avatar, background image, and description`, + )}> + + Edit Profile - ) - ) : !profile.viewer?.blockedBy ? ( - <> - {!isProfilePreview && hasSession && ( + ) : profile.viewer?.blocking ? ( + profile.viewer?.blockingByList ? null : ( setShowSuggestedFollows(!showSuggestedFollows)} - style={[ - styles.btn, - styles.mainBtn, - pal.btn, - { - paddingHorizontal: 10, - backgroundColor: showSuggestedFollows - ? pal.colors.text - : pal.colors.backgroundLight, - }, - ]} - accessibilityRole="button" - accessibilityLabel={_( - msg`Show follows similar to ${profile.handle}`, - )} - accessibilityHint={_( - msg`Shows a list of users similar to this user.`, - )}> - - - )} - - {profile.viewer?.following ? ( - - - - Following + accessibilityLabel={_(msg`Unblock`)} + accessibilityHint=""> + + Unblock - ) : ( - + ) + ) : !profile.viewer?.blockedBy ? ( + <> + {!isProfilePreview && hasSession && ( + + setShowSuggestedFollows(!showSuggestedFollows) + } + style={[ + styles.btn, + styles.mainBtn, + pal.btn, + { + paddingHorizontal: 10, + backgroundColor: showSuggestedFollows + ? pal.colors.text + : pal.colors.backgroundLight, + }, + ]} + accessibilityRole="button" + accessibilityLabel={_( + msg`Show follows similar to ${profile.handle}`, + )} + accessibilityHint={_( + msg`Shows a list of users similar to this user.`, + )}> + + + )} + + {profile.viewer?.following ? ( + + + + Following + + + ) : ( + + + + Follow + + + )} + + ) : null} + {dropdownItems?.length ? ( + + - - Follow - - + + + ) : undefined} + + + + {sanitizeDisplayName( + profile.displayName || sanitizeHandle(profile.handle), + moderation.profile, )} - - ) : null} - {dropdownItems?.length ? ( - - - - - - ) : undefined} - - - - {sanitizeDisplayName( - profile.displayName || sanitizeHandle(profile.handle), - moderation.profile, - )} - - - - {profile.viewer?.followedBy && !blockHide ? ( - - - Follows you - - - ) : undefined} - - {invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`} - - - {!blockHide && ( - <> - - - track(`ProfileHeader:FollowersButtonClicked`, { - handle: profile.handle, - }) - } - asAnchor - accessibilityLabel={`${followers} ${pluralizedFollowers}`} - accessibilityHint={_(msg`Opens followers list`)}> - - {followers}{' '} + + + + {profile.viewer?.followedBy && !blockHide ? ( + + + Follows you - - {pluralizedFollowers} - - - - track(`ProfileHeader:FollowsButtonClicked`, { - handle: profile.handle, - }) - } - asAnchor - accessibilityLabel={_(msg`${following} following`)} - accessibilityHint={_(msg`Opens following list`)}> - - - {following}{' '} - - - following - - - - - {formatCount(profile.postsCount || 0)}{' '} - - {pluralize(profile.postsCount || 0, 'post')} - - - - {descriptionRT && !moderation.profile.blur ? ( - - ) : undefined} - - )} - - {isMe && ( - - )} - + + {invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`} + + + {!blockHide && ( + <> + + + track(`ProfileHeader:FollowersButtonClicked`, { + handle: profile.handle, + }) + } + asAnchor + accessibilityLabel={`${followers} ${pluralizedFollowers}`} + accessibilityHint={_(msg`Opens followers list`)}> + + {followers}{' '} + + + {pluralizedFollowers} + + + + track(`ProfileHeader:FollowsButtonClicked`, { + handle: profile.handle, + }) + } + asAnchor + accessibilityLabel={_(msg`${following} following`)} + accessibilityHint={_(msg`Opens following list`)}> + + + {following}{' '} + + + following + + + + + {formatCount(profile.postsCount || 0)}{' '} + + {pluralize(profile.postsCount || 0, 'post')} + + + + {descriptionRT && !moderation.profile.blur ? ( + + + + ) : undefined} + + )} + + {isMe && ( + + )} + - {!isProfilePreview && showSuggestedFollows && ( - { - if (showSuggestedFollows) { - setShowSuggestedFollows(false) - } else { - track('ProfileHeader:SuggestedFollowsOpened') - setShowSuggestedFollows(true) - } - }} - /> - )} + {!isProfilePreview && showSuggestedFollows && ( + { + if (showSuggestedFollows) { + setShowSuggestedFollows(false) + } else { + track('ProfileHeader:SuggestedFollowsOpened') + setShowSuggestedFollows(true) + } + }} + /> + )} - {!isDesktop && !hideBackButton && ( + {!isDesktop && !hideBackButton && ( + + + + + + + + )} - - - - + + - )} - - - - - - + + ) } ProfileHeaderLoaded = memo(ProfileHeaderLoaded) diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 7fc4d7a20a..6bb972b89f 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -40,6 +40,7 @@ import {Text} from '#/view/com/util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {isNative} from '#/platform/detection' import {isInvalidHandle} from '#/lib/strings/handles' +import {useSharedValue} from 'react-native-reanimated' interface SectionRef { scrollToTop: () => void @@ -254,17 +255,36 @@ function ProfileScreenLoaded({ // rendering // = + let scrollHeader = () => { + 'worklet' + } + React.useEffect(() => { + scrollHeader = pageRef.current.scrollTo + }) + const lastTranslationY = useSharedValue(0) const renderHeader = React.useCallback(() => { return ( { + 'worklet' + // console.log() + if (e) { + scrollHeader(lastTranslationY.value - e.translationY) + lastTranslationY.value = e.translationY + } else { + lastTranslationY.value = 0 + } + }} /> ) }, [profile, moderation, hideBackButton]) + const pageRef = React.useRef(null) + return (