mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
discover screen init
This commit is contained in:
parent
a30fa604d7
commit
cf5cc2d8f9
6 changed files with 353 additions and 50 deletions
268
src/components/common/Poster.tsx
Normal file
268
src/components/common/Poster.tsx
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
Platform,
|
||||
Dimensions,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import { useSettings } from '../../hooks/useSettings';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
|
||||
// Enhanced responsive breakpoints
|
||||
const BREAKPOINTS = {
|
||||
phone: 0,
|
||||
tablet: 768,
|
||||
largeTablet: 1024,
|
||||
tv: 1440,
|
||||
};
|
||||
|
||||
const getDeviceType = (screenWidth: number) => {
|
||||
if (screenWidth >= BREAKPOINTS.tv) return 'tv';
|
||||
if (screenWidth >= BREAKPOINTS.largeTablet) return 'largeTablet';
|
||||
if (screenWidth >= BREAKPOINTS.tablet) return 'tablet';
|
||||
return 'phone';
|
||||
};
|
||||
|
||||
export type PosterShape = 'poster' | 'landscape' | 'square';
|
||||
|
||||
export interface PosterProps {
|
||||
/** The poster image URL */
|
||||
uri?: string | null;
|
||||
/** Width of the poster */
|
||||
width: number;
|
||||
/** Shape of the poster - determines aspect ratio */
|
||||
shape?: PosterShape;
|
||||
/** Optional custom aspect ratio override */
|
||||
aspectRatio?: number;
|
||||
/** Optional custom border radius (uses settings.posterBorderRadius by default) */
|
||||
borderRadius?: number;
|
||||
/** Optional title to display below the poster */
|
||||
title?: string;
|
||||
/** Whether to show the title */
|
||||
showTitle?: boolean;
|
||||
/** Fallback text to show when no poster is available */
|
||||
fallbackText?: string;
|
||||
/** Additional styles for the container */
|
||||
style?: ViewStyle;
|
||||
/** Additional styles for the poster container */
|
||||
posterStyle?: ViewStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared Poster component with consistent styling across the app.
|
||||
* Matches the design from ContentItem.tsx with:
|
||||
* - Border: 1.5px solid rgba(255,255,255,0.15)
|
||||
* - Border Radius: settings.posterBorderRadius (default 12)
|
||||
* - Shadow: elevation 1 on Android, subtle shadow on iOS
|
||||
* - Aspect Ratio: 2/3 for poster, 16/9 for landscape, 1/1 for square
|
||||
*/
|
||||
export const Poster: React.FC<PosterProps> = ({
|
||||
uri,
|
||||
width: posterWidth,
|
||||
shape = 'poster',
|
||||
aspectRatio: customAspectRatio,
|
||||
borderRadius: customBorderRadius,
|
||||
title,
|
||||
showTitle = false,
|
||||
fallbackText,
|
||||
style,
|
||||
posterStyle,
|
||||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const { settings, isLoaded } = useSettings();
|
||||
const [imageError, setImageError] = useState(false);
|
||||
|
||||
// Reset error state when URI changes
|
||||
useEffect(() => {
|
||||
setImageError(false);
|
||||
}, [uri]);
|
||||
|
||||
// Determine aspect ratio based on shape
|
||||
const aspectRatio = useMemo(() => {
|
||||
if (customAspectRatio) return customAspectRatio;
|
||||
switch (shape) {
|
||||
case 'landscape':
|
||||
return 16 / 9;
|
||||
case 'square':
|
||||
return 1;
|
||||
case 'poster':
|
||||
default:
|
||||
return 2 / 3;
|
||||
}
|
||||
}, [shape, customAspectRatio]);
|
||||
|
||||
// Border radius from settings or custom
|
||||
const borderRadius = customBorderRadius ??
|
||||
(typeof settings.posterBorderRadius === 'number' ? settings.posterBorderRadius : 12);
|
||||
|
||||
// Device type for responsive title sizing
|
||||
const deviceType = getDeviceType(width);
|
||||
|
||||
// Title font size based on device type
|
||||
const titleFontSize = useMemo(() => {
|
||||
switch (deviceType) {
|
||||
case 'tv':
|
||||
return 16;
|
||||
case 'largeTablet':
|
||||
return 15;
|
||||
case 'tablet':
|
||||
return 14;
|
||||
default:
|
||||
return 13;
|
||||
}
|
||||
}, [deviceType]);
|
||||
|
||||
// Optimize poster URL for TMDB
|
||||
const optimizedUrl = useMemo(() => {
|
||||
if (!uri || uri.includes('placeholder')) {
|
||||
return null;
|
||||
}
|
||||
if (uri.includes('image.tmdb.org')) {
|
||||
return uri.replace(/\/w\d+\//, '/w154/');
|
||||
}
|
||||
return uri;
|
||||
}, [uri]);
|
||||
|
||||
// Placeholder while settings load
|
||||
if (!isLoaded) {
|
||||
return (
|
||||
<View style={[styles.container, { width: posterWidth }, style]}>
|
||||
<View
|
||||
style={[
|
||||
styles.posterContainer,
|
||||
{
|
||||
width: posterWidth,
|
||||
aspectRatio,
|
||||
borderRadius,
|
||||
backgroundColor: currentTheme.colors.elevation1,
|
||||
},
|
||||
posterStyle,
|
||||
]}
|
||||
/>
|
||||
{showTitle && <View style={{ height: 18, marginTop: 4 }} />}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { width: posterWidth }, style]}>
|
||||
<View
|
||||
style={[
|
||||
styles.posterContainer,
|
||||
{
|
||||
width: posterWidth,
|
||||
aspectRatio,
|
||||
borderRadius,
|
||||
backgroundColor: currentTheme.colors.elevation1,
|
||||
},
|
||||
posterStyle,
|
||||
]}
|
||||
>
|
||||
{optimizedUrl && !imageError ? (
|
||||
<FastImage
|
||||
source={{
|
||||
uri: optimizedUrl,
|
||||
priority: FastImage.priority.normal,
|
||||
cache: FastImage.cacheControl.immutable,
|
||||
}}
|
||||
style={[styles.poster, { borderRadius }]}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
onLoad={() => setImageError(false)}
|
||||
onError={() => setImageError(true)}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
styles.poster,
|
||||
styles.fallbackContainer,
|
||||
{
|
||||
backgroundColor: currentTheme.colors.elevation1,
|
||||
borderRadius,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{imageError ? (
|
||||
<MaterialIcons
|
||||
name="broken-image"
|
||||
size={24}
|
||||
color={currentTheme.colors.textMuted}
|
||||
/>
|
||||
) : fallbackText ? (
|
||||
<Text
|
||||
style={[styles.fallbackText, { color: currentTheme.colors.textMuted }]}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{fallbackText.length > 20 ? `${fallbackText.substring(0, 20)}...` : fallbackText}
|
||||
</Text>
|
||||
) : (
|
||||
<MaterialIcons
|
||||
name="image"
|
||||
size={24}
|
||||
color={currentTheme.colors.textMuted}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{showTitle && title && (
|
||||
<Text
|
||||
style={[
|
||||
styles.title,
|
||||
{
|
||||
color: currentTheme.colors.mediumEmphasis,
|
||||
fontSize: titleFontSize,
|
||||
},
|
||||
]}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {},
|
||||
posterContainer: {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
// Consistent shadow/elevation matching ContentItem
|
||||
elevation: Platform.OS === 'android' ? 1 : 0,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
// Consistent border styling
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
marginBottom: 8,
|
||||
},
|
||||
poster: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
fallbackContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
fallbackText: {
|
||||
fontSize: 10,
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
title: {
|
||||
fontWeight: '500',
|
||||
marginTop: 4,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default Poster;
|
||||
|
|
@ -1089,7 +1089,8 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
borderColor: currentTheme.colors.border,
|
||||
shadowColor: currentTheme.colors.black,
|
||||
width: computedItemWidth,
|
||||
height: computedItemHeight
|
||||
height: computedItemHeight,
|
||||
borderRadius: settings.posterBorderRadius ?? 12,
|
||||
}
|
||||
]}
|
||||
activeOpacity={0.8}
|
||||
|
|
@ -1110,7 +1111,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
priority: FastImage.priority.high,
|
||||
cache: FastImage.cacheControl.immutable
|
||||
}}
|
||||
style={styles.continueWatchingPoster}
|
||||
style={[styles.continueWatchingPoster, { borderTopLeftRadius: settings.posterBorderRadius ?? 12, borderBottomLeftRadius: settings.posterBorderRadius ?? 12 }]}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
|
||||
|
|
@ -1348,13 +1349,15 @@ const styles = StyleSheet.create({
|
|||
width: 280,
|
||||
height: 120,
|
||||
flexDirection: 'row',
|
||||
borderRadius: 14,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
elevation: 6,
|
||||
shadowOffset: { width: 0, height: 3 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 6,
|
||||
borderWidth: 1,
|
||||
elevation: 1,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
},
|
||||
posterContainer: {
|
||||
width: 80,
|
||||
|
|
@ -1364,8 +1367,8 @@ const styles = StyleSheet.create({
|
|||
continueWatchingPoster: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderTopLeftRadius: 14,
|
||||
borderBottomLeftRadius: 14,
|
||||
borderTopLeftRadius: 12,
|
||||
borderBottomLeftRadius: 12,
|
||||
},
|
||||
deletingOverlay: {
|
||||
position: 'absolute',
|
||||
|
|
@ -1451,26 +1454,28 @@ const styles = StyleSheet.create({
|
|||
width: POSTER_WIDTH,
|
||||
aspectRatio: 2 / 3,
|
||||
margin: 0,
|
||||
borderRadius: 8,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
elevation: 8,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
borderWidth: 1,
|
||||
elevation: 1,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
},
|
||||
contentItemContainer: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 8,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
poster: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 8,
|
||||
borderRadius: 12,
|
||||
},
|
||||
episodeInfoContainer: {
|
||||
position: 'absolute',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
Dimensions,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
|
|
@ -14,6 +15,7 @@ import { NavigationProp } from '@react-navigation/native';
|
|||
import { RootStackParamList } from '../../navigation/AppNavigator';
|
||||
import { StreamingContent } from '../../services/catalogService';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import { useSettings } from '../../hooks/useSettings';
|
||||
import { TMDBService } from '../../services/tmdbService';
|
||||
import { catalogService } from '../../services/catalogService';
|
||||
import CustomAlert from '../../components/CustomAlert';
|
||||
|
|
@ -33,12 +35,14 @@ interface MoreLikeThisSectionProps {
|
|||
loadingRecommendations: boolean;
|
||||
}
|
||||
|
||||
export const MoreLikeThisSection: React.FC<MoreLikeThisSectionProps> = ({
|
||||
recommendations,
|
||||
loadingRecommendations
|
||||
export const MoreLikeThisSection: React.FC<MoreLikeThisSectionProps> = ({
|
||||
recommendations,
|
||||
loadingRecommendations
|
||||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const { settings } = useSettings();
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const borderRadius = settings.posterBorderRadius ?? 12;
|
||||
|
||||
// Determine device type
|
||||
const deviceWidth = Dimensions.get('window').width;
|
||||
|
|
@ -91,16 +95,16 @@ export const MoreLikeThisSection: React.FC<MoreLikeThisSectionProps> = ({
|
|||
try {
|
||||
// Extract TMDB ID from the tmdb:123456 format
|
||||
const tmdbId = item.id.replace('tmdb:', '');
|
||||
|
||||
|
||||
// Get Stremio ID directly using catalogService
|
||||
// The catalogService.getStremioId method already handles the conversion internally
|
||||
const stremioId = await catalogService.getStremioId(item.type, tmdbId);
|
||||
|
||||
|
||||
if (stremioId) {
|
||||
navigation.dispatch(
|
||||
StackActions.push('Metadata', {
|
||||
id: stremioId,
|
||||
type: item.type
|
||||
StackActions.push('Metadata', {
|
||||
id: stremioId,
|
||||
type: item.type
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
|
@ -110,19 +114,19 @@ export const MoreLikeThisSection: React.FC<MoreLikeThisSectionProps> = ({
|
|||
if (__DEV__) console.error('Error navigating to recommendation:', error);
|
||||
setAlertTitle('Error');
|
||||
setAlertMessage('Unable to load this content. Please try again later.');
|
||||
setAlertActions([{ label: 'OK', onPress: () => {} }]);
|
||||
setAlertActions([{ label: 'OK', onPress: () => { } }]);
|
||||
setAlertVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
const renderItem = ({ item }: { item: StreamingContent }) => (
|
||||
<TouchableOpacity
|
||||
<TouchableOpacity
|
||||
style={[styles.itemContainer, { width: posterWidth, marginRight: itemSpacing }]}
|
||||
onPress={() => handleItemPress(item)}
|
||||
>
|
||||
<FastImage
|
||||
source={{ uri: item.poster }}
|
||||
style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, width: posterWidth, height: posterHeight, borderRadius: isTV ? 12 : isLargeTablet ? 10 : isTablet ? 10 : 8 }]}
|
||||
style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, width: posterWidth, height: posterHeight, borderRadius }]}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<Text style={[styles.title, { color: currentTheme.colors.mediumEmphasis, fontSize: isTV ? 14 : isLargeTablet ? 13 : isTablet ? 13 : 13, lineHeight: isTV ? 20 : 18 }]} numberOfLines={2}>
|
||||
|
|
@ -144,7 +148,7 @@ export const MoreLikeThisSection: React.FC<MoreLikeThisSectionProps> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { paddingLeft: 0 }] }>
|
||||
<View style={[styles.container, { paddingLeft: 0 }]}>
|
||||
<Text style={[styles.sectionTitle, { color: currentTheme.colors.highEmphasis, fontSize: isTV ? 24 : isLargeTablet ? 22 : isTablet ? 20 : 20, paddingHorizontal: horizontalPadding }]}>More Like This</Text>
|
||||
<FlatList
|
||||
data={recommendations}
|
||||
|
|
@ -183,10 +187,17 @@ const styles = StyleSheet.create({
|
|||
marginRight: 12, // will be overridden responsively
|
||||
},
|
||||
poster: {
|
||||
borderRadius: 8, // overridden responsively
|
||||
borderRadius: 12,
|
||||
marginBottom: 8,
|
||||
borderWidth: 1,
|
||||
// Consistent border styling matching ContentItem
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
// Consistent shadow/elevation
|
||||
elevation: Platform.OS === 'android' ? 1 : 0,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
},
|
||||
title: {
|
||||
fontSize: 13, // overridden responsively
|
||||
|
|
|
|||
|
|
@ -103,8 +103,10 @@ const DownloadItemComponent: React.FC<{
|
|||
onRequestRemove: (item: DownloadItem) => void;
|
||||
}> = React.memo(({ item, onPress, onAction, onRequestRemove }) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const { settings } = useSettings();
|
||||
const { showSuccess, showInfo } = useToast();
|
||||
const [posterUrl, setPosterUrl] = useState<string | null>(item.posterUrl || null);
|
||||
const borderRadius = settings.posterBorderRadius ?? 12;
|
||||
|
||||
// Try to fetch poster if not available
|
||||
useEffect(() => {
|
||||
|
|
@ -212,10 +214,10 @@ const DownloadItemComponent: React.FC<{
|
|||
activeOpacity={0.8}
|
||||
>
|
||||
{/* Poster */}
|
||||
<View style={styles.posterContainer}>
|
||||
<View style={[styles.posterContainer, { borderRadius }]}>
|
||||
<FastImage
|
||||
source={{ uri: optimizePosterUrl(posterUrl) }}
|
||||
style={styles.poster}
|
||||
style={[styles.poster, { borderRadius }]}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{/* Status indicator overlay */}
|
||||
|
|
@ -790,16 +792,25 @@ const styles = StyleSheet.create({
|
|||
posterContainer: {
|
||||
width: POSTER_WIDTH,
|
||||
height: POSTER_HEIGHT,
|
||||
borderRadius: 8,
|
||||
borderRadius: 12,
|
||||
marginRight: isTablet ? 20 : 16,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#333',
|
||||
// Consistent border styling matching ContentItem
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
// Consistent shadow/elevation
|
||||
elevation: Platform.OS === 'android' ? 1 : 0,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
},
|
||||
poster: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 8,
|
||||
borderRadius: 12,
|
||||
},
|
||||
statusOverlay: {
|
||||
position: 'absolute',
|
||||
|
|
|
|||
|
|
@ -405,10 +405,10 @@ const LibraryScreen = () => {
|
|||
activeOpacity={0.7}
|
||||
>
|
||||
<View>
|
||||
<View style={[styles.posterContainer, { shadowColor: currentTheme.colors.black }]}>
|
||||
<View style={[styles.posterContainer, { shadowColor: currentTheme.colors.black, borderRadius: settings.posterBorderRadius ?? 12 }]}>
|
||||
<FastImage
|
||||
source={{ uri: item.poster || 'https://via.placeholder.com/300x450' }}
|
||||
style={styles.poster}
|
||||
style={[styles.poster, { borderRadius: settings.posterBorderRadius ?? 12 }]}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{item.watched && (
|
||||
|
|
@ -1063,11 +1063,14 @@ const styles = StyleSheet.create({
|
|||
overflow: 'hidden',
|
||||
backgroundColor: 'rgba(255,255,255,0.03)',
|
||||
aspectRatio: 2 / 3,
|
||||
elevation: 5,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 8,
|
||||
borderWidth: 1,
|
||||
// Consistent shadow/elevation matching ContentItem
|
||||
elevation: Platform.OS === 'android' ? 1 : 0,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
// Consistent border styling
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
},
|
||||
poster: {
|
||||
|
|
|
|||
|
|
@ -1014,16 +1014,14 @@ const SearchScreen = () => {
|
|||
>
|
||||
<View style={[styles.horizontalItemPosterContainer, {
|
||||
width: itemWidth,
|
||||
height: undefined, // Let aspect ratio control height or keep fixed height with width?
|
||||
// Actually, since we derived width from fixed height, we can keep height fixed or use aspect.
|
||||
// Using aspect ratio is safer if baseHeight changes.
|
||||
height: undefined, // Let aspect ratio control height
|
||||
aspectRatio: aspectRatio,
|
||||
backgroundColor: currentTheme.colors.darkBackground,
|
||||
borderColor: 'rgba(255,255,255,0.05)'
|
||||
borderRadius: settings.posterBorderRadius ?? 12,
|
||||
}]}>
|
||||
<FastImage
|
||||
source={{ uri: item.poster || PLACEHOLDER_POSTER }}
|
||||
style={styles.horizontalItemPoster}
|
||||
style={[styles.horizontalItemPoster, { borderRadius: settings.posterBorderRadius ?? 12 }]}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{/* Bookmark and watched icons top right, bookmark to the left of watched */}
|
||||
|
|
@ -1723,10 +1721,17 @@ const styles = StyleSheet.create({
|
|||
horizontalItemPosterContainer: {
|
||||
width: HORIZONTAL_ITEM_WIDTH,
|
||||
height: HORIZONTAL_POSTER_HEIGHT,
|
||||
borderRadius: 16,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 8,
|
||||
borderWidth: 1,
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
// Consistent shadow/elevation matching ContentItem
|
||||
elevation: Platform.OS === 'android' ? 1 : 0,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 1,
|
||||
},
|
||||
horizontalItemPoster: {
|
||||
width: '100%',
|
||||
|
|
|
|||
Loading…
Reference in a new issue