feat(ChatScreen): enhance message input and attachment handling
- Updated ChatInput to manage pending attachments, allowing users to upload multiple images before sending. - Introduced functionality to remove pending attachments and display their thumbnails in the input area. - Enhanced ChatScreen to reflect changes in attachment handling, including dynamic messaging during uploads. - Refactored message segment rendering to support inline text and grouped images, improving visual organization. - Added constants for maximum pending images to enforce limits on user uploads.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { View, TouchableOpacity, TextInput, ActivityIndicator } from 'react-native';
|
||||
import { View, TouchableOpacity, TextInput, ActivityIndicator, ScrollView, StyleSheet, Image as RNImage } from 'react-native';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { Text } from '../../../../components/common';
|
||||
import { colors } from '../../../../theme';
|
||||
@@ -24,7 +24,7 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
onToggleEmoji,
|
||||
onToggleMore,
|
||||
activePanel,
|
||||
sending,
|
||||
isComposerBusy,
|
||||
isMuted,
|
||||
isGroupChat,
|
||||
muteAll,
|
||||
@@ -33,6 +33,8 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
onFocus,
|
||||
restrictionHint,
|
||||
disableMore = false,
|
||||
pendingAttachments = [],
|
||||
onRemovePendingAttachment,
|
||||
currentUser,
|
||||
otherUser,
|
||||
getSenderInfo,
|
||||
@@ -51,6 +53,11 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
isWideScreen ? { maxWidth: 900, alignSelf: 'center' as const, width: '100%' as const } : null,
|
||||
]), [isWideScreen, styles.inputContainer]);
|
||||
|
||||
const canSend =
|
||||
(!!inputText.trim() || pendingAttachments.length > 0) &&
|
||||
!isComposerBusy &&
|
||||
!isDisabled;
|
||||
|
||||
// 回复预览组件 - 定义在内部以访问 styles
|
||||
const ReplyPreview: React.FC<{
|
||||
replyingTo: GroupMessage;
|
||||
@@ -69,7 +76,16 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
<View style={styles.replyPreviewText}>
|
||||
<Text style={styles.replyPreviewName}>回复 {senderInfo.nickname}</Text>
|
||||
<Text style={styles.replyPreviewMessage} numberOfLines={1}>
|
||||
{replyingTo.segments?.some(seg => seg.type === 'image') ? '[图片]' : extractTextFromSegments(replyingTo.segments)}
|
||||
{(() => {
|
||||
const segs = replyingTo.segments;
|
||||
const imgCount = segs?.filter(seg => seg.type === 'image').length ?? 0;
|
||||
const raw = extractTextFromSegments(segs);
|
||||
if (imgCount > 1) {
|
||||
const textPart = raw.replace(/\[图片\]/g, '').replace(/\s+/g, ' ').trim();
|
||||
return textPart ? `${textPart} [${imgCount}张图片]` : `[${imgCount}张图片]`;
|
||||
}
|
||||
return raw;
|
||||
})()}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -106,6 +122,30 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
<Text style={styles.mutedBannerText}>{restrictionHint}</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{pendingAttachments.length > 0 && (
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={attachmentStyles.stripScroll}
|
||||
contentContainerStyle={attachmentStyles.stripContent}
|
||||
>
|
||||
{pendingAttachments.map(item => (
|
||||
<View key={item.id} style={attachmentStyles.thumbWrap}>
|
||||
<RNImage source={{ uri: item.uri }} style={attachmentStyles.thumb} />
|
||||
{!isDisabled && onRemovePendingAttachment ? (
|
||||
<TouchableOpacity
|
||||
style={attachmentStyles.removeBtn}
|
||||
onPress={() => onRemovePendingAttachment(item.id)}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
>
|
||||
<MaterialCommunityIcons name="close-circle" size={22} color="rgba(0,0,0,0.55)" />
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
)}
|
||||
|
||||
<View style={[styles.inputInner, isDisabled && styles.inputInnerMuted]}>
|
||||
<TouchableOpacity
|
||||
@@ -140,7 +180,7 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
/>
|
||||
</View>
|
||||
|
||||
{inputText.trim() && !sending && !isDisabled ? (
|
||||
{canSend ? (
|
||||
<TouchableOpacity
|
||||
style={styles.sendButtonActive}
|
||||
onPress={onSend}
|
||||
@@ -148,7 +188,7 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
>
|
||||
<MaterialCommunityIcons name="send" size={20} color="#FFF" />
|
||||
</TouchableOpacity>
|
||||
) : sending ? (
|
||||
) : isComposerBusy && !isDisabled ? (
|
||||
<TouchableOpacity
|
||||
style={[styles.sendButtonActive, styles.sendButtonDisabled]}
|
||||
disabled
|
||||
@@ -173,4 +213,34 @@ export const ChatInput: React.FC<ChatInputProps & {
|
||||
);
|
||||
};
|
||||
|
||||
const attachmentStyles = StyleSheet.create({
|
||||
stripScroll: {
|
||||
maxHeight: 88,
|
||||
marginBottom: 6,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
stripContent: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 4,
|
||||
},
|
||||
thumbWrap: {
|
||||
position: 'relative',
|
||||
marginRight: 8,
|
||||
},
|
||||
thumb: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 10,
|
||||
backgroundColor: '#E8EAED',
|
||||
},
|
||||
removeBtn: {
|
||||
position: 'absolute',
|
||||
top: -6,
|
||||
right: -6,
|
||||
backgroundColor: '#FFF',
|
||||
borderRadius: 12,
|
||||
},
|
||||
});
|
||||
|
||||
export default ChatInput;
|
||||
|
||||
Reference in New Issue
Block a user