feat(post): add post reference functionality with inline cards and suggestion search
Add support for referencing/quoting other posts within post content. Includes: - New `post_ref` segment type with `PostRefSegmentData` interface for post references - New `PostRefCard` component to display referenced posts inline - Post suggestion search triggered by `#` in `PostMentionInput` - Updated `PostContentRenderer` to render post reference segments - Add `suggestPosts` and `recordRefClick` API methods in postService Additional improvements: - Add image gallery preview for user profile covers and avatars - Make text selectable across CommentItem, PostContentRenderer, and PostDetailScreen - Migrate FlatList to FlashList in PostDetailScreen and UserProfileScreen - Add nestedScrollEnabled to CreatePostScreen - Add clickable member avatars in GroupMembersScreen
This commit is contained in:
@@ -795,6 +795,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
nestedScrollEnabled
|
||||
>
|
||||
{/* 内容输入区 */}
|
||||
{renderContentSection()}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import {
|
||||
View,
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
RefreshControl,
|
||||
TouchableOpacity,
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
Clipboard,
|
||||
Dimensions,
|
||||
} from 'react-native';
|
||||
import { FlashList, ListRenderItem, FlashListRef } from '@shopify/flash-list';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useNavigation, useRouter, useLocalSearchParams } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
@@ -253,7 +253,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
const [isFollowing, setIsFollowing] = useState(false);
|
||||
const [isFollowingMe, setIsFollowingMe] = useState(false);
|
||||
const [isFollowLoading, setIsFollowLoading] = useState(false);
|
||||
const flatListRef = useRef<FlatList>(null);
|
||||
const flatListRef = useRef<FlashListRef<Comment> | null>(null);
|
||||
const hasRecordedView = useRef(false); // 是否已记录浏览量
|
||||
|
||||
// 举报相关状态
|
||||
@@ -1136,6 +1136,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
<View style={styles.titleContainer}>
|
||||
<Text
|
||||
variant="h2"
|
||||
selectable
|
||||
style={[
|
||||
styles.title,
|
||||
{
|
||||
@@ -1361,7 +1362,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
// 渲染评论 - 带楼层号
|
||||
const commentKeyExtractor = useCallback((item: Comment) => item.id, []);
|
||||
|
||||
const renderComment = useCallback(({ item, index }: { item: Comment; index: number }) => {
|
||||
const renderComment = useCallback<ListRenderItem<Comment>>(({ item, index }) => {
|
||||
const authorId = item.author?.id || '';
|
||||
// 收集当前评论的所有回复,用于根据 target_id 查找被回复用户
|
||||
const allReplies = item.replies || [];
|
||||
@@ -1597,10 +1598,10 @@ export const PostDetailScreen: React.FC = () => {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Emoji Panel —— 使用 FlatList 虚拟化渲染,解决显示不全和卡顿 */}
|
||||
{/* Emoji Panel —— 使用 FlashList 虚拟化渲染,解决显示不全和卡顿 */}
|
||||
{showEmojiPanel && (
|
||||
<View style={styles.emojiPanel}>
|
||||
<FlatList
|
||||
<FlashList
|
||||
data={EMOJI_ROWS}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={({ item }) => (
|
||||
@@ -1619,14 +1620,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
)}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
initialNumToRender={8}
|
||||
maxToRenderPerBatch={8}
|
||||
windowSize={5}
|
||||
getItemLayout={(_data, index) => ({
|
||||
length: EMOJI_ROW_HEIGHT,
|
||||
offset: EMOJI_ROW_HEIGHT * index,
|
||||
index,
|
||||
})}
|
||||
drawDistance={100}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
@@ -1637,7 +1631,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
|
||||
// 渲染评论列表
|
||||
const renderCommentsList = () => (
|
||||
<FlatList
|
||||
<FlashList
|
||||
ref={flatListRef}
|
||||
data={comments}
|
||||
renderItem={renderComment}
|
||||
@@ -1645,11 +1639,6 @@ export const PostDetailScreen: React.FC = () => {
|
||||
ListEmptyComponent={renderEmptyComments}
|
||||
contentContainerStyle={{ paddingBottom: responsiveGap }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScrollToIndexFailed={() => {
|
||||
setTimeout(() => {
|
||||
flatListRef.current?.scrollToEnd({ animated: true });
|
||||
}, 100);
|
||||
}}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isPostRefreshing || isCommentsRefreshing}
|
||||
@@ -1660,11 +1649,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
}
|
||||
onEndReached={handleLoadMoreComments}
|
||||
onEndReachedThreshold={0.3}
|
||||
initialNumToRender={10}
|
||||
maxToRenderPerBatch={10}
|
||||
updateCellsBatchingPeriod={60}
|
||||
windowSize={7}
|
||||
removeClippedSubviews
|
||||
drawDistance={250}
|
||||
ListFooterComponent={
|
||||
isCommentsInitialLoading ? (
|
||||
<View style={styles.commentsLoadingFooter}>
|
||||
@@ -1735,8 +1720,13 @@ export const PostDetailScreen: React.FC = () => {
|
||||
sidebarPosition="right"
|
||||
>
|
||||
<View style={styles.flex}>
|
||||
<ScrollView
|
||||
style={styles.flex}
|
||||
<FlashList
|
||||
ref={flatListRef}
|
||||
data={comments}
|
||||
renderItem={renderComment}
|
||||
keyExtractor={commentKeyExtractor}
|
||||
ListHeaderComponent={renderPostHeader}
|
||||
ListEmptyComponent={renderEmptyComments}
|
||||
contentContainerStyle={{ paddingBottom: responsivePadding }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
refreshControl={
|
||||
@@ -1747,10 +1737,34 @@ export const PostDetailScreen: React.FC = () => {
|
||||
tintColor={colors.primary.main}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{renderPostHeader()}
|
||||
{renderCommentsList()}
|
||||
</ScrollView>
|
||||
onEndReached={handleLoadMoreComments}
|
||||
onEndReachedThreshold={0.3}
|
||||
drawDistance={250}
|
||||
ListFooterComponent={
|
||||
isCommentsInitialLoading ? (
|
||||
<View style={styles.commentsLoadingFooter}>
|
||||
<Loading size="sm" />
|
||||
</View>
|
||||
) : isCommentsLoadingMore ? (
|
||||
<View style={styles.commentsLoadingFooter}>
|
||||
<Loading size="sm" />
|
||||
</View>
|
||||
) : hasMoreComments ? (
|
||||
<TouchableOpacity
|
||||
style={styles.loadMoreButton}
|
||||
onPress={handleLoadMoreComments}
|
||||
>
|
||||
<Text variant="caption" color={colors.primary.main}>
|
||||
加载更多评论
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
) : (comments?.length ?? 0) > 0 ? (
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.noMoreComments}>
|
||||
没有更多评论了
|
||||
</Text>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
{renderCommentInput()}
|
||||
</View>
|
||||
</AdaptiveLayout>
|
||||
@@ -1778,7 +1792,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={88}
|
||||
>
|
||||
<FlatList
|
||||
<FlashList
|
||||
ref={flatListRef}
|
||||
data={comments}
|
||||
renderItem={renderComment}
|
||||
@@ -1787,11 +1801,6 @@ export const PostDetailScreen: React.FC = () => {
|
||||
ListEmptyComponent={renderEmptyComments}
|
||||
contentContainerStyle={{ paddingBottom: responsiveGap }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScrollToIndexFailed={() => {
|
||||
setTimeout(() => {
|
||||
flatListRef.current?.scrollToEnd({ animated: true });
|
||||
}, 100);
|
||||
}}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isPostRefreshing || isCommentsRefreshing}
|
||||
@@ -1802,11 +1811,7 @@ export const PostDetailScreen: React.FC = () => {
|
||||
}
|
||||
onEndReached={handleLoadMoreComments}
|
||||
onEndReachedThreshold={0.3}
|
||||
initialNumToRender={10}
|
||||
maxToRenderPerBatch={10}
|
||||
updateCellsBatchingPeriod={60}
|
||||
windowSize={7}
|
||||
removeClippedSubviews
|
||||
drawDistance={250}
|
||||
ListFooterComponent={
|
||||
isCommentsInitialLoading ? (
|
||||
<View style={styles.commentsLoadingFooter}>
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import { hrefUserProfile } from '../../navigation/hrefs';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { blurActiveElement } from '../../infrastructure/platform';
|
||||
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
|
||||
@@ -383,24 +384,36 @@ const GroupMembersScreen: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 点击头像进入对方主页
|
||||
const handleAvatarPress = (member: GroupMemberResponse) => {
|
||||
if (member.user_id) {
|
||||
router.push(hrefUserProfile(String(member.user_id)));
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染成员项
|
||||
const renderMember: ListRenderItem<GroupMemberResponse> = ({ item }) => {
|
||||
const isSelf = item.user_id === currentUser?.id;
|
||||
const canManage = isAdmin && item.role !== 'owner' && (isOwner || item.role === 'member');
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.memberItem}
|
||||
onPress={() => canManage ? openActionModal(item) : null}
|
||||
onLongPress={() => isSelf ? openNicknameModal(item) : null}
|
||||
activeOpacity={canManage ? 0.7 : 1}
|
||||
>
|
||||
<Avatar
|
||||
source={item.user?.avatar}
|
||||
size={48}
|
||||
name={item.user?.nickname || item.nickname}
|
||||
/>
|
||||
<View style={styles.memberInfo}>
|
||||
<View style={styles.memberItem}>
|
||||
<TouchableOpacity
|
||||
onPress={() => handleAvatarPress(item)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Avatar
|
||||
source={item.user?.avatar}
|
||||
size={48}
|
||||
name={item.user?.nickname || item.nickname}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.memberInfo}
|
||||
onPress={() => isSelf ? openNicknameModal(item) : handleAvatarPress(item)}
|
||||
onLongPress={() => isSelf ? openNicknameModal(item) : null}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.memberNameRow}>
|
||||
<Text variant="body" style={styles.memberName}>
|
||||
{item.nickname || item.user?.nickname || '未知用户'}
|
||||
@@ -422,11 +435,16 @@ const GroupMembersScreen: React.FC = () => {
|
||||
<Text variant="label" color={colors.error.main}> 禁言中</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{canManage && (
|
||||
<MaterialCommunityIcons name="dots-vertical" size={20} color={colors.text.hint} />
|
||||
<TouchableOpacity
|
||||
onPress={() => openActionModal(item)}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
>
|
||||
<MaterialCommunityIcons name="dots-vertical" size={20} color={colors.text.hint} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
* 支持桌面端双栏布局
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import {
|
||||
View,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
} from 'react-native';
|
||||
import { FlashList, ListRenderItem } from '@shopify/flash-list';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useAppColors } from '../../theme';
|
||||
import { Post } from '../../types';
|
||||
@@ -54,113 +55,90 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
|
||||
currentUser,
|
||||
} = useUserProfile({ mode, userId, isDesktop, isTablet });
|
||||
|
||||
// 渲染帖子列表 - Twitter 风格无边框
|
||||
const renderPostList = useCallback((postList: Post[], emptyTitle: string, emptyDesc: string, blockedTitle?: string, blockedDesc?: string) => {
|
||||
if (isBlockedProfile && blockedTitle) {
|
||||
// 当前显示的帖子列表
|
||||
const currentPosts = activeTab === 0 ? posts : favorites;
|
||||
|
||||
// 渲染帖子项
|
||||
const renderPostItem = useCallback<ListRenderItem<Post>>(({ item, index }) => {
|
||||
const isPostAuthor = currentUser?.id === item.author?.id;
|
||||
const isLast = index === currentPosts.length - 1;
|
||||
return (
|
||||
<View style={[
|
||||
sharedStyles.postWrapper,
|
||||
isLast && sharedStyles.lastPost,
|
||||
]}>
|
||||
<PostCard
|
||||
post={item}
|
||||
onAction={(action) => handlePostAction(item, action)}
|
||||
isPostAuthor={isPostAuthor}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}, [currentUser?.id, handlePostAction, currentPosts.length]);
|
||||
|
||||
const postKeyExtractor = useCallback((item: Post) => item.id, []);
|
||||
|
||||
// 渲染空状态
|
||||
const renderEmptyPosts = useCallback(() => {
|
||||
if (loading) return <Loading />;
|
||||
|
||||
if (isBlockedProfile && activeTab === 0 && mode === 'other') {
|
||||
return (
|
||||
<EmptyState
|
||||
title={blockedTitle}
|
||||
description={blockedDesc || ''}
|
||||
title="已将该用户拉黑"
|
||||
description="你已将此用户拉黑,不再显示其帖子"
|
||||
icon="account-off-outline"
|
||||
variant="modern"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (postList.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
title={emptyTitle}
|
||||
description={emptyDesc}
|
||||
icon={activeTab === 0 ? 'file-document-edit-outline' : 'bookmark-heart-outline'}
|
||||
variant="modern"
|
||||
/>
|
||||
);
|
||||
}
|
||||
const emptyTitle = activeTab === 0
|
||||
? (mode === 'self' ? '还没有帖子' : '这个用户还没有发布任何帖子')
|
||||
: (mode === 'self' ? '还没有收藏' : '这个用户还没有收藏任何帖子');
|
||||
const emptyDesc = activeTab === 0
|
||||
? (mode === 'self' ? '分享你的想法,发布第一条帖子吧' : '')
|
||||
: (mode === 'self' ? '发现喜欢的内容,点击收藏按钮保存' : '');
|
||||
|
||||
return (
|
||||
<View style={sharedStyles.postsContainer}>
|
||||
{postList.map((post, index) => {
|
||||
const isPostAuthor = currentUser?.id === post.author?.id;
|
||||
return (
|
||||
<View key={post.id} style={[
|
||||
sharedStyles.postWrapper,
|
||||
index === postList.length - 1 && sharedStyles.lastPost,
|
||||
]}>
|
||||
<PostCard
|
||||
post={post}
|
||||
onAction={(action) => handlePostAction(post, action)}
|
||||
isPostAuthor={isPostAuthor}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}, [currentUser?.id, handlePostAction, activeTab, isBlockedProfile]);
|
||||
|
||||
// 渲染内容
|
||||
const renderContent = useCallback(() => {
|
||||
if (loading) return <Loading />;
|
||||
|
||||
if (activeTab === 0) {
|
||||
return renderPostList(
|
||||
posts,
|
||||
mode === 'self' ? '还没有帖子' : '这个用户还没有发布任何帖子',
|
||||
mode === 'self' ? '分享你的想法,发布第一条帖子吧' : '',
|
||||
mode === 'other' ? '已将该用户拉黑' : undefined,
|
||||
mode === 'other' ? '你已将此用户拉黑,不再显示其帖子' : undefined
|
||||
);
|
||||
}
|
||||
|
||||
if (activeTab === 1) {
|
||||
return renderPostList(
|
||||
favorites,
|
||||
mode === 'self' ? '还没有收藏' : '这个用户还没有收藏任何帖子',
|
||||
mode === 'self' ? '发现喜欢的内容,点击收藏按钮保存' : ''
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [loading, activeTab, posts, favorites, mode, renderPostList]);
|
||||
|
||||
// 渲染用户信息头部
|
||||
const renderUserHeader = useMemo(() => {
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<UserProfileHeader
|
||||
user={user}
|
||||
isCurrentUser={isCurrentUser}
|
||||
isBlocked={isBlocked}
|
||||
onFollow={handleFollow}
|
||||
onSettings={handleSettings}
|
||||
onEditProfile={handleEditProfile}
|
||||
onMessage={handleMessage}
|
||||
onBlock={handleBlock}
|
||||
onFollowingPress={handleFollowingPress}
|
||||
onFollowersPress={handleFollowersPress}
|
||||
<EmptyState
|
||||
title={emptyTitle}
|
||||
description={emptyDesc}
|
||||
icon={activeTab === 0 ? 'file-document-edit-outline' : 'bookmark-heart-outline'}
|
||||
variant="modern"
|
||||
/>
|
||||
);
|
||||
}, [user, isCurrentUser, isBlocked, handleFollow, handleSettings, handleEditProfile, handleMessage, handleBlock, handleFollowingPress, handleFollowersPress]);
|
||||
}, [loading, activeTab, mode, isBlockedProfile]);
|
||||
|
||||
// 渲染 TabBar 和内容
|
||||
const renderTabBarAndContent = useMemo(() => (
|
||||
<>
|
||||
<View style={sharedStyles.tabBarContainer}>
|
||||
<TabBar
|
||||
tabs={TABS}
|
||||
activeIndex={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
variant="modern"
|
||||
icons={TAB_ICONS}
|
||||
// 渲染 FlashList 头部(用户信息 + TabBar)
|
||||
const renderListHeader = useCallback(() => {
|
||||
if (!user) return null;
|
||||
return (
|
||||
<>
|
||||
<UserProfileHeader
|
||||
user={user}
|
||||
isCurrentUser={isCurrentUser}
|
||||
isBlocked={isBlocked}
|
||||
onFollow={handleFollow}
|
||||
onSettings={handleSettings}
|
||||
onEditProfile={handleEditProfile}
|
||||
onMessage={handleMessage}
|
||||
onBlock={handleBlock}
|
||||
onFollowingPress={handleFollowingPress}
|
||||
onFollowersPress={handleFollowersPress}
|
||||
/>
|
||||
</View>
|
||||
<View style={sharedStyles.contentContainer}>
|
||||
{renderContent()}
|
||||
</View>
|
||||
</>
|
||||
), [activeTab, renderContent]);
|
||||
<View style={sharedStyles.tabBarContainer}>
|
||||
<TabBar
|
||||
tabs={TABS}
|
||||
activeIndex={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
variant="modern"
|
||||
icons={TAB_ICONS}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}, [user, isCurrentUser, isBlocked, handleFollow, handleSettings, handleEditProfile, handleMessage, handleBlock, handleFollowingPress, handleFollowersPress, activeTab, setActiveTab]);
|
||||
|
||||
// 未登录/用户不存在状态
|
||||
if (mode === 'self' && !currentUser) {
|
||||
@@ -209,18 +187,40 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
|
||||
/>
|
||||
}
|
||||
>
|
||||
{renderUserHeader}
|
||||
{renderListHeader()}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 右侧:帖子列表 */}
|
||||
<View style={sharedStyles.desktopContent}>
|
||||
<ScrollView
|
||||
<FlashList
|
||||
data={currentPosts}
|
||||
renderItem={renderPostItem}
|
||||
keyExtractor={postKeyExtractor}
|
||||
ListHeaderComponent={
|
||||
<View style={sharedStyles.tabBarContainer}>
|
||||
<TabBar
|
||||
tabs={TABS}
|
||||
activeIndex={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
variant="modern"
|
||||
icons={TAB_ICONS}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
ListEmptyComponent={renderEmptyPosts}
|
||||
contentContainerStyle={{ paddingBottom: scrollBottomInset }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={[sharedStyles.desktopScrollContent, { paddingBottom: scrollBottomInset }]}
|
||||
>
|
||||
{renderTabBarAndContent}
|
||||
</ScrollView>
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={[colors.primary.main]}
|
||||
tintColor={colors.primary.main}
|
||||
/>
|
||||
}
|
||||
drawDistance={250}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</ResponsiveContainer>
|
||||
@@ -231,7 +231,13 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
|
||||
// 移动端使用单栏布局
|
||||
return (
|
||||
<SafeAreaView style={sharedStyles.container} edges={safeAreaEdges as any}>
|
||||
<ScrollView
|
||||
<FlashList
|
||||
data={currentPosts}
|
||||
renderItem={renderPostItem}
|
||||
keyExtractor={postKeyExtractor}
|
||||
ListHeaderComponent={renderListHeader}
|
||||
ListEmptyComponent={renderEmptyPosts}
|
||||
contentContainerStyle={{ paddingBottom: scrollBottomInset }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
@@ -241,11 +247,8 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
|
||||
tintColor={colors.primary.main}
|
||||
/>
|
||||
}
|
||||
contentContainerStyle={[sharedStyles.scrollContent, { paddingBottom: scrollBottomInset }]}
|
||||
>
|
||||
{renderUserHeader}
|
||||
{renderTabBarAndContent}
|
||||
</ScrollView>
|
||||
drawDistance={250}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user