Post UI updates (Profile Preview on mobile) (#990)

* Update postmeta to put the timestamp on the right side on mobile

* Drop the two-line PostMeta mode

* Add ProfilePreview modal

* Tune PostMeta to give the best behavior possible for a given platform

* Remove old showFollowBtn attributes

* Fix style issue

* Switch the follow button in the profile header to use the inverted color for consistency with the rest of the app

* Fix lint

* Fix darkmode

* Tune the profile preview footer

* Better analytics choice
zio/stable
Paul Frazee 2023-07-06 21:12:54 -05:00 committed by GitHub
parent df7552135a
commit 6f69157269
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 215 additions and 190 deletions

View File

@ -129,6 +129,7 @@ interface ScreenPropertiesMap {
Feed: {}
Notifications: {}
Profile: {}
'Profile:Preview': {}
Settings: {}
AppPasswords: {}
Moderation: {}

View File

@ -31,6 +31,11 @@ export interface EditProfileModal {
onUpdate?: () => void
}
export interface ProfilePreviewModal {
name: 'profile-preview'
did: string
}
export interface ServerInputModal {
name: 'server-input'
initialService: string
@ -128,6 +133,7 @@ export type Modal =
| ChangeHandleModal
| DeleteAccountModal
| EditProfileModal
| ProfilePreviewModal
// Curation
| ContentFilteringSettingsModal

View File

@ -9,6 +9,7 @@ import {usePalette} from 'lib/hooks/usePalette'
import * as ConfirmModal from './Confirm'
import * as EditProfileModal from './EditProfile'
import * as ProfilePreviewModal from './ProfilePreview'
import * as ServerInputModal from './ServerInput'
import * as ReportPostModal from './report/ReportPost'
import * as RepostModal from './Repost'
@ -62,6 +63,9 @@ export const ModalsContainer = observer(function ModalsContainer() {
} else if (activeModal?.name === 'edit-profile') {
snapPoints = EditProfileModal.snapPoints
element = <EditProfileModal.Component {...activeModal} />
} else if (activeModal?.name === 'profile-preview') {
snapPoints = ProfilePreviewModal.snapPoints
element = <ProfilePreviewModal.Component {...activeModal} />
} else if (activeModal?.name === 'server-input') {
snapPoints = ServerInputModal.snapPoints
element = <ServerInputModal.Component {...activeModal} />

View File

@ -8,6 +8,7 @@ import {isMobileWeb} from 'platform/detection'
import * as ConfirmModal from './Confirm'
import * as EditProfileModal from './EditProfile'
import * as ProfilePreviewModal from './ProfilePreview'
import * as ServerInputModal from './ServerInput'
import * as ReportPostModal from './report/ReportPost'
import * as ReportAccountModal from './report/ReportAccount'
@ -68,6 +69,8 @@ function Modal({modal}: {modal: ModalIface}) {
element = <ConfirmModal.Component {...modal} />
} else if (modal.name === 'edit-profile') {
element = <EditProfileModal.Component {...modal} />
} else if (modal.name === 'profile-preview') {
element = <ProfilePreviewModal.Component {...modal} />
} else if (modal.name === 'server-input') {
element = <ServerInputModal.Component {...modal} />
} else if (modal.name === 'report-post') {

View File

@ -0,0 +1,89 @@
import React, {useState, useEffect, useCallback} from 'react'
import {StyleSheet, View} from 'react-native'
import {observer} from 'mobx-react-lite'
import {useNavigation, StackActions} from '@react-navigation/native'
import {Text} from '../util/text/Text'
import {useStores} from 'state/index'
import {ProfileModel} from 'state/models/content/profile'
import {usePalette} from 'lib/hooks/usePalette'
import {useAnalytics} from 'lib/analytics/analytics'
import {ProfileHeader} from '../profile/ProfileHeader'
import {Button} from '../util/forms/Button'
import {NavigationProp} from 'lib/routes/types'
export const snapPoints = [560]
export const Component = observer(({did}: {did: string}) => {
const store = useStores()
const pal = usePalette('default')
const palInverted = usePalette('inverted')
const navigation = useNavigation<NavigationProp>()
const [model] = useState(new ProfileModel(store, {actor: did}))
const {screen} = useAnalytics()
useEffect(() => {
screen('Profile:Preview')
model.setup()
}, [model, screen])
const onPressViewProfile = useCallback(() => {
navigation.dispatch(StackActions.push('Profile', {name: model.handle}))
store.shell.closeModal()
}, [navigation, store, model])
return (
<View style={pal.view}>
<View style={styles.headerWrapper}>
<ProfileHeader view={model} hideBackButton onRefreshAll={() => {}} />
</View>
<View style={[styles.buttonsContainer, pal.view]}>
<View style={styles.buttons}>
<Button
type="inverted"
style={[styles.button, styles.buttonWide]}
onPress={onPressViewProfile}
accessibilityLabel="View profile"
accessibilityHint="">
<Text type="button-lg" style={palInverted.text}>
View Profile
</Text>
</Button>
<Button
type="default"
style={styles.button}
onPress={() => store.shell.closeModal()}
accessibilityLabel="Close this preview"
accessibilityHint="">
<Text type="button-lg" style={pal.text}>
Close
</Text>
</Button>
</View>
</View>
</View>
)
})
const styles = StyleSheet.create({
headerWrapper: {
height: 440,
},
buttonsContainer: {
height: 120,
},
buttons: {
flexDirection: 'row',
gap: 8,
paddingHorizontal: 14,
paddingTop: 16,
},
button: {
flex: 2,
flexDirection: 'row',
justifyContent: 'center',
paddingVertical: 12,
},
buttonWide: {
flex: 3,
},
})

View File

@ -13,7 +13,7 @@ import {RichText} from '../util/text/RichText'
import {Text} from '../util/text/Text'
import {PostDropdownBtn} from '../util/forms/DropdownButton'
import * as Toast from '../util/Toast'
import {UserAvatar} from '../util/UserAvatar'
import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
import {ago, niceDate} from 'lib/strings/time'
import {sanitizeDisplayName} from 'lib/strings/display-names'
@ -163,22 +163,17 @@ export const PostThreadItem = observer(function PostThreadItem({
<PostSandboxWarning />
<View style={styles.layout}>
<View style={styles.layoutAvi}>
<Link
href={authorHref}
title={authorTitle}
asAnchor
accessibilityLabel={`${item.post.author.handle}'s avatar`}
accessibilityHint="">
<UserAvatar
<PreviewableUserAvatar
size={52}
did={item.post.author.did}
handle={item.post.author.handle}
avatar={item.post.author.avatar}
moderation={item.moderation.avatar}
/>
</Link>
</View>
<View style={styles.layoutContent}>
<View style={[styles.meta, styles.metaExpandedLine1]}>
<View style={[s.flexRow, s.alignBaseline]}>
<View style={[s.flexRow]}>
<Link
style={styles.metaItem}
href={authorHref}
@ -353,13 +348,13 @@ export const PostThreadItem = observer(function PostThreadItem({
<PostSandboxWarning />
<View style={styles.layout}>
<View style={styles.layoutAvi}>
<Link href={authorHref} title={authorTitle} asAnchor>
<UserAvatar
<PreviewableUserAvatar
size={52}
did={item.post.author.did}
handle={item.post.author.handle}
avatar={item.post.author.avatar}
moderation={item.moderation.avatar}
/>
</Link>
</View>
<View style={styles.layoutContent}>
<PostMeta
@ -368,7 +363,6 @@ export const PostThreadItem = observer(function PostThreadItem({
authorHasWarning={!!item.post.author.labels?.length}
timestamp={item.post.indexedAt}
postHref={itemHref}
did={item.post.author.did}
/>
<ContentHider
moderation={item.moderation.thread}

View File

@ -229,7 +229,6 @@ const PostLoaded = observer(
authorHasWarning={!!item.post.author.labels?.length}
timestamp={item.post.indexedAt}
postHref={itemHref}
did={item.post.author.did}
/>
{replyAuthorDid !== '' && (
<View style={[s.flexRow, s.mb2, s.alignCenter]}>

View File

@ -28,7 +28,6 @@ const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
export const Feed = observer(function Feed({
feed,
style,
showPostFollowBtn,
scrollElRef,
onPressTryAgain,
onScroll,
@ -41,7 +40,6 @@ export const Feed = observer(function Feed({
}: {
feed: PostsFeedModel
style?: StyleProp<ViewStyle>
showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject<FlatList<any> | null>
onPressTryAgain?: () => void
onScroll?: OnScrollCb
@ -138,15 +136,9 @@ export const Feed = observer(function Feed({
} else if (item === LOADING_ITEM) {
return <PostFeedLoadingPlaceholder />
}
return <FeedSlice slice={item} showFollowBtn={showPostFollowBtn} />
return <FeedSlice slice={item} />
},
[
feed,
onPressTryAgain,
onPressRetryLoadMore,
showPostFollowBtn,
renderEmptyState,
],
[feed, onPressTryAgain, onPressRetryLoadMore, renderEmptyState],
)
const FeedFooter = React.useCallback(

View File

@ -21,7 +21,7 @@ import {ImageHider} from '../util/moderation/ImageHider'
import {RichText} from '../util/text/RichText'
import {PostSandboxWarning} from '../util/PostSandboxWarning'
import * as Toast from '../util/Toast'
import {UserAvatar} from '../util/UserAvatar'
import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
import {useStores} from 'state/index'
import {usePalette} from 'lib/hooks/usePalette'
@ -33,14 +33,12 @@ export const FeedItem = observer(function ({
item,
isThreadChild,
isThreadParent,
showFollowBtn,
ignoreMuteFor,
}: {
item: PostsFeedItemModel
isThreadChild?: boolean
isThreadParent?: boolean
showReplyLine?: boolean
showFollowBtn?: boolean
ignoreMuteFor?: string
}) {
const store = useStores()
@ -55,7 +53,6 @@ export const FeedItem = observer(function ({
return `/profile/${item.post.author.handle}/post/${urip.rkey}`
}, [item.post.uri, item.post.author.handle])
const itemTitle = `Post by ${item.post.author.handle}`
const authorHref = `/profile/${item.post.author.handle}`
const replyAuthorDid = useMemo(() => {
if (!record?.reply) {
return ''
@ -214,13 +211,13 @@ export const FeedItem = observer(function ({
<PostSandboxWarning />
<View style={styles.layout}>
<View style={styles.layoutAvi}>
<Link href={authorHref} title={item.post.author.handle} asAnchor>
<UserAvatar
<PreviewableUserAvatar
size={52}
did={item.post.author.did}
handle={item.post.author.handle}
avatar={item.post.author.avatar}
moderation={item.moderation.avatar}
/>
</Link>
</View>
<View style={styles.layoutContent}>
<PostMeta
@ -229,8 +226,6 @@ export const FeedItem = observer(function ({
authorHasWarning={!!item.post.author.labels?.length}
timestamp={item.post.indexedAt}
postHref={itemHref}
did={item.post.author.did}
showFollowBtn={showFollowBtn}
/>
{!isThreadChild && replyAuthorDid !== '' && (
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
@ -357,9 +352,9 @@ const styles = StyleSheet.create({
layout: {
flexDirection: 'row',
marginTop: 1,
gap: 10,
},
layoutAvi: {
width: 70,
paddingLeft: 8,
},
layoutContent: {

View File

@ -11,11 +11,9 @@ import {ModerationBehaviorCode} from 'lib/labeling/types'
export function FeedSlice({
slice,
showFollowBtn,
ignoreMuteFor,
}: {
slice: PostsFeedSliceModel
showFollowBtn?: boolean
ignoreMuteFor?: string
}) {
if (slice.moderation.list.behavior === ModerationBehaviorCode.Hide) {
@ -32,7 +30,6 @@ export function FeedSlice({
item={slice.items[0]}
isThreadParent={slice.isThreadParentAt(0)}
isThreadChild={slice.isThreadChildAt(0)}
showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
<FeedItem
@ -40,7 +37,6 @@ export function FeedSlice({
item={slice.items[1]}
isThreadParent={slice.isThreadParentAt(1)}
isThreadChild={slice.isThreadChildAt(1)}
showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
<ViewFullThread slice={slice} />
@ -49,7 +45,6 @@ export function FeedSlice({
item={slice.items[last]}
isThreadParent={slice.isThreadParentAt(last)}
isThreadChild={slice.isThreadChildAt(last)}
showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
</>
@ -64,7 +59,6 @@ export function FeedSlice({
item={item}
isThreadParent={slice.isThreadParentAt(i)}
isThreadChild={slice.isThreadChildAt(i)}
showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
))}

View File

@ -28,7 +28,6 @@ import {CogIcon} from 'lib/icons'
export const MultiFeed = observer(function Feed({
multifeed,
style,
showPostFollowBtn,
scrollElRef,
onScroll,
scrollEventThrottle,
@ -38,7 +37,6 @@ export const MultiFeed = observer(function Feed({
}: {
multifeed: PostsMultiFeedModel
style?: StyleProp<ViewStyle>
showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject<FlatList<any> | null>
onPressTryAgain?: () => void
onScroll?: OnScrollCb
@ -105,9 +103,7 @@ export const MultiFeed = observer(function Feed({
</View>
)
} else if (item.type === 'feed-slice') {
return (
<FeedSlice slice={item.slice} showFollowBtn={showPostFollowBtn} />
)
return <FeedSlice slice={item.slice} />
} else if (item.type === 'feed-loading') {
return <PostFeedLoadingPlaceholder />
} else if (item.type === 'feed-error') {
@ -139,7 +135,7 @@ export const MultiFeed = observer(function Feed({
}
return null
},
[showPostFollowBtn, pal],
[pal],
)
const ListFooter = React.useCallback(

View File

@ -6,10 +6,7 @@ import {
TouchableWithoutFeedback,
View,
} from 'react-native'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native'
import {BlurView} from '../util/BlurView'
import {ProfileModel} from 'state/models/content/profile'
@ -102,6 +99,7 @@ export const ProfileHeader = observer(
const ProfileHeaderLoaded = observer(
({view, onRefreshAll, hideBackButton = false}: Props) => {
const pal = usePalette('default')
const palInverted = usePalette('inverted')
const store = useStores()
const navigation = useNavigation<NavigationProp>()
const {track} = useAnalytics()
@ -351,15 +349,15 @@ const ProfileHeaderLoaded = observer(
<TouchableOpacity
testID="followBtn"
onPress={onPressToggleFollow}
style={[styles.btn, styles.primaryBtn]}
style={[styles.btn, styles.mainBtn, palInverted.view]}
accessibilityRole="button"
accessibilityLabel={`Follow ${view.handle}`}
accessibilityHint={`Shows direct posts from ${view.handle} in your feed`}>
<FontAwesomeIcon
icon="plus"
style={[s.white as FontAwesomeIconStyle, s.mr5]}
style={[palInverted.text, s.mr5]}
/>
<Text type="button" style={[s.white, s.bold]}>
<Text type="button" style={[palInverted.text, s.bold]}>
Follow
</Text>
</TouchableOpacity>
@ -609,7 +607,6 @@ const styles = StyleSheet.create({
},
description: {
flex: 1,
marginBottom: 8,
},

View File

@ -6,6 +6,7 @@ import {
Platform,
StyleProp,
TextStyle,
TextProps,
View,
ViewStyle,
TouchableOpacity,
@ -144,7 +145,7 @@ export const TextLink = observer(function TextLink({
numberOfLines?: number
lineHeight?: number
dataSet?: any
}) {
} & TextProps) {
const {...props} = useLinkProps({to: sanitizeUrl(href)})
const store = useStores()
const navigation = useNavigation<NavigationProp>()
@ -186,16 +187,7 @@ export const TextLink = observer(function TextLink({
/**
* Only acts as a link on desktop web
*/
export const DesktopWebTextLink = observer(function DesktopWebTextLink({
testID,
type = 'md',
style,
href,
text,
numberOfLines,
lineHeight,
...props
}: {
interface DesktopWebTextLinkProps extends TextProps {
testID?: string
type?: TypographyVariant
style?: StyleProp<TextStyle>
@ -206,7 +198,17 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({
accessible?: boolean
accessibilityLabel?: string
accessibilityHint?: string
}) {
}
export const DesktopWebTextLink = observer(function DesktopWebTextLink({
testID,
type = 'md',
style,
href,
text,
numberOfLines,
lineHeight,
...props
}: DesktopWebTextLinkProps) {
if (isDesktopWeb) {
return (
<TextLink

View File

@ -4,12 +4,10 @@ import {Text} from './text/Text'
import {DesktopWebTextLink} from './Link'
import {ago, niceDate} from 'lib/strings/time'
import {usePalette} from 'lib/hooks/usePalette'
import {useStores} from 'state/index'
import {UserAvatar} from './UserAvatar'
import {observer} from 'mobx-react-lite'
import {FollowButton} from '../profile/FollowButton'
import {FollowState} from 'state/models/cache/my-follows'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {isAndroid, isIOS} from 'platform/detection'
interface PostMetaOpts {
authorAvatar?: string
@ -18,88 +16,17 @@ interface PostMetaOpts {
authorHasWarning: boolean
postHref: string
timestamp: string
did?: string
showFollowBtn?: boolean
}
export const PostMeta = observer(function (opts: PostMetaOpts) {
const pal = usePalette('default')
const displayName = opts.authorDisplayName || opts.authorHandle
const handle = opts.authorHandle
const store = useStores()
const isMe = opts.did === store.me.did
const followState =
typeof opts.did === 'string'
? store.me.follows.getFollowState(opts.did)
: FollowState.Unknown
const [didFollow, setDidFollow] = React.useState(false)
const onToggleFollow = React.useCallback(() => {
setDidFollow(true)
}, [setDidFollow])
if (
opts.showFollowBtn &&
!isMe &&
(followState === FollowState.NotFollowing || didFollow) &&
opts.did
) {
// two-liner with follow button
return (
<View style={styles.metaTwoLine}>
<View style={styles.metaTwoLineLeft}>
<View style={styles.metaTwoLineTop}>
<DesktopWebTextLink
type="lg-bold"
style={pal.text}
numberOfLines={1}
lineHeight={1.2}
text={sanitizeDisplayName(displayName)}
href={`/profile/${opts.authorHandle}`}
/>
<Text
type="md"
style={pal.textLight}
lineHeight={1.2}
accessible={false}>
&nbsp;&middot;&nbsp;
</Text>
<DesktopWebTextLink
type="md"
style={[styles.metaItem, pal.textLight]}
lineHeight={1.2}
text={ago(opts.timestamp)}
accessibilityLabel={niceDate(opts.timestamp)}
accessibilityHint=""
href={opts.postHref}
/>
</View>
<DesktopWebTextLink
type="md"
style={[styles.metaItem, pal.textLight]}
lineHeight={1.2}
numberOfLines={1}
text={`@${handle}`}
href={`/profile/${opts.authorHandle}`}
/>
</View>
<View>
<FollowButton
unfollowedType="default"
did={opts.did}
onToggleFollow={onToggleFollow}
/>
</View>
</View>
)
}
// one-liner
return (
<View style={styles.meta}>
<View style={styles.metaOneLine}>
{typeof opts.authorAvatar !== 'undefined' && (
<View style={[styles.metaItem, styles.avatar]}>
<View style={styles.avatar}>
<UserAvatar
avatar={opts.authorAvatar}
size={16}
@ -107,7 +34,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
/>
</View>
)}
<View style={[styles.metaItem, styles.maxWidth]}>
<View style={styles.maxWidth}>
<DesktopWebTextLink
type="lg-bold"
style={pal.text}
@ -128,12 +55,18 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
href={`/profile/${opts.authorHandle}`}
/>
</View>
<Text type="md" style={pal.textLight} lineHeight={1.2} accessible={false}>
&middot;&nbsp;
{!isAndroid && (
<Text
type="md"
style={pal.textLight}
lineHeight={1.2}
accessible={false}>
&middot;
</Text>
)}
<DesktopWebTextLink
type="md"
style={[styles.metaItem, pal.textLight]}
style={pal.textLight}
lineHeight={1.2}
text={ago(opts.timestamp)}
accessibilityLabel={niceDate(opts.timestamp)}
@ -145,32 +78,16 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
})
const styles = StyleSheet.create({
meta: {
metaOneLine: {
flexDirection: 'row',
paddingBottom: 2,
},
metaTwoLine: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
paddingBottom: 4,
},
metaTwoLineLeft: {
flex: 1,
paddingRight: 40,
},
metaTwoLineTop: {
flexDirection: 'row',
alignItems: 'baseline',
},
metaItem: {
paddingRight: 5,
gap: 4,
},
avatar: {
alignSelf: 'center',
},
maxWidth: {
maxWidth: '80%',
flex: isAndroid ? 1 : undefined,
maxWidth: isIOS ? '80%' : undefined,
},
})

View File

@ -1,5 +1,5 @@
import React, {useMemo} from 'react'
import {StyleSheet, View} from 'react-native'
import {Pressable, StyleSheet, View} from 'react-native'
import Svg, {Circle, Rect, Path} from 'react-native-svg'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {IconProp} from '@fortawesome/fontawesome-svg-core'
@ -12,13 +12,31 @@ import {
import {useStores} from 'state/index'
import {colors} from 'lib/styles'
import {DropdownButton} from './forms/DropdownButton'
import {Link} from './Link'
import {usePalette} from 'lib/hooks/usePalette'
import {isWeb, isAndroid} from 'platform/detection'
import {Image as RNImage} from 'react-native-image-crop-picker'
import {AvatarModeration} from 'lib/labeling/types'
import {isDesktopWeb} from 'platform/detection'
type Type = 'user' | 'algo' | 'list'
interface BaseUserAvatarProps {
type?: Type
size: number
avatar?: string | null
moderation?: AvatarModeration
}
interface UserAvatarProps extends BaseUserAvatarProps {
onSelectNewAvatar?: (img: RNImage | null) => void
}
interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
did: string
handle: string
}
const BLUR_AMOUNT = isWeb ? 5 : 100
function DefaultAvatar({type, size}: {type: Type; size: number}) {
@ -91,13 +109,7 @@ export function UserAvatar({
avatar,
moderation,
onSelectNewAvatar,
}: {
type?: Type
size: number
avatar?: string | null
moderation?: AvatarModeration
onSelectNewAvatar?: (img: RNImage | null) => void
}) {
}: UserAvatarProps) {
const store = useStores()
const pal = usePalette('default')
const {requestCameraAccessIfNeeded} = useCameraPermission()
@ -244,6 +256,32 @@ export function UserAvatar({
)
}
export function PreviewableUserAvatar(props: PreviewableUserAvatarProps) {
const store = useStores()
if (isDesktopWeb) {
return (
<Link href={`/profile/${props.handle}`} title={props.handle} asAnchor>
<UserAvatar {...props} />
</Link>
)
}
return (
<Pressable
onPress={() =>
store.shell.openModal({
name: 'profile-preview',
did: props.did,
})
}
accessibilityRole="button"
accessibilityLabel={props.handle}
accessibilityHint="">
<UserAvatar {...props} />
</Pressable>
)
}
const styles = StyleSheet.create({
editButtonContainer: {
position: 'absolute',

View File

@ -106,7 +106,6 @@ export const FeedsScreen = withAuthRequired(
onScroll={onMainScroll}
scrollEventThrottle={100}
headerOffset={HEADER_OFFSET}
showPostFollowBtn
/>
<ViewHeader
title="My Feeds"

View File

@ -266,7 +266,6 @@ const FeedPage = observer(
key="default"
feed={feed}
scrollElRef={scrollElRef}
showPostFollowBtn
onPressTryAgain={onPressTryAgain}
onScroll={onMainScroll}
scrollEventThrottle={100}