add ios app
This commit is contained in:
186
app-ios/app/chat/[id].tsx
Normal file
186
app-ios/app/chat/[id].tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import { ChatBubble, TimeSeparator, TypingIndicator } from '@/components/chat/ChatBubble';
|
||||
import { ChatInput } from '@/components/chat/ChatInput';
|
||||
import { AppColors } from '@/constants/theme';
|
||||
import {
|
||||
getCurrentTimeString,
|
||||
getRandomAIResponse,
|
||||
Message,
|
||||
mockConversations,
|
||||
mockMessages,
|
||||
} from '@/data/mockData';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { router, useLocalSearchParams } from 'expo-router';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
export default function ChatDetailScreen() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const scrollViewRef = useRef<ScrollView>(null);
|
||||
|
||||
const [messages, setMessages] = useState<Message[]>(mockMessages);
|
||||
const [isTyping, setIsTyping] = useState(false);
|
||||
|
||||
// Find conversation info
|
||||
const conversation = mockConversations.find(c => c.id === id) || mockConversations[0];
|
||||
|
||||
// Scroll to bottom when messages change
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
scrollViewRef.current?.scrollToEnd({ animated: true });
|
||||
}, 100);
|
||||
}, [messages, isTyping]);
|
||||
|
||||
const handleSendMessage = (text: string) => {
|
||||
// Add user message
|
||||
const userMessage: Message = {
|
||||
id: `m${Date.now()}`,
|
||||
conversationId: id || '1',
|
||||
senderType: 'user',
|
||||
contentType: 'text',
|
||||
content: text,
|
||||
timestamp: getCurrentTimeString(),
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, userMessage]);
|
||||
|
||||
// Simulate AI typing
|
||||
setTimeout(() => {
|
||||
setIsTyping(true);
|
||||
}, 500);
|
||||
|
||||
// Add AI response after delay
|
||||
setTimeout(() => {
|
||||
setIsTyping(false);
|
||||
const aiMessage: Message = {
|
||||
id: `m${Date.now() + 1}`,
|
||||
conversationId: id || '1',
|
||||
senderType: 'ai',
|
||||
contentType: 'text',
|
||||
content: getRandomAIResponse(),
|
||||
timestamp: getCurrentTimeString(),
|
||||
};
|
||||
setMessages(prev => [...prev, aiMessage]);
|
||||
}, 1500 + Math.random() * 1000);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
router.back();
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity
|
||||
style={styles.backButton}
|
||||
onPress={handleBack}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Ionicons name="chevron-back" size={24} color={AppColors.white} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.headerTitleArea}>
|
||||
<Text style={styles.headerTitle}>{conversation.title}</Text>
|
||||
<Text style={styles.headerStatus}>在线</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Messages */}
|
||||
<KeyboardAvoidingView
|
||||
style={styles.messageContainer}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={0}
|
||||
>
|
||||
<ScrollView
|
||||
ref={scrollViewRef}
|
||||
style={styles.messageList}
|
||||
contentContainerStyle={styles.messageListContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<TimeSeparator time="今天 14:30" />
|
||||
|
||||
{messages.map((message) => (
|
||||
<ChatBubble key={message.id} message={message} />
|
||||
))}
|
||||
|
||||
{isTyping && <TypingIndicator />}
|
||||
</ScrollView>
|
||||
|
||||
{/* Input Area */}
|
||||
<ChatInput
|
||||
onSendMessage={handleSendMessage}
|
||||
onVoicePress={() => {
|
||||
// Voice input placeholder
|
||||
console.log('Voice input pressed');
|
||||
}}
|
||||
onEmojiPress={() => {
|
||||
// Emoji picker placeholder
|
||||
console.log('Emoji picker pressed');
|
||||
}}
|
||||
onMorePress={() => {
|
||||
// More options placeholder
|
||||
console.log('More options pressed');
|
||||
}}
|
||||
/>
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: AppColors.mediumPurple,
|
||||
},
|
||||
header: {
|
||||
height: 62,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: AppColors.mediumPurple,
|
||||
},
|
||||
backButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 12,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerTitleArea: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
marginRight: 40, // Balance the back button
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
color: AppColors.white,
|
||||
},
|
||||
headerStatus: {
|
||||
fontSize: 12,
|
||||
color: 'rgba(255, 255, 255, 0.8)',
|
||||
marginTop: 2,
|
||||
},
|
||||
messageContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: AppColors.cream,
|
||||
},
|
||||
messageList: {
|
||||
flex: 1,
|
||||
backgroundColor: AppColors.cream,
|
||||
},
|
||||
messageListContent: {
|
||||
paddingVertical: 16,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user