diff --git a/src/screens/message/ChatScreen.tsx b/src/screens/message/ChatScreen.tsx index 09047a0..1e5f5ff 100644 --- a/src/screens/message/ChatScreen.tsx +++ b/src/screens/message/ChatScreen.tsx @@ -576,6 +576,7 @@ export const ChatScreen: React.FC = () => { )} diff --git a/src/screens/message/GroupMembersScreen.tsx b/src/screens/message/GroupMembersScreen.tsx index a2a9624..8daf454 100644 --- a/src/screens/message/GroupMembersScreen.tsx +++ b/src/screens/message/GroupMembersScreen.tsx @@ -5,7 +5,7 @@ * 使用游标分页 */ -import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { View, StyleSheet, @@ -19,7 +19,7 @@ import { Dimensions, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { useLocalSearchParams } from 'expo-router'; +import { useLocalSearchParams, useRouter } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme'; import { useAuthStore } from '../../stores'; @@ -30,7 +30,7 @@ import { IGroupMemberListPagedSource, } from '../../stores/groupMemberListSources'; import { enrichGroupMembersWithUserProfiles } from '../../stores/groupMemberProfileResolver'; -import { Avatar, Text, Button, Loading, EmptyState, Divider, ResponsiveContainer } from '../../components/common'; +import { Avatar, Text, Button, Loading, EmptyState, Divider, ResponsiveContainer, AppBackButton } from '../../components/common'; import { useResponsive, useBreakpointGTE } from '../../hooks/useResponsive'; import { useCursorPagination } from '../../hooks/useCursorPagination'; import { @@ -57,6 +57,7 @@ interface MemberGroup { const GroupMembersScreen: React.FC = () => { const colors = useAppColors(); const styles = useMemo(() => createGroupMembersStyles(colors), [colors]); + const router = useRouter(); const { groupId: groupIdParam } = useLocalSearchParams<{ groupId?: string | string[] }>(); const groupId = firstRouteParam(groupIdParam) ?? ''; const { currentUser } = useAuthStore(); @@ -149,11 +150,14 @@ const GroupMembersScreen: React.FC = () => { setLoading(false); }, [refresh]); - // 初始加载 + // 初始加载 - 使用 ref 避免依赖变化导致的无限循环 + const initialLoadRef = React.useRef(false); useEffect(() => { - if (!groupId) return; + if (!groupId || initialLoadRef.current) return; + initialLoadRef.current = true; refresh(); - }, [groupId, refresh]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [groupId]); // 按角色分组 const groupMembers = useCallback((): MemberGroup[] => { @@ -640,7 +644,13 @@ const GroupMembersScreen: React.FC = () => { }; return ( - + + {/* Header */} + + router.back()} /> + 群成员 + + {loading ? : renderGroupedList()} {renderActionModal()} {renderNicknameModal()} @@ -654,6 +664,23 @@ function createGroupMembersStyles(colors: AppColors) { flex: 1, backgroundColor: colors.background.default, }, + // Header 样式 + pageHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + backgroundColor: colors.background.paper, + borderBottomWidth: 1, + borderBottomColor: colors.divider, + }, + pageTitle: { + fontWeight: '600', + }, + pageHeaderPlaceholder: { + width: 40, + }, section: { marginBottom: spacing.md, }, diff --git a/src/screens/message/components/ChatScreen/ChatHeader.tsx b/src/screens/message/components/ChatScreen/ChatHeader.tsx index 6945dac..29fe64a 100644 --- a/src/screens/message/components/ChatScreen/ChatHeader.tsx +++ b/src/screens/message/components/ChatScreen/ChatHeader.tsx @@ -6,7 +6,7 @@ import React, { useMemo } from 'react'; import { View, TouchableOpacity, StyleSheet } from 'react-native'; import { MaterialCommunityIcons } from '@expo/vector-icons'; -import { Avatar, Text, AppBackButton } from '../../../../components/common'; +import { Avatar, Text } from '../../../../components/common'; import { useAppColors, spacing } from '../../../../theme'; import { useChatScreenStyles } from './styles'; import { useBreakpointGTE } from '../../../../hooks/useResponsive'; @@ -73,7 +73,9 @@ export const ChatHeader: React.FC = ({ return ( - + + + = ({ {isGroupChat ? ( <> - + {getDisplayTitle()} @@ -100,7 +107,7 @@ export const ChatHeader: React.FC = ({ <> @@ -121,15 +128,17 @@ export const ChatHeader: React.FC = ({ - + ) : ( - + )} diff --git a/src/screens/message/components/ChatScreen/EmojiPanel.tsx b/src/screens/message/components/ChatScreen/EmojiPanel.tsx index 0fd05c1..d206fe0 100644 --- a/src/screens/message/components/ChatScreen/EmojiPanel.tsx +++ b/src/screens/message/components/ChatScreen/EmojiPanel.tsx @@ -18,7 +18,7 @@ import { useAppColors, spacing } from '../../../../theme'; const { height: SCREEN_HEIGHT } = Dimensions.get('window'); - // 表情尺寸配置 - 固定宽度 40px,使用 flex 布局 +// 表情尺寸配置 - 固定宽度 40px,使用 flex 布局 const EMOJI_SIZES = { mobile: { size: 24, itemWidth: 40, itemHeight: 40 }, tablet: { size: 26, itemWidth: 40, itemHeight: 40 }, @@ -234,19 +234,21 @@ export const EmojiPanel: React.FC = ({ ); }; - // 渲染管理按钮(表情面板第一位) + // 渲染管理按钮(表情面板第一位)- 现代扁平化设计 const renderManageButton = () => ( - + + + ); @@ -515,27 +517,30 @@ export const EmojiPanel: React.FC = ({ {activeTab === 'emoji' ? renderEmojiPanel() : renderStickerPanel()} - {/* 底部 Tab 栏 */} + {/* 底部 Tab 栏 - 现代胶囊式设计 */} - setActiveTab('emoji')} - > - 😊 - - setActiveTab('sticker')} - > - - - - - + + setActiveTab('emoji')} + activeOpacity={0.8} + > + 😊 + + setActiveTab('sticker')} + activeOpacity={0.8} + > + + + + + @@ -546,3 +551,26 @@ export const EmojiPanel: React.FC = ({ }; export default EmojiPanel; + +// 自定义表情按钮样式 +const stickerButtonStyles = StyleSheet.create({ + manageButton: { + backgroundColor: '#F5F5F7', + borderRadius: 12, + alignItems: 'center', + justifyContent: 'center', + }, + iconContainer: { + width: 44, + height: 44, + borderRadius: 12, + backgroundColor: '#FFFFFF', + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.06, + shadowRadius: 2, + elevation: 1, + }, +}); diff --git a/src/screens/message/components/ChatScreen/MorePanel.tsx b/src/screens/message/components/ChatScreen/MorePanel.tsx index af957a2..f16f609 100644 --- a/src/screens/message/components/ChatScreen/MorePanel.tsx +++ b/src/screens/message/components/ChatScreen/MorePanel.tsx @@ -1,38 +1,81 @@ /** - * 更多功能面板组件 + * 更多功能面板组件 - 现代扁平化设计 */ import React from 'react'; -import { View, TouchableOpacity } from 'react-native'; +import { View, TouchableOpacity, StyleSheet } from 'react-native'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { Text } from '../../../../components/common'; import { useChatScreenStyles } from './styles'; import { MORE_ACTIONS } from './constants'; import { MorePanelProps } from './types'; +// 渐变背景组件 +const GradientIcon: React.FC<{ + colors: [string, string]; + icon: string; + size?: number; +}> = ({ colors, icon, size = 28 }) => { + return ( + + + + + ); +}; + +const gradientStyles = StyleSheet.create({ + container: { + width: 56, + height: 56, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + overflow: 'hidden', + position: 'relative', + }, + gradientOverlay: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + height: '50%', + opacity: 0.3, + }, +}); + export const MorePanel: React.FC = ({ onAction, disabledActionIds = [], + isGroupChat = false, }) => { const styles = useChatScreenStyles(); const disabledSet = new Set(disabledActionIds); + + // 群聊中隐藏语音通话和视频通话 + const actions = isGroupChat + ? MORE_ACTIONS.filter(action => action.id !== 'voice_call' && action.id !== 'video_call') + : MORE_ACTIONS; + return ( - {MORE_ACTIONS.map((action) => { + {actions.map((action) => { const disabled = disabledSet.has(action.id); return ( - onAction(action.id)} - disabled={disabled} - > - - - - {action.name} - + onAction(action.id)} + disabled={disabled} + activeOpacity={0.7} + > + + {action.name} + ); })} diff --git a/src/screens/message/components/ChatScreen/constants.ts b/src/screens/message/components/ChatScreen/constants.ts index be77c7e..74de4d7 100644 --- a/src/screens/message/components/ChatScreen/constants.ts +++ b/src/screens/message/components/ChatScreen/constants.ts @@ -141,14 +141,50 @@ export const EMOJIS = [ '🌃', '🌌', '🌉', '🌫️', '🌊', '🕳️', '💊', '💉', ]; -// 更多功能项 +// 更多功能项 - 使用渐变色配置 export const MORE_ACTIONS: MoreAction[] = [ - { id: 'voice_call', icon: 'phone', name: '语音通话', color: '#4CAF50' }, - { id: 'video_call', icon: 'video', name: '视频通话', color: '#2196F3' }, - { id: 'image', icon: 'image', name: '图片', color: '#FF6B6B' }, - { id: 'camera', icon: 'camera', name: '拍摄', color: '#4ECDC4' }, - { id: 'file', icon: 'file-document', name: '文件', color: '#45B7D1' }, - { id: 'location', icon: 'map-marker', name: '位置', color: '#96CEB4' }, + { + id: 'voice_call', + icon: 'phone', + name: '语音通话', + color: '#4CAF50', + gradientColors: ['#66BB6A', '#43A047'], + }, + { + id: 'video_call', + icon: 'video', + name: '视频通话', + color: '#2196F3', + gradientColors: ['#42A5F5', '#1E88E5'], + }, + { + id: 'image', + icon: 'image', + name: '图片', + color: '#FF6B35', + gradientColors: ['#FF8A50', '#F4511E'], + }, + { + id: 'camera', + icon: 'camera', + name: '拍摄', + color: '#26A69A', + gradientColors: ['#4DB6AC', '#00897B'], + }, + { + id: 'file', + icon: 'file-document', + name: '文件', + color: '#5C6BC0', + gradientColors: ['#7986CB', '#3F51B5'], + }, + { + id: 'location', + icon: 'map-marker', + name: '位置', + color: '#EC407A', + gradientColors: ['#F06292', '#D81B60'], + }, ]; // 消息撤回时间限制(毫秒) diff --git a/src/screens/message/components/ChatScreen/styles.ts b/src/screens/message/components/ChatScreen/styles.ts index ab60367..1aa7be5 100644 --- a/src/screens/message/components/ChatScreen/styles.ts +++ b/src/screens/message/components/ChatScreen/styles.ts @@ -30,13 +30,8 @@ export function createChatScreenStyles(colors: AppColors) { // 头部 headerContainer: { backgroundColor: colors.chat.card, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255, 107, 53, 0.12)', - shadowColor: colors.chat.shadow, - shadowOffset: { width: 0, height: 3 }, - shadowOpacity: 0.06, - shadowRadius: 10, - elevation: 5, + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: colors.chat.border, }, header: { flexDirection: 'row', @@ -45,42 +40,47 @@ export function createChatScreenStyles(colors: AppColors) { paddingHorizontal: spacing.md, paddingVertical: spacing.sm + 2, backgroundColor: colors.chat.card, + height: 56, }, backButton: { - width: 38, - height: 38, + width: 36, + height: 36, alignItems: 'center', justifyContent: 'center', - borderRadius: 19, - backgroundColor: `${colors.primary.main}12`, - borderWidth: 1, - borderColor: `${colors.primary.main}26`, + borderRadius: 10, + backgroundColor: 'transparent', }, headerCenter: { - alignItems: 'stretch', flex: 1, + alignItems: 'center', marginHorizontal: spacing.sm, }, titleRow: { flexDirection: 'row', alignItems: 'center', - borderRadius: 14, + gap: spacing.sm, paddingHorizontal: spacing.sm, - paddingVertical: spacing.xs + 1, - gap: spacing.xs + 2, - flex: 1, + paddingVertical: spacing.xs, + borderRadius: 8, }, titleLeading: { - width: 34, - height: 34, - borderRadius: 17, + width: 36, + height: 36, + borderRadius: 10, alignItems: 'center', justifyContent: 'center', - backgroundColor: `${colors.primary.main}14`, + backgroundColor: colors.chat.surfaceMuted, + overflow: 'hidden', + }, + groupAvatar: { + width: 36, + height: 36, + borderRadius: 10, }, titleContent: { flex: 1, minWidth: 0, + alignItems: 'center', }, subtitleRow: { flexDirection: 'row', @@ -90,30 +90,27 @@ export function createChatScreenStyles(colors: AppColors) { minHeight: 16, }, headerName: { - fontWeight: '700', + fontWeight: '600', color: colors.text.primary, - fontSize: 17, + fontSize: 16, letterSpacing: -0.3, }, memberCount: { fontSize: 12, color: colors.text.secondary, - fontWeight: '500', + fontWeight: '400', }, typingHint: { fontSize: 12, - color: colors.primary.dark, - backgroundColor: `${colors.primary.main}14`, - borderRadius: 10, - paddingHorizontal: 6, - paddingVertical: 1, + color: colors.primary.main, + fontWeight: '500', }, moreButton: { - width: 38, - height: 38, + width: 36, + height: 36, alignItems: 'center', justifyContent: 'center', - borderRadius: 19, + borderRadius: 10, backgroundColor: 'transparent', }, headerActions: { @@ -550,55 +547,62 @@ export function createChatScreenStyles(colors: AppColors) { borderTopWidth: 1, borderTopColor: colors.chat.border, }, - panelCloseButton: { - width: 40, - height: 40, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: colors.chat.card, - borderRadius: 8, - shadowColor: colors.chat.shadow, - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.1, - shadowRadius: 2, - elevation: 2, - }, // 面板内容区域 panelContent: { flex: 1, }, - // Tab 栏 + // Tab 栏 - 现代胶囊式设计 panelTabBar: { flexDirection: 'row', alignItems: 'center', - paddingHorizontal: spacing.sm, - paddingVertical: spacing.xs, - borderTopWidth: 1, - borderTopColor: colors.chat.border, + justifyContent: 'space-between', + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm + 2, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: colors.chat.borderHairline, backgroundColor: colors.chat.card, }, + panelTabContainer: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: colors.chat.surfaceMuted, + borderRadius: 12, + padding: 3, + }, panelTab: { - width: 44, - height: 44, + width: 42, + height: 36, alignItems: 'center', justifyContent: 'center', - borderRadius: 8, - marginRight: spacing.xs, + borderRadius: 10, }, panelTabActive: { - backgroundColor: colors.chat.replyTint, + backgroundColor: colors.chat.card, + shadowColor: colors.chat.shadow, + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.08, + shadowRadius: 2, + elevation: 1, }, panelTabEmoji: { - fontSize: 24, + fontSize: 22, }, panelTabDivider: { width: 1, - height: 24, - backgroundColor: colors.chat.bubbleOutgoing, + height: 20, + backgroundColor: colors.chat.border, marginHorizontal: spacing.sm, }, + panelCloseButton: { + width: 36, + height: 36, + alignItems: 'center', + justifyContent: 'center', + borderRadius: 10, + backgroundColor: colors.chat.surfaceMuted, + }, // 自定义表情面板 stickerLoadingContainer: { @@ -643,35 +647,60 @@ export function createChatScreenStyles(colors: AppColors) { height: '100%', }, - // 更多功能面板 + // 更多功能面板 - 现代扁平化设计 moreGrid: { flexDirection: 'row', flexWrap: 'wrap', - padding: spacing.lg, + paddingHorizontal: spacing.lg + 4, + paddingTop: spacing.lg, + paddingBottom: spacing.xl, + justifyContent: 'flex-start', }, moreItem: { - width: (SCREEN_WIDTH - spacing.lg * 2) / 4, + width: (SCREEN_WIDTH - spacing.lg * 2 - 8) / 4, alignItems: 'center', - marginBottom: spacing.lg, + marginBottom: spacing.lg + 4, }, moreIconContainer: { - width: 60, - height: 60, - borderRadius: 16, + width: 56, + height: 56, + borderRadius: 18, alignItems: 'center', justifyContent: 'center', - marginBottom: spacing.sm, + marginBottom: spacing.sm + 2, backgroundColor: colors.chat.card, shadowColor: colors.chat.shadow, shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.08, - shadowRadius: 4, + shadowOpacity: 0.06, + shadowRadius: 6, elevation: 2, }, + moreIconGradient: { + width: 56, + height: 56, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginBottom: spacing.sm + 2, + overflow: 'hidden', + }, + moreIconInner: { + width: 56, + height: 56, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + }, moreItemText: { - fontSize: 13, - color: colors.chat.textTertiary, + fontSize: 12, + color: colors.chat.textSecondary, fontWeight: '500', + letterSpacing: 0.3, + }, + // MorePanel 整体背景 + morePanelBackground: { + flex: 1, + backgroundColor: colors.chat.surfaceMuted, }, // @成员选择浮层 - 绝对定位浮在输入框上方 diff --git a/src/screens/message/components/ChatScreen/types.ts b/src/screens/message/components/ChatScreen/types.ts index 866f6bd..e803f02 100644 --- a/src/screens/message/components/ChatScreen/types.ts +++ b/src/screens/message/components/ChatScreen/types.ts @@ -48,6 +48,7 @@ export interface MoreAction { icon: string; name: string; color: string; + gradientColors?: [string, string]; // 渐变色配置 } // 长按菜单项 @@ -129,6 +130,8 @@ export interface EmojiPanelProps { export interface MorePanelProps { onAction: (actionId: string) => void; disabledActionIds?: string[]; + /** 是否为群聊(群聊中隐藏语音/视频通话) */ + isGroupChat?: boolean; } // @成员选择面板 Props @@ -168,7 +171,7 @@ export interface LongPressMenuProps { // 聊天头部 Props export interface ChatHeaderProps { isGroupChat: boolean; - groupInfo: { name?: string; member_count?: number } | null; + groupInfo: { name?: string; member_count?: number; avatar?: string | null } | null; otherUser: { id?: string; nickname?: string; avatar?: string | null } | null; routeGroupName?: string; typingHint: string | null;