This commit is contained in:
tapframe 2025-05-04 02:36:17 +05:30
parent 74a288bc1a
commit 6f2ccfa38b
4 changed files with 60 additions and 156 deletions

View file

@ -18,12 +18,14 @@ interface MetadataDetailsProps {
metadata: any; metadata: any;
imdbId: string | null; imdbId: string | null;
type: 'movie' | 'series'; type: 'movie' | 'series';
renderRatings?: () => React.ReactNode;
} }
const MetadataDetails: React.FC<MetadataDetailsProps> = ({ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
metadata, metadata,
imdbId, imdbId,
type, type,
renderRatings,
}) => { }) => {
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false); const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false);
@ -53,6 +55,9 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
)} )}
</View> </View>
{/* Ratings Section */}
{renderRatings && renderRatings()}
{/* Creator/Director Info */} {/* Creator/Director Info */}
<Animated.View <Animated.View
entering={FadeIn.duration(500).delay(200)} entering={FadeIn.duration(500).delay(200)}

View file

@ -87,21 +87,6 @@ export const RatingsSection: React.FC<RatingsSectionProps> = ({ imdbId, type })
} }
}; };
useEffect(() => {
return () => {
};
}, [imdbId, type]);
useEffect(() => {
if (error) {
}
}, [error]);
useEffect(() => {
if (ratings) {
}
}, [ratings]);
useEffect(() => { useEffect(() => {
if (ratings && Object.keys(ratings).length > 0) { if (ratings && Object.keys(ratings).length > 0) {
// Start fade-in animation when ratings are loaded // Start fade-in animation when ratings are loaded
@ -114,21 +99,9 @@ export const RatingsSection: React.FC<RatingsSectionProps> = ({ imdbId, type })
}, [ratings, fadeAnim]); }, [ratings, fadeAnim]);
// If MDBList is disabled, don't show anything // If MDBList is disabled, don't show anything
if (!isMDBEnabled) { if (!isMDBEnabled) return null;
return null; if (loading) return <View style={styles.loadingContainer}><ActivityIndicator size="small" color={currentTheme.colors.primary} /></View>;
} if (error || !ratings || Object.keys(ratings).length === 0) return null;
if (loading) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="small" color={currentTheme.colors.primary} />
</View>
);
}
if (error || !ratings || Object.keys(ratings).length === 0) {
return null;
}
// Define the order and icons/colors for the ratings // Define the order and icons/colors for the ratings
const ratingConfig = { const ratingConfig = {
@ -136,56 +109,42 @@ export const RatingsSection: React.FC<RatingsSectionProps> = ({ imdbId, type })
icon: require('../../../assets/rating-icons/imdb.png'), icon: require('../../../assets/rating-icons/imdb.png'),
isImage: true, isImage: true,
color: '#F5C518', color: '#F5C518',
prefix: '',
suffix: '',
transform: (value: number) => value.toFixed(1) transform: (value: number) => value.toFixed(1)
}, },
tmdb: { tmdb: {
icon: TMDBIcon, icon: TMDBIcon,
isImage: false, isImage: false,
color: '#01B4E4', color: '#01B4E4',
prefix: '',
suffix: '',
transform: (value: number) => value.toFixed(0) transform: (value: number) => value.toFixed(0)
}, },
trakt: { trakt: {
icon: TraktIcon, icon: TraktIcon,
isImage: false, isImage: false,
color: '#ED1C24', color: '#ED1C24',
prefix: '',
suffix: '',
transform: (value: number) => value.toFixed(0) transform: (value: number) => value.toFixed(0)
}, },
letterboxd: { letterboxd: {
icon: LetterboxdIcon, icon: LetterboxdIcon,
isImage: false, isImage: false,
color: '#00E054', color: '#00E054',
prefix: '',
suffix: '',
transform: (value: number) => value.toFixed(1) transform: (value: number) => value.toFixed(1)
}, },
tomatoes: { tomatoes: {
icon: RottenTomatoesIcon, icon: RottenTomatoesIcon,
isImage: false, isImage: false,
color: '#FA320A', color: '#FA320A',
prefix: '', transform: (value: number) => Math.round(value).toString() + '%'
suffix: '%',
transform: (value: number) => Math.round(value).toString()
}, },
audience: { audience: {
icon: AudienceScoreIcon, icon: AudienceScoreIcon,
isImage: true, isImage: true,
color: '#FA320A', color: '#FA320A',
prefix: '', transform: (value: number) => Math.round(value).toString() + '%'
suffix: '%',
transform: (value: number) => Math.round(value).toString()
}, },
metacritic: { metacritic: {
icon: MetacriticIcon, icon: MetacriticIcon,
isImage: true, isImage: true,
color: '#FFCC33', color: '#FFCC33',
prefix: '',
suffix: '',
transform: (value: number) => Math.round(value).toString() transform: (value: number) => Math.round(value).toString()
} }
}; };
@ -215,55 +174,30 @@ export const RatingsSection: React.FC<RatingsSectionProps> = ({ imdbId, type })
}, },
]} ]}
> >
<View style={styles.header}> <View style={styles.compactRatingsContainer}>
<Text style={[styles.title, { color: currentTheme.colors.highEmphasis }]}>Ratings</Text>
</View>
<View style={styles.ratingsContainer}>
{displayRatings.map(([source, value]) => { {displayRatings.map(([source, value]) => {
const config = ratingConfig[source as keyof typeof ratingConfig]; const config = ratingConfig[source as keyof typeof ratingConfig];
const displayValue = config.transform(parseFloat(value as string)); const displayValue = config.transform(parseFloat(value as string));
// Get a short display name for the rating source
const getSourceLabel = (src: string): string => {
switch(src) {
case 'imdb': return 'IMDb';
case 'tmdb': return 'TMDB';
case 'tomatoes': return 'RT';
case 'audience': return 'Aud';
case 'metacritic': return 'Meta';
case 'letterboxd': return 'LBXD';
case 'trakt': return 'Trakt';
default: return src;
}
};
return ( return (
<View key={source} style={styles.ratingItem}> <View key={source} style={styles.compactRatingItem}>
<View style={styles.ratingIconContainer}> {config.isImage ? (
{config.isImage ? ( <Image
<Image source={config.icon as any}
source={config.icon as any} style={styles.compactRatingIcon}
style={styles.ratingIconImage} resizeMode="contain"
resizeMode="contain" />
/> ) : (
) : ( <View style={styles.compactSvgContainer}>
<View style={styles.svgContainer}> {React.createElement(config.icon as any, {
{React.createElement(config.icon as any, { width: 16,
width: 24, height: 16,
height: 24, })}
})} </View>
</View> )}
)} <Text style={[styles.compactRatingValue, { color: config.color }]}>
</View> {displayValue}
<Text
style={[
styles.ratingValue,
{ color: config.color }
]}
>
{config.prefix}{displayValue}{config.suffix}
</Text> </Text>
<Text style={[styles.ratingSource, { color: currentTheme.colors.mediumEmphasis }]}>{getSourceLabel(source)}</Text>
</View> </View>
); );
})} })}
@ -274,69 +208,35 @@ export const RatingsSection: React.FC<RatingsSectionProps> = ({ imdbId, type })
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
marginBottom: 20, marginTop: 2,
marginBottom: 8,
paddingHorizontal: 16, paddingHorizontal: 16,
}, },
loadingContainer: { loadingContainer: {
height: 80, height: 40,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}, },
header: { compactRatingsContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', flexWrap: 'nowrap',
marginBottom: 12,
}, },
title: { compactRatingItem: {
fontSize: 18,
fontWeight: '700',
},
ratingsContainer: {
flexDirection: 'row', flexDirection: 'row',
flexWrap: 'wrap',
gap: 10,
},
ratingItem: {
flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
width: 55, marginRight: 12,
}, },
ratingIconContainer: { compactRatingIcon: {
width: 32, width: 16,
height: 32, height: 16,
justifyContent: 'center', marginRight: 4,
alignItems: 'center',
marginBottom: 4,
}, },
ratingIconImage: { compactSvgContainer: {
width: 32, marginRight: 4,
height: 32,
}, },
svgContainer: { compactRatingValue: {
alignItems: 'center',
justifyContent: 'center',
},
ratingValue: {
fontSize: 16,
fontWeight: '700',
marginVertical: 2,
},
ratingSource: {
fontSize: 11,
textAlign: 'center',
},
noRatingsText: {
fontSize: 14, fontSize: 14,
color: 'gray', fontWeight: '600',
fontStyle: 'italic',
textAlign: 'center',
marginVertical: 16,
},
errorText: {
fontSize: 12,
color: '#ff0000',
textAlign: 'center',
marginVertical: 8,
}, },
}); });

View file

@ -389,6 +389,7 @@ const WrappedScreen: React.FC<{Screen: React.ComponentType<any>}> = ({ Screen })
const MainTabs = () => { const MainTabs = () => {
// Always use dark mode // Always use dark mode
const isDarkMode = true; const isDarkMode = true;
const { currentTheme } = useTheme();
const renderTabBar = (props: BottomTabBarProps) => { const renderTabBar = (props: BottomTabBarProps) => {
return ( return (
@ -409,9 +410,9 @@ const MainTabs = () => {
position: 'absolute', position: 'absolute',
height: '100%', height: '100%',
width: '100%', width: '100%',
borderTopColor: 'rgba(255,255,255,0.2)', borderTopColor: currentTheme.colors.border,
borderTopWidth: 0.5, borderTopWidth: 0.5,
shadowColor: '#000', shadowColor: currentTheme.colors.black,
shadowOffset: { width: 0, height: -2 }, shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1, shadowOpacity: 0.1,
shadowRadius: 3, shadowRadius: 3,
@ -495,7 +496,7 @@ const MainTabs = () => {
> >
<TabIcon <TabIcon
focused={isFocused} focused={isFocused}
color={isFocused ? colors.primary : '#FFFFFF'} color={isFocused ? currentTheme.colors.primary : currentTheme.colors.white}
iconName={iconName} iconName={iconName}
/> />
<Text <Text
@ -503,7 +504,7 @@ const MainTabs = () => {
fontSize: 12, fontSize: 12,
fontWeight: '600', fontWeight: '600',
marginTop: 4, marginTop: 4,
color: isFocused ? colors.primary : '#FFFFFF', color: isFocused ? currentTheme.colors.primary : currentTheme.colors.white,
opacity: isFocused ? 1 : 0.7, opacity: isFocused ? 1 : 0.7,
}} }}
> >
@ -519,7 +520,7 @@ const MainTabs = () => {
}; };
return ( return (
<View style={{ flex: 1, backgroundColor: colors.darkBackground }}> <View style={{ flex: 1, backgroundColor: currentTheme.colors.darkBackground }}>
{/* Common StatusBar for all tabs */} {/* Common StatusBar for all tabs */}
<StatusBar <StatusBar
translucent translucent
@ -550,8 +551,8 @@ const MainTabs = () => {
return <TabIcon focused={focused} color={color} iconName={iconName} />; return <TabIcon focused={focused} color={color} iconName={iconName} />;
}, },
tabBarActiveTintColor: colors.primary, tabBarActiveTintColor: currentTheme.colors.primary,
tabBarInactiveTintColor: '#FFFFFF', tabBarInactiveTintColor: currentTheme.colors.white,
tabBarStyle: { tabBarStyle: {
position: 'absolute', position: 'absolute',
backgroundColor: 'transparent', backgroundColor: 'transparent',
@ -583,9 +584,9 @@ const MainTabs = () => {
position: 'absolute', position: 'absolute',
height: '100%', height: '100%',
width: '100%', width: '100%',
borderTopColor: 'rgba(255,255,255,0.2)', borderTopColor: currentTheme.colors.border,
borderTopWidth: 0.5, borderTopWidth: 0.5,
shadowColor: '#000', shadowColor: currentTheme.colors.black,
shadowOffset: { width: 0, height: -2 }, shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1, shadowOpacity: 0.1,
shadowRadius: 3, shadowRadius: 3,
@ -612,7 +613,7 @@ const MainTabs = () => {
headerShown: route.name === 'Home', headerShown: route.name === 'Home',
// Add fixed screen styling to help with consistency // Add fixed screen styling to help with consistency
contentStyle: { contentStyle: {
backgroundColor: colors.darkBackground, backgroundColor: currentTheme.colors.darkBackground,
}, },
})} })}
// Global configuration for the tab navigator // Global configuration for the tab navigator

View file

@ -335,16 +335,14 @@ const MetadataScreen = () => {
metadata={metadata} metadata={metadata}
imdbId={imdbId} imdbId={imdbId}
type={type as 'movie' | 'series'} type={type as 'movie' | 'series'}
renderRatings={() => imdbId ? (
<RatingsSection
imdbId={imdbId}
type={type === 'series' ? 'show' : 'movie'}
/>
) : null}
/> />
{/* Add RatingsSection right under the main metadata */}
{imdbId && (
<RatingsSection
imdbId={imdbId}
type={type === 'series' ? 'show' : 'movie'}
/>
)}
{/* Cast Section */} {/* Cast Section */}
<CastSection <CastSection
cast={cast} cast={cast}