mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-18 15:22:05 +00:00
This update introduces a new optional prop,
This commit is contained in:
parent
6f2ccfa38b
commit
9ab154f8b8
4 changed files with 54 additions and 83 deletions
|
|
@ -5,7 +5,7 @@ import { MaterialIcons } from '@expo/vector-icons';
|
|||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import Animated, { FadeIn } from 'react-native-reanimated';
|
||||
import { CatalogContent, StreamingContent } from '../../services/catalogService';
|
||||
import { colors } from '../../styles/colors';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import ContentItem from './ContentItem';
|
||||
import { RootStackParamList } from '../../navigation/AppNavigator';
|
||||
|
||||
|
|
@ -18,6 +18,7 @@ const POSTER_WIDTH = (width - 50) / 3;
|
|||
|
||||
const CatalogSection = ({ catalog }: CatalogSectionProps) => {
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { currentTheme } = useTheme();
|
||||
|
||||
const handleContentPress = (id: string, type: string) => {
|
||||
navigation.navigate('Metadata', { id, type });
|
||||
|
|
@ -43,9 +44,9 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
|
|||
>
|
||||
<View style={styles.catalogHeader}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.catalogTitle}>{catalog.name}</Text>
|
||||
<Text style={[styles.catalogTitle, { color: currentTheme.colors.highEmphasis }]}>{catalog.name}</Text>
|
||||
<LinearGradient
|
||||
colors={[colors.primary, colors.secondary]}
|
||||
colors={[currentTheme.colors.primary, currentTheme.colors.secondary]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.titleUnderline}
|
||||
|
|
@ -61,8 +62,8 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
|
|||
}
|
||||
style={styles.seeAllButton}
|
||||
>
|
||||
<Text style={styles.seeAllText}>See More</Text>
|
||||
<MaterialIcons name="arrow-forward" color={colors.primary} size={16} />
|
||||
<Text style={[styles.seeAllText, { color: currentTheme.colors.primary }]}>See More</Text>
|
||||
<MaterialIcons name="arrow-forward" color={currentTheme.colors.primary} size={16} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
|
|
@ -94,8 +95,6 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
|
|||
const styles = StyleSheet.create({
|
||||
catalogContainer: {
|
||||
marginBottom: 24,
|
||||
paddingTop: 0,
|
||||
marginTop: 16,
|
||||
},
|
||||
catalogHeader: {
|
||||
flexDirection: 'row',
|
||||
|
|
@ -110,7 +109,6 @@ const styles = StyleSheet.create({
|
|||
catalogTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '800',
|
||||
color: colors.highEmphasis,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 6,
|
||||
|
|
@ -126,21 +124,14 @@ const styles = StyleSheet.create({
|
|||
seeAllButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.elevation1,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: 16,
|
||||
gap: 4,
|
||||
},
|
||||
seeAllText: {
|
||||
color: colors.primary,
|
||||
fontSize: 13,
|
||||
fontWeight: '700',
|
||||
marginRight: 4,
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
},
|
||||
catalogList: {
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 12,
|
||||
paddingTop: 6,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { RootStackParamList } from '../../navigation/AppNavigator';
|
|||
import { StreamingContent, catalogService } from '../../services/catalogService';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { Image as ExpoImage } from 'expo-image';
|
||||
import { colors } from '../../styles/colors';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import { storageService } from '../../services/storageService';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ const POSTER_WIDTH = (width - 40) / 2.7;
|
|||
// Create a proper imperative handle with React.forwardRef and updated type
|
||||
const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, ref) => {
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { currentTheme } = useTheme();
|
||||
const [continueWatchingItems, setContinueWatchingItems] = useState<ContinueWatchingItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const appState = useRef(AppState.currentState);
|
||||
|
|
@ -213,9 +214,9 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
<View style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title}>Continue Watching</Text>
|
||||
<Text style={[styles.title, { color: currentTheme.colors.highEmphasis }]}>Continue Watching</Text>
|
||||
<LinearGradient
|
||||
colors={[colors.primary, colors.secondary]}
|
||||
colors={[currentTheme.colors.primary, currentTheme.colors.secondary]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.titleUnderline}
|
||||
|
|
@ -227,7 +228,10 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
data={continueWatchingItems}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableOpacity
|
||||
style={styles.contentItem}
|
||||
style={[styles.contentItem, {
|
||||
borderColor: currentTheme.colors.border,
|
||||
shadowColor: currentTheme.colors.black
|
||||
}]}
|
||||
activeOpacity={0.7}
|
||||
onPress={() => handleContentPress(item.id, item.type)}
|
||||
>
|
||||
|
|
@ -240,12 +244,12 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
cachePolicy="memory-disk"
|
||||
/>
|
||||
{item.type === 'series' && item.season && item.episode && (
|
||||
<View style={styles.episodeInfoContainer}>
|
||||
<Text style={styles.episodeInfo}>
|
||||
<View style={[styles.episodeInfoContainer, { backgroundColor: 'rgba(0, 0, 0, 0.7)' }]}>
|
||||
<Text style={[styles.episodeInfo, { color: currentTheme.colors.white }]}>
|
||||
S{item.season.toString().padStart(2, '0')}E{item.episode.toString().padStart(2, '0')}
|
||||
</Text>
|
||||
{item.episodeTitle && (
|
||||
<Text style={styles.episodeTitle} numberOfLines={1}>
|
||||
<Text style={[styles.episodeTitle, { color: currentTheme.colors.white, opacity: 0.9 }]} numberOfLines={1}>
|
||||
{item.episodeTitle}
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -256,7 +260,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
<View
|
||||
style={[
|
||||
styles.progressBar,
|
||||
{ width: `${item.progress}%` }
|
||||
{ width: `${item.progress}%`, backgroundColor: currentTheme.colors.primary }
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
|
|
@ -295,7 +299,6 @@ const styles = StyleSheet.create({
|
|||
title: {
|
||||
fontSize: 18,
|
||||
fontWeight: '800',
|
||||
color: colors.highEmphasis,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 6,
|
||||
|
|
@ -321,12 +324,10 @@ const styles = StyleSheet.create({
|
|||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
elevation: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.1)',
|
||||
},
|
||||
contentItemContainer: {
|
||||
width: '100%',
|
||||
|
|
@ -347,17 +348,13 @@ const styles = StyleSheet.create({
|
|||
right: 0,
|
||||
padding: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
||||
},
|
||||
episodeInfo: {
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
},
|
||||
episodeTitle: {
|
||||
fontSize: 10,
|
||||
color: colors.white,
|
||||
opacity: 0.9,
|
||||
},
|
||||
progressBarContainer: {
|
||||
position: 'absolute',
|
||||
|
|
@ -365,20 +362,10 @@ const styles = StyleSheet.create({
|
|||
left: 0,
|
||||
right: 0,
|
||||
height: 3,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
},
|
||||
progressBar: {
|
||||
height: '100%',
|
||||
backgroundColor: colors.primary,
|
||||
},
|
||||
emptyContainer: {
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
emptyText: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import { useSettings } from '../../hooks/useSettings';
|
|||
import { TMDBService } from '../../services/tmdbService';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import type { Theme } from '../../contexts/ThemeContext';
|
||||
|
||||
interface FeaturedContentProps {
|
||||
featuredContent: StreamingContent | null;
|
||||
|
|
@ -47,10 +46,18 @@ const { width, height } = Dimensions.get('window');
|
|||
|
||||
const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: FeaturedContentProps) => {
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { settings } = useSettings();
|
||||
const { currentTheme } = useTheme();
|
||||
const [logoUrl, setLogoUrl] = useState<string | null>(null);
|
||||
const [bannerUrl, setBannerUrl] = useState<string | null>(null);
|
||||
const [logoUrl, setLogoUrl] = useState<string | null>(null);
|
||||
const [logoLoaded, setLogoLoaded] = useState(false);
|
||||
const [bannerLoaded, setBannerLoaded] = useState(false);
|
||||
const [showSkeleton, setShowSkeleton] = useState(true);
|
||||
const [logoError, setLogoError] = useState(false);
|
||||
const [bannerError, setBannerError] = useState(false);
|
||||
const { settings } = useSettings();
|
||||
const logoOpacity = useSharedValue(0);
|
||||
const bannerOpacity = useSharedValue(0);
|
||||
const posterOpacity = useSharedValue(0);
|
||||
const prevContentIdRef = useRef<string | null>(null);
|
||||
// Add state for tracking logo load errors
|
||||
const [logoLoadError, setLogoLoadError] = useState(false);
|
||||
|
|
@ -58,11 +65,6 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
const logoFetchInProgress = useRef<boolean>(false);
|
||||
|
||||
// Animation values
|
||||
const posterOpacity = useSharedValue(0);
|
||||
const logoOpacity = useSharedValue(0);
|
||||
const contentOpacity = useSharedValue(1); // Start visible
|
||||
const buttonsOpacity = useSharedValue(1);
|
||||
|
||||
const posterAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: posterOpacity.value,
|
||||
}));
|
||||
|
|
@ -71,6 +73,9 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
opacity: logoOpacity.value,
|
||||
}));
|
||||
|
||||
const contentOpacity = useSharedValue(1); // Start visible
|
||||
const buttonsOpacity = useSharedValue(1);
|
||||
|
||||
const contentAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: contentOpacity.value,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { NavigationProp } from '@react-navigation/native';
|
|||
import { Image } from 'expo-image';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { colors } from '../../styles/colors';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import { stremioService } from '../../services/stremioService';
|
||||
import { tmdbService } from '../../services/tmdbService';
|
||||
import { useLibrary } from '../../hooks/useLibrary';
|
||||
|
|
@ -47,6 +47,7 @@ export const ThisWeekSection = () => {
|
|||
const { libraryItems, loading: libraryLoading } = useLibrary();
|
||||
const [episodes, setEpisodes] = useState<ThisWeekEpisode[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { currentTheme } = useTheme();
|
||||
|
||||
const fetchThisWeekEpisodes = useCallback(async () => {
|
||||
if (libraryItems.length === 0) {
|
||||
|
|
@ -172,7 +173,7 @@ export const ThisWeekSection = () => {
|
|||
if (loading) {
|
||||
return (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="small" color={colors.primary} />
|
||||
<ActivityIndicator size="small" color={currentTheme.colors.primary} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
@ -217,26 +218,27 @@ export const ThisWeekSection = () => {
|
|||
<View style={styles.badgeContainer}>
|
||||
<View style={[
|
||||
styles.badge,
|
||||
isReleased ? styles.releasedBadge : styles.upcomingBadge
|
||||
isReleased ? styles.releasedBadge : styles.upcomingBadge,
|
||||
{ backgroundColor: isReleased ? currentTheme.colors.success + 'CC' : currentTheme.colors.primary + 'CC' }
|
||||
]}>
|
||||
<MaterialIcons
|
||||
name={isReleased ? "check-circle" : "event"}
|
||||
size={12}
|
||||
color={isReleased ? "#ffffff" : "#ffffff"}
|
||||
color={currentTheme.colors.white}
|
||||
/>
|
||||
<Text style={styles.badgeText}>
|
||||
<Text style={[styles.badgeText, { color: currentTheme.colors.white }]}>
|
||||
{isReleased ? 'Released' : 'Coming Soon'}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{item.vote_average > 0 && (
|
||||
<View style={styles.ratingBadge}>
|
||||
<View style={[styles.ratingBadge, { backgroundColor: 'rgba(0,0,0,0.8)' }]}>
|
||||
<MaterialIcons
|
||||
name="star"
|
||||
size={12}
|
||||
color={colors.primary}
|
||||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
<Text style={styles.ratingText}>
|
||||
<Text style={[styles.ratingText, { color: currentTheme.colors.primary }]}>
|
||||
{item.vote_average.toFixed(1)}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
@ -244,18 +246,18 @@ export const ThisWeekSection = () => {
|
|||
</View>
|
||||
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.seriesName} numberOfLines={1}>
|
||||
<Text style={[styles.seriesName, { color: currentTheme.colors.text }]} numberOfLines={1}>
|
||||
{item.seriesName}
|
||||
</Text>
|
||||
<Text style={styles.episodeTitle} numberOfLines={2}>
|
||||
<Text style={[styles.episodeTitle, { color: currentTheme.colors.lightGray }]} numberOfLines={2}>
|
||||
S{item.season}:E{item.episode} - {item.title}
|
||||
</Text>
|
||||
{item.overview ? (
|
||||
<Text style={styles.overview} numberOfLines={2}>
|
||||
<Text style={[styles.overview, { color: currentTheme.colors.lightGray, opacity: 0.8 }]} numberOfLines={2}>
|
||||
{item.overview}
|
||||
</Text>
|
||||
) : null}
|
||||
<Text style={styles.releaseDate}>
|
||||
<Text style={[styles.releaseDate, { color: currentTheme.colors.primary }]}>
|
||||
{formattedDate}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
@ -268,10 +270,10 @@ export const ThisWeekSection = () => {
|
|||
return (
|
||||
<Animated.View entering={FadeIn.duration(300)} style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>This Week</Text>
|
||||
<Text style={[styles.title, { color: currentTheme.colors.text }]}>This Week</Text>
|
||||
<TouchableOpacity onPress={handleViewAll} style={styles.viewAllButton}>
|
||||
<Text style={styles.viewAllText}>View All</Text>
|
||||
<MaterialIcons name="chevron-right" size={18} color={colors.lightGray} />
|
||||
<Text style={[styles.viewAllText, { color: currentTheme.colors.lightGray }]}>View All</Text>
|
||||
<MaterialIcons name="chevron-right" size={18} color={currentTheme.colors.lightGray} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
|
|
@ -303,7 +305,6 @@ const styles = StyleSheet.create({
|
|||
title: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: colors.text,
|
||||
},
|
||||
viewAllButton: {
|
||||
flexDirection: 'row',
|
||||
|
|
@ -311,7 +312,6 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
viewAllText: {
|
||||
fontSize: 14,
|
||||
color: colors.lightGray,
|
||||
marginRight: 4,
|
||||
},
|
||||
listContent: {
|
||||
|
|
@ -358,14 +358,9 @@ const styles = StyleSheet.create({
|
|||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
},
|
||||
releasedBadge: {
|
||||
backgroundColor: colors.success + 'CC', // 80% opacity
|
||||
},
|
||||
upcomingBadge: {
|
||||
backgroundColor: colors.primary + 'CC', // 80% opacity
|
||||
},
|
||||
releasedBadge: {},
|
||||
upcomingBadge: {},
|
||||
badgeText: {
|
||||
color: '#ffffff',
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
marginLeft: 4,
|
||||
|
|
@ -373,13 +368,11 @@ const styles = StyleSheet.create({
|
|||
ratingBadge: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
},
|
||||
ratingText: {
|
||||
color: colors.primary,
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
marginLeft: 4,
|
||||
|
|
@ -388,24 +381,19 @@ const styles = StyleSheet.create({
|
|||
width: '100%',
|
||||
},
|
||||
seriesName: {
|
||||
color: colors.text,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 4,
|
||||
},
|
||||
episodeTitle: {
|
||||
color: colors.lightGray,
|
||||
fontSize: 14,
|
||||
marginBottom: 4,
|
||||
},
|
||||
overview: {
|
||||
color: colors.lightGray,
|
||||
fontSize: 12,
|
||||
marginBottom: 4,
|
||||
opacity: 0.8,
|
||||
},
|
||||
releaseDate: {
|
||||
color: colors.primary,
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue