feat(CommentItem, PostDetailScreen, ReportDialog): integrate report functionality
Some checks failed
Frontend CI / build-and-push-web (push) Successful in 2m54s
Frontend CI / build-android-apk (push) Has been cancelled
Frontend CI / ota-android (push) Has been cancelled

- Added report button to CommentItem for reporting comments and replies.
- Integrated ReportDialog into PostDetailScreen for handling report submissions.
- Enhanced ReportDialog with detailed report reasons and improved UI interactions.
- Updated styles across various components for a more modern look and feel.

Made-with: Cursor
This commit is contained in:
lafay
2026-03-30 17:53:30 +08:00
parent e0ee29caf8
commit 774b5c4b47
14 changed files with 793 additions and 373 deletions

View File

@@ -215,6 +215,7 @@ const CommentItem: React.FC<CommentItemProps> = ({
onDelete, onDelete,
onImagePress, onImagePress,
currentUserId, currentUserId,
onReport,
}) => { }) => {
const colors = useAppColors(); const colors = useAppColors();
const styles = useMemo(() => createCommentItemStyles(colors), [colors]); const styles = useMemo(() => createCommentItemStyles(colors), [colors]);
@@ -541,6 +542,18 @@ const CommentItem: React.FC<CommentItemProps> = ({
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
{/* 举报按钮 - 对非子评论作者显示 */}
{!isSubReplyAuthor && onReport && (
<TouchableOpacity
style={styles.subActionButton}
onPress={() => onReport(reply)}
>
<MaterialCommunityIcons name="flag-outline" size={12} color={colors.text.hint} />
<Text variant="caption" color={colors.text.hint} style={styles.subActionText}>
</Text>
</TouchableOpacity>
)}
</View> </View>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
@@ -635,6 +648,19 @@ const CommentItem: React.FC<CommentItemProps> = ({
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
{/* 举报按钮 - 只对非评论作者显示 */}
{!isCommentAuthor && onReport && (
<TouchableOpacity
style={styles.actionButton}
onPress={() => onReport(comment)}
>
<MaterialCommunityIcons name="flag-outline" size={14} color={colors.text.hint} />
<Text variant="caption" color={colors.text.hint} style={styles.actionText}>
</Text>
</TouchableOpacity>
)}
{/* 删除按钮 - 只对评论作者显示 */} {/* 删除按钮 - 只对评论作者显示 */}
{isCommentAuthor && ( {isCommentAuthor && (
<TouchableOpacity <TouchableOpacity

View File

@@ -1,24 +1,27 @@
/** /**
* 举报对话框组件 * 举报对话框组件
* 支持举报帖子、评论、消息 * 支持举报帖子、评论、消息
* 提供友好的用户界面和流畅的交互体验
*/ */
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback, useMemo } from 'react';
import { import {
Modal, Modal,
View, View,
Text,
TouchableOpacity, TouchableOpacity,
StyleSheet, StyleSheet,
ScrollView, ScrollView,
TextInput, TextInput,
ActivityIndicator, ActivityIndicator,
Alert, Alert,
Platform,
KeyboardAvoidingView,
} from 'react-native'; } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { useAppColors } from '../../../theme'; import { useAppColors } from '../../../theme';
import { spacing, borderRadius, fontSizes } from '../../../theme'; import { spacing, borderRadius, fontSizes, shadows } from '../../../theme';
import reportService, { ReportReason, ReportTargetType, REPORT_REASONS } from '../../../services/reportService'; import reportService, { ReportReason, ReportTargetType, REPORT_REASONS } from '../../../services/reportService';
import Text from '../../common/Text';
export interface ReportDialogProps { export interface ReportDialogProps {
visible: boolean; visible: boolean;
@@ -28,14 +31,23 @@ export interface ReportDialogProps {
onSuccess?: () => void; onSuccess?: () => void;
} }
// 目标类型中文名称映射 // 目标类型配置
const TARGET_TYPE_LABELS: Record<ReportTargetType, string> = { const TARGET_CONFIG: Record<ReportTargetType, { label: string; icon: string; color: string }> = {
post: '帖子', post: { label: '帖子', icon: 'file-document-outline', color: '#FF6B35' },
comment: '评论', comment: { label: '评论', icon: 'comment-text-outline', color: '#4CAF50' },
message: '消息', message: { label: '消息', icon: 'message-text-outline', color: '#2196F3' },
}; };
export const ReportDialog: React.FC<ReportDialogProps> = ({ // 举报原因详细配置(带图标和说明)
const REPORT_REASONS_DETAILED = [
{ value: 'spam' as ReportReason, label: '垃圾广告', icon: 'email-newsletter', description: '包含广告、推广或重复内容' },
{ value: 'inappropriate' as ReportReason, label: '违规内容', icon: 'alert-circle-outline', description: '包含违法、色情或暴力内容' },
{ value: 'harassment' as ReportReason, label: '辱骂攻击', icon: 'account-off-outline', description: '包含人身攻击、骚扰或歧视' },
{ value: 'misinformation' as ReportReason, label: '虚假信息', icon: 'alert-outline', description: '包含谣言、诈骗或误导信息' },
{ value: 'other' as ReportReason, label: '其他原因', icon: 'dots-horizontal-circle-outline', description: '其他需要举报的情况' },
];
const ReportDialog: React.FC<ReportDialogProps> = ({
visible, visible,
targetType, targetType,
targetId, targetId,
@@ -48,10 +60,23 @@ export const ReportDialog: React.FC<ReportDialogProps> = ({
const [description, setDescription] = useState(''); const [description, setDescription] = useState('');
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const targetConfig = TARGET_CONFIG[targetType];
// 判断补充说明是否必填(选择"其他原因"时必填)
const isDescriptionRequired = selectedReason === 'other';
const descriptionPlaceholder = isDescriptionRequired
? '请详细描述您举报的原因(必填)...'
: '请提供更多详细信息(选填)...';
// 提交举报 // 提交举报
const handleSubmit = useCallback(async () => { const handleSubmit = useCallback(async () => {
if (!selectedReason) { if (!selectedReason) {
Alert.alert('提示', '请选择举报原因'); Alert.alert('提示', '请选择举报原因', [{ text: '确定' }]);
return;
}
if (isDescriptionRequired && !description.trim()) {
Alert.alert('提示', '请选择"其他原因"时,请详细描述举报原因', [{ text: '确定' }]);
return; return;
} }
@@ -75,11 +100,11 @@ export const ReportDialog: React.FC<ReportDialogProps> = ({
}, },
]); ]);
} else { } else {
Alert.alert('举报失败', '请稍后重试'); Alert.alert('举报失败', '请稍后重试', [{ text: '确定' }]);
} }
} catch (error) { } catch (error) {
console.error('Submit report error:', error); console.error('Submit report error:', error);
Alert.alert('举报失败', '网络错误,请稍后重试'); Alert.alert('举报失败', '网络错误,请稍后重试', [{ text: '确定' }]);
} finally { } finally {
setSubmitting(false); setSubmitting(false);
} }
@@ -92,6 +117,11 @@ export const ReportDialog: React.FC<ReportDialogProps> = ({
onClose(); onClose();
}, [onClose]); }, [onClose]);
const selectedReasonData = useMemo(
() => REPORT_REASONS_DETAILED.find(r => r.value === selectedReason),
[selectedReason]
);
return ( return (
<Modal <Modal
visible={visible} visible={visible}
@@ -99,99 +129,153 @@ export const ReportDialog: React.FC<ReportDialogProps> = ({
animationType="fade" animationType="fade"
onRequestClose={handleClose} onRequestClose={handleClose}
> >
<View style={styles.overlay}> <KeyboardAvoidingView
<View style={styles.container}> behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
{/* 头部 */} style={styles.keyboardView}
<View style={styles.header}> >
<Text style={styles.title}> <View style={styles.overlay}>
{TARGET_TYPE_LABELS[targetType]} <View style={styles.container}>
</Text> {/* 头部 */}
<TouchableOpacity <View style={styles.header}>
style={styles.closeButton} <View style={styles.headerContent}>
onPress={handleClose} <View style={[styles.headerIcon, { backgroundColor: targetConfig.color + '15' }]}>
disabled={submitting} <MaterialCommunityIcons
> name={targetConfig.icon as any}
<MaterialCommunityIcons size={20}
name="close" color={targetConfig.color}
size={24} />
color={colors.textSecondary} </View>
/> <View style={styles.headerText}>
</TouchableOpacity> <Text style={styles.title}>{targetConfig.label}</Text>
</View> <Text style={styles.subtitle}></Text>
</View>
{/* 内容 */} </View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}> <TouchableOpacity
{/* 举报原因选择 */} style={styles.closeButton}
<Text style={styles.sectionTitle}></Text> onPress={handleClose}
<View style={styles.reasonList}> disabled={submitting}
{REPORT_REASONS.map((item) => ( hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
<TouchableOpacity >
key={item.value} <MaterialCommunityIcons
style={[ name="close"
styles.reasonItem, size={24}
selectedReason === item.value && styles.reasonItemSelected, color={colors.chat.textSecondary}
]} />
onPress={() => setSelectedReason(item.value)} </TouchableOpacity>
disabled={submitting}
>
<View style={styles.radioContainer}>
<View
style={[
styles.radio,
selectedReason === item.value && styles.radioSelected,
]}
>
{selectedReason === item.value && (
<View style={styles.radioDot} />
)}
</View>
<Text style={styles.reasonLabel}>{item.label}</Text>
</View>
</TouchableOpacity>
))}
</View> </View>
{/* 补充说明 */} {/* 内容 */}
<Text style={styles.sectionTitle}></Text> <ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
<TextInput {/* 举报原因选择 */}
style={styles.textInput} <View style={styles.section}>
placeholder="请提供更多详细信息..." <Text style={styles.sectionTitle}></Text>
placeholderTextColor={colors.textTertiary} <View style={styles.reasonList}>
multiline {REPORT_REASONS_DETAILED.map((item) => (
maxLength={500} <TouchableOpacity
value={description} key={item.value}
onChangeText={setDescription} style={[
editable={!submitting} styles.reasonItem,
/> selectedReason === item.value && [
<Text style={styles.charCount}>{description.length}/500</Text> styles.reasonItemSelected,
</ScrollView> { borderColor: colors.primary.main + '40' }
],
]}
onPress={() => setSelectedReason(item.value)}
disabled={submitting}
activeOpacity={0.7}
>
<View style={styles.reasonLeft}>
<View
style={[
styles.reasonIcon,
selectedReason === item.value && styles.reasonIconSelected,
]}
>
<MaterialCommunityIcons
name={item.icon as any}
size={20}
color={selectedReason === item.value ? colors.primary.main : colors.chat.textSecondary}
/>
</View>
<View style={styles.reasonTextContainer}>
<Text style={[
styles.reasonLabel,
...(selectedReason === item.value ? [styles.reasonLabelSelected] : []),
]}>
{item.label}
</Text>
<Text style={styles.reasonDescription}>{item.description}</Text>
</View>
</View>
<View
style={[
styles.radio,
selectedReason === item.value && styles.radioSelected,
]}
>
{selectedReason === item.value && (
<View style={styles.radioDot} />
)}
</View>
</TouchableOpacity>
))}
</View>
</View>
{/* 底部按钮 */} {/* 补充说明 */}
<View style={styles.footer}> <View style={styles.section}>
<TouchableOpacity <View style={styles.sectionHeader}>
style={styles.cancelButton} <Text style={styles.sectionTitle}>
onPress={handleClose} {isDescriptionRequired ? ' *' : ''}
disabled={submitting} </Text>
> <Text style={styles.charCount}>{description.length}/500</Text>
<Text style={styles.cancelButtonText}></Text> </View>
</TouchableOpacity> <TextInput
<TouchableOpacity style={[
style={[ styles.textInput,
styles.submitButton, isDescriptionRequired && !description && styles.textInputError,
(!selectedReason || submitting) && styles.submitButtonDisabled, ]}
]} placeholder={descriptionPlaceholder}
onPress={handleSubmit} placeholderTextColor={colors.chat.textPlaceholder}
disabled={!selectedReason || submitting} multiline
> maxLength={500}
{submitting ? ( value={description}
<ActivityIndicator size="small" color="#FFFFFF" /> onChangeText={setDescription}
) : ( editable={!submitting}
<Text style={styles.submitButtonText}></Text> textAlignVertical="top"
)} />
</TouchableOpacity> </View>
</ScrollView>
{/* 底部按钮 */}
<View style={styles.footer}>
<TouchableOpacity
style={styles.cancelButton}
onPress={handleClose}
disabled={submitting}
activeOpacity={0.7}
>
<Text style={styles.cancelButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.submitButton,
(!selectedReason || submitting) && styles.submitButtonDisabled,
]}
onPress={handleSubmit}
disabled={!selectedReason || submitting}
activeOpacity={0.9}
>
{submitting ? (
<ActivityIndicator size="small" color="#FFFFFF" />
) : (
<Text style={styles.submitButtonText}></Text>
)}
</TouchableOpacity>
</View>
</View> </View>
</View> </View>
</View> </KeyboardAvoidingView>
</Modal> </Modal>
); );
}; };
@@ -199,6 +283,9 @@ export const ReportDialog: React.FC<ReportDialogProps> = ({
// 创建样式 // 创建样式
const useStyles = (colors: ReturnType<typeof useAppColors>) => const useStyles = (colors: ReturnType<typeof useAppColors>) =>
StyleSheet.create({ StyleSheet.create({
keyboardView: {
flex: 1,
},
overlay: { overlay: {
flex: 1, flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
@@ -207,129 +294,193 @@ const useStyles = (colors: ReturnType<typeof useAppColors>) =>
padding: spacing.lg, padding: spacing.lg,
}, },
container: { container: {
backgroundColor: colors.backgroundPrimary, backgroundColor: colors.chat.card,
borderRadius: borderRadius.lg, borderRadius: borderRadius.xl,
width: '100%', width: '100%',
maxWidth: 400, maxWidth: 420,
maxHeight: '80%', maxHeight: '85%',
...shadows.lg,
}, },
header: { header: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
padding: spacing.lg, padding: spacing.lg,
borderBottomWidth: 1, paddingBottom: spacing.md,
borderBottomColor: colors.borderLight, },
headerContent: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.md,
},
headerIcon: {
width: 40,
height: 40,
borderRadius: borderRadius.md,
justifyContent: 'center',
alignItems: 'center',
},
headerText: {
flex: 1,
}, },
title: { title: {
fontSize: fontSizes.lg, fontSize: fontSizes.xl,
fontWeight: '600', fontWeight: '600',
color: colors.textPrimary, color: colors.chat.textPrimary,
},
subtitle: {
fontSize: fontSizes.sm,
color: colors.chat.textSecondary,
marginTop: 2,
}, },
closeButton: { closeButton: {
padding: spacing.xs, padding: spacing.xs,
borderRadius: borderRadius.full,
}, },
content: { content: {
padding: spacing.lg, padding: spacing.lg,
paddingTop: 0,
maxHeight: 400, maxHeight: 400,
}, },
section: {
marginBottom: spacing.lg,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: spacing.sm,
},
sectionTitle: { sectionTitle: {
fontSize: fontSizes.md, fontSize: fontSizes.md,
fontWeight: '500', fontWeight: '600',
color: colors.textPrimary, color: colors.chat.textPrimary,
marginBottom: spacing.sm,
}, },
reasonList: { reasonList: {
marginBottom: spacing.lg, gap: spacing.sm,
}, },
reasonItem: { reasonItem: {
paddingVertical: spacing.sm, flexDirection: 'row',
borderBottomWidth: 1, justifyContent: 'space-between',
borderBottomColor: colors.borderLight, alignItems: 'center',
padding: spacing.md,
borderRadius: borderRadius.md,
borderWidth: 1,
borderColor: 'transparent',
backgroundColor: colors.chat.surfaceMuted,
}, },
reasonItemSelected: { reasonItemSelected: {
backgroundColor: colors.backgroundSecondary, backgroundColor: colors.primary.main + '08',
marginHorizontal: -spacing.sm, borderWidth: 1,
paddingHorizontal: spacing.sm,
borderRadius: borderRadius.sm,
}, },
radioContainer: { reasonLeft: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
flex: 1,
gap: spacing.md,
}, },
radio: { reasonIcon: {
width: 20, width: 36,
height: 20, height: 36,
borderRadius: 10, borderRadius: borderRadius.md,
borderWidth: 2, backgroundColor: colors.chat.surfaceRaised,
borderColor: colors.border,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: spacing.sm, },
reasonIconSelected: {
backgroundColor: colors.primary.main + '15',
},
reasonTextContainer: {
flex: 1,
},
reasonLabel: {
fontSize: fontSizes.md,
fontWeight: '500',
color: colors.chat.textPrimary,
},
reasonLabelSelected: {
color: colors.primary.main,
fontWeight: '600',
},
reasonDescription: {
fontSize: fontSizes.sm,
color: colors.chat.textSecondary,
marginTop: 2,
},
radio: {
width: 22,
height: 22,
borderRadius: 11,
borderWidth: 2,
borderColor: colors.chat.border,
justifyContent: 'center',
alignItems: 'center',
marginLeft: spacing.sm,
}, },
radioSelected: { radioSelected: {
borderColor: colors.primary, borderColor: colors.primary.main,
backgroundColor: colors.primary.main,
}, },
radioDot: { radioDot: {
width: 10, width: 10,
height: 10, height: 10,
borderRadius: 5, borderRadius: 5,
backgroundColor: colors.primary, backgroundColor: '#FFFFFF',
},
reasonLabel: {
fontSize: fontSizes.md,
color: colors.textPrimary,
}, },
textInput: { textInput: {
borderWidth: 1, borderWidth: 1,
borderColor: colors.border, borderColor: colors.chat.border,
borderRadius: borderRadius.md, borderRadius: borderRadius.md,
padding: spacing.sm, padding: spacing.md,
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.textPrimary, color: colors.chat.textPrimary,
minHeight: 100, minHeight: 100,
textAlignVertical: 'top', backgroundColor: colors.chat.surfaceMuted,
},
textInputError: {
borderColor: colors.error.main,
backgroundColor: colors.error.main + '08',
}, },
charCount: { charCount: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.textTertiary, color: colors.chat.textSecondary,
textAlign: 'right',
marginTop: spacing.xs,
}, },
footer: { footer: {
flexDirection: 'row', flexDirection: 'row',
padding: spacing.lg, padding: spacing.lg,
gap: spacing.md,
borderTopWidth: 1, borderTopWidth: 1,
borderTopColor: colors.borderLight, borderTopColor: colors.chat.borderLight,
gap: spacing.sm,
}, },
cancelButton: { cancelButton: {
flex: 1, flex: 1,
paddingVertical: spacing.sm, paddingVertical: spacing.md,
borderRadius: borderRadius.md, borderRadius: borderRadius.md,
borderWidth: 1, borderWidth: 1,
borderColor: colors.border, borderColor: colors.chat.border,
alignItems: 'center', alignItems: 'center',
backgroundColor: colors.chat.surfaceMuted,
}, },
cancelButtonText: { cancelButtonText: {
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.textSecondary, fontWeight: '500',
color: colors.chat.textSecondary,
}, },
submitButton: { submitButton: {
flex: 1, flex: 1,
paddingVertical: spacing.sm, paddingVertical: spacing.md,
borderRadius: borderRadius.md, borderRadius: borderRadius.md,
backgroundColor: colors.primary, backgroundColor: colors.primary.main,
alignItems: 'center', alignItems: 'center',
}, },
submitButtonDisabled: { submitButtonDisabled: {
backgroundColor: colors.primary + '60', backgroundColor: colors.background.disabled,
}, },
submitButtonText: { submitButtonText: {
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: '#FFFFFF', color: '#FFFFFF',
fontWeight: '500', fontWeight: '600',
}, },
}); });
export default ReportDialog; export default ReportDialog;

View File

@@ -1,2 +1,2 @@
export { ReportDialog } from './ReportDialog'; export { default } from './ReportDialog';
export type { ReportDialogProps } from './ReportDialog'; export type { ReportDialogProps } from './ReportDialog';

View File

@@ -39,7 +39,7 @@ import { useCurrentUser } from '../../stores/authStore';
import { postService, commentService, uploadService, authService, showPrompt, voteService } from '../../services'; import { postService, commentService, uploadService, authService, showPrompt, voteService } from '../../services';
import { processPostUseCase } from '../../core/usecases/ProcessPostUseCase'; import { processPostUseCase } from '../../core/usecases/ProcessPostUseCase';
import { useCursorPagination } from '../../hooks/useCursorPagination'; import { useCursorPagination } from '../../hooks/useCursorPagination';
import { CommentItem, VoteCard } from '../../components/business'; import { CommentItem, VoteCard, ReportDialog } from '../../components/business';
import { Avatar, Button, Loading, EmptyState, Text, ImageGallery, ImageGrid, ImageGridItem, AdaptiveLayout, AppBackButton } from '../../components/common'; import { Avatar, Button, Loading, EmptyState, Text, ImageGallery, ImageGrid, ImageGridItem, AdaptiveLayout, AppBackButton } from '../../components/common';
import { useResponsive, useResponsiveValue, useResponsiveSpacing } from '../../hooks/useResponsive'; import { useResponsive, useResponsiveValue, useResponsiveSpacing } from '../../hooks/useResponsive';
import * as hrefs from '../../navigation/hrefs'; import * as hrefs from '../../navigation/hrefs';
@@ -177,6 +177,10 @@ export const PostDetailScreen: React.FC = () => {
const flatListRef = useRef<FlatList>(null); const flatListRef = useRef<FlatList>(null);
const hasRecordedView = useRef(false); // 是否已记录浏览量 const hasRecordedView = useRef(false); // 是否已记录浏览量
// 举报相关状态
const [reportDialogVisible, setReportDialogVisible] = useState(false);
const [reportTarget, setReportTarget] = useState<{ type: 'post' | 'comment'; id: string } | null>(null);
// 投票相关状态 // 投票相关状态
const [voteResult, setVoteResult] = useState<VoteResultDTO | null>(null); const [voteResult, setVoteResult] = useState<VoteResultDTO | null>(null);
const [isVoteLoading, setIsVoteLoading] = useState(false); const [isVoteLoading, setIsVoteLoading] = useState(false);
@@ -1243,6 +1247,25 @@ export const PostDetailScreen: React.FC = () => {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
)} )}
{/* 举报按钮 - 只对非帖子作者显示 */}
{currentUser?.id !== post.author?.id && (
<TouchableOpacity
style={styles.reportButtonInline}
onPress={() => {
setReportTarget({ type: 'post', id: post.id });
setReportDialogVisible(true);
}}
>
<MaterialCommunityIcons
name="flag-outline"
size={14}
color={colors.text.hint}
/>
<Text variant="caption" color={colors.text.hint} style={styles.reportButtonText}>
</Text>
</TouchableOpacity>
)}
</View> </View>
{/* 底部操作栏 - QQ频道风格 */} {/* 底部操作栏 - QQ频道风格 */}
@@ -1347,6 +1370,12 @@ export const PostDetailScreen: React.FC = () => {
} }
}; };
// 处理举报评论
const handleReportComment = useCallback((comment: Comment) => {
setReportTarget({ type: 'comment', id: comment.id });
setReportDialogVisible(true);
}, []);
// 渲染评论 - 带楼层号 // 渲染评论 - 带楼层号
const commentKeyExtractor = useCallback((item: Comment) => item.id, []); const commentKeyExtractor = useCallback((item: Comment) => item.id, []);
@@ -1372,9 +1401,10 @@ export const PostDetailScreen: React.FC = () => {
onDelete={handleDeleteComment} onDelete={handleDeleteComment}
onImagePress={handleImagePress} onImagePress={handleImagePress}
currentUserId={currentUser?.id} currentUserId={currentUser?.id}
onReport={handleReportComment}
/> />
); );
}, [currentUser?.id, handleDeleteComment, handleImagePress, handleLikeComment, handleLoadMoreReplies, post?.user_id]); }, [currentUser?.id, handleDeleteComment, handleImagePress, handleLikeComment, handleLoadMoreReplies, post?.user_id, handleReportComment]);
// 渲染空评论 - 现代化设计 // 渲染空评论 - 现代化设计
const renderEmptyComments = useCallback(() => ( const renderEmptyComments = useCallback(() => (
@@ -1696,6 +1726,20 @@ export const PostDetailScreen: React.FC = () => {
onClose={() => setShowImageModal(false)} onClose={() => setShowImageModal(false)}
enableSave enableSave
/> />
{/* 举报对话框 */}
<ReportDialog
visible={reportDialogVisible}
targetType={reportTarget?.type || 'post'}
targetId={reportTarget?.id || ''}
onClose={() => {
setReportDialogVisible(false);
setReportTarget(null);
}}
onSuccess={() => {
setReportTarget(null);
}}
/>
</SafeAreaView> </SafeAreaView>
); );
}; };
@@ -1858,7 +1902,13 @@ function createPostDetailStyles(colors: AppColors) {
alignItems: 'center', alignItems: 'center',
padding: spacing.xs, padding: spacing.xs,
}, },
editButtonText: { reportButtonInline: {
flexDirection: 'row',
alignItems: 'center',
marginLeft: spacing.sm,
padding: spacing.xs,
},
reportButtonText: {
marginLeft: 2, marginLeft: 2,
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
}, },

View File

@@ -322,10 +322,10 @@ function createCreateGroupStyles(colors: AppColors) {
flex: 1, flex: 1,
}, },
scrollContent: { scrollContent: {
padding: spacing.lg, padding: spacing.md,
paddingBottom: spacing.xl, paddingBottom: spacing.xl * 2,
}, },
// 头部区域样式 // 头部区域样式 - 扁平化
headerSection: { headerSection: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'flex-start', alignItems: 'flex-start',
@@ -341,12 +341,17 @@ function createCreateGroupStyles(colors: AppColors) {
width: 80, width: 80,
height: 80, height: 80,
borderRadius: 40, borderRadius: 40,
borderWidth: 3,
borderColor: colors.primary.light + '40',
}, },
avatarPlaceholder: { avatarPlaceholder: {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
borderRadius: 40, borderRadius: 40,
borderWidth: 2,
borderColor: colors.divider,
borderStyle: 'dashed',
}, },
avatarBadge: { avatarBadge: {
position: 'absolute', position: 'absolute',
@@ -358,7 +363,7 @@ function createCreateGroupStyles(colors: AppColors) {
borderRadius: 14, borderRadius: 14,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
borderWidth: 2, borderWidth: 3,
borderColor: colors.background.paper, borderColor: colors.background.paper,
}, },
nameInputContainer: { nameInputContainer: {
@@ -367,7 +372,9 @@ function createCreateGroupStyles(colors: AppColors) {
}, },
inputLabel: { inputLabel: {
marginBottom: spacing.sm, marginBottom: spacing.sm,
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.sm + 1,
letterSpacing: 0.3,
}, },
nameInput: { nameInput: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
@@ -376,12 +383,15 @@ function createCreateGroupStyles(colors: AppColors) {
paddingVertical: spacing.md, paddingVertical: spacing.md,
fontSize: fontSizes.lg, fontSize: fontSizes.lg,
color: colors.text.primary, color: colors.text.primary,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
fontWeight: '600',
}, },
charCount: { charCount: {
textAlign: 'right', textAlign: 'right',
marginTop: spacing.xs, marginTop: spacing.xs,
fontWeight: '500',
fontSize: fontSizes.sm,
}, },
// 区域样式 // 区域样式
section: { section: {
@@ -391,18 +401,20 @@ function createCreateGroupStyles(colors: AppColors) {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
marginBottom: spacing.sm, marginBottom: spacing.md,
}, },
sectionTitle: { sectionTitle: {
fontWeight: '600', fontWeight: '800',
fontSize: fontSizes.md + 1,
marginBottom: spacing.sm, marginBottom: spacing.sm,
letterSpacing: 0.3,
}, },
// 文本域样式 // 文本域样式 - 更现代
textAreaContainer: { textAreaContainer: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
padding: spacing.md, padding: spacing.md,
}, },
textArea: { textArea: {
@@ -414,6 +426,8 @@ function createCreateGroupStyles(colors: AppColors) {
textAreaCharCount: { textAreaCharCount: {
textAlign: 'right', textAlign: 'right',
marginTop: spacing.sm, marginTop: spacing.sm,
fontWeight: '500',
fontSize: fontSizes.sm,
}, },
// 已选成员样式 // 已选成员样式
selectedMembersList: { selectedMembersList: {
@@ -443,8 +457,10 @@ function createCreateGroupStyles(colors: AppColors) {
marginTop: spacing.xs, marginTop: spacing.xs,
textAlign: 'center', textAlign: 'center',
width: '100%', width: '100%',
fontWeight: '600',
fontSize: fontSizes.sm,
}, },
// 邀请按钮样式 // 邀请按钮样式 - 扁平化
inviteButton: { inviteButton: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
@@ -452,13 +468,14 @@ function createCreateGroupStyles(colors: AppColors) {
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
padding: spacing.md, padding: spacing.md,
marginBottom: spacing.xl, marginBottom: spacing.xl,
...shadows.sm, borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
inviteIconContainer: { inviteIconContainer: {
width: 48, width: 48,
height: 48, height: 48,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
backgroundColor: colors.primary.light + '20', backgroundColor: colors.primary.light + '15',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: spacing.md, marginRight: spacing.md,
@@ -467,16 +484,18 @@ function createCreateGroupStyles(colors: AppColors) {
flex: 1, flex: 1,
}, },
inviteTitle: { inviteTitle: {
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.md + 1,
marginBottom: 2, marginBottom: 2,
letterSpacing: 0.3,
}, },
// 底部按钮样式 // 底部按钮样式
footer: { footer: {
padding: spacing.lg, padding: spacing.lg,
paddingBottom: spacing.xl, paddingBottom: spacing.xl,
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderTopWidth: 1, borderTopWidth: 0.5,
borderTopColor: colors.divider, borderTopColor: colors.divider + '60',
}, },
}); });
} }

View File

@@ -1091,13 +1091,15 @@ function createGroupInfoStyles(colors: AppColors) {
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
paddingHorizontal: spacing.md, paddingHorizontal: spacing.md,
paddingVertical: spacing.sm, paddingVertical: spacing.md,
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderBottomWidth: 1, borderBottomWidth: 0.5,
borderBottomColor: colors.divider, borderBottomColor: colors.divider + '60',
}, },
pageTitle: { pageTitle: {
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.xl,
letterSpacing: 0.5,
}, },
pageHeaderPlaceholder: { pageHeaderPlaceholder: {
width: 40, width: 40,
@@ -1107,8 +1109,8 @@ function createGroupInfoStyles(colors: AppColors) {
flex: 1, flex: 1,
}, },
scrollContent: { scrollContent: {
padding: spacing.lg, padding: spacing.md,
paddingBottom: spacing.xl, paddingBottom: spacing.xl * 2,
}, },
errorContainer: { errorContainer: {
flex: 1, flex: 1,
@@ -1116,13 +1118,14 @@ function createGroupInfoStyles(colors: AppColors) {
alignItems: 'center', alignItems: 'center',
}, },
// 头部卡片样式 // 头部卡片样式 - 扁平化设计
headerCard: { headerCard: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.xl, borderRadius: borderRadius.xl,
padding: spacing.lg, padding: spacing.lg,
marginBottom: spacing.lg, marginBottom: spacing.md,
...shadows.sm, borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
groupHeader: { groupHeader: {
flexDirection: 'row', flexDirection: 'row',
@@ -1134,30 +1137,33 @@ function createGroupInfoStyles(colors: AppColors) {
flex: 1, flex: 1,
}, },
groupName: { groupName: {
fontWeight: '700', fontWeight: '800',
fontSize: fontSizes.xl + 2,
marginBottom: spacing.xs, marginBottom: spacing.xs,
letterSpacing: 0.3,
}, },
groupMeta: { groupMeta: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: spacing.xs, gap: spacing.xs,
marginBottom: spacing.xs,
}, },
groupNoRow: { groupNoRow: {
marginTop: spacing.xs,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', gap: spacing.sm,
}, },
copyGroupNoBtn: { copyGroupNoBtn: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: spacing.sm, paddingHorizontal: spacing.sm,
paddingVertical: 4, paddingVertical: 4,
borderRadius: borderRadius.sm, borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20', backgroundColor: colors.primary.light + '15',
}, },
copyGroupNoBtnText: { copyGroupNoBtnText: {
marginLeft: 4, marginLeft: 4,
fontWeight: '500',
}, },
descriptionContainer: { descriptionContainer: {
flexDirection: 'row', flexDirection: 'row',
@@ -1165,20 +1171,24 @@ function createGroupInfoStyles(colors: AppColors) {
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
padding: spacing.md, padding: spacing.md,
borderWidth: 0.5,
borderColor: colors.divider + '30',
}, },
groupDesc: { groupDesc: {
marginLeft: spacing.sm, marginLeft: spacing.sm,
flex: 1, flex: 1,
lineHeight: 20, lineHeight: 22,
fontWeight: '400',
}, },
// 通用卡片样式 // 通用卡片样式 - 扁平化
card: { card: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.xl, borderRadius: borderRadius.xl,
padding: spacing.lg, padding: spacing.lg,
marginBottom: spacing.lg, marginBottom: spacing.md,
...shadows.sm, borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
cardHeader: { cardHeader: {
flexDirection: 'row', flexDirection: 'row',
@@ -1189,33 +1199,41 @@ function createGroupInfoStyles(colors: AppColors) {
width: 36, width: 36,
height: 36, height: 36,
borderRadius: borderRadius.md, borderRadius: borderRadius.md,
backgroundColor: colors.info.light + '20', backgroundColor: colors.info.light + '15',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: spacing.sm, marginRight: spacing.sm,
}, },
cardTitle: { cardTitle: {
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.md + 1,
flex: 1, flex: 1,
letterSpacing: 0.3,
}, },
// 公告样式 // 公告样式 - 更现代的卡片
announcementContent: { announcementContent: {
backgroundColor: colors.background.default, backgroundColor: colors.warning.light + '08',
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
padding: spacing.md, padding: spacing.md,
borderLeftWidth: 3,
borderLeftColor: colors.warning.main,
}, },
announcementText: { announcementText: {
lineHeight: 22, lineHeight: 22,
marginBottom: spacing.sm, marginBottom: spacing.sm,
fontWeight: '400',
}, },
announcementTime: { announcementTime: {
textAlign: 'right', textAlign: 'right',
fontWeight: '500',
}, },
// 成员预览样式 // 成员预览样式
memberCount: { memberCount: {
marginRight: spacing.xs, marginRight: spacing.xs,
fontWeight: '600',
color: colors.text.secondary,
}, },
memberPreview: { memberPreview: {
flexDirection: 'row', flexDirection: 'row',
@@ -1226,7 +1244,7 @@ function createGroupInfoStyles(colors: AppColors) {
}, },
memberAvatar: { memberAvatar: {
position: 'relative', position: 'relative',
marginLeft: -10, marginLeft: -8,
}, },
memberAvatarFirst: { memberAvatarFirst: {
marginLeft: 0, marginLeft: 0,
@@ -1239,12 +1257,12 @@ function createGroupInfoStyles(colors: AppColors) {
borderRadius: borderRadius.sm, borderRadius: borderRadius.sm,
paddingHorizontal: 4, paddingHorizontal: 4,
paddingVertical: 1, paddingVertical: 1,
borderWidth: 1.5, borderWidth: 2,
borderColor: colors.background.paper, borderColor: colors.background.paper,
}, },
ownerBadgeText: { ownerBadgeText: {
fontSize: 8, fontSize: 8,
fontWeight: '700', fontWeight: '800',
}, },
moreMembers: { moreMembers: {
minWidth: 44, minWidth: 44,
@@ -1254,49 +1272,55 @@ function createGroupInfoStyles(colors: AppColors) {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
borderWidth: 1, borderWidth: 1,
borderColor: colors.divider, borderColor: colors.divider + '60',
marginLeft: -10, marginLeft: -8,
paddingHorizontal: spacing.xs, paddingHorizontal: spacing.xs,
}, },
moreMembersText: { moreMembersText: {
fontWeight: '600', fontWeight: '700',
includeFontPadding: false, includeFontPadding: false,
fontSize: fontSizes.sm,
}, },
// 邀请按钮样式 // 邀请按钮样式 - 更加醒目
inviteButton: { inviteButton: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
paddingVertical: spacing.md, paddingVertical: spacing.md,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
backgroundColor: colors.primary.light + '15', backgroundColor: colors.primary.light + '10',
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.primary.light, borderColor: colors.primary.light + '60',
borderStyle: 'dashed', borderStyle: 'dashed',
}, },
inviteIconContainer: { inviteIconContainer: {
width: 32, width: 32,
height: 32, height: 32,
borderRadius: 16, borderRadius: 16,
backgroundColor: colors.background.paper, backgroundColor: colors.primary.light + '20',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: spacing.sm, marginRight: spacing.sm,
}, },
inviteButtonText: { inviteButtonText: {
fontWeight: '600', fontWeight: '700',
marginRight: spacing.xs, marginRight: spacing.xs,
letterSpacing: 0.3,
}, },
// 设置项样式 // 设置项样式 - 更加扁平
settingsList: { settingsList: {
marginTop: spacing.sm, marginTop: spacing.sm,
}, },
settingItem: { settingItem: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: spacing.sm, paddingVertical: spacing.md,
borderRadius: borderRadius.md,
paddingHorizontal: spacing.sm,
marginLeft: -spacing.sm,
marginRight: -spacing.sm,
}, },
settingIconContainer: { settingIconContainer: {
width: 40, width: 40,
@@ -1308,24 +1332,29 @@ function createGroupInfoStyles(colors: AppColors) {
marginRight: spacing.md, marginRight: spacing.md,
}, },
settingIconDanger: { settingIconDanger: {
backgroundColor: colors.error.light + '20', backgroundColor: colors.error.light + '15',
}, },
settingContent: { settingContent: {
flex: 1, flex: 1,
}, },
dangerText: { dangerText: {
color: colors.error.main, color: colors.error.main,
fontWeight: '600',
}, },
settingDivider: { settingDivider: {
marginLeft: 56, marginLeft: 56,
width: 'auto', width: 'auto',
marginVertical: spacing.xs, marginVertical: spacing.xs,
opacity: 0.5,
}, },
// 危险操作卡片 // 危险操作卡片 - 更加醒目
dangerCard: { dangerCard: {
padding: 0, padding: 0,
overflow: 'hidden', overflow: 'hidden',
borderTopWidth: 1,
borderTopColor: colors.error.light + '30',
marginTop: spacing.sm,
}, },
dangerButton: { dangerButton: {
flexDirection: 'row', flexDirection: 'row',
@@ -1333,15 +1362,17 @@ function createGroupInfoStyles(colors: AppColors) {
justifyContent: 'center', justifyContent: 'center',
paddingVertical: spacing.lg, paddingVertical: spacing.lg,
gap: spacing.sm, gap: spacing.sm,
backgroundColor: colors.error.light + '08',
}, },
dangerButtonText: { dangerButtonText: {
fontWeight: '600', fontWeight: '700',
letterSpacing: 0.3,
}, },
// 模态框样式 // 模态框样式 - 更现代
modalOverlay: { modalOverlay: {
flex: 1, flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.45)',
justifyContent: 'flex-end', justifyContent: 'flex-end',
}, },
modalContent: { modalContent: {
@@ -1350,7 +1381,7 @@ function createGroupInfoStyles(colors: AppColors) {
borderTopRightRadius: borderRadius['2xl'], borderTopRightRadius: borderRadius['2xl'],
height: '80%', height: '80%',
paddingHorizontal: spacing.lg, paddingHorizontal: spacing.lg,
paddingTop: spacing.lg, paddingTop: spacing.md,
paddingBottom: spacing.xl, paddingBottom: spacing.xl,
}, },
modalHeader: { modalHeader: {
@@ -1358,24 +1389,29 @@ function createGroupInfoStyles(colors: AppColors) {
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
marginBottom: spacing.lg, marginBottom: spacing.lg,
paddingBottom: spacing.md,
borderBottomWidth: 0.5,
borderBottomColor: colors.divider + '60',
}, },
modalHeaderButton: { modalHeaderButton: {
paddingVertical: spacing.sm, paddingVertical: spacing.sm,
minWidth: 60, minWidth: 60,
}, },
modalTitle: { modalTitle: {
fontWeight: '700', fontWeight: '800',
fontSize: fontSizes.xl, fontSize: fontSizes.xl + 1,
letterSpacing: 0.5,
}, },
saveButton: { saveButton: {
fontWeight: '600', fontWeight: '700',
letterSpacing: 0.3,
}, },
confirmButton: { confirmButton: {
fontWeight: '600', fontWeight: '700',
textAlign: 'right', textAlign: 'right',
}, },
// 编辑表单样式 // 编辑表单样式 - 更现代
editForm: { editForm: {
alignItems: 'center', alignItems: 'center',
}, },
@@ -1387,12 +1423,17 @@ function createGroupInfoStyles(colors: AppColors) {
width: 90, width: 90,
height: 90, height: 90,
borderRadius: 45, borderRadius: 45,
borderWidth: 3,
borderColor: colors.primary.light + '40',
}, },
editAvatarPlaceholder: { editAvatarPlaceholder: {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
borderRadius: 45, borderRadius: 45,
borderWidth: 2,
borderColor: colors.divider,
borderStyle: 'dashed',
}, },
editAvatarButton: { editAvatarButton: {
position: 'absolute', position: 'absolute',
@@ -1413,7 +1454,9 @@ function createGroupInfoStyles(colors: AppColors) {
}, },
editLabel: { editLabel: {
marginBottom: spacing.sm, marginBottom: spacing.sm,
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.sm + 1,
letterSpacing: 0.3,
}, },
editInput: { editInput: {
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
@@ -1422,8 +1465,9 @@ function createGroupInfoStyles(colors: AppColors) {
paddingVertical: spacing.md, paddingVertical: spacing.md,
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.text.primary, color: colors.text.primary,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
fontWeight: '400',
}, },
editTextArea: { editTextArea: {
minHeight: 100, minHeight: 100,
@@ -1433,6 +1477,7 @@ function createGroupInfoStyles(colors: AppColors) {
editCharCount: { editCharCount: {
textAlign: 'right', textAlign: 'right',
marginTop: spacing.xs, marginTop: spacing.xs,
fontWeight: '500',
}, },
// 公告表单样式 // 公告表单样式
@@ -1446,17 +1491,18 @@ function createGroupInfoStyles(colors: AppColors) {
padding: spacing.md, padding: spacing.md,
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.text.primary, color: colors.text.primary,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
lineHeight: 22, lineHeight: 22,
textAlignVertical: 'top', textAlignVertical: 'top',
}, },
announcementCharCount: { announcementCharCount: {
textAlign: 'right', textAlign: 'right',
marginTop: spacing.sm, marginTop: spacing.sm,
fontWeight: '500',
}, },
// 加群方式样式 // 加群方式样式 - 更现代的选中状态
joinTypeList: { joinTypeList: {
gap: spacing.md, gap: spacing.md,
}, },
@@ -1465,36 +1511,39 @@ function createGroupInfoStyles(colors: AppColors) {
alignItems: 'center', alignItems: 'center',
padding: spacing.md, padding: spacing.md,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
backgroundColor: colors.background.default,
}, },
joinTypeItemSelected: { joinTypeItemSelected: {
borderColor: colors.primary.main, borderColor: colors.primary.main,
backgroundColor: colors.primary.light + '10', backgroundColor: colors.primary.light + '12',
borderWidth: 2,
}, },
joinTypeIcon: { joinTypeIcon: {
width: 48, width: 48,
height: 48, height: 48,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
backgroundColor: colors.background.default, backgroundColor: colors.background.paper,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: spacing.md, marginRight: spacing.md,
}, },
joinTypeIconSelected: { joinTypeIconSelected: {
backgroundColor: colors.primary.light + '20', backgroundColor: colors.primary.light + '25',
}, },
joinTypeInfo: { joinTypeInfo: {
flex: 1, flex: 1,
}, },
joinTypeTextSelected: { joinTypeTextSelected: {
fontWeight: '600', fontWeight: '700',
color: colors.primary.main, color: colors.primary.main,
}, },
// 转让群主样式 // 转让群主样式
transferDesc: { transferDesc: {
marginBottom: spacing.md, marginBottom: spacing.md,
fontWeight: '500',
}, },
transferList: { transferList: {
flex: 1, flex: 1,
@@ -1504,11 +1553,15 @@ function createGroupInfoStyles(colors: AppColors) {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: spacing.md, paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
marginBottom: spacing.xs, marginBottom: spacing.sm,
backgroundColor: colors.background.default,
}, },
transferItemSelected: { transferItemSelected: {
backgroundColor: colors.primary.light + '15', backgroundColor: colors.primary.light + '12',
borderWidth: 1.5,
borderColor: colors.primary.light + '60',
}, },
transferItemInfo: { transferItemInfo: {
flex: 1, flex: 1,
@@ -1530,6 +1583,7 @@ function createGroupInfoStyles(colors: AppColors) {
borderColor: colors.divider, borderColor: colors.divider,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: colors.background.paper,
}, },
transferRadioSelected: { transferRadioSelected: {
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
@@ -1546,6 +1600,8 @@ function createGroupInfoStyles(colors: AppColors) {
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
padding: spacing.xs, padding: spacing.xs,
marginBottom: spacing.md, marginBottom: spacing.md,
borderWidth: 1,
borderColor: colors.divider + '60',
}, },
tab: { tab: {
flex: 1, flex: 1,
@@ -1555,10 +1611,11 @@ function createGroupInfoStyles(colors: AppColors) {
}, },
tabActive: { tabActive: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
...shadows.sm, borderWidth: 1,
borderColor: colors.divider + '60',
}, },
tabTextActive: { tabTextActive: {
fontWeight: '600', fontWeight: '700',
}, },
selectedBadge: { selectedBadge: {
backgroundColor: colors.primary.light + '20', backgroundColor: colors.primary.light + '20',
@@ -1577,10 +1634,13 @@ function createGroupInfoStyles(colors: AppColors) {
paddingVertical: spacing.md, paddingVertical: spacing.md,
paddingHorizontal: spacing.sm, paddingHorizontal: spacing.sm,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
marginBottom: spacing.xs, marginBottom: spacing.sm,
backgroundColor: colors.background.default,
}, },
friendItemSelected: { friendItemSelected: {
backgroundColor: colors.primary.light + '15', backgroundColor: colors.primary.light + '12',
borderWidth: 1.5,
borderColor: colors.primary.light + '60',
}, },
friendInfo: { friendInfo: {
flex: 1, flex: 1,
@@ -1588,7 +1648,7 @@ function createGroupInfoStyles(colors: AppColors) {
marginRight: spacing.sm, marginRight: spacing.sm,
}, },
nickname: { nickname: {
fontWeight: '500', fontWeight: '600',
marginBottom: 2, marginBottom: 2,
}, },
checkbox: { checkbox: {
@@ -1599,6 +1659,7 @@ function createGroupInfoStyles(colors: AppColors) {
borderColor: colors.divider, borderColor: colors.divider,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: colors.background.paper,
}, },
checkboxSelected: { checkboxSelected: {
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,

View File

@@ -3,7 +3,7 @@ import { View, StyleSheet, ActivityIndicator, Alert, ScrollView, TouchableOpacit
import { useRouter, useLocalSearchParams } from 'expo-router'; import { useRouter, useLocalSearchParams } from 'expo-router';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { spacing, borderRadius, shadows, useAppColors, type AppColors } from '../../theme'; import { spacing, borderRadius, fontSizes, useAppColors, type AppColors } from '../../theme';
import { Avatar, Text } from '../../components/common'; import { Avatar, Text } from '../../components/common';
import { routePayloadCache } from '../../stores/routePayloadCache'; import { routePayloadCache } from '../../stores/routePayloadCache';
import { groupService } from '../../services/groupService'; import { groupService } from '../../services/groupService';
@@ -196,14 +196,15 @@ function createGroupInviteDetailStyles(colors: AppColors) {
flex: 1, flex: 1,
}, },
scrollContent: { scrollContent: {
padding: spacing.lg, padding: spacing.md,
paddingBottom: spacing.xl, paddingBottom: spacing.xl * 2,
}, },
card: { card: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg, borderRadius: borderRadius.xl,
padding: spacing.lg, padding: spacing.lg,
...shadows.sm, borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
cardHeader: { cardHeader: {
flexDirection: 'row', flexDirection: 'row',
@@ -211,20 +212,23 @@ function createGroupInviteDetailStyles(colors: AppColors) {
marginBottom: spacing.md, marginBottom: spacing.md,
}, },
cardIconContainer: { cardIconContainer: {
width: 30, width: 36,
height: 30, height: 36,
borderRadius: 15, borderRadius: borderRadius.md,
backgroundColor: colors.info.light + '30', backgroundColor: colors.info.light + '15',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginRight: spacing.sm, marginRight: spacing.sm,
}, },
cardTitle: { cardTitle: {
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.md + 1,
flex: 1, flex: 1,
letterSpacing: 0.3,
}, },
memberCount: { memberCount: {
marginRight: spacing.xs, marginRight: spacing.xs,
fontWeight: '600',
}, },
loadingWrap: { loadingWrap: {
paddingVertical: spacing.md, paddingVertical: spacing.md,
@@ -247,13 +251,15 @@ function createGroupInviteDetailStyles(colors: AppColors) {
bottom: -2, bottom: -2,
right: -2, right: -2,
backgroundColor: colors.warning.main, backgroundColor: colors.warning.main,
borderRadius: 8, borderRadius: borderRadius.sm,
paddingHorizontal: 4, paddingHorizontal: 4,
paddingVertical: 1, paddingVertical: 1,
borderWidth: 1.5,
borderColor: colors.background.paper,
}, },
ownerBadgeText: { ownerBadgeText: {
fontSize: 10, fontSize: 8,
fontWeight: '700', fontWeight: '800',
}, },
}); });
} }

View File

@@ -664,19 +664,21 @@ function createGroupMembersStyles(colors: AppColors) {
flex: 1, flex: 1,
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
}, },
// Header 样式 // Header 样式 - 扁平化
pageHeader: { pageHeader: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
paddingHorizontal: spacing.md, paddingHorizontal: spacing.md,
paddingVertical: spacing.sm, paddingVertical: spacing.md,
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderBottomWidth: 1, borderBottomWidth: 0.5,
borderBottomColor: colors.divider, borderBottomColor: colors.divider + '60',
}, },
pageTitle: { pageTitle: {
fontWeight: '600', fontWeight: '800',
fontSize: fontSizes.xl + 1,
letterSpacing: 0.5,
}, },
pageHeaderPlaceholder: { pageHeaderPlaceholder: {
width: 40, width: 40,
@@ -688,18 +690,20 @@ function createGroupMembersStyles(colors: AppColors) {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: spacing.md, paddingHorizontal: spacing.lg,
paddingVertical: spacing.sm, paddingVertical: spacing.md,
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
borderTopWidth: 0.5,
borderTopColor: colors.divider + '40',
}, },
memberItem: { memberItem: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: spacing.md, paddingHorizontal: spacing.lg,
paddingVertical: spacing.md, paddingVertical: spacing.md,
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderBottomWidth: 1, borderBottomWidth: 0.5,
borderBottomColor: colors.divider, borderBottomColor: colors.divider + '40',
}, },
memberInfo: { memberInfo: {
flex: 1, flex: 1,
@@ -711,63 +715,81 @@ function createGroupMembersStyles(colors: AppColors) {
marginBottom: 2, marginBottom: 2,
}, },
memberName: { memberName: {
fontWeight: '500', fontWeight: '700',
fontSize: fontSizes.md + 1,
letterSpacing: 0.3,
}, },
roleBadge: { roleBadge: {
paddingHorizontal: spacing.xs, paddingHorizontal: spacing.sm,
paddingVertical: 2, paddingVertical: 2,
borderRadius: borderRadius.sm, borderRadius: borderRadius.sm,
marginLeft: spacing.sm, marginLeft: spacing.sm,
fontWeight: '800',
fontSize: fontSizes.xs,
color: colors.background.paper,
}, },
mutedBadge: { mutedBadge: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginTop: 2, marginTop: 2,
}, },
// 模态框样式 // 模态框样式 - 更现代
modalOverlay: { modalOverlay: {
flex: 1, flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.45)',
justifyContent: 'flex-end', justifyContent: 'flex-end',
}, },
modalContent: { modalContent: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderTopLeftRadius: borderRadius.lg, borderTopLeftRadius: borderRadius['2xl'],
borderTopRightRadius: borderRadius.lg, borderTopRightRadius: borderRadius['2xl'],
padding: spacing.lg, padding: spacing.lg,
maxHeight: '80%', maxHeight: '80%',
}, },
modalHeader: { modalHeader: {
alignItems: 'center', alignItems: 'center',
marginBottom: spacing.md, marginBottom: spacing.lg,
paddingBottom: spacing.md,
borderBottomWidth: 0.5,
borderBottomColor: colors.divider + '60',
}, },
modalTitle: { modalTitle: {
fontWeight: '700', fontWeight: '800',
fontSize: fontSizes.xl + 1,
marginTop: spacing.sm, marginTop: spacing.sm,
marginBottom: spacing.xs, marginBottom: spacing.xs,
letterSpacing: 0.5,
}, },
actionItem: { actionItem: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: spacing.md, paddingVertical: spacing.md,
borderBottomWidth: 1, paddingHorizontal: spacing.sm,
borderBottomColor: colors.divider, marginLeft: -spacing.sm,
marginRight: -spacing.sm,
borderRadius: borderRadius.md,
borderBottomWidth: 0.5,
borderBottomColor: colors.divider + '40',
}, },
actionText: { actionText: {
marginLeft: spacing.md, marginLeft: spacing.md,
fontWeight: '600',
fontSize: fontSizes.md,
}, },
inputLabel: { inputLabel: {
marginBottom: spacing.xs, marginBottom: spacing.xs,
fontWeight: '700',
fontSize: fontSizes.sm + 1,
}, },
input: { input: {
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
borderRadius: borderRadius.md, borderRadius: borderRadius.lg,
paddingHorizontal: spacing.md, paddingHorizontal: spacing.md,
paddingVertical: spacing.md, paddingVertical: spacing.md,
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.text.primary, color: colors.text.primary,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
marginBottom: spacing.md, marginBottom: spacing.md,
}, },
modalButtons: { modalButtons: {
@@ -791,6 +813,8 @@ function createGroupMembersStyles(colors: AppColors) {
noMoreText: { noMoreText: {
textAlign: 'center', textAlign: 'center',
paddingVertical: spacing.md, paddingVertical: spacing.md,
fontWeight: '500',
color: colors.text.hint,
}, },
}); });
} }

View File

@@ -13,7 +13,7 @@ import {
import { useRouter } from 'expo-router'; import { useRouter } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { spacing, borderRadius, useAppColors, type AppColors } from '../../theme'; import { spacing, borderRadius, fontSizes, useAppColors, type AppColors } from '../../theme';
import Avatar from '../../components/common/Avatar'; import Avatar from '../../components/common/Avatar';
import Text from '../../components/common/Text'; import Text from '../../components/common/Text';
import { groupService } from '../../services/groupService'; import { groupService } from '../../services/groupService';
@@ -27,47 +27,58 @@ function createJoinGroupStyles(colors: AppColors) {
container: { container: {
flex: 1, flex: 1,
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
padding: spacing.lg, padding: spacing.md,
}, },
heroCard: { heroCard: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg, borderRadius: borderRadius.xl,
padding: spacing.lg, padding: spacing.lg,
marginBottom: spacing.lg, marginBottom: spacing.md,
borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
heroIconWrap: { heroIconWrap: {
width: 48, width: 48,
height: 48, height: 48,
borderRadius: 24, borderRadius: 24,
backgroundColor: colors.primary.light + '26', backgroundColor: colors.primary.light + '15',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginBottom: spacing.md, marginBottom: spacing.md,
}, },
heroTitle: { heroTitle: {
marginBottom: spacing.xs, marginBottom: spacing.xs,
fontWeight: '800',
fontSize: fontSizes.xl + 1,
letterSpacing: 0.3,
}, },
tip: { tip: {
lineHeight: 20, lineHeight: 22,
fontWeight: '400',
}, },
formCard: { formCard: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg, borderRadius: borderRadius.xl,
padding: spacing.lg, padding: spacing.lg,
flex: 1, flex: 1,
borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
label: { label: {
marginBottom: spacing.xs, marginBottom: spacing.xs,
fontWeight: '700',
fontSize: fontSizes.sm + 1,
}, },
input: { input: {
flex: 1, flex: 1,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.divider, borderColor: colors.divider + '80',
borderRadius: borderRadius.md, borderRadius: borderRadius.lg,
backgroundColor: colors.background.paper, backgroundColor: colors.background.default,
paddingHorizontal: spacing.md, paddingHorizontal: spacing.md,
paddingVertical: spacing.md, paddingVertical: spacing.md,
color: colors.text.primary, color: colors.text.primary,
fontSize: fontSizes.md,
}, },
searchRow: { searchRow: {
flexDirection: 'row', flexDirection: 'row',
@@ -77,26 +88,28 @@ function createJoinGroupStyles(colors: AppColors) {
searchBtn: { searchBtn: {
width: 46, width: 46,
height: 46, height: 46,
borderRadius: borderRadius.md, borderRadius: borderRadius.lg,
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginLeft: spacing.sm, marginLeft: spacing.sm,
}, },
searchResultSection: { searchResultSection: {
marginBottom: spacing.lg, marginBottom: spacing.md,
}, },
sectionTitle: { sectionTitle: {
marginBottom: spacing.sm, marginBottom: spacing.sm,
fontWeight: '600', fontWeight: '800',
fontSize: fontSizes.md + 1,
letterSpacing: 0.3,
}, },
listSection: { listSection: {
flex: 1, flex: 1,
}, },
groupCard: { groupCard: {
borderWidth: 1, borderWidth: 0.5,
borderColor: colors.divider, borderColor: colors.divider + '40',
borderRadius: borderRadius.md, borderRadius: borderRadius.lg,
padding: spacing.md, padding: spacing.md,
backgroundColor: colors.background.default, backgroundColor: colors.background.default,
marginBottom: spacing.md, marginBottom: spacing.md,
@@ -111,11 +124,14 @@ function createJoinGroupStyles(colors: AppColors) {
}, },
groupName: { groupName: {
marginBottom: spacing.xs, marginBottom: spacing.xs,
fontWeight: '600', fontWeight: '700',
fontSize: fontSizes.md + 1,
letterSpacing: 0.3,
}, },
groupDesc: { groupDesc: {
marginTop: spacing.sm, marginTop: spacing.sm,
lineHeight: 18, lineHeight: 20,
fontWeight: '400',
}, },
groupInfoRow: { groupInfoRow: {
marginTop: spacing.sm, marginTop: spacing.sm,
@@ -135,16 +151,17 @@ function createJoinGroupStyles(colors: AppColors) {
paddingHorizontal: spacing.sm, paddingHorizontal: spacing.sm,
paddingVertical: 4, paddingVertical: 4,
borderRadius: borderRadius.sm, borderRadius: borderRadius.sm,
backgroundColor: colors.primary.light + '22', backgroundColor: colors.primary.light + '15',
}, },
copyBtnText: { copyBtnText: {
marginLeft: 4, marginLeft: 4,
fontWeight: '600',
}, },
submitBtn: { submitBtn: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
borderRadius: borderRadius.md, borderRadius: borderRadius.lg,
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
minHeight: 46, minHeight: 46,
}, },
@@ -153,10 +170,13 @@ function createJoinGroupStyles(colors: AppColors) {
}, },
submitText: { submitText: {
marginLeft: spacing.xs, marginLeft: spacing.xs,
fontWeight: '700',
fontSize: fontSizes.md,
}, },
emptyText: { emptyText: {
marginTop: spacing.sm, marginTop: spacing.sm,
textAlign: 'center', textAlign: 'center',
fontWeight: '500',
}, },
loadingFooter: { loadingFooter: {
paddingVertical: spacing.md, paddingVertical: spacing.md,
@@ -169,6 +189,8 @@ function createJoinGroupStyles(colors: AppColors) {
noMoreText: { noMoreText: {
textAlign: 'center', textAlign: 'center',
paddingVertical: spacing.md, paddingVertical: spacing.md,
fontWeight: '500',
color: colors.text.hint,
}, },
}); });
} }

View File

@@ -185,9 +185,11 @@ export const ChatInput: React.FC<ChatInputProps & {
<TouchableOpacity <TouchableOpacity
style={styles.sendButtonActive} style={styles.sendButtonActive}
onPress={onSend} onPress={onSend}
activeOpacity={0.8} activeOpacity={0.85}
> >
<MaterialCommunityIcons name="send" size={20} color="#FFF" /> {/* 光泽层 - 模拟渐变效果 */}
<View style={styles.sendButtonShine} pointerEvents="none" />
<MaterialCommunityIcons name="send" size={18} color="#FFF" />
</TouchableOpacity> </TouchableOpacity>
) : isComposerBusy && !isDisabled ? ( ) : isComposerBusy && !isDisabled ? (
<TouchableOpacity <TouchableOpacity

View File

@@ -374,7 +374,7 @@ function createGroupInfoPanelStyles(colors: AppColors) {
return StyleSheet.create({ return StyleSheet.create({
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.3)', backgroundColor: 'rgba(0, 0, 0, 0.35)',
zIndex: 100, zIndex: 100,
}, },
overlayTouchable: { overlayTouchable: {
@@ -388,24 +388,28 @@ function createGroupInfoPanelStyles(colors: AppColors) {
width: PANEL_WIDTH, width: PANEL_WIDTH,
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
zIndex: 101, zIndex: 101,
...shadows.lg, borderLeftWidth: 0.5,
borderLeftColor: colors.divider + '60',
}, },
header: { header: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
paddingHorizontal: spacing.md, paddingHorizontal: spacing.lg,
paddingVertical: spacing.md, paddingVertical: spacing.md,
borderBottomWidth: 1, borderBottomWidth: 0.5,
borderBottomColor: colors.divider, borderBottomColor: colors.divider + '60',
backgroundColor: colors.background.paper,
}, },
headerTitle: { headerTitle: {
fontSize: fontSizes.xl, fontSize: fontSizes.xl + 1,
fontWeight: '600', fontWeight: '800',
color: colors.text.primary, color: colors.text.primary,
letterSpacing: 0.5,
}, },
closeButton: { closeButton: {
padding: spacing.xs, padding: spacing.xs,
borderRadius: borderRadius.md,
}, },
content: { content: {
flex: 1, flex: 1,
@@ -413,22 +417,26 @@ function createGroupInfoPanelStyles(colors: AppColors) {
groupInfoSection: { groupInfoSection: {
alignItems: 'center', alignItems: 'center',
paddingVertical: spacing.xl, paddingVertical: spacing.xl,
paddingHorizontal: spacing.md,
}, },
groupName: { groupName: {
fontSize: fontSizes.xl, fontSize: fontSizes.xl + 1,
fontWeight: '600', fontWeight: '800',
color: colors.text.primary, color: colors.text.primary,
marginTop: spacing.md, marginTop: spacing.md,
letterSpacing: 0.3,
}, },
memberCount: { memberCount: {
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.text.secondary, color: colors.text.secondary,
marginTop: spacing.xs, marginTop: spacing.xs,
fontWeight: '500',
}, },
joinType: { joinType: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.primary.main, color: colors.primary.main,
marginTop: spacing.xs, marginTop: spacing.xs,
fontWeight: '600',
}, },
inviteButton: { inviteButton: {
flexDirection: 'row', flexDirection: 'row',
@@ -436,25 +444,27 @@ function createGroupInfoPanelStyles(colors: AppColors) {
justifyContent: 'center', justifyContent: 'center',
paddingVertical: spacing.sm, paddingVertical: spacing.sm,
paddingHorizontal: spacing.md, paddingHorizontal: spacing.md,
backgroundColor: colors.primary.light + '15', backgroundColor: colors.primary.light + '12',
borderRadius: 20, borderRadius: borderRadius.lg,
marginTop: spacing.md, marginTop: spacing.md,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.primary.light, borderColor: colors.primary.light + '60',
borderStyle: 'dashed',
}, },
inviteButtonText: { inviteButtonText: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.primary.main, color: colors.primary.main,
marginLeft: spacing.xs, marginLeft: spacing.xs,
fontWeight: '500', fontWeight: '700',
letterSpacing: 0.3,
}, },
divider: { divider: {
height: 1, height: 0.5,
backgroundColor: colors.divider, backgroundColor: colors.divider + '60',
marginHorizontal: spacing.md, marginHorizontal: spacing.lg,
}, },
section: { section: {
padding: spacing.md, padding: spacing.lg,
}, },
sectionHeader: { sectionHeader: {
flexDirection: 'row', flexDirection: 'row',
@@ -462,38 +472,44 @@ function createGroupInfoPanelStyles(colors: AppColors) {
marginBottom: spacing.sm, marginBottom: spacing.sm,
}, },
sectionTitle: { sectionTitle: {
fontSize: fontSizes.md, fontSize: fontSizes.md + 1,
fontWeight: '600', fontWeight: '700',
color: colors.text.primary, color: colors.text.primary,
marginLeft: spacing.xs, marginLeft: spacing.xs,
letterSpacing: 0.3,
}, },
noticeBox: { noticeBox: {
backgroundColor: colors.chat.tipBg, backgroundColor: colors.warning.light + '08',
padding: spacing.md, padding: spacing.md,
borderRadius: 8, borderRadius: borderRadius.lg,
borderLeftWidth: 3, borderLeftWidth: 3,
borderLeftColor: colors.warning.main, borderLeftColor: colors.warning.main,
}, },
noticeText: { noticeText: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.text.secondary, color: colors.text.secondary,
lineHeight: 20, lineHeight: 22,
fontWeight: '400',
}, },
noticeTime: { noticeTime: {
fontSize: fontSizes.xs, fontSize: fontSizes.xs,
color: colors.text.hint, color: colors.text.hint,
marginTop: spacing.xs, marginTop: spacing.xs,
textAlign: 'right', textAlign: 'right',
fontWeight: '500',
}, },
descriptionBox: { descriptionBox: {
backgroundColor: colors.chat.surfaceMuted, backgroundColor: colors.background.default,
padding: spacing.md, padding: spacing.md,
borderRadius: 8, borderRadius: borderRadius.lg,
borderWidth: 0.5,
borderColor: colors.divider + '30',
}, },
description: { description: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.text.secondary, color: colors.text.secondary,
lineHeight: 20, lineHeight: 22,
fontWeight: '400',
}, },
memberList: { memberList: {
gap: spacing.sm, gap: spacing.sm,
@@ -511,15 +527,20 @@ function createGroupInfoPanelStyles(colors: AppColors) {
viewAllText: { viewAllText: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.primary.main, color: colors.primary.main,
fontWeight: '600',
}, },
memberItem: { memberItem: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: spacing.xs, paddingVertical: spacing.sm,
paddingHorizontal: spacing.sm,
borderRadius: borderRadius.md,
marginLeft: -spacing.sm,
marginRight: -spacing.sm,
}, },
memberInfo: { memberInfo: {
flex: 1, flex: 1,
marginLeft: spacing.sm, marginLeft: spacing.md,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
@@ -528,48 +549,55 @@ function createGroupInfoPanelStyles(colors: AppColors) {
fontSize: fontSizes.md, fontSize: fontSizes.md,
color: colors.text.primary, color: colors.text.primary,
flex: 1, flex: 1,
fontWeight: '600',
}, },
ownerBadge: { ownerBadge: {
fontSize: fontSizes.xs, fontSize: fontSizes.xs,
color: colors.warning.main, color: colors.background.paper,
marginLeft: spacing.xs, marginLeft: spacing.xs,
backgroundColor: colors.warning.light + '30', backgroundColor: colors.warning.main,
paddingHorizontal: 6, paddingHorizontal: 6,
paddingVertical: 2, paddingVertical: 2,
borderRadius: 4, borderRadius: borderRadius.sm,
fontWeight: '800',
}, },
adminBadge: { adminBadge: {
fontSize: fontSizes.xs, fontSize: fontSizes.xs,
color: colors.primary.main, color: colors.primary.main,
marginLeft: spacing.xs, marginLeft: spacing.xs,
backgroundColor: colors.primary.light + '30', backgroundColor: colors.primary.light + '20',
paddingHorizontal: 6, paddingHorizontal: 6,
paddingVertical: 2, paddingVertical: 2,
borderRadius: 4, borderRadius: borderRadius.sm,
fontWeight: '700',
}, },
moreMembers: { moreMembers: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.text.secondary, color: colors.text.secondary,
textAlign: 'center', textAlign: 'center',
paddingVertical: spacing.sm, paddingVertical: spacing.sm,
fontWeight: '500',
}, },
infoRow: { infoRow: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
paddingVertical: spacing.sm, paddingVertical: spacing.md,
borderBottomWidth: 1, borderBottomWidth: 0.5,
borderBottomColor: colors.chat.borderHairline, borderBottomColor: colors.divider + '40',
}, },
infoLabel: { infoLabel: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.text.secondary, color: colors.text.secondary,
fontWeight: '500',
}, },
infoValue: { infoValue: {
fontSize: fontSizes.sm, fontSize: fontSizes.sm,
color: colors.text.primary, color: colors.text.primary,
fontWeight: '600',
textAlign: 'right',
}, },
actionSection: { actionSection: {
padding: spacing.md, padding: spacing.lg,
paddingBottom: spacing.xl, paddingBottom: spacing.xl,
}, },
actionButton: { actionButton: {
@@ -577,24 +605,27 @@ function createGroupInfoPanelStyles(colors: AppColors) {
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
paddingVertical: spacing.md, paddingVertical: spacing.md,
backgroundColor: colors.primary.light + '15', backgroundColor: colors.primary.light + '10',
borderRadius: 12, borderRadius: borderRadius.lg,
marginBottom: spacing.sm, marginBottom: spacing.sm,
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.primary.light, borderColor: colors.primary.light + '50',
}, },
actionButtonText: { actionButtonText: {
fontSize: fontSizes.md, fontSize: fontSizes.md,
fontWeight: '600', fontWeight: '700',
color: colors.primary.main, color: colors.primary.main,
marginLeft: spacing.sm, marginLeft: spacing.sm,
letterSpacing: 0.3,
}, },
leaveButton: { leaveButton: {
backgroundColor: colors.error.light + '15', backgroundColor: colors.error.light + '08',
borderColor: colors.error.light, borderColor: colors.error.light + '50',
borderWidth: 1.5,
}, },
leaveButtonText: { leaveButtonText: {
color: colors.error.main, color: colors.error.main,
fontWeight: '700',
}, },
}); });
} }

View File

@@ -432,18 +432,34 @@ export function createChatScreenStyles(colors: AppColors) {
transform: [{ translateY: -1 }], transform: [{ translateY: -1 }],
}, },
// 发送按钮 // 发送按钮 - QQ/微信风格
sendButtonActive: { sendButtonActive: {
width: 38, width: 36,
height: 30, height: 36,
borderRadius: 15, borderRadius: 18, // 正圆形
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
...shadows.md, // 微信风格:轻微阴影,不夸张
shadowColor: colors.primary.main,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 3,
elevation: 2,
}, },
sendButtonDisabled: { sendButtonDisabled: {
opacity: 0.6, opacity: 0.4,
},
// 发送按钮光泽层(叠加在按钮上模拟渐变)
sendButtonShine: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '50%',
borderTopLeftRadius: 18,
borderTopRightRadius: 18,
backgroundColor: 'rgba(255, 255, 255, 0.08)',
}, },
// 输入框 // 输入框

View File

@@ -6,7 +6,7 @@
import React, { memo, useEffect, useMemo, useRef, useState } from 'react'; import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
import { Animated, StyleSheet, TouchableOpacity, View } from 'react-native'; import { Animated, StyleSheet, TouchableOpacity, View } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { spacing, shadows, useAppColors, type AppColors } from '../../../theme'; import { spacing, shadows, borderRadius, useAppColors, type AppColors } from '../../../theme';
import { import {
ConversationResponse, ConversationResponse,
extractTextFromSegments, extractTextFromSegments,
@@ -160,13 +160,14 @@ function createConversationRowStyles(colors: AppColors) {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
marginHorizontal: spacing.md, marginHorizontal: spacing.md,
marginTop: spacing.sm, marginTop: spacing.sm,
borderRadius: 12, borderRadius: borderRadius.lg,
...shadows.sm, borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
conversationItemSelected: { conversationItemSelected: {
backgroundColor: colors.primary.light + '20', backgroundColor: colors.primary.light + '12',
borderColor: colors.primary.main, borderColor: colors.primary.main + '60',
borderWidth: 1, borderWidth: 1.5,
}, },
avatarContainer: { avatarContainer: {
position: 'relative', position: 'relative',
@@ -174,7 +175,7 @@ function createConversationRowStyles(colors: AppColors) {
systemAvatar: { systemAvatar: {
width: 50, width: 50,
height: 50, height: 50,
borderRadius: 12, borderRadius: borderRadius.md,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
@@ -195,7 +196,7 @@ function createConversationRowStyles(colors: AppColors) {
}, },
officialBadge: { officialBadge: {
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
borderRadius: 4, borderRadius: borderRadius.sm,
paddingHorizontal: 6, paddingHorizontal: 6,
paddingVertical: 2, paddingVertical: 2,
marginRight: spacing.xs, marginRight: spacing.xs,
@@ -203,12 +204,13 @@ function createConversationRowStyles(colors: AppColors) {
officialBadgeText: { officialBadgeText: {
color: colors.primary.contrast, color: colors.primary.contrast,
fontSize: 10, fontSize: 10,
fontWeight: '600', fontWeight: '800',
}, },
userName: { userName: {
fontWeight: '600', fontWeight: '700',
color: colors.text.primary, color: colors.text.primary,
fontSize: 16, fontSize: 16,
letterSpacing: 0.3,
}, },
groupIcon: { groupIcon: {
marginRight: 4, marginRight: 4,
@@ -217,6 +219,7 @@ function createConversationRowStyles(colors: AppColors) {
fontSize: 12, fontSize: 12,
color: colors.text.secondary, color: colors.text.secondary,
marginLeft: 2, marginLeft: 2,
fontWeight: '500',
}, },
pinnedIcon: { pinnedIcon: {
marginLeft: 6, marginLeft: 6,
@@ -228,7 +231,7 @@ function createConversationRowStyles(colors: AppColors) {
groupAvatarPlaceholder: { groupAvatarPlaceholder: {
width: 50, width: 50,
height: 50, height: 50,
borderRadius: 12, borderRadius: borderRadius.md,
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
@@ -236,6 +239,7 @@ function createConversationRowStyles(colors: AppColors) {
timeText: { timeText: {
color: colors.text.secondary, color: colors.text.secondary,
fontSize: 12, fontSize: 12,
fontWeight: '500',
}, },
messageRow: { messageRow: {
flexDirection: 'row', flexDirection: 'row',
@@ -245,15 +249,16 @@ function createConversationRowStyles(colors: AppColors) {
flex: 1, flex: 1,
color: colors.text.secondary, color: colors.text.secondary,
fontSize: 14, fontSize: 14,
fontWeight: '400',
}, },
unreadMessageText: { unreadMessageText: {
color: colors.text.primary, color: colors.text.primary,
fontWeight: '500', fontWeight: '600',
}, },
unreadBadge: { unreadBadge: {
minWidth: 20, minWidth: 22,
height: 20, height: 22,
borderRadius: 10, borderRadius: 11,
backgroundColor: colors.primary.main, backgroundColor: colors.primary.main,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
@@ -263,7 +268,7 @@ function createConversationRowStyles(colors: AppColors) {
unreadBadgeText: { unreadBadgeText: {
color: colors.primary.contrast, color: colors.primary.contrast,
fontSize: 12, fontSize: 12,
fontWeight: '600', fontWeight: '800',
}, },
}); });
} }

View File

@@ -4,7 +4,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Avatar, Text } from '../../../components/common'; import { Avatar, Text } from '../../../components/common';
import { spacing, borderRadius, shadows, useAppColors, type AppColors } from '../../../theme'; import { spacing, borderRadius, fontSizes, useAppColors, type AppColors } from '../../../theme';
interface GroupInfoSummaryCardProps { interface GroupInfoSummaryCardProps {
groupName?: string; groupName?: string;
@@ -18,10 +18,11 @@ function createGroupRequestSharedStyles(colors: AppColors) {
return StyleSheet.create({ return StyleSheet.create({
headerCard: { headerCard: {
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg, borderRadius: borderRadius.xl,
padding: spacing.lg, padding: spacing.lg,
marginBottom: spacing.md, marginBottom: spacing.md,
...shadows.sm, borderWidth: 0.5,
borderColor: colors.divider + '40',
}, },
groupHeader: { groupHeader: {
flexDirection: 'row', flexDirection: 'row',
@@ -33,7 +34,9 @@ function createGroupRequestSharedStyles(colors: AppColors) {
}, },
groupName: { groupName: {
marginBottom: spacing.xs, marginBottom: spacing.xs,
fontWeight: '700', fontWeight: '800',
fontSize: fontSizes.xl + 1,
letterSpacing: 0.3,
}, },
groupMeta: { groupMeta: {
flexDirection: 'row', flexDirection: 'row',
@@ -42,6 +45,7 @@ function createGroupRequestSharedStyles(colors: AppColors) {
}, },
groupNoText: { groupNoText: {
marginTop: spacing.xs, marginTop: spacing.xs,
fontWeight: '500',
}, },
descriptionContainer: { descriptionContainer: {
flexDirection: 'row', flexDirection: 'row',
@@ -50,11 +54,14 @@ function createGroupRequestSharedStyles(colors: AppColors) {
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
padding: spacing.md, padding: spacing.md,
marginTop: spacing.md, marginTop: spacing.md,
borderWidth: 0.5,
borderColor: colors.divider + '30',
}, },
groupDesc: { groupDesc: {
marginLeft: spacing.sm, marginLeft: spacing.sm,
flex: 1, flex: 1,
lineHeight: 20, lineHeight: 22,
fontWeight: '400',
}, },
footer: { footer: {
paddingHorizontal: spacing.lg, paddingHorizontal: spacing.lg,
@@ -64,16 +71,16 @@ function createGroupRequestSharedStyles(colors: AppColors) {
}, },
btn: { btn: {
flex: 1, flex: 1,
height: 42, height: 46,
borderRadius: borderRadius.md, borderRadius: borderRadius.lg,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
reject: { reject: {
marginRight: spacing.sm, marginRight: spacing.sm,
backgroundColor: colors.error.light + '25', backgroundColor: colors.error.light + '12',
borderWidth: 1, borderWidth: 1.5,
borderColor: colors.error.light, borderColor: colors.error.light + '60',
}, },
approve: { approve: {
marginLeft: spacing.sm, marginLeft: spacing.sm,