import React, {memo, useState} from 'react' import {type LayoutChangeEvent, StyleSheet, View} from 'react-native' import Animated, { runOnJS, useAnimatedReaction, useAnimatedStyle, withTiming, } from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import { type AppBskyActorDefs, type AppBskyLabelerDefs, type ModerationOpts, type RichText as RichTextAPI, } from '@atproto/api' import {useIsFocused} from '@react-navigation/native' import {isNative} from '#/platform/detection' import {useSetLightStatusBar} from '#/state/shell/light-status-bar' import {usePagerHeaderContext} from '#/view/com/pager/PagerHeaderContext' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {atoms as a, useTheme} from '#/alf' import {ProfileHeaderLabeler} from './ProfileHeaderLabeler' import {ProfileHeaderStandard} from './ProfileHeaderStandard' let ProfileHeaderLoading = (_props: {}): React.ReactNode => { const t = useTheme() return ( ) } ProfileHeaderLoading = memo(ProfileHeaderLoading) export {ProfileHeaderLoading} interface Props { profile: AppBskyActorDefs.ProfileViewDetailed labeler: AppBskyLabelerDefs.LabelerViewDetailed | undefined descriptionRT: RichTextAPI | null moderationOpts: ModerationOpts hideBackButton?: boolean isPlaceholderProfile?: boolean setMinimumHeight: (height: number) => void } let ProfileHeader = ({setMinimumHeight, ...props}: Props): React.ReactNode => { let content if (props.profile.associated?.labeler) { if (!props.labeler) { content = } else { content = } } else { content = } return ( <> {isNative && ( setMinimumHeight(evt.nativeEvent.layout.height)} profile={props.profile} hideBackButton={props.hideBackButton} /> )} {content} ) } ProfileHeader = memo(ProfileHeader) export {ProfileHeader} const MinimalHeader = React.memo(function MinimalHeader({ onLayout, }: { onLayout: (e: LayoutChangeEvent) => void profile: AppBskyActorDefs.ProfileViewDetailed hideBackButton?: boolean }) { const t = useTheme() const insets = useSafeAreaInsets() const ctx = usePagerHeaderContext() const [visible, setVisible] = useState(false) const [minimalHeaderHeight, setMinimalHeaderHeight] = React.useState(0) const isScreenFocused = useIsFocused() if (!ctx) throw new Error('MinimalHeader cannot be used on web') const {scrollY, headerHeight} = ctx const animatedStyle = useAnimatedStyle(() => { // if we don't yet have the min header height in JS, hide if (!_WORKLET || minimalHeaderHeight === 0) { return { opacity: 0, } } const pastThreshold = scrollY.get() > 100 return { opacity: pastThreshold ? withTiming(1, {duration: 75}) : withTiming(0, {duration: 75}), transform: [ { translateY: Math.min( scrollY.get(), headerHeight - minimalHeaderHeight, ), }, ], } }) useAnimatedReaction( () => scrollY.get() > 100, (value, prev) => { if (prev !== value) { runOnJS(setVisible)(value) } }, ) useSetLightStatusBar(isScreenFocused && !visible) return ( { setMinimalHeaderHeight(evt.nativeEvent.layout.height) onLayout(evt) }} style={[ a.absolute, a.z_50, t.atoms.bg, { top: 0, left: 0, right: 0, paddingTop: insets.top, }, animatedStyle, ]} /> ) }) MinimalHeader.displayName = 'MinimalHeader' const styles = StyleSheet.create({ avi: { position: 'absolute', top: 110, left: 10, width: 94, height: 94, borderRadius: 47, borderWidth: 2, }, content: { paddingTop: 12, paddingHorizontal: 16, paddingBottom: 8, }, buttonsLine: { flexDirection: 'row', marginLeft: 'auto', }, br45: {borderRadius: 45}, br50: {borderRadius: 50}, })