A React Native app for the ultimate thinking partner.
at main 93 lines 2.8 kB view raw
1import React, { useState, useCallback, useRef } from 'react'; 2import { View, Text, TouchableOpacity, StyleSheet, Platform, UIManager } from 'react-native'; 3import MessageContent from './MessageContent'; 4import { darkTheme } from '../theme'; 5 6// Enable LayoutAnimation on Android 7if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) { 8 UIManager.setLayoutAnimationEnabledExperimental(true); 9} 10 11interface ExpandableMessageContentProps { 12 content: string; 13 isUser: boolean; 14 isDark?: boolean; 15 lineLimit?: number; 16 onToggle?: (expanding: boolean) => void; 17} 18 19const ExpandableMessageContent: React.FC<ExpandableMessageContentProps> = ({ 20 content, 21 isUser, 22 isDark = true, 23 lineLimit = 3, 24 onToggle 25}) => { 26 const [isExpanded, setIsExpanded] = useState(false); 27 const [shouldShowToggle, setShouldShowToggle] = useState(false); 28 29 // Only apply expandable behavior to user messages 30 if (!isUser) { 31 return <MessageContent content={content} isUser={isUser} isDark={isDark} />; 32 } 33 34 // Simple heuristic: estimate if content would exceed line limit 35 // Assuming average ~60 chars per line in the chat bubble 36 const estimatedLines = content.length / 60; 37 const hasLineBreaks = (content.match(/\n/g) || []).length; 38 const totalEstimatedLines = Math.max(estimatedLines, hasLineBreaks + 1); 39 40 // Show toggle if content is likely to exceed limit 41 const showToggle = totalEstimatedLines > lineLimit; 42 43 if (!showToggle) { 44 return <MessageContent content={content} isUser={isUser} isDark={isDark} />; 45 } 46 47 const handleToggle = useCallback(() => { 48 const nextExpanded = !isExpanded; 49 // Notify parent before state change 50 onToggle?.(nextExpanded); 51 // Avoid global LayoutAnimation to prevent list flicker; just toggle state 52 setIsExpanded(nextExpanded); 53 }, [isExpanded, onToggle]); 54 55 return ( 56 <View> 57 <View style={isExpanded ? undefined : styles.collapsedContainer}> 58 <MessageContent 59 content={isExpanded ? content : content.slice(0, 180) + '...'} 60 isUser={isUser} 61 isDark={isDark} 62 /> 63 </View> 64 <TouchableOpacity 65 onPress={handleToggle} 66 style={styles.toggleButton} 67 activeOpacity={0.7} 68 > 69 <Text style={[styles.toggleText, { color: isDark ? '#000000' : '#FFFFFF' }]}> 70 {isExpanded ? 'See less' : 'See more'} 71 </Text> 72 </TouchableOpacity> 73 </View> 74 ); 75}; 76 77const styles = StyleSheet.create({ 78 collapsedContainer: { 79 maxHeight: 72, // Approximately 3 lines with standard font size 80 overflow: 'hidden', 81 }, 82 toggleButton: { 83 marginTop: 4, 84 paddingVertical: 2, 85 }, 86 toggleText: { 87 fontSize: 13, 88 fontWeight: '500', 89 opacity: 0.6, 90 }, 91}); 92 93export default React.memo(ExpandableMessageContent);