simplified thisweeeksection cards

This commit is contained in:
tapframe 2025-11-25 01:57:50 +05:30
parent 771765f32b
commit 011f480fc1

View file

@ -60,7 +60,7 @@ export const ThisWeekSection = React.memo(() => {
// Enhanced responsive sizing for tablets and TV screens // Enhanced responsive sizing for tablets and TV screens
const deviceWidth = Dimensions.get('window').width; const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('window').height; const deviceHeight = Dimensions.get('window').height;
// Determine device type based on width // Determine device type based on width
const getDeviceType = useCallback(() => { const getDeviceType = useCallback(() => {
if (deviceWidth >= BREAKPOINTS.tv) return 'tv'; if (deviceWidth >= BREAKPOINTS.tv) return 'tv';
@ -68,13 +68,13 @@ export const ThisWeekSection = React.memo(() => {
if (deviceWidth >= BREAKPOINTS.tablet) return 'tablet'; if (deviceWidth >= BREAKPOINTS.tablet) return 'tablet';
return 'phone'; return 'phone';
}, [deviceWidth]); }, [deviceWidth]);
const deviceType = getDeviceType(); const deviceType = getDeviceType();
const isTablet = deviceType === 'tablet'; const isTablet = deviceType === 'tablet';
const isLargeTablet = deviceType === 'largeTablet'; const isLargeTablet = deviceType === 'largeTablet';
const isTV = deviceType === 'tv'; const isTV = deviceType === 'tv';
const isLargeScreen = isTablet || isLargeTablet || isTV; const isLargeScreen = isTablet || isLargeTablet || isTV;
// Enhanced responsive sizing // Enhanced responsive sizing
const computedItemWidth = useMemo(() => { const computedItemWidth = useMemo(() => {
switch (deviceType) { switch (deviceType) {
@ -88,7 +88,7 @@ export const ThisWeekSection = React.memo(() => {
return ITEM_WIDTH; // phone return ITEM_WIDTH; // phone
} }
}, [deviceType, deviceWidth]); }, [deviceType, deviceWidth]);
const computedItemHeight = useMemo(() => { const computedItemHeight = useMemo(() => {
switch (deviceType) { switch (deviceType) {
case 'tv': case 'tv':
@ -101,7 +101,7 @@ export const ThisWeekSection = React.memo(() => {
return ITEM_HEIGHT; // phone return ITEM_HEIGHT; // phone
} }
}, [deviceType]); }, [deviceType]);
// Enhanced spacing and padding // Enhanced spacing and padding
const horizontalPadding = useMemo(() => { const horizontalPadding = useMemo(() => {
switch (deviceType) { switch (deviceType) {
@ -115,7 +115,7 @@ export const ThisWeekSection = React.memo(() => {
return 16; // phone return 16; // phone
} }
}, [deviceType]); }, [deviceType]);
const itemSpacing = useMemo(() => { const itemSpacing = useMemo(() => {
switch (deviceType) { switch (deviceType) {
case 'tv': case 'tv':
@ -136,13 +136,13 @@ export const ThisWeekSection = React.memo(() => {
// Limit episodes to prevent memory issues and add release status // Limit episodes to prevent memory issues and add release status
const episodes = memoryManager.limitArraySize(thisWeekSection.data, 20); // Limit to 20 for home screen const episodes = memoryManager.limitArraySize(thisWeekSection.data, 20); // Limit to 20 for home screen
return episodes.map(episode => ({ return episodes.map(episode => ({
...episode, ...episode,
isReleased: episode.releaseDate ? isBefore(parseISO(episode.releaseDate), new Date()) : false, isReleased: episode.releaseDate ? isBefore(parseISO(episode.releaseDate), new Date()) : false,
})); }));
}, [calendarData]); }, [calendarData]);
const handleEpisodePress = (episode: ThisWeekEpisode) => { const handleEpisodePress = (episode: ThisWeekEpisode) => {
// For upcoming episodes, go to the metadata screen // For upcoming episodes, go to the metadata screen
if (!episode.isReleased) { if (!episode.isReleased) {
@ -154,7 +154,7 @@ export const ThisWeekSection = React.memo(() => {
}); });
return; return;
} }
// For released episodes, go to the streams screen // For released episodes, go to the streams screen
const episodeId = `${episode.seriesId}:${episode.season}:${episode.episode}`; const episodeId = `${episode.seriesId}:${episode.season}:${episode.episode}`;
navigation.navigate('Streams', { navigation.navigate('Streams', {
@ -163,136 +163,114 @@ export const ThisWeekSection = React.memo(() => {
episodeId episodeId
}); });
}; };
const handleViewAll = () => { const handleViewAll = () => {
navigation.navigate('Calendar' as any); navigation.navigate('Calendar' as any);
}; };
if (thisWeekEpisodes.length === 0) { if (thisWeekEpisodes.length === 0) {
return null; return null;
} }
const renderEpisodeItem = ({ item, index }: { item: ThisWeekEpisode, index: number }) => { const renderEpisodeItem = ({ item, index }: { item: ThisWeekEpisode, index: number }) => {
// Handle episodes without release dates gracefully // Handle episodes without release dates gracefully
const releaseDate = item.releaseDate ? parseISO(item.releaseDate) : null; const releaseDate = item.releaseDate ? parseISO(item.releaseDate) : null;
const formattedDate = releaseDate ? format(releaseDate, 'E, MMM d') : 'TBA'; const formattedDate = releaseDate ? format(releaseDate, 'MMM d') : 'TBA';
const isReleased = item.isReleased; const isReleased = item.isReleased;
// Use episode still image if available, fallback to series poster // Use episode still image if available, fallback to series poster
const imageUrl = item.still_path ? const imageUrl = item.still_path ?
tmdbService.getImageUrl(item.still_path) : tmdbService.getImageUrl(item.still_path) :
(item.season_poster_path ? (item.season_poster_path ?
tmdbService.getImageUrl(item.season_poster_path) : tmdbService.getImageUrl(item.season_poster_path) :
item.poster); item.poster);
return ( return (
<View style={[styles.episodeItemContainer, { width: computedItemWidth, height: computedItemHeight }]}> <View style={[styles.episodeItemContainer, { width: computedItemWidth, height: computedItemHeight }]}>
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.episodeItem, styles.episodeItem,
{ {
shadowColor: currentTheme.colors.black,
backgroundColor: currentTheme.colors.background, backgroundColor: currentTheme.colors.background,
borderColor: 'rgba(255,255,255,0.08)',
borderWidth: 1,
} }
]} ]}
onPress={() => handleEpisodePress(item)} onPress={() => handleEpisodePress(item)}
activeOpacity={0.8} activeOpacity={0.7}
> >
<View style={styles.imageContainer}> <View style={styles.imageContainer}>
<FastImage <FastImage
source={{ source={{
uri: imageUrl || undefined, uri: imageUrl || undefined,
priority: FastImage.priority.normal, priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable cache: FastImage.cacheControl.immutable
}} }}
style={styles.poster} style={styles.poster}
resizeMode={FastImage.resizeMode.cover} resizeMode={FastImage.resizeMode.cover}
/> />
{/* Enhanced gradient overlay */} <LinearGradient
<LinearGradient
colors={[ colors={[
'transparent',
'transparent', 'transparent',
'rgba(0,0,0,0.4)', 'rgba(0,0,0,0.0)',
'rgba(0,0,0,0.8)', 'rgba(0,0,0,0.5)',
'rgba(0,0,0,0.95)' 'rgba(0,0,0,0.9)'
]} ]}
style={[ style={styles.gradient}
styles.gradient, locations={[0, 0.4, 0.7, 1]}
{
padding: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12
}
]}
locations={[0, 0.4, 0.6, 0.8, 1]}
> >
{/* Content area */} <View style={styles.cardHeader}>
<View style={[
styles.statusBadge,
{ backgroundColor: isReleased ? currentTheme.colors.primary : 'rgba(0,0,0,0.6)' }
]}>
<Text style={styles.statusText}>
{isReleased ? 'New' : formattedDate}
</Text>
</View>
</View>
<View style={styles.contentArea}> <View style={styles.contentArea}>
<Text style={[ <Text style={[
styles.seriesName, styles.seriesName,
{ {
color: currentTheme.colors.white, color: currentTheme.colors.white,
fontSize: isTV ? 22 : isLargeTablet ? 20 : isTablet ? 18 : 16 fontSize: isTV ? 20 : isLargeTablet ? 18 : isTablet ? 17 : 16
} }
]} numberOfLines={1}> ]} numberOfLines={1}>
{item.seriesName} {item.seriesName}
</Text> </Text>
<Text style={[ <View style={styles.metaContainer}>
styles.episodeTitle,
{
color: 'rgba(255,255,255,0.9)',
fontSize: isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 14
}
]} numberOfLines={2}>
{item.title}
</Text>
{item.overview && (
<Text style={[ <Text style={[
styles.overview, styles.seasonBadge,
{ {
color: 'rgba(255,255,255,0.8)',
fontSize: isTV ? 15 : isLargeTablet ? 14 : isTablet ? 13 : 12
}
]} numberOfLines={isLargeScreen ? 3 : 2}>
{item.overview}
</Text>
)}
<View style={styles.dateContainer}>
<Text style={[
styles.episodeInfo,
{
color: 'rgba(255,255,255,0.7)',
fontSize: isTV ? 15 : isLargeTablet ? 14 : isTablet ? 13 : 12
}
]}>
S{item.season}:E{item.episode}
</Text>
<MaterialIcons
name="event"
size={isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 14}
color={currentTheme.colors.primary}
/>
<Text style={[
styles.releaseDate,
{
color: currentTheme.colors.primary, color: currentTheme.colors.primary,
fontSize: isTV ? 16 : isLargeTablet ? 15 : isTablet ? 14 : 13 fontSize: isTV ? 14 : isLargeTablet ? 13 : isTablet ? 13 : 12
} }
]}> ]}>
{formattedDate} S{item.season} E{item.episode}
</Text>
<Text style={styles.dotSeparator}></Text>
<Text style={[
styles.episodeTitle,
{
color: 'rgba(255,255,255,0.7)',
fontSize: isTV ? 14 : isLargeTablet ? 13 : isTablet ? 13 : 12
}
]} numberOfLines={1}>
{item.title}
</Text> </Text>
</View> </View>
</View> </View>
</LinearGradient> </LinearGradient>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );
}; };
return ( return (
<Animated.View <Animated.View
style={styles.container} style={styles.container}
@ -300,16 +278,16 @@ export const ThisWeekSection = React.memo(() => {
> >
<View style={[styles.header, { paddingHorizontal: horizontalPadding }]}> <View style={[styles.header, { paddingHorizontal: horizontalPadding }]}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>
<Text style={[ <Text style={[
styles.title, styles.title,
{ {
color: currentTheme.colors.text, color: currentTheme.colors.text,
fontSize: isTV ? 32 : isLargeTablet ? 28 : isTablet ? 26 : 24 fontSize: isTV ? 32 : isLargeTablet ? 28 : isTablet ? 26 : 24
} }
]}>This Week</Text> ]}>This Week</Text>
<View style={[ <View style={[
styles.titleUnderline, styles.titleUnderline,
{ {
backgroundColor: currentTheme.colors.primary, backgroundColor: currentTheme.colors.primary,
width: isTV ? 50 : isLargeTablet ? 45 : isTablet ? 40 : 40, width: isTV ? 50 : isLargeTablet ? 45 : isTablet ? 40 : 40,
height: isTV ? 4 : isLargeTablet ? 3.5 : isTablet ? 3 : 3 height: isTV ? 4 : isLargeTablet ? 3.5 : isTablet ? 3 : 3
@ -324,20 +302,20 @@ export const ThisWeekSection = React.memo(() => {
} }
]}> ]}>
<Text style={[ <Text style={[
styles.viewAllText, styles.viewAllText,
{ {
color: currentTheme.colors.textMuted, color: currentTheme.colors.textMuted,
fontSize: isTV ? 18 : isLargeTablet ? 16 : isTablet ? 15 : 14 fontSize: isTV ? 18 : isLargeTablet ? 16 : isTablet ? 15 : 14
} }
]}>View All</Text> ]}>View All</Text>
<MaterialIcons <MaterialIcons
name="chevron-right" name="chevron-right"
size={isTV ? 24 : isLargeTablet ? 22 : isTablet ? 20 : 20} size={isTV ? 24 : isLargeTablet ? 22 : isTablet ? 20 : 20}
color={currentTheme.colors.textMuted} color={currentTheme.colors.textMuted}
/> />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<FlatList <FlatList
data={thisWeekEpisodes} data={thisWeekEpisodes}
keyExtractor={(item) => item.id} keyExtractor={(item) => item.id}
@ -345,10 +323,10 @@ export const ThisWeekSection = React.memo(() => {
horizontal horizontal
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
contentContainerStyle={[ contentContainerStyle={[
styles.listContent, styles.listContent,
{ {
paddingLeft: horizontalPadding, paddingLeft: horizontalPadding,
paddingRight: horizontalPadding paddingRight: horizontalPadding
} }
]} ]}
snapToInterval={computedItemWidth + itemSpacing} snapToInterval={computedItemWidth + itemSpacing}
@ -371,7 +349,7 @@ export const ThisWeekSection = React.memo(() => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
marginVertical: 20, marginVertical: 24,
}, },
header: { header: {
flexDirection: 'row', flexDirection: 'row',
@ -400,14 +378,15 @@ const styles = StyleSheet.create({
viewAllButton: { viewAllButton: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: 8, paddingVertical: 6,
paddingHorizontal: 10, paddingHorizontal: 12,
borderRadius: 20, borderRadius: 20,
backgroundColor: 'rgba(255,255,255,0.1)', backgroundColor: 'rgba(255,255,255,0.08)',
marginRight: -10, borderWidth: 1,
borderColor: 'rgba(255,255,255,0.05)',
}, },
viewAllText: { viewAllText: {
fontSize: 14, fontSize: 13,
fontWeight: '600', fontWeight: '600',
marginRight: 4, marginRight: 4,
}, },
@ -432,10 +411,11 @@ const styles = StyleSheet.create({
height: '100%', height: '100%',
borderRadius: 16, borderRadius: 16,
overflow: 'hidden', overflow: 'hidden',
shadowOffset: { width: 0, height: 8 }, shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3, shadowOpacity: 0.3,
shadowRadius: 12, shadowRadius: 8,
elevation: 12, elevation: 8,
}, },
imageContainer: { imageContainer: {
width: '100%', width: '100%',
@ -453,44 +433,54 @@ const styles = StyleSheet.create({
right: 0, right: 0,
top: 0, top: 0,
bottom: 0, bottom: 0,
justifyContent: 'flex-end', justifyContent: 'space-between',
padding: 12, padding: 12,
borderRadius: 16, borderRadius: 16,
}, },
cardHeader: {
flexDirection: 'row',
justifyContent: 'flex-end',
width: '100%',
},
statusBadge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 8,
overflow: 'hidden',
},
statusText: {
color: '#fff',
fontSize: 10,
fontWeight: '700',
textTransform: 'uppercase',
},
contentArea: { contentArea: {
width: '100%', width: '100%',
}, },
seriesName: { seriesName: {
fontSize: 16, fontSize: 16,
fontWeight: '700', fontWeight: '800',
marginBottom: 6,
},
episodeTitle: {
fontSize: 14,
fontWeight: '600',
marginBottom: 4, marginBottom: 4,
lineHeight: 18, textShadowColor: 'rgba(0, 0, 0, 0.75)',
textShadowOffset: { width: 0, height: 1 },
textShadowRadius: 3,
}, },
overview: { metaContainer: {
fontSize: 12,
lineHeight: 16,
marginBottom: 6,
opacity: 0.9,
},
dateContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginTop: 4,
}, },
episodeInfo: { seasonBadge: {
fontSize: 12, fontSize: 12,
fontWeight: '600', fontWeight: '700',
marginRight: 4,
}, },
releaseDate: { dotSeparator: {
fontSize: 13, marginHorizontal: 6,
fontWeight: '600', fontSize: 12,
marginLeft: 6, color: 'rgba(255,255,255,0.5)',
letterSpacing: 0.3, },
episodeTitle: {
fontSize: 12,
fontWeight: '500',
flex: 1,
}, },
}); });