AIChatScreen optimziation

This commit is contained in:
tapframe 2025-09-11 20:09:24 +05:30
parent 9410b18962
commit fa7352f4fa
2 changed files with 44 additions and 19 deletions

View file

@ -1254,11 +1254,11 @@ const InnerNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootSta
name="AIChat" name="AIChat"
component={AIChatScreen} component={AIChatScreen}
options={{ options={{
animation: Platform.OS === 'android' ? 'none' : 'slide_from_bottom', animation: Platform.OS === 'android' ? 'none' : 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 220 : 300, animationDuration: Platform.OS === 'android' ? 220 : 300,
presentation: 'modal', presentation: Platform.OS === 'ios' ? 'fullScreenModal' : 'modal',
gestureEnabled: true, gestureEnabled: true,
gestureDirection: Platform.OS === 'android' ? 'vertical' : 'vertical', gestureDirection: Platform.OS === 'ios' ? 'horizontal' : 'vertical',
headerShown: false, headerShown: false,
contentStyle: { contentStyle: {
backgroundColor: currentTheme.colors.darkBackground, backgroundColor: currentTheme.colors.darkBackground,

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useCallback } from 'react'; import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { import {
View, View,
Text, Text,
@ -64,7 +64,7 @@ interface ChatBubbleProps {
isLast: boolean; isLast: boolean;
} }
const ChatBubble: React.FC<ChatBubbleProps> = ({ message, isLast }) => { const ChatBubble: React.FC<ChatBubbleProps> = React.memo(({ message, isLast }) => {
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
const isUser = message.role === 'user'; const isUser = message.role === 'user';
@ -265,14 +265,22 @@ const ChatBubble: React.FC<ChatBubbleProps> = ({ message, isLast }) => {
)} )}
</Animated.View> </Animated.View>
); );
}; }, (prev, next) => {
return (
prev.isLast === next.isLast &&
prev.message.id === next.message.id &&
prev.message.role === next.message.role &&
prev.message.content === next.message.content &&
prev.message.timestamp === next.message.timestamp
);
});
interface SuggestionChipProps { interface SuggestionChipProps {
text: string; text: string;
onPress: () => void; onPress: () => void;
} }
const SuggestionChip: React.FC<SuggestionChipProps> = ({ text, onPress }) => { const SuggestionChip: React.FC<SuggestionChipProps> = React.memo(({ text, onPress }) => {
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
return ( return (
@ -286,7 +294,7 @@ const SuggestionChip: React.FC<SuggestionChipProps> = ({ text, onPress }) => {
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); );
}; }, (prev, next) => prev.text === next.text && prev.onPress === next.onPress);
const AIChatScreen: React.FC = () => { const AIChatScreen: React.FC = () => {
const route = useRoute<AIChatScreenRouteProp>(); const route = useRoute<AIChatScreenRouteProp>();
@ -329,20 +337,35 @@ const AIChatScreen: React.FC = () => {
loadContext(); loadContext();
}, []); }, []);
// Track keyboard to adjust bottom padding only when hidden on iOS // Track keyboard and animate input to avoid gaps on iOS
useEffect(() => { useEffect(() => {
const onShow = (e: any) => {
setIsKeyboardVisible(true);
if (Platform.OS === 'ios') {
const kbHeight = e?.endCoordinates?.height ?? 0;
const lift = Math.max(0, kbHeight - insets.bottom);
inputContainerY.value = withTiming(-lift, { duration: 220 });
}
};
const onHide = () => {
setIsKeyboardVisible(false);
if (Platform.OS === 'ios') {
inputContainerY.value = withTiming(0, { duration: 220 });
}
};
const showSub = Platform.OS === 'ios' const showSub = Platform.OS === 'ios'
? Keyboard.addListener('keyboardWillShow', () => setIsKeyboardVisible(true)) ? Keyboard.addListener('keyboardWillShow', onShow)
: Keyboard.addListener('keyboardDidShow', () => setIsKeyboardVisible(true)); : Keyboard.addListener('keyboardDidShow', onShow);
const hideSub = Platform.OS === 'ios' const hideSub = Platform.OS === 'ios'
? Keyboard.addListener('keyboardWillHide', () => setIsKeyboardVisible(false)) ? Keyboard.addListener('keyboardWillHide', onHide)
: Keyboard.addListener('keyboardDidHide', () => setIsKeyboardVisible(false)); : Keyboard.addListener('keyboardDidHide', onHide);
return () => { return () => {
showSub.remove(); showSub.remove();
hideSub.remove(); hideSub.remove();
}; };
}, []); }, [insets.bottom, inputContainerY]);
// Animate in on Android for full-screen modal feel // Animate in on Android for full-screen modal feel
useEffect(() => { useEffect(() => {
@ -601,7 +624,7 @@ const AIChatScreen: React.FC = () => {
styles.header, styles.header,
{ {
backgroundColor: 'transparent', backgroundColor: 'transparent',
paddingTop: Platform.OS === 'ios' ? 8 : insets.top paddingTop: Platform.OS === 'ios' ? insets.top : insets.top
}, },
headerAnimatedStyle headerAnimatedStyle
]}> ]}>
@ -639,17 +662,19 @@ const AIChatScreen: React.FC = () => {
{/* Chat Messages */} {/* Chat Messages */}
<KeyboardAvoidingView <KeyboardAvoidingView
style={styles.chatContainer} style={styles.chatContainer}
behavior={Platform.OS === 'ios' ? 'padding' : undefined} behavior={Platform.OS === 'ios' ? undefined : undefined}
keyboardVerticalOffset={Platform.OS === 'ios' ? insets.top + 60 : 0} keyboardVerticalOffset={0}
> >
<ScrollView <ScrollView
ref={scrollViewRef} ref={scrollViewRef}
style={styles.messagesContainer} style={styles.messagesContainer}
contentContainerStyle={[ contentContainerStyle={[
styles.messagesContent, styles.messagesContent,
{ paddingBottom: ((Platform.OS === 'ios' ? (isKeyboardVisible ? 120 : 190) : 120) + insets.bottom) } { paddingBottom: isKeyboardVisible ? 20 : (56 + (isLoading ? 20 : 0)) }
]} ]}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
removeClippedSubviews
maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
> >
{messages.length === 0 && suggestions.length > 0 && ( {messages.length === 0 && suggestions.length > 0 && (
@ -711,7 +736,7 @@ const AIChatScreen: React.FC = () => {
styles.inputContainer, styles.inputContainer,
{ {
backgroundColor: 'transparent', backgroundColor: 'transparent',
paddingBottom: Platform.OS === 'ios' ? (isKeyboardVisible ? 12 : 56) : 12 paddingBottom: 12
}, },
inputAnimatedStyle inputAnimatedStyle
]}> ]}>