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:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user