mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
adaptive sizing for playercontrols,seriescontent,and commentsection
This commit is contained in:
parent
0b764412b2
commit
175d47f71f
4 changed files with 202 additions and 85 deletions
|
|
@ -837,41 +837,66 @@ export const CommentsSection: React.FC<CommentsSectionProps> = ({
|
||||||
|
|
||||||
const renderSkeletons = useCallback(() => {
|
const renderSkeletons = useCallback(() => {
|
||||||
const placeholders = [0, 1, 2];
|
const placeholders = [0, 1, 2];
|
||||||
|
// Responsive skeleton sizes to match CompactCommentCard
|
||||||
|
const skWidth = isTV ? 360 : isLargeTablet ? 320 : isTablet ? 300 : 280;
|
||||||
|
const skHeight = isTV ? 200 : isLargeTablet ? 185 : isTablet ? 175 : 170;
|
||||||
|
const skPad = isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12;
|
||||||
|
const gap = isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12;
|
||||||
|
const headLineWidth = isTV ? 160 : isLargeTablet ? 140 : isTablet ? 130 : 120;
|
||||||
|
const ratingWidth = isTV ? 100 : isLargeTablet ? 90 : isTablet ? 85 : 80;
|
||||||
|
const statWidth = isTV ? 44 : isLargeTablet ? 40 : isTablet ? 38 : 36;
|
||||||
|
const badgeW = isTV ? 60 : isLargeTablet ? 56 : isTablet ? 52 : 50;
|
||||||
|
const badgeH = isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 12;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.horizontalList}>
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={[styles.horizontalList, { paddingRight: gap }]}>
|
||||||
{placeholders.map((i) => (
|
{placeholders.map((i) => (
|
||||||
<View key={`skeleton-${i}`} style={[styles.compactCard, { backgroundColor: currentTheme.colors.card, borderColor: currentTheme.colors.border }]}>
|
<View
|
||||||
|
key={`skeleton-${i}`}
|
||||||
|
style={[
|
||||||
|
styles.compactCard,
|
||||||
|
{
|
||||||
|
backgroundColor: currentTheme.colors.card,
|
||||||
|
borderColor: currentTheme.colors.border,
|
||||||
|
width: skWidth,
|
||||||
|
height: skHeight,
|
||||||
|
marginRight: gap,
|
||||||
|
padding: skPad,
|
||||||
|
borderRadius: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
<View style={styles.skeletonTraktContainer}>
|
<View style={styles.skeletonTraktContainer}>
|
||||||
<View style={[styles.skeletonDot]} />
|
<View style={[styles.skeletonDot, { width: isTV ? 20 : isLargeTablet ? 18 : isTablet ? 16 : 16, height: isTV ? 20 : isLargeTablet ? 18 : isTablet ? 16 : 16, borderRadius: isTV ? 10 : isLargeTablet ? 9 : 8 }]} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.compactHeader}>
|
<View style={[styles.compactHeader, { marginBottom: isTV ? 10 : isLargeTablet ? 8 : isTablet ? 8 : 8 }]}>
|
||||||
<View style={[styles.skeletonLine, { width: 120 }]} />
|
<View style={[styles.skeletonLine, { width: headLineWidth, height: isTV ? 14 : 12 }]} />
|
||||||
<View style={[styles.miniVipBadge, styles.skeletonBadge]} />
|
<View style={[styles.miniVipBadge, styles.skeletonBadge, { width: isTV ? 36 : isLargeTablet ? 32 : isTablet ? 28 : 24, height: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12, borderRadius: isTV ? 10 : isLargeTablet ? 9 : 8 }]} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.compactRating}>
|
<View style={[styles.compactRating, { marginBottom: isTV ? 10 : isLargeTablet ? 8 : isTablet ? 8 : 8 }]}>
|
||||||
<View style={[styles.skeletonLine, { width: 80, height: 10 }]} />
|
<View style={[styles.skeletonLine, { width: ratingWidth, height: isTV ? 12 : 10 }]} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.commentContainer}>
|
<View style={[styles.commentContainer, { marginBottom: isTV ? 10 : isLargeTablet ? 8 : isTablet ? 8 : 8 }]}>
|
||||||
<View style={[styles.skeletonLine, { width: '95%' }]} />
|
<View style={[styles.skeletonLine, { width: '95%', height: isTV ? 14 : 12 }]} />
|
||||||
<View style={[styles.skeletonLine, { width: '90%', marginTop: 6 }]} />
|
<View style={[styles.skeletonLine, { width: '90%', height: isTV ? 14 : 12, marginTop: 6 }]} />
|
||||||
<View style={[styles.skeletonLine, { width: '70%', marginTop: 6 }]} />
|
<View style={[styles.skeletonLine, { width: '70%', height: isTV ? 14 : 12, marginTop: 6 }]} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.compactMeta}>
|
<View style={[styles.compactMeta, { paddingTop: isTV ? 8 : isLargeTablet ? 6 : isTablet ? 6 : 6 }]}>
|
||||||
<View style={[styles.skeletonBadge, { width: 50, height: 12, borderRadius: 6 }]} />
|
<View style={[styles.skeletonBadge, { width: badgeW, height: badgeH, borderRadius: Math.min(6, badgeH / 2) }]} />
|
||||||
<View style={{ flexDirection: 'row', gap: 8 }}>
|
<View style={{ flexDirection: 'row', gap }}>
|
||||||
<View style={[styles.skeletonLine, { width: 36, height: 10 }]} />
|
<View style={[styles.skeletonLine, { width: statWidth, height: isTV ? 12 : 10 }]} />
|
||||||
<View style={[styles.skeletonLine, { width: 36, height: 10 }]} />
|
<View style={[styles.skeletonLine, { width: statWidth, height: isTV ? 12 : 10 }]} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}, [currentTheme]);
|
}, [currentTheme, isTV, isLargeTablet, isTablet]);
|
||||||
|
|
||||||
// Don't show section if not authenticated, if comments are disabled in settings, or if still checking authentication
|
// Don't show section if not authenticated, if comments are disabled in settings, or if still checking authentication
|
||||||
// Only show when authentication is definitively true and settings allow it
|
// Only show when authentication is definitively true and settings allow it
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,46 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
return 16; // phone
|
return 16; // phone
|
||||||
}
|
}
|
||||||
}, [deviceType]);
|
}, [deviceType]);
|
||||||
|
|
||||||
|
// Match ThisWeekSection card sizing for horizontal episode cards
|
||||||
|
const horizontalCardWidth = useMemo(() => {
|
||||||
|
switch (deviceType) {
|
||||||
|
case 'tv':
|
||||||
|
return Math.min(deviceWidth * 0.25, 400);
|
||||||
|
case 'largeTablet':
|
||||||
|
return Math.min(deviceWidth * 0.35, 350);
|
||||||
|
case 'tablet':
|
||||||
|
return Math.min(deviceWidth * 0.46, 300);
|
||||||
|
default:
|
||||||
|
return width * 0.75;
|
||||||
|
}
|
||||||
|
}, [deviceType, deviceWidth, width]);
|
||||||
|
|
||||||
|
const horizontalCardHeight = useMemo(() => {
|
||||||
|
switch (deviceType) {
|
||||||
|
case 'tv':
|
||||||
|
return 280;
|
||||||
|
case 'largeTablet':
|
||||||
|
return 250;
|
||||||
|
case 'tablet':
|
||||||
|
return 220;
|
||||||
|
default:
|
||||||
|
return 180;
|
||||||
|
}
|
||||||
|
}, [deviceType]);
|
||||||
|
|
||||||
|
const horizontalItemSpacing = useMemo(() => {
|
||||||
|
switch (deviceType) {
|
||||||
|
case 'tv':
|
||||||
|
return 20;
|
||||||
|
case 'largeTablet':
|
||||||
|
return 18;
|
||||||
|
case 'tablet':
|
||||||
|
return 16;
|
||||||
|
default:
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
}, [deviceType]);
|
||||||
|
|
||||||
// Enhanced season poster sizing
|
// Enhanced season poster sizing
|
||||||
const seasonPosterWidth = useMemo(() => {
|
const seasonPosterWidth = useMemo(() => {
|
||||||
|
|
@ -730,6 +770,20 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
<MaterialIcons name="check" size={isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 12} color={currentTheme.colors.white} />
|
<MaterialIcons name="check" size={isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 12} color={currentTheme.colors.white} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
{(!progress || progressPercent === 0) && (
|
||||||
|
<View style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 8,
|
||||||
|
left: 8,
|
||||||
|
width: isTV ? 24 : isLargeTablet ? 22 : isTablet ? 20 : 20,
|
||||||
|
height: isTV ? 24 : isLargeTablet ? 22 : isTablet ? 20 : 20,
|
||||||
|
borderRadius: isTV ? 12 : isLargeTablet ? 11 : isTablet ? 10 : 10,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderStyle: 'dashed',
|
||||||
|
borderColor: currentTheme.colors.textMuted,
|
||||||
|
opacity: 0.85,
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={[
|
<View style={[
|
||||||
|
|
@ -874,7 +928,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
styles.episodeCardHorizontal,
|
styles.episodeCardHorizontal,
|
||||||
{
|
{
|
||||||
borderRadius: isTV ? 20 : isLargeTablet ? 18 : isTablet ? 16 : 16,
|
borderRadius: isTV ? 20 : isLargeTablet ? 18 : isTablet ? 16 : 16,
|
||||||
height: isTV ? 280 : isLargeTablet ? 260 : isTablet ? 240 : 200,
|
height: horizontalCardHeight,
|
||||||
elevation: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 8,
|
elevation: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 8,
|
||||||
shadowOpacity: isTV ? 0.4 : isLargeTablet ? 0.35 : isTablet ? 0.3 : 0.3,
|
shadowOpacity: isTV ? 0.4 : isLargeTablet ? 0.35 : isTablet ? 0.3 : 0.3,
|
||||||
shadowRadius: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 8
|
shadowRadius: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 8
|
||||||
|
|
@ -882,7 +936,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
// Gradient border styling
|
// Gradient border styling
|
||||||
{
|
{
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: 'transparent',
|
borderColor: 'rgba(255,255,255,0.12)',
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 4 },
|
shadowOffset: { width: 0, height: 4 },
|
||||||
}
|
}
|
||||||
|
|
@ -890,32 +944,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
onPress={() => onSelectEpisode(episode)}
|
onPress={() => onSelectEpisode(episode)}
|
||||||
activeOpacity={0.85}
|
activeOpacity={0.85}
|
||||||
>
|
>
|
||||||
{/* Gradient Border Container */}
|
{/* Solid outline replaces gradient border */}
|
||||||
<View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: -1,
|
|
||||||
left: -1,
|
|
||||||
right: -1,
|
|
||||||
bottom: -1,
|
|
||||||
borderRadius: 17,
|
|
||||||
zIndex: -1,
|
|
||||||
}}>
|
|
||||||
<LinearGradient
|
|
||||||
colors={[
|
|
||||||
'#ffffff80', // White with 50% opacity
|
|
||||||
'#ffffff40', // White with 25% opacity
|
|
||||||
'#ffffff20', // White with 12% opacity
|
|
||||||
'#ffffff40', // White with 25% opacity
|
|
||||||
'#ffffff80', // White with 50% opacity
|
|
||||||
]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 1, y: 1 }}
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
borderRadius: 17,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Background Image */}
|
{/* Background Image */}
|
||||||
<FastImage
|
<FastImage
|
||||||
|
|
@ -1057,6 +1086,20 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
<MaterialIcons name="check" size={isTV ? 20 : isLargeTablet ? 18 : isTablet ? 16 : 16} color="#fff" />
|
<MaterialIcons name="check" size={isTV ? 20 : isLargeTablet ? 18 : isTablet ? 16 : 16} color="#fff" />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
{(!progress || progressPercent === 0) && (
|
||||||
|
<View style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12,
|
||||||
|
left: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12,
|
||||||
|
width: isTV ? 32 : isLargeTablet ? 28 : isTablet ? 24 : 24,
|
||||||
|
height: isTV ? 32 : isLargeTablet ? 28 : isTablet ? 24 : 24,
|
||||||
|
borderRadius: isTV ? 16 : isLargeTablet ? 14 : isTablet ? 12 : 12,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderStyle: 'dashed',
|
||||||
|
borderColor: currentTheme.colors.textMuted,
|
||||||
|
opacity: 0.9,
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
@ -1115,8 +1158,8 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
style={[
|
style={[
|
||||||
styles.episodeCardWrapperHorizontal,
|
styles.episodeCardWrapperHorizontal,
|
||||||
{
|
{
|
||||||
width: isTV ? width * 0.45 : isLargeTablet ? width * 0.4 : isTablet ? width * 0.4 : width * 0.75,
|
width: horizontalCardWidth,
|
||||||
marginRight: isTV ? 24 : isLargeTablet ? 20 : isTablet ? 20 : 16
|
marginRight: horizontalItemSpacing
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|
@ -1138,11 +1181,10 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
maxToRenderPerBatch={5}
|
maxToRenderPerBatch={5}
|
||||||
windowSize={5}
|
windowSize={5}
|
||||||
getItemLayout={(data, index) => {
|
getItemLayout={(data, index) => {
|
||||||
const cardWidth = isTV ? width * 0.45 : isLargeTablet ? width * 0.4 : isTablet ? width * 0.4 : width * 0.75;
|
const length = horizontalCardWidth + horizontalItemSpacing;
|
||||||
const margin = isTV ? 24 : isLargeTablet ? 20 : isTablet ? 20 : 16;
|
|
||||||
return {
|
return {
|
||||||
length: cardWidth + margin,
|
length,
|
||||||
offset: (cardWidth + margin) * index,
|
offset: length * index,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, TouchableOpacity, Animated, StyleSheet, Platform } from 'react-native';
|
import { View, Text, TouchableOpacity, Animated, StyleSheet, Platform, Dimensions } from 'react-native';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import Slider from '@react-native-community/slider';
|
import Slider from '@react-native-community/slider';
|
||||||
|
|
@ -82,6 +82,22 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
playerBackend,
|
playerBackend,
|
||||||
}) => {
|
}) => {
|
||||||
const { currentTheme } = useTheme();
|
const { currentTheme } = useTheme();
|
||||||
|
const deviceWidth = Dimensions.get('window').width;
|
||||||
|
const BREAKPOINTS = { phone: 0, tablet: 768, largeTablet: 1024, tv: 1440 } as const;
|
||||||
|
const getDeviceType = (w: number) => {
|
||||||
|
if (w >= BREAKPOINTS.tv) return 'tv';
|
||||||
|
if (w >= BREAKPOINTS.largeTablet) return 'largeTablet';
|
||||||
|
if (w >= BREAKPOINTS.tablet) return 'tablet';
|
||||||
|
return 'phone';
|
||||||
|
};
|
||||||
|
const deviceType = getDeviceType(deviceWidth);
|
||||||
|
const isTablet = deviceType === 'tablet';
|
||||||
|
const isLargeTablet = deviceType === 'largeTablet';
|
||||||
|
const isTV = deviceType === 'tv';
|
||||||
|
|
||||||
|
const closeIconSize = isTV ? 28 : isLargeTablet ? 26 : isTablet ? 24 : 24;
|
||||||
|
const skipIconSize = isTV ? 28 : isLargeTablet ? 26 : isTablet ? 24 : 24;
|
||||||
|
const playIconSize = isTV ? 56 : isLargeTablet ? 48 : isTablet ? 44 : 40;
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[StyleSheet.absoluteFill, { opacity: fadeAnim, zIndex: 20 }]}
|
style={[StyleSheet.absoluteFill, { opacity: fadeAnim, zIndex: 20 }]}
|
||||||
|
|
@ -141,7 +157,7 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity style={styles.closeButton} onPress={handleClose}>
|
<TouchableOpacity style={styles.closeButton} onPress={handleClose}>
|
||||||
<Ionicons name="close" size={24} color="white" />
|
<Ionicons name="close" size={closeIconSize} color="white" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
|
|
@ -149,14 +165,14 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
{/* Center Controls (Play/Pause, Skip) */}
|
{/* Center Controls (Play/Pause, Skip) */}
|
||||||
<View style={styles.controls}>
|
<View style={styles.controls}>
|
||||||
<TouchableOpacity onPress={() => skip(-10)} style={styles.skipButton}>
|
<TouchableOpacity onPress={() => skip(-10)} style={styles.skipButton}>
|
||||||
<Ionicons name="play-back" size={24} color="white" />
|
<Ionicons name="play-back" size={skipIconSize} color="white" />
|
||||||
<Text style={styles.skipText}>10</Text>
|
<Text style={styles.skipText}>10</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity onPress={togglePlayback} style={styles.playButton}>
|
<TouchableOpacity onPress={togglePlayback} style={styles.playButton}>
|
||||||
<Ionicons name={paused ? "play" : "pause"} size={40} color="white" />
|
<Ionicons name={paused ? "play" : "pause"} size={playIconSize} color="white" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity onPress={() => skip(10)} style={styles.skipButton}>
|
<TouchableOpacity onPress={() => skip(10)} style={styles.skipButton}>
|
||||||
<Ionicons name="play-forward" size={24} color="white" />
|
<Ionicons name="play-forward" size={skipIconSize} color="white" />
|
||||||
<Text style={styles.skipText}>10</Text>
|
<Text style={styles.skipText}>10</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,38 @@
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet, Dimensions } from 'react-native';
|
||||||
|
|
||||||
|
const deviceWidth = Dimensions.get('window').width;
|
||||||
|
const BREAKPOINTS = { phone: 0, tablet: 768, largeTablet: 1024, tv: 1440 } as const;
|
||||||
|
const getDeviceType = (w: number) => {
|
||||||
|
if (w >= BREAKPOINTS.tv) return 'tv';
|
||||||
|
if (w >= BREAKPOINTS.largeTablet) return 'largeTablet';
|
||||||
|
if (w >= BREAKPOINTS.tablet) return 'tablet';
|
||||||
|
return 'phone';
|
||||||
|
};
|
||||||
|
const deviceType = getDeviceType(deviceWidth);
|
||||||
|
const isTablet = deviceType === 'tablet';
|
||||||
|
const isLargeTablet = deviceType === 'largeTablet';
|
||||||
|
const isTV = deviceType === 'tv';
|
||||||
|
|
||||||
|
// Scales for larger displays
|
||||||
|
const padH = isTV ? 28 : isLargeTablet ? 24 : isTablet ? 20 : 20;
|
||||||
|
const padV = isTV ? 24 : isLargeTablet ? 20 : isTablet ? 16 : 16;
|
||||||
|
const titleFont = isTV ? 28 : isLargeTablet ? 24 : isTablet ? 22 : 18;
|
||||||
|
const episodeInfoFont = isTV ? 16 : isLargeTablet ? 15 : isTablet ? 14 : 14;
|
||||||
|
const metadataFont = isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 12;
|
||||||
|
const qualityPadH = isTV ? 10 : isLargeTablet ? 9 : isTablet ? 8 : 8;
|
||||||
|
const qualityPadV = isTV ? 4 : isLargeTablet ? 3 : isTablet ? 3 : 2;
|
||||||
|
const qualityRadius = isTV ? 6 : isLargeTablet ? 5 : isTablet ? 4 : 4;
|
||||||
|
const qualityTextFont = isTV ? 13 : isLargeTablet ? 12 : isTablet ? 11 : 11;
|
||||||
|
const controlsGap = isTV ? 56 : isLargeTablet ? 48 : isTablet ? 44 : 40;
|
||||||
|
const controlsTranslateY = isTV ? -48 : isLargeTablet ? -42 : isTablet ? -36 : -30;
|
||||||
|
const skipTextFont = isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 12;
|
||||||
|
const sliderBottom = isTV ? 80 : isLargeTablet ? 70 : isTablet ? 65 : 55;
|
||||||
|
const progressTouchHeight = isTV ? 48 : isLargeTablet ? 44 : isTablet ? 40 : 40;
|
||||||
|
const progressBarHeight = isTV ? 6 : isLargeTablet ? 5 : isTablet ? 5 : 4;
|
||||||
|
const progressThumbSize = isTV ? 24 : isLargeTablet ? 20 : isTablet ? 18 : 16;
|
||||||
|
const progressThumbTop = isTV ? -10 : isLargeTablet ? -8 : isTablet ? -7 : -6;
|
||||||
|
const durationFont = isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 12;
|
||||||
|
const bottomButtonTextFont = isTV ? 16 : isLargeTablet ? 15 : isTablet ? 14 : 12;
|
||||||
|
|
||||||
export const styles = StyleSheet.create({
|
export const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
|
@ -37,14 +71,14 @@ export const styles = StyleSheet.create({
|
||||||
padding: 0,
|
padding: 0,
|
||||||
},
|
},
|
||||||
topGradient: {
|
topGradient: {
|
||||||
paddingTop: 20,
|
paddingTop: padV,
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: padH,
|
||||||
paddingBottom: 10,
|
paddingBottom: Math.max(10, Math.round(padV * 0.6)),
|
||||||
},
|
},
|
||||||
bottomGradient: {
|
bottomGradient: {
|
||||||
paddingBottom: 20,
|
paddingBottom: padV,
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: padH,
|
||||||
paddingTop: 20,
|
paddingTop: padV,
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
@ -57,12 +91,12 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: 18,
|
fontSize: titleFont,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
episodeInfo: {
|
episodeInfo: {
|
||||||
color: 'rgba(255, 255, 255, 0.9)',
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
fontSize: 14,
|
fontSize: episodeInfoFont,
|
||||||
marginTop: 3,
|
marginTop: 3,
|
||||||
},
|
},
|
||||||
metadataRow: {
|
metadataRow: {
|
||||||
|
|
@ -73,20 +107,20 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
metadataText: {
|
metadataText: {
|
||||||
color: 'rgba(255, 255, 255, 0.7)',
|
color: 'rgba(255, 255, 255, 0.7)',
|
||||||
fontSize: 12,
|
fontSize: metadataFont,
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
},
|
},
|
||||||
qualityBadge: {
|
qualityBadge: {
|
||||||
backgroundColor: 'rgba(229, 9, 20, 0.2)',
|
backgroundColor: 'rgba(229, 9, 20, 0.2)',
|
||||||
paddingHorizontal: 8,
|
paddingHorizontal: qualityPadH,
|
||||||
paddingVertical: 2,
|
paddingVertical: qualityPadV,
|
||||||
borderRadius: 4,
|
borderRadius: qualityRadius,
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
qualityText: {
|
qualityText: {
|
||||||
color: '#E50914',
|
color: '#E50914',
|
||||||
fontSize: 11,
|
fontSize: qualityTextFont,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
providerText: {
|
providerText: {
|
||||||
|
|
@ -102,11 +136,11 @@ export const styles = StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 40,
|
gap: controlsGap,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
top: '50%',
|
top: '50%',
|
||||||
transform: [{ translateY: -30 }],
|
transform: [{ translateY: controlsTranslateY }],
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
},
|
},
|
||||||
playButton: {
|
playButton: {
|
||||||
|
|
@ -120,7 +154,7 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
skipText: {
|
skipText: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: 12,
|
fontSize: skipTextFont,
|
||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
},
|
},
|
||||||
bottomControls: {
|
bottomControls: {
|
||||||
|
|
@ -128,19 +162,19 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
sliderContainer: {
|
sliderContainer: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 55,
|
bottom: sliderBottom,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: padH,
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
},
|
},
|
||||||
progressTouchArea: {
|
progressTouchArea: {
|
||||||
height: 40, // Increased from 30 to give more space for the thumb
|
height: progressTouchHeight, // Increased touch area for larger displays
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
progressBarContainer: {
|
progressBarContainer: {
|
||||||
height: 4,
|
height: progressBarHeight,
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
|
@ -164,12 +198,12 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
progressThumb: {
|
progressThumb: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: 16,
|
width: progressThumbSize,
|
||||||
height: 16,
|
height: progressThumbSize,
|
||||||
borderRadius: 8,
|
borderRadius: progressThumbSize / 2,
|
||||||
backgroundColor: '#E50914',
|
backgroundColor: '#E50914',
|
||||||
top: -6, // Position to center on the progress bar
|
top: progressThumbTop, // Position to center on the progress bar
|
||||||
marginLeft: -8, // Center the thumb horizontally
|
marginLeft: -(progressThumbSize / 2), // Center the thumb horizontally
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 2 },
|
shadowOffset: { width: 0, height: 2 },
|
||||||
shadowOpacity: 0.3,
|
shadowOpacity: 0.3,
|
||||||
|
|
@ -187,7 +221,7 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
duration: {
|
duration: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: 12,
|
fontSize: durationFont,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
bottomButtons: {
|
bottomButtons: {
|
||||||
|
|
@ -202,7 +236,7 @@ export const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
bottomButtonText: {
|
bottomButtonText: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: 12,
|
fontSize: bottomButtonTextFont,
|
||||||
},
|
},
|
||||||
modalOverlay: {
|
modalOverlay: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue