dev #5
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "carrot_bbs",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "carrot_bbs",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^15.1.1",
|
||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||
|
||||
74
src/components/business/PostCard/PostCard.tsx
Normal file
74
src/components/business/PostCard/PostCard.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* PostCard 主组件
|
||||
* 帖子卡片组件 - 支持列表模式和网格模式
|
||||
*
|
||||
* @example
|
||||
* // 新 API 使用方式
|
||||
* <PostCard
|
||||
* post={post}
|
||||
* onAction={(action) => handleAction(post, action)}
|
||||
* variant="list"
|
||||
* features="full"
|
||||
* />
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { PostCardProps, LegacyPostCardProps } from './types';
|
||||
import PostCardList from './PostCardList';
|
||||
import PostCardGrid from './PostCardGrid';
|
||||
import { usePostCardFeatures } from './hooks/usePostCardFeatures';
|
||||
import { createLegacyAdapter, isLegacyProps } from './legacyAdapter';
|
||||
|
||||
/**
|
||||
* PostCard 主组件
|
||||
* 支持新旧两种 API
|
||||
*/
|
||||
const PostCard: React.FC<PostCardProps | LegacyPostCardProps> = (props) => {
|
||||
// 检测是否使用旧 API
|
||||
const isLegacy = isLegacyProps(props as LegacyPostCardProps);
|
||||
|
||||
// 兼容层:转换旧 Props 到新 Props
|
||||
const normalizedProps: PostCardProps = isLegacy
|
||||
? createLegacyAdapter(props as LegacyPostCardProps)
|
||||
: (props as PostCardProps);
|
||||
|
||||
const {
|
||||
post,
|
||||
onAction,
|
||||
variant = 'list',
|
||||
features: featuresConfig,
|
||||
isPostAuthor = false,
|
||||
isAuthor = false,
|
||||
index,
|
||||
style,
|
||||
} = normalizedProps;
|
||||
|
||||
// 解析 Features 配置
|
||||
const resolvedFeatures = usePostCardFeatures(featuresConfig);
|
||||
|
||||
// 根据 variant 选择渲染模式
|
||||
if (variant === 'grid') {
|
||||
return (
|
||||
<PostCardGrid
|
||||
post={post}
|
||||
onAction={onAction}
|
||||
features={resolvedFeatures}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PostCardList
|
||||
post={post}
|
||||
onAction={onAction}
|
||||
features={resolvedFeatures}
|
||||
isPostAuthor={isPostAuthor}
|
||||
isAuthor={isAuthor}
|
||||
index={index}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostCard;
|
||||
115
src/components/business/PostCard/PostCardGrid.tsx
Normal file
115
src/components/business/PostCard/PostCardGrid.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* PostCardGrid 容器组件
|
||||
* 网格模式(小红书风格)的帖子卡片容器
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { TouchableOpacity, StyleSheet, StyleProp, ViewStyle, GestureResponderEvent } from 'react-native';
|
||||
import { colors, borderRadius } from '../../../theme';
|
||||
import { useResponsive } from '../../../hooks/useResponsive';
|
||||
import { PostCardGridProps } from './types';
|
||||
import { usePostCardActions } from './hooks/usePostCardActions';
|
||||
import { usePostGridStyles } from './hooks/usePostCardStyles';
|
||||
import { PostGridCover, PostGridInfo } from './components';
|
||||
import { convertToImageGridItems, getConsistentAspectRatio } from './utils';
|
||||
|
||||
const PostCardGrid: React.FC<PostCardGridProps> = ({
|
||||
post,
|
||||
onAction,
|
||||
features,
|
||||
style,
|
||||
}) => {
|
||||
const { isDesktop } = useResponsive();
|
||||
const gridStyles = usePostGridStyles();
|
||||
|
||||
const {
|
||||
handlePress,
|
||||
handleUserPress,
|
||||
handleImagePress,
|
||||
} = usePostCardActions(onAction);
|
||||
|
||||
// 防御性检查:确保 author 存在
|
||||
const author = post.author ?? {
|
||||
id: '',
|
||||
nickname: '匿名用户',
|
||||
avatar: '',
|
||||
username: '',
|
||||
cover_url: '',
|
||||
bio: '',
|
||||
website: '',
|
||||
location: '',
|
||||
posts_count: 0,
|
||||
followers_count: 0,
|
||||
following_count: 0,
|
||||
created_at: '',
|
||||
};
|
||||
|
||||
// 获取封面图(第一张图)
|
||||
const coverImage = post.images && post.images.length > 0 ? post.images[0] : null;
|
||||
|
||||
// 根据帖子 ID 生成一致的宽高比
|
||||
const aspectRatio = getConsistentAspectRatio(post.id);
|
||||
|
||||
// 样式计算
|
||||
const containerStyle: StyleProp<ViewStyle> = [
|
||||
styles.container,
|
||||
!coverImage && styles.containerNoImage,
|
||||
isDesktop && styles.containerDesktop,
|
||||
];
|
||||
|
||||
const handleContainerPress = () => {
|
||||
handlePress();
|
||||
};
|
||||
|
||||
const handleImagePressLocal = () => {
|
||||
if (post.images && post.images.length > 0) {
|
||||
handleImagePress(convertToImageGridItems(post.images), 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUserPressLocal = () => {
|
||||
handleUserPress();
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[containerStyle, style]}
|
||||
onPress={handleContainerPress}
|
||||
activeOpacity={0.9}
|
||||
>
|
||||
{/* 封面 */}
|
||||
<PostGridCover
|
||||
image={coverImage}
|
||||
content={post.content}
|
||||
isVote={post.is_vote}
|
||||
aspectRatio={aspectRatio}
|
||||
onImagePress={handleImagePressLocal}
|
||||
/>
|
||||
|
||||
{/* 标题和信息 */}
|
||||
<PostGridInfo
|
||||
title={post.title}
|
||||
author={author}
|
||||
likesCount={post.likes_count}
|
||||
onUserPress={handleUserPressLocal}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#FFF',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 8,
|
||||
},
|
||||
containerDesktop: {
|
||||
borderRadius: 12,
|
||||
},
|
||||
containerNoImage: {
|
||||
minHeight: 200,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
});
|
||||
|
||||
export default PostCardGrid;
|
||||
53
src/components/business/PostCard/components/PostImages.tsx
Normal file
53
src/components/business/PostCard/components/PostImages.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* PostImages 子组件
|
||||
* 显示帖子图片网格
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { colors, spacing, borderRadius } from '../../../../theme';
|
||||
import { useResponsive } from '../../../../hooks/useResponsive';
|
||||
import { ImageGrid, ImageGridItem } from '../../../common';
|
||||
import { PostImagesProps } from '../types';
|
||||
|
||||
const PostImages: React.FC<PostImagesProps> = ({ images, displayMode, onImagePress }) => {
|
||||
const { isDesktop, isWideScreen } = useResponsive();
|
||||
|
||||
if (!images || images.length === 0) return null;
|
||||
|
||||
// 计算图片间距和圆角
|
||||
const imageGap = isDesktop ? 12 : 8;
|
||||
const imageBorderRadius = isDesktop ? borderRadius.xl : borderRadius.md;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ImageGrid
|
||||
images={images.map((img) => ({
|
||||
id: img.id,
|
||||
url: img.url,
|
||||
thumbnail_url: img.thumbnail_url,
|
||||
preview_url: img.preview_url,
|
||||
preview_url_large: img.preview_url_large,
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
}))}
|
||||
maxDisplayCount={isWideScreen ? 12 : 9}
|
||||
mode="auto"
|
||||
gap={imageGap}
|
||||
borderRadius={imageBorderRadius}
|
||||
showMoreOverlay={true}
|
||||
onImagePress={onImagePress}
|
||||
displayMode={displayMode}
|
||||
usePreview={true}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default PostImages;
|
||||
12
src/components/business/PostCard/components/index.ts
Normal file
12
src/components/business/PostCard/components/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* PostCard 子组件导出
|
||||
*/
|
||||
|
||||
export { default as PostHeader } from './PostHeader';
|
||||
export { default as PostTitle } from './PostTitle';
|
||||
export { default as PostContent } from './PostContent';
|
||||
export { default as PostImages } from './PostImages';
|
||||
export { default as PostActions } from './PostActions';
|
||||
export { default as PostTopComment } from './PostTopComment';
|
||||
export { default as PostGridCover } from './PostGridCover';
|
||||
export { default as PostGridInfo } from './PostGridInfo';
|
||||
8
src/components/business/PostCard/hooks/index.ts
Normal file
8
src/components/business/PostCard/hooks/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* PostCard Hooks 导出
|
||||
*/
|
||||
|
||||
export { usePostCardActions } from './usePostCardActions';
|
||||
export { usePostCardFeatures } from './usePostCardFeatures';
|
||||
export { usePostCardStyles, usePostGridStyles } from './usePostCardStyles';
|
||||
export type { PostCardStylesConfig, PostGridStylesConfig } from './usePostCardStyles';
|
||||
58
src/components/business/PostCard/index.ts
Normal file
58
src/components/business/PostCard/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* PostCard 组件模块导出
|
||||
*/
|
||||
|
||||
// 主组件(默认导出)
|
||||
export { default } from './PostCard';
|
||||
|
||||
// 类型导出
|
||||
export type {
|
||||
PostCardActionType,
|
||||
PostCardActionPayload,
|
||||
PostCardAction,
|
||||
PostCardOnAction,
|
||||
PostCardFeatures,
|
||||
PostCardFeaturesPreset,
|
||||
PostCardFeaturesConfig,
|
||||
PostCardProps,
|
||||
PostHeaderProps,
|
||||
PostTitleProps,
|
||||
PostContentProps,
|
||||
PostImagesProps,
|
||||
PostActionsProps,
|
||||
PostVotePreviewProps,
|
||||
PostTopCommentProps,
|
||||
PostGridCoverProps,
|
||||
PostGridInfoProps,
|
||||
PostCardListProps,
|
||||
PostCardGridProps,
|
||||
LegacyPostCardProps,
|
||||
Badge,
|
||||
} from './types';
|
||||
|
||||
// 预设配置导出
|
||||
export { PostCardPresets, defaultFeatures, resolveFeatures } from './presets';
|
||||
|
||||
// Hooks 导出
|
||||
export {
|
||||
usePostCardActions,
|
||||
usePostCardFeatures,
|
||||
usePostCardStyles,
|
||||
usePostGridStyles,
|
||||
} from './hooks';
|
||||
export type { PostCardStylesConfig, PostGridStylesConfig } from './hooks';
|
||||
|
||||
// 子组件导出
|
||||
export {
|
||||
PostHeader,
|
||||
PostTitle,
|
||||
PostContent,
|
||||
PostImages,
|
||||
PostActions,
|
||||
PostTopComment,
|
||||
PostGridCover,
|
||||
PostGridInfo,
|
||||
} from './components';
|
||||
|
||||
// 兼容层导出(用于迁移帮助)
|
||||
export { isLegacyProps, createLegacyAdapter } from './legacyAdapter';
|
||||
285
src/components/business/PostCard/types.ts
Normal file
285
src/components/business/PostCard/types.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* PostCard 组件类型定义
|
||||
* 统一的帖子卡片组件类型系统
|
||||
*/
|
||||
|
||||
import { StyleProp, ViewStyle } from 'react-native';
|
||||
import { Post, PostImageDTO, UserDTO, CommentDTO } from '../../../types';
|
||||
import { ImageGridItem } from '../../common';
|
||||
|
||||
// ==================== Action 类型定义 ====================
|
||||
|
||||
/**
|
||||
* PostCard 支持的操作类型
|
||||
*/
|
||||
export type PostCardActionType =
|
||||
| 'press' // 点击帖子主体
|
||||
| 'userPress' // 点击用户头像/名称
|
||||
| 'like' // 点赞
|
||||
| 'unlike' // 取消点赞
|
||||
| 'comment' // 评论
|
||||
| 'bookmark' // 收藏
|
||||
| 'unbookmark' // 取消收藏
|
||||
| 'share' // 分享
|
||||
| 'imagePress' // 点击图片
|
||||
| 'delete'; // 删除
|
||||
|
||||
/**
|
||||
* Action payload 类型
|
||||
*/
|
||||
export interface PostCardActionPayload {
|
||||
images?: ImageGridItem[];
|
||||
imageIndex?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的 Action 对象
|
||||
*/
|
||||
export interface PostCardAction {
|
||||
type: PostCardActionType;
|
||||
payload?: PostCardActionPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的回调函数类型
|
||||
*/
|
||||
export type PostCardOnAction = (action: PostCardAction) => void;
|
||||
|
||||
// ==================== Features 配置类型 ====================
|
||||
|
||||
/**
|
||||
* 显示特性配置
|
||||
*/
|
||||
export interface PostCardFeatures {
|
||||
/** 显示用户信息 */
|
||||
showAuthor?: boolean;
|
||||
/** 显示标题 */
|
||||
showTitle?: boolean;
|
||||
/** 显示内容 */
|
||||
showContent?: boolean;
|
||||
/** 显示图片 */
|
||||
showImages?: boolean;
|
||||
/** 显示投票预览 */
|
||||
showVote?: boolean;
|
||||
/** 显示热门评论 */
|
||||
showTopComment?: boolean;
|
||||
/** 显示操作栏 */
|
||||
showActions?: boolean;
|
||||
/** 显示浏览数 */
|
||||
showViews?: boolean;
|
||||
/** 显示分享按钮 */
|
||||
showShare?: boolean;
|
||||
/** 显示收藏按钮 */
|
||||
showBookmark?: boolean;
|
||||
/** 显示删除按钮(需配合权限判断) */
|
||||
showDelete?: boolean;
|
||||
/** 显示置顶标签 */
|
||||
showPinned?: boolean;
|
||||
/** 显示徽章(楼主/管理员) */
|
||||
showBadges?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Features 预设名称
|
||||
*/
|
||||
export type PostCardFeaturesPreset = 'full' | 'compact' | 'grid';
|
||||
|
||||
/**
|
||||
* Features 配置(可以是预设名称或自定义对象)
|
||||
*/
|
||||
export type PostCardFeaturesConfig = PostCardFeaturesPreset | PostCardFeatures;
|
||||
|
||||
// ==================== 主组件 Props ====================
|
||||
|
||||
/**
|
||||
* PostCard 主组件 Props
|
||||
*/
|
||||
export interface PostCardProps {
|
||||
/** 帖子数据 */
|
||||
post: Post;
|
||||
|
||||
/** 统一事件回调 */
|
||||
onAction: PostCardOnAction;
|
||||
|
||||
/** 显示模式:列表模式 | 网格模式 */
|
||||
variant?: 'list' | 'grid';
|
||||
|
||||
/** 预设配置名称,或自定义配置对象 */
|
||||
features?: PostCardFeaturesConfig;
|
||||
|
||||
/** 当前用户是否为帖子作者(用于显示删除按钮) */
|
||||
isPostAuthor?: boolean;
|
||||
|
||||
/** 当前用户是否为楼主(在帖子详情页显示"楼主"徽章) */
|
||||
isAuthor?: boolean;
|
||||
|
||||
/** 索引(用于虚拟化列表优化) */
|
||||
index?: number;
|
||||
|
||||
/** 自定义样式 */
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
// ==================== 子组件 Props ====================
|
||||
|
||||
/**
|
||||
* PostHeader 子组件 Props
|
||||
*/
|
||||
export interface PostHeaderProps {
|
||||
author: UserDTO;
|
||||
createdAt: string;
|
||||
editedAt?: string;
|
||||
isPinned?: boolean;
|
||||
badges?: Array<{ type: 'author' | 'admin'; label: string }>;
|
||||
onDelete?: () => void;
|
||||
onUserPress: () => void;
|
||||
showDelete?: boolean;
|
||||
showPinned?: boolean;
|
||||
showBadges?: boolean;
|
||||
isDeleting?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostTitle 子组件 Props
|
||||
*/
|
||||
export interface PostTitleProps {
|
||||
title?: string;
|
||||
numberOfLines?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostContent 子组件 Props
|
||||
*/
|
||||
export interface PostContentProps {
|
||||
content?: string;
|
||||
compact?: boolean;
|
||||
maxLines?: number;
|
||||
isExpanded?: boolean;
|
||||
onToggleExpand?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostImages 子组件 Props
|
||||
*/
|
||||
export interface PostImagesProps {
|
||||
images: PostImageDTO[];
|
||||
displayMode: 'list' | 'grid';
|
||||
onImagePress: (images: ImageGridItem[], index: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostActions 子组件 Props
|
||||
*/
|
||||
export interface PostActionsProps {
|
||||
viewsCount: number;
|
||||
likesCount: number;
|
||||
commentsCount: number;
|
||||
isLiked: boolean;
|
||||
isFavorited: boolean;
|
||||
onLike: () => void;
|
||||
onComment: () => void;
|
||||
onShare: () => void;
|
||||
onBookmark: () => void;
|
||||
showViews?: boolean;
|
||||
showShare?: boolean;
|
||||
showBookmark?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostVotePreview 子组件 Props
|
||||
*/
|
||||
export interface PostVotePreviewProps {
|
||||
isVote: boolean;
|
||||
totalVotes?: number;
|
||||
optionsCount?: number;
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostTopComment 子组件 Props
|
||||
*/
|
||||
export interface PostTopCommentProps {
|
||||
comment: CommentDTO | null;
|
||||
totalComments: number;
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostGridCover 子组件 Props
|
||||
*/
|
||||
export interface PostGridCoverProps {
|
||||
image: PostImageDTO | null;
|
||||
content?: string;
|
||||
isVote?: boolean;
|
||||
aspectRatio: number;
|
||||
onImagePress: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostGridInfo 子组件 Props
|
||||
*/
|
||||
export interface PostGridInfoProps {
|
||||
title?: string;
|
||||
author: UserDTO;
|
||||
likesCount: number;
|
||||
onUserPress: () => void;
|
||||
}
|
||||
|
||||
// ==================== 容器组件 Props ====================
|
||||
|
||||
/**
|
||||
* PostCardList 容器组件 Props
|
||||
*/
|
||||
export interface PostCardListProps {
|
||||
post: Post;
|
||||
onAction: PostCardOnAction;
|
||||
features: PostCardFeatures;
|
||||
isPostAuthor?: boolean;
|
||||
isAuthor?: boolean;
|
||||
index?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostCardGrid 容器组件 Props
|
||||
*/
|
||||
export interface PostCardGridProps {
|
||||
post: Post;
|
||||
onAction: PostCardOnAction;
|
||||
features: PostCardFeatures;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
// ==================== 旧版 Props(兼容层使用) ====================
|
||||
|
||||
/**
|
||||
* 旧版 PostCard Props(用于兼容层)
|
||||
* @deprecated 请迁移到新的 PostCardProps
|
||||
*/
|
||||
export interface LegacyPostCardProps {
|
||||
post: Post;
|
||||
onPress: () => void;
|
||||
onUserPress: () => void;
|
||||
onLike: () => void;
|
||||
onComment: () => void;
|
||||
onBookmark: () => void;
|
||||
onShare: () => void;
|
||||
onImagePress?: (images: ImageGridItem[], index: number) => void;
|
||||
onDelete?: () => void;
|
||||
compact?: boolean;
|
||||
index?: number;
|
||||
isAuthor?: boolean;
|
||||
isPostAuthor?: boolean;
|
||||
variant?: 'default' | 'grid';
|
||||
}
|
||||
|
||||
// ==================== 工具类型 ====================
|
||||
|
||||
/**
|
||||
* 徽章类型
|
||||
*/
|
||||
export interface Badge {
|
||||
type: 'author' | 'admin';
|
||||
label: string;
|
||||
}
|
||||
@@ -2,7 +2,21 @@
|
||||
* 业务组件导出
|
||||
*/
|
||||
|
||||
// PostCard 新架构(从 PostCard 目录导出)
|
||||
export { default as PostCard } from './PostCard';
|
||||
// 导出 PostCard 相关类型和工具
|
||||
export type {
|
||||
PostCardActionType,
|
||||
PostCardAction,
|
||||
PostCardOnAction,
|
||||
PostCardFeatures,
|
||||
PostCardFeaturesPreset,
|
||||
PostCardFeaturesConfig,
|
||||
PostCardProps,
|
||||
LegacyPostCardProps,
|
||||
} from './PostCard';
|
||||
export { PostCardPresets, resolveFeatures } from './PostCard';
|
||||
export { usePostCardActions, usePostCardFeatures } from './PostCard';
|
||||
export { default as CommentItem } from './CommentItem';
|
||||
export { default as UserProfileHeader } from './UserProfileHeader';
|
||||
export { default as SystemMessageItem } from './SystemMessageItem';
|
||||
|
||||
@@ -29,6 +29,7 @@ import { useUserStore } from '../../stores';
|
||||
import { useCurrentUser } from '../../stores/authStore';
|
||||
import { postService } from '../../services';
|
||||
import { PostCard, TabBar, SearchBar } from '../../components/business';
|
||||
import type { PostCardAction } from '../../components/business/PostCard';
|
||||
import { Loading, EmptyState, Text, ImageGallery, ImageGridItem, ResponsiveGrid } from '../../components/common';
|
||||
import { HomeStackParamList, RootStackParamList } from '../../navigation/types';
|
||||
import { useResponsive, useResponsiveSpacing } from '../../hooks/useResponsive';
|
||||
@@ -368,6 +369,41 @@ export const HomeScreen: React.FC = () => {
|
||||
setShowImageViewer(true);
|
||||
};
|
||||
|
||||
// 统一处理 PostCard 的所有操作
|
||||
const handlePostAction = (post: Post, action: PostCardAction) => {
|
||||
switch (action.type) {
|
||||
case 'press':
|
||||
handlePostPress(post.id);
|
||||
break;
|
||||
case 'userPress':
|
||||
const authorId = post.author?.id;
|
||||
if (authorId) handleUserPress(authorId);
|
||||
break;
|
||||
case 'like':
|
||||
case 'unlike':
|
||||
handleLike(post);
|
||||
break;
|
||||
case 'comment':
|
||||
handlePostPress(post.id, true);
|
||||
break;
|
||||
case 'bookmark':
|
||||
case 'unbookmark':
|
||||
handleBookmark(post);
|
||||
break;
|
||||
case 'share':
|
||||
handleShare(post);
|
||||
break;
|
||||
case 'imagePress':
|
||||
if (action.payload?.images && action.payload?.imageIndex !== undefined) {
|
||||
handleImagePress(action.payload.images, action.payload.imageIndex);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
handleDeletePost(post.id);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到发帖页面(使用 Modal 方式)
|
||||
const handleCreatePost = () => {
|
||||
setShowCreatePost(true);
|
||||
@@ -392,19 +428,12 @@ export const HomeScreen: React.FC = () => {
|
||||
]}>
|
||||
<PostCard
|
||||
post={item}
|
||||
onPress={() => handlePostPress(item.id)}
|
||||
onUserPress={() => authorId && handleUserPress(authorId)}
|
||||
onLike={() => handleLike(item)}
|
||||
onComment={() => handlePostPress(item.id, true)}
|
||||
onBookmark={() => handleBookmark(item)}
|
||||
onShare={() => handleShare(item)}
|
||||
onImagePress={(images, index) => handleImagePress(images, index)}
|
||||
onDelete={() => handleDeletePost(item.id)}
|
||||
onAction={(action) => handlePostAction(item, action)}
|
||||
isPostAuthor={isPostAuthor}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}, [currentUser?.id, handleBookmark, handleDeletePost, handleImagePress, handleLike, handlePostPress, handleShare, handleUserPress, isMobile, listItemWidth, responsiveGap]);
|
||||
}, [currentUser?.id, handlePostAction, isMobile, listItemWidth, responsiveGap]);
|
||||
|
||||
// 估算帖子在瀑布流中的高度(用于均匀分配)
|
||||
const estimatePostHeight = (post: Post, columnWidth: number): number => {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Post } from '../../types';
|
||||
import { useAuthStore, useUserStore } from '../../stores';
|
||||
import { postService } from '../../services';
|
||||
import { UserProfileHeader, PostCard, TabBar } from '../../components/business';
|
||||
import { PostCardAction } from '../../components/business/PostCard';
|
||||
import { Loading, EmptyState, Text } from '../../components/common';
|
||||
import { ResponsiveContainer } from '../../components/common';
|
||||
import { useResponsive } from '../../hooks';
|
||||
@@ -245,6 +246,37 @@ export const ProfileScreen: React.FC = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 统一的 PostCard Action 处理
|
||||
const handlePostAction = useCallback((post: Post, action: PostCardAction) => {
|
||||
switch (action.type) {
|
||||
case 'press':
|
||||
handlePostPress(post.id);
|
||||
break;
|
||||
case 'userPress':
|
||||
if (post.author) {
|
||||
handleUserPress(post.author.id);
|
||||
}
|
||||
break;
|
||||
case 'like':
|
||||
case 'unlike':
|
||||
post.is_liked ? postService.unlikePost(post.id) : postService.likePost(post.id);
|
||||
break;
|
||||
case 'comment':
|
||||
handlePostPress(post.id, true);
|
||||
break;
|
||||
case 'bookmark':
|
||||
case 'unbookmark':
|
||||
post.is_favorited ? postService.unfavoritePost(post.id) : postService.favoritePost(post.id);
|
||||
break;
|
||||
case 'share':
|
||||
// 暂不处理分享
|
||||
break;
|
||||
case 'delete':
|
||||
handleDeletePost(post.id);
|
||||
break;
|
||||
}
|
||||
}, [handlePostPress, handleUserPress, handleDeletePost]);
|
||||
|
||||
// 渲染内容
|
||||
const renderContent = useCallback(() => {
|
||||
if (loading) return <Loading />;
|
||||
@@ -273,13 +305,7 @@ export const ProfileScreen: React.FC = () => {
|
||||
]}>
|
||||
<PostCard
|
||||
post={post}
|
||||
onPress={() => handlePostPress(post.id)}
|
||||
onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}}
|
||||
onLike={() => post.is_liked ? postService.unlikePost(post.id) : postService.likePost(post.id)}
|
||||
onComment={() => handlePostPress(post.id, true)}
|
||||
onBookmark={() => post.is_favorited ? postService.unfavoritePost(post.id) : postService.favoritePost(post.id)}
|
||||
onShare={() => {}}
|
||||
onDelete={() => handleDeletePost(post.id)}
|
||||
onAction={(action) => handlePostAction(post, action)}
|
||||
isPostAuthor={isPostAuthor}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { useCurrentUser } from '../../stores/authStore';
|
||||
import { authService, postService, messageService } from '../../services';
|
||||
import { userManager } from '../../stores/userManager';
|
||||
import { UserProfileHeader, PostCard, TabBar } from '../../components/business';
|
||||
import { PostCardAction } from '../../components/business/PostCard';
|
||||
import { Loading, EmptyState, ResponsiveContainer } from '../../components/common';
|
||||
import { useResponsive } from '../../hooks';
|
||||
import { HomeStackParamList, RootStackParamList } from '../../navigation/types';
|
||||
@@ -218,6 +219,39 @@ export const UserScreen: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 统一处理 PostCard 的所有操作
|
||||
const handlePostAction = (post: Post, action: PostCardAction) => {
|
||||
switch (action.type) {
|
||||
case 'press':
|
||||
handlePostPress(post.id);
|
||||
break;
|
||||
case 'userPress':
|
||||
if (post.author) handleUserPress(post.author.id);
|
||||
break;
|
||||
case 'like':
|
||||
postService.likePost(post.id);
|
||||
break;
|
||||
case 'unlike':
|
||||
postService.unlikePost(post.id);
|
||||
break;
|
||||
case 'comment':
|
||||
handlePostPress(post.id, true);
|
||||
break;
|
||||
case 'bookmark':
|
||||
postService.favoritePost(post.id);
|
||||
break;
|
||||
case 'unbookmark':
|
||||
postService.unfavoritePost(post.id);
|
||||
break;
|
||||
case 'share':
|
||||
// 暂不处理
|
||||
break;
|
||||
case 'delete':
|
||||
handleDeletePost(post.id);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到聊天界面
|
||||
const handleMessage = async () => {
|
||||
if (!user) return;
|
||||
@@ -317,13 +351,7 @@ export const UserScreen: React.FC = () => {
|
||||
]}>
|
||||
<PostCard
|
||||
post={post}
|
||||
onPress={() => handlePostPress(post.id)}
|
||||
onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}}
|
||||
onLike={() => post.is_liked ? postService.unlikePost(post.id) : postService.likePost(post.id)}
|
||||
onComment={() => handlePostPress(post.id, true)}
|
||||
onBookmark={() => post.is_favorited ? postService.unfavoritePost(post.id) : postService.favoritePost(post.id)}
|
||||
onShare={() => {}}
|
||||
onDelete={() => handleDeletePost(post.id)}
|
||||
onAction={(action) => handlePostAction(post, action)}
|
||||
isPostAuthor={isPostAuthor}
|
||||
/>
|
||||
</View>
|
||||
@@ -357,13 +385,7 @@ export const UserScreen: React.FC = () => {
|
||||
]}>
|
||||
<PostCard
|
||||
post={post}
|
||||
onPress={() => handlePostPress(post.id)}
|
||||
onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}}
|
||||
onLike={() => post.is_liked ? postService.unlikePost(post.id) : postService.likePost(post.id)}
|
||||
onComment={() => handlePostPress(post.id, true)}
|
||||
onBookmark={() => post.is_favorited ? postService.unfavoritePost(post.id) : postService.favoritePost(post.id)}
|
||||
onShare={() => {}}
|
||||
onDelete={() => handleDeletePost(post.id)}
|
||||
onAction={(action) => handlePostAction(post, action)}
|
||||
isPostAuthor={isPostAuthor}
|
||||
/>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user