import React, { useCallback } from 'react'; import { View, Text, StyleSheet, StatusBar, ActivityIndicator, Dimensions, TouchableOpacity, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { useRoute, useNavigation } from '@react-navigation/native'; import { MaterialIcons } from '@expo/vector-icons'; import * as Haptics from 'expo-haptics'; import { useTheme } from '../contexts/ThemeContext'; import { useMetadata } from '../hooks/useMetadata'; import { CastSection } from '../components/metadata/CastSection'; import { SeriesContent } from '../components/metadata/SeriesContent'; import { MovieContent } from '../components/metadata/MovieContent'; import { MoreLikeThisSection } from '../components/metadata/MoreLikeThisSection'; import { RatingsSection } from '../components/metadata/RatingsSection'; import { RouteParams, Episode } from '../types/metadata'; import Animated, { useAnimatedStyle, interpolate, Extrapolate, } from 'react-native-reanimated'; import { RouteProp } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; import { RootStackParamList } from '../navigation/AppNavigator'; import { useSettings } from '../hooks/useSettings'; // Import our new components and hooks import HeroSection from '../components/metadata/HeroSection'; import FloatingHeader from '../components/metadata/FloatingHeader'; import MetadataDetails from '../components/metadata/MetadataDetails'; import { useMetadataAnimations } from '../hooks/useMetadataAnimations'; import { useMetadataAssets } from '../hooks/useMetadataAssets'; import { useWatchProgress } from '../hooks/useWatchProgress'; const { height } = Dimensions.get('window'); const MetadataScreen = () => { const route = useRoute, string>>(); const navigation = useNavigation>(); const { id, type, episodeId } = route.params; // Add settings hook const { settings } = useSettings(); // Get theme context const { currentTheme } = useTheme(); // Get safe area insets const { top: safeAreaTop } = useSafeAreaInsets(); const { metadata, loading, error: metadataError, cast, loadingCast, episodes, selectedSeason, loadingSeasons, loadMetadata, handleSeasonChange, toggleLibrary, inLibrary, groupedEpisodes, recommendations, loadingRecommendations, setMetadata, imdbId, } = useMetadata({ id, type }); // Use our new hooks const { watchProgress, getEpisodeDetails, getPlayButtonText, } = useWatchProgress(id, type as 'movie' | 'series', episodeId, episodes); const { bannerImage, loadingBanner, logoLoadError, setLogoLoadError, setBannerImage, } = useMetadataAssets(metadata, id, type, imdbId, settings, setMetadata); const animations = useMetadataAnimations(safeAreaTop, watchProgress); // Add wrapper for toggleLibrary that includes haptic feedback const handleToggleLibrary = useCallback(() => { // Trigger appropriate haptic feedback based on action if (inLibrary) { // Removed from library - light impact Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); } else { // Added to library - success feedback Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); } // Call the original toggleLibrary function toggleLibrary(); }, [inLibrary, toggleLibrary]); // Add wrapper for season change with distinctive haptic feedback const handleSeasonChangeWithHaptics = useCallback((seasonNumber: number) => { // Change to Light impact for a more subtle feedback Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); // Wait a tiny bit before changing season, making the feedback more noticeable setTimeout(() => { handleSeasonChange(seasonNumber); }, 10); }, [handleSeasonChange]); // Handler functions const handleShowStreams = useCallback(() => { if (type === 'series') { // If we have watch progress with an episodeId, use that if (watchProgress?.episodeId) { navigation.navigate('Streams', { id, type, episodeId: watchProgress.episodeId }); return; } // If we have a specific episodeId from route params, use that if (episodeId) { navigation.navigate('Streams', { id, type, episodeId }); return; } // Otherwise, if we have episodes, start with the first one if (episodes.length > 0) { const firstEpisode = episodes[0]; const newEpisodeId = firstEpisode.stremioId || `${id}:${firstEpisode.season_number}:${firstEpisode.episode_number}`; navigation.navigate('Streams', { id, type, episodeId: newEpisodeId }); return; } } navigation.navigate('Streams', { id, type, episodeId }); }, [navigation, id, type, episodes, episodeId, watchProgress]); const handleSelectCastMember = useCallback((castMember: any) => { // Future implementation }, []); const handleEpisodeSelect = useCallback((episode: Episode) => { const episodeId = episode.stremioId || `${id}:${episode.season_number}:${episode.episode_number}`; navigation.navigate('Streams', { id, type, episodeId }); }, [navigation, id, type]); const handleBack = useCallback(() => { navigation.goBack(); }, [navigation]); // Animated styles const containerAnimatedStyle = useAnimatedStyle(() => ({ flex: 1, transform: [{ scale: animations.screenScale.value }], opacity: animations.screenOpacity.value })); const contentAnimatedStyle = useAnimatedStyle(() => ({ transform: [{ translateY: animations.contentTranslateY.value }], opacity: interpolate( animations.contentTranslateY.value, [60, 0], [0, 1], Extrapolate.CLAMP ) })); if (loading) { return ( Loading content... ); } if (metadataError || !metadata) { return ( {metadataError || 'Content not found'} Try Again Go Back ); } return ( {/* Floating Header */} {/* Hero Section */} {/* Main Content */} {/* Metadata Details */} {/* Add RatingsSection right under the main metadata */} {imdbId && ( )} {/* Cast Section */} {/* More Like This Section - Only for movies */} {type === 'movie' && ( )} {/* Type-specific content */} {type === 'series' ? ( ) : ( )} ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'transparent', paddingTop: 0, }, scrollView: { flex: 1, }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 16, }, loadingText: { marginTop: 16, fontSize: 16, }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 32, }, errorText: { fontSize: 18, textAlign: 'center', marginTop: 16, marginBottom: 24, }, retryButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingHorizontal: 24, paddingVertical: 12, borderRadius: 24, marginBottom: 16, }, retryButtonText: { fontSize: 16, fontWeight: '600', }, backButton: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', borderRadius: 20, }, backButtonText: { fontSize: 16, fontWeight: '600', }, }); export default MetadataScreen;