refactor(ui): remove QQ-style action bar from PostDetailScreen
Replace the bottom action bar (like, comment, share, favorite buttons) with a simplified comment section header showing only the comment count. Also add `setIsComposerVisible(false)` after comment submission to hide the composer.
This commit is contained in:
115
.kilo/package-lock.json
generated
Normal file
115
.kilo/package-lock.json
generated
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": ".kilo",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"@kilocode/plugin": "7.2.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kilocode/plugin": {
|
||||||
|
"version": "7.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.10.tgz",
|
||||||
|
"integrity": "sha512-VJPhJC+E5WWu7XgEJzrVOxKJlwJ+OATwxEzgjqEPj8KN5N38YxUPBY/rzUTjv90x7nkzyk1rFGfCVqXdA/Koug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kilocode/sdk": "7.2.10",
|
||||||
|
"zod": "4.1.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@opentui/core": ">=0.1.97",
|
||||||
|
"@opentui/solid": ">=0.1.97"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@opentui/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@opentui/solid": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kilocode/sdk": {
|
||||||
|
"version": "7.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.10.tgz",
|
||||||
|
"integrity": "sha512-H6jGXYAhN/yjOGX3MRZ0OxyEAuRGY3VOwDbLTh4O6ljpgutFHaLvomDZ82qNVy7gl7AjJgi3SAQAt9UQpeGl/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": "7.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-spawn": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.1.0",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"which": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/isexe": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/path-key": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-command": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"shebang-regex": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/which": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-which": "bin/node-which"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "4.1.8",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
Image,
|
Image,
|
||||||
Clipboard,
|
Clipboard,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { useNavigation, useRouter, useLocalSearchParams } from 'expo-router';
|
import { useNavigation, useRouter, useLocalSearchParams } from 'expo-router';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
@@ -795,8 +795,9 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCommentText('');
|
setCommentText('');
|
||||||
setCommentImages([]); // 清除图片
|
setCommentImages([]);
|
||||||
setReplyingTo(null); // 清除回复状态
|
setReplyingTo(null);
|
||||||
|
setIsComposerVisible(false);
|
||||||
|
|
||||||
// 更新帖子评论数
|
// 更新帖子评论数
|
||||||
setPost(prev => prev ? {
|
setPost(prev => prev ? {
|
||||||
@@ -1235,74 +1236,35 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 底部操作栏 - QQ频道风格 */}
|
{/* 评论标题 - 类似图片中的样式 */}
|
||||||
<View style={[styles.actionBar, { marginTop: responsiveGap }]}>
|
<View style={[styles.commentSectionHeader, { marginTop: responsiveGap * 1.5, marginBottom: responsiveGap }]}>
|
||||||
<TouchableOpacity style={styles.actionButton} onPress={handleLike}>
|
<View style={styles.commentSectionTitleRow}>
|
||||||
<MaterialCommunityIcons
|
<Text
|
||||||
name={post.is_liked ? 'heart' : 'heart-outline'}
|
variant="body"
|
||||||
size={isDesktop ? 24 : 20}
|
style={[
|
||||||
color={post.is_liked ? colors.error.main : colors.text.secondary}
|
styles.commentSectionTitle,
|
||||||
/>
|
{ fontSize: isDesktop ? fontSizes.lg : fontSizes.md }
|
||||||
<Text variant="caption" color={post.is_liked ? colors.error.main : colors.text.secondary} style={styles.actionText}>
|
]}
|
||||||
{post.likes_count > 0 ? formatNumber(post.likes_count) : '点赞'}
|
>
|
||||||
|
评论
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
<Text variant="caption" color={colors.text.hint} style={styles.commentSectionCount}>
|
||||||
|
{post.comments_count || 0}
|
||||||
<TouchableOpacity style={styles.actionButton}>
|
|
||||||
<MaterialCommunityIcons name="comment-outline" size={isDesktop ? 24 : 20} color={colors.text.secondary} />
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.actionText}>
|
|
||||||
{post.comments_count > 0 ? formatNumber(post.comments_count) : '评论'}
|
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity style={styles.actionButton} onPress={handleShare}>
|
|
||||||
<MaterialCommunityIcons name="share-outline" size={isDesktop ? 24 : 20} color={colors.text.secondary} />
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.actionText}>
|
|
||||||
{post.shares_count > 0 ? formatNumber(post.shares_count) : '分享'}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity style={styles.actionButton} onPress={handleFavorite}>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={post.is_favorited ? 'bookmark' : 'bookmark-outline'}
|
|
||||||
size={isDesktop ? 24 : 20}
|
|
||||||
color={post.is_favorited ? colors.warning.main : colors.text.secondary}
|
|
||||||
/>
|
|
||||||
<Text variant="caption" color={post.is_favorited ? colors.warning.main : colors.text.secondary} style={styles.actionText}>
|
|
||||||
{post.is_favorited ? '已收藏' : '收藏'}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 评论标题 - 现代化简洁分区头 */}
|
|
||||||
<View style={[styles.commentSectionHeader, { marginTop: responsiveGap, marginBottom: responsiveGap }]}>
|
|
||||||
<View style={styles.commentSectionTitleBlock}>
|
|
||||||
<View style={styles.commentSectionTitleRow}>
|
|
||||||
<Text
|
|
||||||
variant="body"
|
|
||||||
style={[
|
|
||||||
styles.commentSectionTitle,
|
|
||||||
{ fontSize: isDesktop ? fontSizes.xl : fontSizes.lg }
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
评论区
|
|
||||||
</Text>
|
|
||||||
<View style={styles.commentCountBadge}>
|
|
||||||
<Text variant="caption" style={styles.commentCountText}>{post.comments_count}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
|
<View style={styles.commentSectionUnderline} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}, [post, postImages, currentUser?.id, isDeleting, handleLike, handleShare, handleFavorite, handleDeletePost, handleEditPost, handleImagePress, voteResult, isVoteLoading, handleVote, handleUnvote, isDesktop, isTablet, isWideScreen, responsivePadding, responsiveGap]);
|
}, [post, postImages, currentUser?.id, isDeleting, handleDeletePost, handleEditPost, handleImagePress, voteResult, isVoteLoading, handleVote, handleUnvote, isDesktop, isTablet, isWideScreen, responsivePadding, responsiveGap]);
|
||||||
|
|
||||||
// 回复评论
|
// 回复评论
|
||||||
const [replyingTo, setReplyingTo] = useState<Comment | null>(null);
|
const [replyingTo, setReplyingTo] = useState<Comment | null>(null);
|
||||||
|
const [isComposerVisible, setIsComposerVisible] = useState(false);
|
||||||
|
|
||||||
const handleReply = (comment: Comment) => {
|
const handleReply = (comment: Comment) => {
|
||||||
setReplyingTo(comment);
|
setReplyingTo(comment);
|
||||||
// 可以在这里添加聚焦输入框的逻辑
|
setIsComposerVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancelReply = () => {
|
const handleCancelReply = () => {
|
||||||
@@ -1373,37 +1335,42 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}, [currentUser?.id, handleDeleteComment, handleImagePress, handleLikeComment, handleLoadMoreReplies, post?.user_id, handleReportComment]);
|
}, [currentUser?.id, handleDeleteComment, handleImagePress, handleLikeComment, handleLoadMoreReplies, post?.user_id, handleReportComment]);
|
||||||
|
|
||||||
// 渲染空评论 - 现代化设计
|
// 渲染空评论 - 类似图片中的样式
|
||||||
const renderEmptyComments = useCallback(() => (
|
const renderEmptyComments = useCallback(() => (
|
||||||
<View style={styles.emptyCommentsContainer}>
|
<View style={styles.emptyCommentsContainer}>
|
||||||
<View style={styles.emptyCommentsIconWrapper}>
|
{/* 四个方块图标 */}
|
||||||
<MaterialCommunityIcons
|
<View style={styles.emptyCommentsGrid}>
|
||||||
name="message-reply-text-outline"
|
<View style={styles.emptyCommentsGridItem} />
|
||||||
size={isDesktop ? 24 : 22}
|
<View style={styles.emptyCommentsGridItem} />
|
||||||
color={colors.text.secondary}
|
<View style={styles.emptyCommentsGridItem} />
|
||||||
/>
|
<View style={[styles.emptyCommentsGridItem, styles.emptyCommentsGridItemActive]} />
|
||||||
</View>
|
</View>
|
||||||
<Text variant="body" style={styles.emptyCommentsTitle}>
|
<Text variant="caption" color={colors.text.hint} style={styles.emptyCommentsSubtitle}>
|
||||||
还没有评论
|
这里空空如也,邀请好友来互动吧!
|
||||||
</Text>
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.emptyCommentsSubtitle}>
|
|
||||||
成为第一个评论的人吧~
|
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
), [isDesktop]);
|
), []);
|
||||||
|
|
||||||
|
const openComposer = () => {
|
||||||
|
setIsComposerVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeComposer = () => {
|
||||||
|
setIsComposerVisible(false);
|
||||||
|
Keyboard.dismiss();
|
||||||
|
};
|
||||||
|
|
||||||
// 渲染评论输入框
|
|
||||||
const renderCommentInput = () => (
|
const renderCommentInput = () => (
|
||||||
<View style={[
|
<View style={[
|
||||||
styles.inputContainer,
|
styles.inputContainer,
|
||||||
{
|
{
|
||||||
paddingHorizontal: responsivePadding,
|
paddingHorizontal: responsivePadding,
|
||||||
paddingVertical: responsiveGap,
|
paddingVertical: responsiveGap,
|
||||||
paddingBottom: keyboardHeight + responsiveGap + insets.bottom
|
paddingBottom: keyboardHeight + responsiveGap + insets.bottom,
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
{/* 回复提示 */}
|
{/* 回复提示 */}
|
||||||
{replyingTo && (
|
{replyingTo && !isComposerVisible && (
|
||||||
<View style={styles.replyingToContainer}>
|
<View style={styles.replyingToContainer}>
|
||||||
<Text variant="caption" color={colors.text.secondary}>
|
<Text variant="caption" color={colors.text.secondary}>
|
||||||
回复 <Text style={styles.replyingToName}>{replyingTo.author.nickname}</Text>
|
回复 <Text style={styles.replyingToName}>{replyingTo.author.nickname}</Text>
|
||||||
@@ -1414,69 +1381,152 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 已选图片预览 */}
|
{!isComposerVisible ? (
|
||||||
{commentImages.length > 0 && (
|
/* Collapsed state - simple input trigger */
|
||||||
<ScrollView horizontal style={styles.commentImagesPreview} showsHorizontalScrollIndicator={false}>
|
<View style={styles.bottomComposerRow}>
|
||||||
{commentImages.map((image, index) => (
|
<TouchableOpacity
|
||||||
<View key={index} style={styles.commentImageWrapper}>
|
style={styles.inputTrigger}
|
||||||
<Image source={{ uri: image.uri }} style={styles.commentImageThumbnail} resizeMode="cover" />
|
onPress={openComposer}
|
||||||
{image.uploading && (
|
activeOpacity={0.7}
|
||||||
<View style={styles.uploadingOverlay}>
|
>
|
||||||
<Loading size="sm" />
|
<Text
|
||||||
</View>
|
style={styles.inputTriggerText}
|
||||||
)}
|
numberOfLines={1}
|
||||||
<TouchableOpacity
|
>
|
||||||
style={styles.removeImageButton}
|
{replyingTo ? `回复 ${replyingTo.author.nickname}...` : '来说点什么吧!'}
|
||||||
onPress={() => handleRemoveCommentImage(index)}
|
</Text>
|
||||||
>
|
</TouchableOpacity>
|
||||||
<MaterialCommunityIcons name="close-circle" size={18} color={colors.text.primary} />
|
|
||||||
|
<TouchableOpacity style={styles.bottomActionItem} onPress={handleLike}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={post?.is_liked ? 'thumb-up' : 'thumb-up-outline'}
|
||||||
|
size={isDesktop ? 23 : 21}
|
||||||
|
color={post?.is_liked ? colors.primary.main : colors.text.secondary}
|
||||||
|
/>
|
||||||
|
<Text variant="caption" style={styles.bottomActionCount}>
|
||||||
|
{formatNumber(post?.likes_count || 0)}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.bottomActionItem} onPress={handleFavorite}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={post?.is_favorited ? 'star' : 'star-outline'}
|
||||||
|
size={isDesktop ? 23 : 21}
|
||||||
|
color={post?.is_favorited ? colors.warning.main : colors.text.secondary}
|
||||||
|
/>
|
||||||
|
<Text variant="caption" style={styles.bottomActionCount}>
|
||||||
|
{formatNumber(post?.favorites_count || 0)}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.bottomActionItem} onPress={handleShare}>
|
||||||
|
<MaterialCommunityIcons name="share-outline" size={isDesktop ? 23 : 21} color={colors.text.secondary} />
|
||||||
|
<Text variant="caption" style={styles.bottomActionCount}>
|
||||||
|
{formatNumber(post?.shares_count || 0)}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
/* Expanded state - full composer */
|
||||||
|
<View style={styles.expandedComposer}>
|
||||||
|
{/* Reply hint */}
|
||||||
|
{replyingTo && (
|
||||||
|
<View style={styles.expandedReplyHint}>
|
||||||
|
<Text variant="caption" color={colors.text.secondary}>
|
||||||
|
回复 <Text style={styles.replyingToName}>{replyingTo.author.nickname}</Text>
|
||||||
|
</Text>
|
||||||
|
<TouchableOpacity onPress={handleCancelReply} style={styles.cancelReplyButton}>
|
||||||
|
<MaterialCommunityIcons name="close" size={16} color={colors.text.hint} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
))}
|
)}
|
||||||
</ScrollView>
|
|
||||||
|
{/* Text Input */}
|
||||||
|
<TextInput
|
||||||
|
style={styles.expandedTextInput}
|
||||||
|
placeholder="来说点什么吧!"
|
||||||
|
placeholderTextColor={colors.text.hint}
|
||||||
|
value={commentText}
|
||||||
|
onChangeText={setCommentText}
|
||||||
|
multiline
|
||||||
|
autoFocus
|
||||||
|
maxLength={500}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Image preview */}
|
||||||
|
{commentImages.length > 0 && (
|
||||||
|
<ScrollView horizontal style={styles.commentImagesPreview} showsHorizontalScrollIndicator={false}>
|
||||||
|
{commentImages.map((image, index) => (
|
||||||
|
<View key={index} style={styles.commentImageWrapper}>
|
||||||
|
<Image source={{ uri: image.uri }} style={styles.commentImageThumbnail} resizeMode="cover" />
|
||||||
|
{image.uploading && (
|
||||||
|
<View style={styles.uploadingOverlay}>
|
||||||
|
<Loading size="sm" />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.removeImageButton}
|
||||||
|
onPress={() => handleRemoveCommentImage(index)}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons name="close-circle" size={18} color={colors.text.primary} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Toolbar */}
|
||||||
|
<View style={styles.expandedToolbar}>
|
||||||
|
<View style={styles.expandedToolbarLeft}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.expandedToolbarItem}
|
||||||
|
onPress={handlePickCommentImage}
|
||||||
|
disabled={commentImages.length >= 3}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="image-outline"
|
||||||
|
size={22}
|
||||||
|
color={commentImages.length >= 3 ? colors.text.disabled : colors.text.secondary}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.expandedToolbarItem}
|
||||||
|
onPress={() => {}}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="at"
|
||||||
|
size={22}
|
||||||
|
color={colors.text.secondary}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.expandedToolbarItem}
|
||||||
|
onPress={() => {}}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="gamepad-variant-outline"
|
||||||
|
size={22}
|
||||||
|
color={colors.text.secondary}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text variant="caption" color={colors.text.hint} style={styles.expandedCharCount}>
|
||||||
|
{commentText.length}/500
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.expandedSendButton,
|
||||||
|
!(commentText.trim() || commentImages.length > 0) && styles.expandedSendButtonDisabled
|
||||||
|
]}
|
||||||
|
onPress={handleSendComment}
|
||||||
|
disabled={!(commentText.trim() || commentImages.length > 0)}
|
||||||
|
>
|
||||||
|
<Text style={styles.expandedSendButtonText}>发送</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View style={styles.inputWrapper}>
|
|
||||||
{/* 图片选择按钮 */}
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.imagePickerButton}
|
|
||||||
onPress={handlePickCommentImage}
|
|
||||||
disabled={commentImages.length >= 3}
|
|
||||||
>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="image-outline"
|
|
||||||
size={isDesktop ? 24 : 22}
|
|
||||||
color={commentImages.length >= 3 ? colors.text.disabled : colors.text.secondary}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TextInput
|
|
||||||
style={[
|
|
||||||
styles.input,
|
|
||||||
{ fontSize: isDesktop ? fontSizes.lg : fontSizes.md }
|
|
||||||
]}
|
|
||||||
placeholder={replyingTo ? `回复 ${replyingTo.author.nickname}...` : "说点什么..."}
|
|
||||||
placeholderTextColor={colors.text.hint}
|
|
||||||
value={commentText}
|
|
||||||
onChangeText={setCommentText}
|
|
||||||
multiline
|
|
||||||
maxLength={500}
|
|
||||||
/>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.sendButton,
|
|
||||||
!(commentText.trim() || commentImages.length > 0) && styles.sendButtonDisabled
|
|
||||||
]}
|
|
||||||
onPress={handleSendComment}
|
|
||||||
disabled={!(commentText.trim() || commentImages.length > 0)}
|
|
||||||
>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="send"
|
|
||||||
size={isDesktop ? 24 : 20}
|
|
||||||
color={(commentText.trim() || commentImages.length > 0) ? colors.primary.main : colors.text.disabled}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1559,20 +1609,20 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} edges={['bottom']}>
|
<View style={styles.container}>
|
||||||
<EmptyState
|
<EmptyState
|
||||||
title="帖子不存在"
|
title="帖子不存在"
|
||||||
description="该帖子可能已被删除"
|
description="该帖子可能已被删除"
|
||||||
icon="alert-circle-outline"
|
icon="alert-circle-outline"
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 桌面端双栏布局
|
// 桌面端双栏布局
|
||||||
if (useDualColumnLayout) {
|
if (useDualColumnLayout) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} edges={['bottom']}>
|
<View style={styles.container}>
|
||||||
<AdaptiveLayout
|
<AdaptiveLayout
|
||||||
sidebar={renderSidebar()}
|
sidebar={renderSidebar()}
|
||||||
sidebarWidth={sidebarWidth}
|
sidebarWidth={sidebarWidth}
|
||||||
@@ -1611,13 +1661,13 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
onClose={() => setShowImageModal(false)}
|
onClose={() => setShowImageModal(false)}
|
||||||
enableSave
|
enableSave
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移动端单栏布局
|
// 移动端单栏布局
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} edges={['bottom']}>
|
<View style={styles.container}>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={styles.flex}
|
style={styles.flex}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||||
@@ -1707,7 +1757,7 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
setReportTarget(null);
|
setReportTarget(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1915,62 +1965,35 @@ function createPostDetailStyles(colors: AppColors) {
|
|||||||
voteLoadingText: {
|
voteLoadingText: {
|
||||||
marginTop: spacing.sm,
|
marginTop: spacing.sm,
|
||||||
},
|
},
|
||||||
actionBar: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
paddingVertical: spacing.md,
|
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderTopColor: colors.divider,
|
|
||||||
backgroundColor: colors.background.paper,
|
|
||||||
},
|
|
||||||
actionButton: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingHorizontal: spacing.md,
|
|
||||||
paddingVertical: spacing.xs,
|
|
||||||
borderRadius: borderRadius.lg,
|
|
||||||
},
|
|
||||||
actionText: {
|
|
||||||
fontSize: fontSizes.sm,
|
|
||||||
marginLeft: 6,
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
commentSectionHeader: {
|
commentSectionHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'flex-start',
|
||||||
justifyContent: 'space-between',
|
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderTopColor: colors.divider,
|
|
||||||
paddingTop: spacing.lg,
|
paddingTop: spacing.lg,
|
||||||
},
|
},
|
||||||
commentSectionTitleBlock: {
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 0,
|
|
||||||
},
|
|
||||||
commentSectionTitleRow: {
|
commentSectionTitleRow: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
commentSectionTitle: {
|
commentSectionTitle: {
|
||||||
fontWeight: '800',
|
fontWeight: '600',
|
||||||
color: colors.text.primary,
|
color: colors.text.primary,
|
||||||
letterSpacing: -0.2,
|
letterSpacing: 0,
|
||||||
},
|
},
|
||||||
commentCountBadge: {
|
commentSectionCount: {
|
||||||
backgroundColor: colors.background.default,
|
marginLeft: spacing.xs,
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
fontSize: fontSizes.sm,
|
||||||
borderColor: colors.divider,
|
|
||||||
paddingHorizontal: spacing.sm + 2,
|
|
||||||
paddingVertical: 3,
|
|
||||||
borderRadius: 999,
|
|
||||||
marginLeft: spacing.sm,
|
|
||||||
minWidth: 24,
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
},
|
||||||
commentCountText: {
|
commentSectionUnderline: {
|
||||||
fontSize: fontSizes.xs,
|
width: 28,
|
||||||
fontWeight: '700',
|
height: 3,
|
||||||
color: colors.text.secondary,
|
backgroundColor: colors.text.primary,
|
||||||
|
marginTop: spacing.xs,
|
||||||
|
borderRadius: borderRadius.xs,
|
||||||
|
},
|
||||||
|
commentSectionCount: {
|
||||||
|
marginLeft: spacing.xs,
|
||||||
|
fontSize: fontSizes.sm,
|
||||||
|
color: colors.text.hint,
|
||||||
},
|
},
|
||||||
inputContainer: {
|
inputContainer: {
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
@@ -1991,29 +2014,37 @@ function createPostDetailStyles(colors: AppColors) {
|
|||||||
marginLeft: spacing.sm,
|
marginLeft: spacing.sm,
|
||||||
padding: 2,
|
padding: 2,
|
||||||
},
|
},
|
||||||
inputWrapper: {
|
inputTrigger: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: colors.background.default,
|
||||||
|
borderRadius: 18,
|
||||||
|
paddingHorizontal: spacing.md,
|
||||||
|
paddingVertical: spacing.sm + 2,
|
||||||
|
minHeight: 38,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
inputTriggerText: {
|
||||||
|
fontSize: fontSizes.sm,
|
||||||
|
color: colors.text.hint,
|
||||||
|
},
|
||||||
|
bottomComposerRow: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: colors.background.default,
|
gap: spacing.xs + 2,
|
||||||
borderRadius: 20,
|
|
||||||
paddingHorizontal: spacing.md,
|
|
||||||
},
|
},
|
||||||
input: {
|
bottomActionItem: {
|
||||||
flex: 1,
|
alignItems: 'center',
|
||||||
minHeight: 40,
|
justifyContent: 'center',
|
||||||
maxHeight: 100,
|
paddingVertical: 2,
|
||||||
color: colors.text.primary,
|
paddingHorizontal: spacing.sm + 1,
|
||||||
paddingVertical: spacing.sm,
|
marginLeft: spacing.xs,
|
||||||
},
|
},
|
||||||
sendButton: {
|
bottomActionCount: {
|
||||||
padding: spacing.sm,
|
marginTop: 1,
|
||||||
},
|
fontSize: fontSizes.xs,
|
||||||
sendButtonDisabled: {
|
color: colors.text.secondary,
|
||||||
opacity: 0.5,
|
lineHeight: fontSizes.xs + 2,
|
||||||
},
|
fontWeight: '500',
|
||||||
imagePickerButton: {
|
|
||||||
padding: spacing.sm,
|
|
||||||
marginRight: spacing.xs,
|
|
||||||
},
|
},
|
||||||
commentImagesPreview: {
|
commentImagesPreview: {
|
||||||
maxHeight: 80,
|
maxHeight: 80,
|
||||||
@@ -2050,6 +2081,62 @@ function createPostDetailStyles(colors: AppColors) {
|
|||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
borderRadius: 9,
|
borderRadius: 9,
|
||||||
},
|
},
|
||||||
|
// Expanded composer styles
|
||||||
|
expandedComposer: {
|
||||||
|
backgroundColor: colors.background.paper,
|
||||||
|
},
|
||||||
|
expandedReplyHint: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: spacing.sm,
|
||||||
|
},
|
||||||
|
expandedTextInput: {
|
||||||
|
minHeight: 80,
|
||||||
|
maxHeight: 160,
|
||||||
|
fontSize: fontSizes.md,
|
||||||
|
color: colors.text.primary,
|
||||||
|
paddingVertical: spacing.sm,
|
||||||
|
textAlignVertical: 'top',
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
expandedToolbar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: spacing.sm,
|
||||||
|
paddingTop: spacing.sm,
|
||||||
|
borderTopWidth: StyleSheet.hairlineWidth,
|
||||||
|
borderTopColor: colors.divider,
|
||||||
|
},
|
||||||
|
expandedToolbarLeft: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
expandedToolbarItem: {
|
||||||
|
padding: spacing.xs,
|
||||||
|
marginRight: spacing.sm,
|
||||||
|
},
|
||||||
|
expandedCharCount: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: spacing.sm,
|
||||||
|
},
|
||||||
|
expandedSendButton: {
|
||||||
|
backgroundColor: colors.primary.main,
|
||||||
|
paddingHorizontal: spacing.md,
|
||||||
|
paddingVertical: spacing.sm,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
minWidth: 60,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
expandedSendButtonDisabled: {
|
||||||
|
backgroundColor: colors.text.disabled,
|
||||||
|
},
|
||||||
|
expandedSendButtonText: {
|
||||||
|
color: colors.text.inverse,
|
||||||
|
fontSize: fontSizes.sm,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
modalContainer: {
|
modalContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
||||||
@@ -2074,33 +2161,33 @@ function createPostDetailStyles(colors: AppColors) {
|
|||||||
paddingVertical: spacing.xs,
|
paddingVertical: spacing.xs,
|
||||||
borderRadius: borderRadius.md,
|
borderRadius: borderRadius.md,
|
||||||
},
|
},
|
||||||
// 空评论状态样式(与平铺评论区一致,无卡片气泡)
|
// 空评论状态样式(类似图片中的四个方块样式)
|
||||||
emptyCommentsContainer: {
|
emptyCommentsContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginHorizontal: spacing.md,
|
paddingVertical: spacing.xl * 2.5,
|
||||||
marginTop: spacing.md,
|
|
||||||
marginBottom: spacing.lg,
|
|
||||||
paddingVertical: spacing.xl,
|
|
||||||
paddingHorizontal: spacing.md,
|
|
||||||
},
|
},
|
||||||
emptyCommentsIconWrapper: {
|
emptyCommentsGrid: {
|
||||||
width: 40,
|
flexDirection: 'row',
|
||||||
height: 40,
|
flexWrap: 'wrap',
|
||||||
alignItems: 'center',
|
width: 48,
|
||||||
justifyContent: 'center',
|
height: 48,
|
||||||
marginBottom: spacing.sm,
|
marginBottom: spacing.md,
|
||||||
opacity: 0.85,
|
|
||||||
},
|
},
|
||||||
emptyCommentsTitle: {
|
emptyCommentsGridItem: {
|
||||||
fontSize: fontSizes.md,
|
width: 20,
|
||||||
fontWeight: '500',
|
height: 20,
|
||||||
color: colors.text.primary,
|
backgroundColor: colors.background.disabled,
|
||||||
marginBottom: spacing.xs,
|
margin: 2,
|
||||||
|
borderRadius: borderRadius.xs,
|
||||||
|
},
|
||||||
|
emptyCommentsGridItemActive: {
|
||||||
|
backgroundColor: colors.text.hint,
|
||||||
},
|
},
|
||||||
emptyCommentsSubtitle: {
|
emptyCommentsSubtitle: {
|
||||||
fontSize: fontSizes.sm,
|
fontSize: fontSizes.sm,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
color: colors.text.hint,
|
||||||
},
|
},
|
||||||
// 侧边栏样式
|
// 侧边栏样式
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
|||||||
Reference in New Issue
Block a user