2026-03-09 21:29:03 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 聊天头部组件
|
|
|
|
|
|
* 支持响应式布局(宽屏下显示更多信息)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useMemo } from 'react';
|
|
|
|
|
|
import { View, TouchableOpacity, StyleSheet } from 'react-native';
|
|
|
|
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
|
|
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
|
|
|
|
import { Avatar, Text } from '../../../../components/common';
|
|
|
|
|
|
import { colors, spacing } from '../../../../theme';
|
|
|
|
|
|
import { chatScreenStyles as baseStyles } from './styles';
|
|
|
|
|
|
import { useResponsive, useBreakpointGTE } from '../../../../hooks/useResponsive';
|
|
|
|
|
|
import { ChatHeaderProps } from './types';
|
|
|
|
|
|
|
|
|
|
|
|
export const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
|
|
|
isGroupChat,
|
|
|
|
|
|
groupInfo,
|
|
|
|
|
|
otherUser,
|
|
|
|
|
|
routeGroupName,
|
|
|
|
|
|
typingHint,
|
|
|
|
|
|
onBack,
|
|
|
|
|
|
onTitlePress,
|
|
|
|
|
|
onMorePress,
|
2026-03-16 17:47:10 +08:00
|
|
|
|
onGroupInfoPress,
|
|
|
|
|
|
isWideScreen: propIsWideScreen,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
}) => {
|
|
|
|
|
|
// 响应式布局
|
|
|
|
|
|
const { width } = useResponsive();
|
2026-03-16 17:47:10 +08:00
|
|
|
|
// 使用 768px 作为大屏幕和小屏幕的分界线
|
|
|
|
|
|
const isWideScreenHook = useBreakpointGTE('lg');
|
|
|
|
|
|
// 优先使用 props 传入的 isWideScreen,否则使用 hook
|
|
|
|
|
|
const isWideScreen = propIsWideScreen !== undefined ? propIsWideScreen : isWideScreenHook;
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
// 合并样式
|
|
|
|
|
|
const styles = useMemo(() => {
|
|
|
|
|
|
return StyleSheet.create({
|
|
|
|
|
|
...baseStyles,
|
|
|
|
|
|
headerContainer: {
|
|
|
|
|
|
...baseStyles.headerContainer,
|
|
|
|
|
|
maxWidth: isWideScreen ? 1200 : undefined,
|
|
|
|
|
|
alignSelf: isWideScreen ? 'center' : undefined,
|
|
|
|
|
|
width: isWideScreen ? '100%' : undefined,
|
|
|
|
|
|
},
|
|
|
|
|
|
header: {
|
|
|
|
|
|
...baseStyles.header,
|
|
|
|
|
|
paddingHorizontal: isWideScreen ? spacing.xl : spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
headerName: {
|
|
|
|
|
|
...baseStyles.headerName,
|
|
|
|
|
|
fontSize: isWideScreen ? 19 : 17,
|
|
|
|
|
|
},
|
|
|
|
|
|
memberCount: {
|
|
|
|
|
|
...baseStyles.memberCount,
|
|
|
|
|
|
fontSize: isWideScreen ? 14 : 13,
|
|
|
|
|
|
},
|
|
|
|
|
|
typingHint: {
|
|
|
|
|
|
...baseStyles.typingHint,
|
|
|
|
|
|
fontSize: isWideScreen ? 13 : 12,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}, [isWideScreen]);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取显示标题
|
|
|
|
|
|
const getDisplayTitle = () => {
|
|
|
|
|
|
if (isGroupChat) {
|
|
|
|
|
|
return routeGroupName || groupInfo?.name || '群聊';
|
|
|
|
|
|
}
|
|
|
|
|
|
return otherUser?.nickname || '用户';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<SafeAreaView edges={['top']} style={styles.headerContainer}>
|
|
|
|
|
|
<View style={styles.header}>
|
2026-03-16 17:47:10 +08:00
|
|
|
|
{/* 大屏幕(>= 768px)时隐藏返回按钮 */}
|
|
|
|
|
|
{!isWideScreen ? (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.backButton}
|
|
|
|
|
|
onPress={onBack}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MaterialCommunityIcons name="arrow-left" size={22} color={colors.primary.dark} />
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<View style={styles.backButton} />
|
|
|
|
|
|
)}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
<View style={styles.headerCenter}>
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.titleRow}
|
|
|
|
|
|
onPress={onTitlePress}
|
|
|
|
|
|
activeOpacity={0.75}
|
|
|
|
|
|
>
|
|
|
|
|
|
{isGroupChat ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<View style={styles.titleLeading}>
|
|
|
|
|
|
<MaterialCommunityIcons name="account-group" size={20} color={colors.primary.main} />
|
|
|
|
|
|
</View>
|
|
|
|
|
|
<View style={styles.titleContent}>
|
|
|
|
|
|
<Text style={styles.headerName} numberOfLines={1}>{getDisplayTitle()}</Text>
|
|
|
|
|
|
<View style={styles.subtitleRow}>
|
|
|
|
|
|
<Text style={styles.memberCount}>{groupInfo?.member_count || 0}位成员</Text>
|
|
|
|
|
|
{typingHint && (
|
|
|
|
|
|
<Text style={styles.typingHint} numberOfLines={1}>{typingHint}</Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Avatar
|
|
|
|
|
|
source={otherUser?.avatar || null}
|
|
|
|
|
|
size={32}
|
|
|
|
|
|
name={otherUser?.nickname || ''}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<View style={styles.titleContent}>
|
|
|
|
|
|
<Text style={styles.headerName} numberOfLines={1}>{getDisplayTitle()}</Text>
|
|
|
|
|
|
{typingHint && (
|
|
|
|
|
|
<View style={styles.subtitleRow}>
|
|
|
|
|
|
<Text style={styles.typingHint} numberOfLines={1}>{typingHint}</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
2026-03-16 17:47:10 +08:00
|
|
|
|
{/* 大屏幕 + 群聊时显示群信息按钮,否则显示三点菜单 */}
|
|
|
|
|
|
{isWideScreen && isGroupChat && onGroupInfoPress ? (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.moreButton}
|
|
|
|
|
|
onPress={onGroupInfoPress}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MaterialCommunityIcons name="dots-horizontal" size={22} color="#667085" />
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.moreButton}
|
|
|
|
|
|
onPress={onMorePress}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MaterialCommunityIcons name="dots-horizontal" size={22} color="#667085" />
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
)}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
</View>
|
|
|
|
|
|
</SafeAreaView>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default ChatHeader;
|