From 0290b9318e2e4ff1f2c6677f5f6239bb7244a19a Mon Sep 17 00:00:00 2001 From: tapframe Date: Thu, 11 Sep 2025 17:27:51 +0530 Subject: [PATCH] ui changes --- src/navigation/AppNavigator.tsx | 6 +- src/screens/AIChatScreen.tsx | 116 ++++++++++++++++++++++++++++---- src/services/aiService.ts | 18 ++++- 3 files changed, 122 insertions(+), 18 deletions(-) diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 451e4d0f..5bb721ab 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -1254,11 +1254,11 @@ const InnerNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootSta name="AIChat" component={AIChatScreen} options={{ - animation: Platform.OS === 'android' ? 'slide_from_bottom' : 'slide_from_bottom', - animationDuration: Platform.OS === 'android' ? 250 : 300, + animation: Platform.OS === 'android' ? 'none' : 'slide_from_bottom', + animationDuration: Platform.OS === 'android' ? 220 : 300, presentation: 'modal', gestureEnabled: true, - gestureDirection: 'vertical', + gestureDirection: Platform.OS === 'android' ? 'vertical' : 'vertical', headerShown: false, contentStyle: { backgroundColor: currentTheme.colors.darkBackground, diff --git a/src/screens/AIChatScreen.tsx b/src/screens/AIChatScreen.tsx index 60443865..9a4db361 100644 --- a/src/screens/AIChatScreen.tsx +++ b/src/screens/AIChatScreen.tsx @@ -14,9 +14,12 @@ import { ActivityIndicator, Alert, } from 'react-native'; -import { useRoute, useNavigation, RouteProp } from '@react-navigation/native'; +import { useRoute, useNavigation, RouteProp, useFocusEffect } from '@react-navigation/native'; import { MaterialIcons } from '@expo/vector-icons'; import { useTheme } from '../contexts/ThemeContext'; +import { Image } from 'expo-image'; +import { BlurView as ExpoBlurView } from 'expo-blur'; +import { BlurView as CommunityBlurView } from '@react-native-community/blur'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { aiService, ChatMessage, ContentContext, createMovieContext, createEpisodeContext, generateConversationStarters } from '../services/aiService'; import { tmdbService } from '../services/tmdbService'; @@ -27,7 +30,8 @@ import Animated, { withSpring, withTiming, interpolate, - Extrapolate + Extrapolate, + runOnJS } from 'react-native-reanimated'; const { width, height } = Dimensions.get('window'); @@ -102,10 +106,18 @@ const ChatBubble: React.FC = ({ message, isLast }) => { styles.userBubble, { backgroundColor: currentTheme.colors.primary } ] : [ - styles.assistantBubble, - { backgroundColor: currentTheme.colors.elevation2 } + styles.assistantBubble, + { backgroundColor: 'transparent' } ] ]}> + {!isUser && ( + + {Platform.OS === 'ios' + ? + : } + + + )} {isUser ? ( {message.content} @@ -281,6 +293,18 @@ const AIChatScreen: React.FC = () => { const [context, setContext] = useState(null); const [isLoadingContext, setIsLoadingContext] = useState(true); const [suggestions, setSuggestions] = useState([]); + const [backdropUrl, setBackdropUrl] = useState(null); + + // Ensure Android cleans up heavy image resources when leaving the screen to avoid flash on back + useFocusEffect( + React.useCallback(() => { + return () => { + if (Platform.OS === 'android') { + setBackdropUrl(null); + } + }; + }, []) + ); const scrollViewRef = useRef(null); const inputRef = useRef(null); @@ -288,11 +312,21 @@ const AIChatScreen: React.FC = () => { // Animation values const headerOpacity = useSharedValue(1); const inputContainerY = useSharedValue(0); + // Android full-screen modal fade + const modalOpacity = useSharedValue(Platform.OS === 'android' ? 0 : 1); useEffect(() => { loadContext(); }, []); + // Animate in on Android for full-screen modal feel + useEffect(() => { + if (Platform.OS === 'android') { + // Use spring to avoid jank on some devices + modalOpacity.value = withSpring(1, { damping: 20, stiffness: 140 }); + } + }, [modalOpacity]); + useEffect(() => { if (context && messages.length === 0) { // Generate conversation starters @@ -320,6 +354,10 @@ const AIChatScreen: React.FC = () => { const movieContext = createMovieContext(movieData); setContext(movieContext); + try { + const path = movieData.backdrop_path || movieData.poster_path || null; + if (path) setBackdropUrl(`https://image.tmdb.org/t/p/w780${path}`); + } catch {} } else { // Series: resolve TMDB numeric id first (contentId may be IMDb/stremio id) let tmdbNumericId: number | null = null; @@ -343,6 +381,10 @@ const AIChatScreen: React.FC = () => { ]); if (!showData) throw new Error('Unable to load TV show details'); + try { + const path = showData.backdrop_path || showData.poster_path || null; + if (path) setBackdropUrl(`https://image.tmdb.org/t/p/w780${path}`); + } catch {} if (episodeData && seasonNumber && episodeNumber) { const episodeContext = createEpisodeContext( @@ -533,21 +575,44 @@ const AIChatScreen: React.FC = () => { } return ( + + {backdropUrl && ( + + + {Platform.OS === 'ios' + ? + : } + + + )} {/* Header */} navigation.goBack()} + onPress={() => { + if (Platform.OS === 'android') { + modalOpacity.value = withSpring(0, { damping: 18, stiffness: 160 }, (finished) => { + if (finished) runOnJS(navigation.goBack)(); + }); + } else { + navigation.goBack(); + } + }} style={styles.backButton} > @@ -571,13 +636,16 @@ const AIChatScreen: React.FC = () => { {/* Chat Messages */} @@ -637,10 +705,19 @@ const AIChatScreen: React.FC = () => { {/* Input Container */} - + + + {Platform.OS === 'ios' + ? + : } + + { + ); }; @@ -827,12 +905,17 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, paddingVertical: 12, borderRadius: 20, + overflow: 'hidden', }, userBubble: { - borderBottomRightRadius: 4, + borderBottomRightRadius: 20, }, assistantBubble: { - borderBottomLeftRadius: 4, + borderBottomLeftRadius: 20, + }, + assistantBlurBackdrop: { + ...StyleSheet.absoluteFillObject, + borderRadius: 20, }, messageText: { fontSize: 16, @@ -876,6 +959,11 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, paddingVertical: 8, gap: 12, + overflow: 'hidden' + }, + inputBlurBackdrop: { + ...StyleSheet.absoluteFillObject, + borderRadius: 24, }, textInput: { flex: 1, diff --git a/src/services/aiService.ts b/src/services/aiService.ts index 92482e2c..b1553861 100644 --- a/src/services/aiService.ts +++ b/src/services/aiService.ts @@ -132,7 +132,16 @@ CRITICAL INSTRUCTIONS: 3. If Release Status shows "RELEASED AND AVAILABLE FOR VIEWING", the content IS AVAILABLE. Do not say it's "upcoming" or "unreleased". 4. Compare air dates to today's date (${currentDate}) to determine if something has already aired. 5. Base ALL responses on the verified information above, NOT on your training knowledge. -6. If asked about release dates or availability, refer ONLY to the database information provided.`; +6. If asked about release dates or availability, refer ONLY to the database information provided. + +FORMATTING RULES (use Markdown): +- Use short paragraphs separated by blank lines. +- Use clear headings (## or ###) when helpful. +- Use bullet lists for points, character lists, and steps. +- Add a blank line before and after lists and headings. +- Keep lines concise; avoid giant unbroken blocks of text. +- Wrap inline code/terms with backticks only when appropriate. +`; } else { const movie = context as MovieContext; const currentDate = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format @@ -164,6 +173,13 @@ CRITICAL INSTRUCTIONS: 5. If asked about release dates or availability, refer ONLY to the database information provided. 6. You can discuss themes, production, performances, and high-level plot setup without revealing twists, surprises, or outcomes. +FORMATTING RULES (use Markdown): +- Use short paragraphs separated by blank lines. +- Use clear headings (## or ###) when helpful. +- Use bullet lists for points and steps. +- Add a blank line before and after lists and headings. +- Keep lines concise; avoid giant unbroken blocks of text. + Answer questions about this movie using only the verified database information above, including plot analysis, character development, themes, cinematography, production notes, and trivia. Provide detailed, informative responses while remaining spoiler-safe.`; } }