diff --git a/src/components/home/CatalogSection.tsx b/src/components/home/CatalogSection.tsx index dbbd73c..c5a71f1 100644 --- a/src/components/home/CatalogSection.tsx +++ b/src/components/home/CatalogSection.tsx @@ -11,6 +11,7 @@ import { RootStackParamList } from '../../navigation/AppNavigator'; interface CatalogSectionProps { catalog: CatalogContent; + onPosterPress?: (content: StreamingContent) => void; } const { width } = Dimensions.get('window'); @@ -53,12 +54,17 @@ const calculatePosterLayout = (screenWidth: number) => { const posterLayout = calculatePosterLayout(width); const POSTER_WIDTH = posterLayout.posterWidth; -const CatalogSection = ({ catalog }: CatalogSectionProps) => { +const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => { const navigation = useNavigation>(); const { currentTheme } = useTheme(); const handleContentPress = (id: string, type: string) => { - navigation.navigate('Metadata', { id, type, addonId: catalog.addon }); + const content = catalog.items.find(item => item.id === id && item.type === type); + if (content && onPosterPress) { + onPosterPress(content); + } else { + navigation.navigate('Metadata', { id, type, addonId: catalog.addon }); + } }; const renderContentItem = ({ item, index }: { item: StreamingContent, index: number }) => { diff --git a/src/components/home/FeaturedContent.tsx b/src/components/home/FeaturedContent.tsx index bfc6147..36f9db4 100644 --- a/src/components/home/FeaturedContent.tsx +++ b/src/components/home/FeaturedContent.tsx @@ -22,6 +22,8 @@ import { imageCacheService } from '../../services/imageCacheService'; interface FeaturedContentProps { featuredContent: StreamingContent | null; + isSaved?: boolean; + handleSaveToLibrary?: () => Promise; } // Cache to store preloaded images @@ -218,6 +220,15 @@ const FeaturedContent = ({ featuredContent }: FeaturedContentProps) => { )} + {/* Description Section */} + {featuredContent.description && ( + + + {featuredContent.description} + + + )} + {/* Enhanced Metadata Section */} {/* Genres */} @@ -241,6 +252,30 @@ const FeaturedContent = ({ featuredContent }: FeaturedContentProps) => { )} + {/* Action Buttons */} + + navigation.navigate('Metadata', { + id: featuredContent.id, + type: featuredContent.type + })} + hasTVPreferredFocus={Platform.isTV} + tvParallaxProperties={Platform.isTV ? { + enabled: true, + shiftDistanceX: 4.0, + shiftDistanceY: 4.0, + tiltAngle: 0.05, + magnification: 1.1, + } : undefined} + > + + Play + + + + + @@ -252,9 +287,9 @@ const FeaturedContent = ({ featuredContent }: FeaturedContentProps) => { const styles = StyleSheet.create({ featuredContainer: { width: '100%', - height: Platform.isTV ? height * 0.75 : height * 0.55, + height: Platform.isTV ? height * 0.5 : height * 0.4, marginTop: 0, - marginBottom: Platform.isTV ? 24 : 12, + marginBottom: Platform.isTV ? 16 : 8, position: 'relative', borderRadius: Platform.isTV ? 0 : 12, overflow: 'hidden', @@ -324,23 +359,25 @@ const styles = StyleSheet.create({ marginBottom: Platform.isTV ? 24 : 16, paddingHorizontal: 0, position: 'relative', - height: Platform.isTV ? 160 : 160, + height: Platform.isTV ? 120 : 100, width: '100%', - marginLeft: Platform.isTV ? -200 : 0, + marginLeft: 0, + justifyContent: 'flex-start', }, featuredLogo: { - width: width * 0.9, - height: 160, + width: Platform.isTV ? 300 : 250, + height: Platform.isTV ? 120 : 100, marginBottom: 0, alignSelf: 'flex-start', - position: Platform.isTV ? 'absolute' : 'relative', - left: Platform.isTV ? 0 : 'auto', + position: 'relative', + left: 0, + resizeMode: 'contain', }, featuredLogoTV: { - width: width * 0.8, - height: 200, - maxWidth: 900, - position: 'absolute', + width: 300, + height: 120, + maxWidth: 300, + position: 'relative', left: 0, }, featuredTitleText: { @@ -389,6 +426,23 @@ const styles = StyleSheet.create({ yearContainer: { marginTop: 8, }, + descriptionContainer: { + marginBottom: Platform.isTV ? 24 : 16, + maxWidth: Platform.isTV ? width * 0.5 : width * 0.8, + }, + descriptionText: { + fontSize: 14, + color: '#FFFFFF', + opacity: 0.9, + lineHeight: 20, + textShadowColor: 'rgba(0,0,0,0.6)', + textShadowOffset: { width: 0, height: 1 }, + textShadowRadius: 3, + }, + descriptionTextTV: { + fontSize: 18, + lineHeight: 26, + }, yearText: { fontSize: 16, fontWeight: '500', @@ -417,6 +471,34 @@ const styles = StyleSheet.create({ fontSize: Platform.isTV ? 18 : 16, fontWeight: '600', }, + actionButtonsContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: Platform.isTV ? 20 : 16, + marginTop: Platform.isTV ? 24 : 16, + }, + playButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#FFFFFF', + paddingHorizontal: Platform.isTV ? 48 : 32, + paddingVertical: Platform.isTV ? 16 : 12, + borderRadius: Platform.isTV ? 12 : 8, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 4, + elevation: 4, + minWidth: Platform.isTV ? 200 : 140, + }, + playButtonText: { + color: '#000', + fontSize: Platform.isTV ? 18 : 16, + fontWeight: '700', + marginLeft: 8, + }, + }); export default React.memo(FeaturedContent); \ No newline at end of file diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 806d462..38188c7 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -106,6 +106,117 @@ const SkeletonCatalog = React.memo(() => { }); const HomeScreen = () => { + const styles = StyleSheet.create({ + stickyHeroContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + zIndex: 10, + }, + container: { + flex: 1, + }, + scrollContent: { + paddingBottom: 90, + }, + loadingMainContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingBottom: 90, + }, + loadingText: { + marginTop: 12, + fontSize: 14, + }, + loadingMoreCatalogs: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + padding: 16, + marginHorizontal: 16, + marginBottom: 16, + backgroundColor: 'rgba(0,0,0,0.2)', + borderRadius: 8, + }, + loadingMoreText: { + marginLeft: 12, + fontSize: 14, + }, + catalogPlaceholder: { + marginBottom: 24, + paddingHorizontal: 16, + }, + placeholderHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + }, + placeholderTitle: { + width: 150, + height: 20, + borderRadius: 4, + }, + placeholderPosters: { + flexDirection: 'row', + paddingVertical: 8, + gap: 8, + }, + placeholderPoster: { + width: POSTER_WIDTH, + aspectRatio: 2/3, + borderRadius: 4, + marginRight: 2, + }, + emptyCatalog: { + padding: 32, + alignItems: 'center', + margin: 16, + borderRadius: 16, + }, + addCatalogButton: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 10, + borderRadius: 30, + marginTop: 16, + elevation: 3, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 3, + }, + addCatalogButtonText: { + fontSize: 14, + fontWeight: '600', + marginLeft: 8, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + featuredContainer: { + width: '100%', + height: height * 0.6, + marginTop: Platform.OS === 'ios' ? 0 : 0, + marginBottom: 8, + position: 'relative', + }, + featuredBanner: { + width: '100%', + height: '100%', + }, + featuredGradient: { + width: '100%', + height: '100%', + justifyContent: 'space-between', + } + }); + const navigation = useNavigation>(); const isDarkMode = useColorScheme() === 'dark'; const { currentTheme } = useTheme(); @@ -513,7 +624,7 @@ const HomeScreen = () => { case 'catalog': return ( - + ); case 'placeholder': @@ -581,6 +692,12 @@ const HomeScreen = () => { ), [catalogsLoading, catalogs, loadedCatalogCount, totalCatalogsRef.current, navigation, currentTheme.colors]); // Memoize the main content section + const [selectedContent, setSelectedContent] = useState(null); + + const handlePosterPress = useCallback((content: StreamingContent) => { + setSelectedContent(content); + }, []); + const renderMainContent = useMemo(() => { if (isLoading) return null; @@ -597,7 +714,7 @@ const HomeScreen = () => { keyExtractor={item => item.key} contentContainerStyle={[ styles.scrollContent, - { paddingTop: Platform.OS === 'ios' ? 100 : 90 } + { paddingTop: 0 } ]} showsVerticalScrollIndicator={false} ListFooterComponent={ListFooterComponent} @@ -612,8 +729,8 @@ const HomeScreen = () => { autoscrollToTopThreshold: 10 }} getItemLayout={(data, index) => ({ - length: index === 0 ? 400 : 280, // Approximate heights for different item types - offset: index === 0 ? 0 : 400 + (index - 1) * 280, + length: 280, + offset: index * 280, index, })} /> @@ -670,7 +787,14 @@ const calculatePosterLayout = (screenWidth: number) => { const posterLayout = calculatePosterLayout(width); const POSTER_WIDTH = posterLayout.posterWidth; -const styles = StyleSheet.create({ + const styles = StyleSheet.create({ + stickyHeroContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + zIndex: 10, + }, container: { flex: 1, },