mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
AIChatScreen optimziation
This commit is contained in:
parent
9410b18962
commit
fa7352f4fa
2 changed files with 44 additions and 19 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
]}>
|
]}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue