From faef964d46a43586d1e59b3eaf01be252b3569bb Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 25 Jun 2025 00:43:30 +0530 Subject: [PATCH] Add react-native-shared-element and react-navigation-shared-element dependencies; refactor MetadataScreen for optimized loading and transitions --- package-lock.json | 22 +++ package.json | 2 + src/screens/MetadataScreen.tsx | 252 +++++++++++++++++++-------------- 3 files changed, 170 insertions(+), 106 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97e4aca6..0fdb38d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", + "react-native-shared-element": "^0.8.9", "react-native-svg": "^15.11.2", "react-native-tab-view": "^4.0.10", "react-native-url-polyfill": "^2.0.0", @@ -69,6 +70,7 @@ "react-native-vlc-media-player": "^1.0.87", "react-native-web": "~0.19.13", "react-native-wheel-color-picker": "^1.3.1", + "react-navigation-shared-element": "^3.1.3", "subsrt": "^1.1.1" }, "devDependencies": { @@ -12591,6 +12593,12 @@ "react-native": "*" } }, + "node_modules/react-native-shared-element": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/react-native-shared-element/-/react-native-shared-element-0.8.9.tgz", + "integrity": "sha512-vlzhv3amkJm+8gA0WSeLzcCKNtN/ypZbic3IZ4Bwwr6GeWDrYzZ6k7PdHCioy7fwIVOJ1X9Pi/aYF9HK4Kb0qg==", + "license": "MIT" + }, "node_modules/react-native-slider": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-native-slider/-/react-native-slider-0.11.0.tgz", @@ -12971,6 +12979,20 @@ "async-limiter": "~1.0.0" } }, + "node_modules/react-navigation-shared-element": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/react-navigation-shared-element/-/react-navigation-shared-element-3.1.3.tgz", + "integrity": "sha512-U1BZp7dEdcTNHggfkq3WEBlJeg4HwFhFdj7a0i0Uql/7mg2IHQg/bZaqM2jQvJITkABge6Hz5fZixIF8jyzpkg==", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-shared-element": "*" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", diff --git a/package.json b/package.json index 9a712234..a6eb7eca 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", + "react-native-shared-element": "^0.8.9", "react-native-svg": "^15.11.2", "react-native-tab-view": "^4.0.10", "react-native-url-polyfill": "^2.0.0", @@ -70,6 +71,7 @@ "react-native-vlc-media-player": "^1.0.87", "react-native-web": "~0.19.13", "react-native-wheel-color-picker": "^1.3.1", + "react-navigation-shared-element": "^3.1.3", "subsrt": "^1.1.1" }, "devDependencies": { diff --git a/src/screens/MetadataScreen.tsx b/src/screens/MetadataScreen.tsx index 3c70a783..d2806383 100644 --- a/src/screens/MetadataScreen.tsx +++ b/src/screens/MetadataScreen.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState, useEffect, useMemo } from 'react'; +import React, { useCallback, useState, useEffect, useMemo, useRef } from 'react'; import { View, Text, @@ -56,9 +56,7 @@ const MetadataScreen: React.FC = () => { // Optimized state management - reduced state variables const [isContentReady, setIsContentReady] = useState(false); - const [showSkeleton, setShowSkeleton] = useState(true); - const transitionOpacity = useSharedValue(0); - const skeletonOpacity = useSharedValue(1); + const transitionOpacity = useSharedValue(1); const { metadata, @@ -187,26 +185,14 @@ const MetadataScreen: React.FC = () => { // Memoized derived values for performance const isReady = useMemo(() => !loading && metadata && !metadataError, [loading, metadata, metadataError]); - // Smooth skeleton to content transition + // Simple content ready state management useEffect(() => { - if (isReady && !isContentReady) { - // Small delay to ensure skeleton is rendered before starting transition - setTimeout(() => { - // Start fade out skeleton and fade in content simultaneously - skeletonOpacity.value = withTiming(0, { duration: 300 }); - transitionOpacity.value = withTiming(1, { duration: 400 }); - - // Hide skeleton after fade out completes - setTimeout(() => { - setShowSkeleton(false); - setIsContentReady(true); - }, 300); - }, 100); + if (isReady) { + setIsContentReady(true); + transitionOpacity.value = withTiming(1, { duration: 200 }); } else if (!isReady && isContentReady) { setIsContentReady(false); - setShowSkeleton(true); transitionOpacity.value = 0; - skeletonOpacity.value = 1; } }, [isReady, isContentReady]); @@ -257,10 +243,6 @@ const MetadataScreen: React.FC = () => { opacity: transitionOpacity.value, }), []); - const skeletonStyle = useAnimatedStyle(() => ({ - opacity: skeletonOpacity.value, - }), []); - // Memoized error component for performance const ErrorComponent = useMemo(() => { if (!metadataError) return null; @@ -300,104 +282,124 @@ const MetadataScreen: React.FC = () => { } return ( - - {/* Skeleton Loading Screen - with fade out transition */} - {showSkeleton && ( - - - - )} - - {/* Main Content - with fade in transition */} + + + {metadata && ( - - + {/* Floating Header - Optimized */} + + + - - - {/* Floating Header - Optimized */} - - - {/* Hero Section - Optimized */} - + imdbId ? ( + + ) : null} /> - {/* Main Content - Optimized */} - - imdbId ? ( - - ) : null} - /> - + {/* Cast Section with skeleton when loading */} + {loadingCast ? ( + + + + {[...Array(4)].map((_, index) => ( + + ))} + + + ) : ( + )} - {type === 'movie' && ( + {/* Recommendations Section with skeleton when loading */} + {type === 'movie' && ( + loadingRecommendations ? ( + + + + {[...Array(3)].map((_, index) => ( + + ))} + + + ) : ( - )} + ) + )} - {type === 'series' ? ( + {/* Series/Movie Content with episode skeleton when loading */} + {type === 'series' ? ( + (loadingSeasons || Object.keys(groupedEpisodes).length === 0) ? ( + + + + {[...Array(6)].map((_, index) => ( + + ))} + + + ) : ( { groupedEpisodes={groupedEpisodes} metadata={metadata || undefined} /> - ) : ( - metadata && - )} - - - - + ) + ) : ( + metadata && + )} + + + )} - + ); }; @@ -466,6 +468,44 @@ const styles = StyleSheet.create({ fontSize: 16, fontWeight: '600', }, + // Skeleton loading styles + skeletonSection: { + padding: 16, + marginBottom: 24, + }, + skeletonTitle: { + width: 150, + height: 20, + borderRadius: 4, + marginBottom: 16, + }, + skeletonCastRow: { + flexDirection: 'row', + gap: 12, + }, + skeletonCastItem: { + width: 80, + height: 120, + borderRadius: 8, + }, + skeletonRecommendationsRow: { + flexDirection: 'row', + gap: 12, + }, + skeletonRecommendationItem: { + width: 120, + height: 180, + borderRadius: 8, + }, + skeletonEpisodesContainer: { + gap: 12, + }, + skeletonEpisodeItem: { + width: '100%', + height: 80, + borderRadius: 8, + marginBottom: 8, + }, }); export default MetadataScreen; \ No newline at end of file