mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-29 04:13:03 +00:00
m
This commit is contained in:
parent
74a288bc1a
commit
6f2ccfa38b
4 changed files with 60 additions and 156 deletions
|
|
@ -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)}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue