mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
Enhance FeaturedContent and HeroCatalogsScreen components with improved loading animations and settings updates. Refactor image loading logic for better performance and add saved indicator in HeroCatalogsScreen. Update HomeScreen to handle settings changes dynamically and adjust layout for better user experience.
This commit is contained in:
parent
3632c44718
commit
4f04ae874f
5 changed files with 214 additions and 129 deletions
|
|
@ -8,7 +8,9 @@ import {
|
|||
Dimensions,
|
||||
ViewStyle,
|
||||
TextStyle,
|
||||
ImageStyle
|
||||
ImageStyle,
|
||||
ActivityIndicator,
|
||||
Platform
|
||||
} from 'react-native';
|
||||
import { NavigationProp, useNavigation } from '@react-navigation/native';
|
||||
import { RootStackParamList } from '../../navigation/AppNavigator';
|
||||
|
|
@ -40,17 +42,15 @@ const { width, height } = Dimensions.get('window');
|
|||
|
||||
const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: FeaturedContentProps) => {
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const [posterLoaded, setPosterLoaded] = useState(false);
|
||||
const [logoLoaded, setLogoLoaded] = useState(false);
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const [bannerUrl, setBannerUrl] = useState<string | null>(null);
|
||||
const [logoUrl, setLogoUrl] = useState<string | null>(null);
|
||||
const [bannerUrl, setBannerUrl] = useState<string | null>(null);
|
||||
const prevContentIdRef = useRef<string | null>(null);
|
||||
|
||||
// Animation values
|
||||
const posterOpacity = useSharedValue(0);
|
||||
const logoOpacity = useSharedValue(0);
|
||||
const contentOpacity = useSharedValue(0);
|
||||
const contentOpacity = useSharedValue(1); // Start visible
|
||||
const buttonsOpacity = useSharedValue(1);
|
||||
|
||||
const posterAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: posterOpacity.value,
|
||||
|
|
@ -64,11 +64,13 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
opacity: contentOpacity.value,
|
||||
}));
|
||||
|
||||
const buttonsAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: buttonsOpacity.value,
|
||||
}));
|
||||
|
||||
// Preload the image
|
||||
const preloadImage = async (url: string): Promise<boolean> => {
|
||||
if (!url) return false;
|
||||
|
||||
// If already cached, return true immediately
|
||||
if (imageCache[url]) return true;
|
||||
|
||||
try {
|
||||
|
|
@ -81,7 +83,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
}
|
||||
};
|
||||
|
||||
// Load poster first, then logo
|
||||
// Load poster and logo
|
||||
useEffect(() => {
|
||||
if (!featuredContent) return;
|
||||
|
||||
|
|
@ -91,49 +93,33 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
|
||||
// Reset states for new content
|
||||
if (contentId !== prevContentIdRef.current) {
|
||||
setPosterLoaded(false);
|
||||
setLogoLoaded(false);
|
||||
setImageError(false);
|
||||
posterOpacity.value = 0;
|
||||
logoOpacity.value = 0;
|
||||
contentOpacity.value = 0;
|
||||
}
|
||||
|
||||
prevContentIdRef.current = contentId;
|
||||
|
||||
// Sequential loading: poster first, then logo
|
||||
// Set URLs immediately for instant display
|
||||
if (posterUrl) setBannerUrl(posterUrl);
|
||||
if (titleLogo) setLogoUrl(titleLogo);
|
||||
|
||||
// Load images in background
|
||||
const loadImages = async () => {
|
||||
// Step 1: Load poster
|
||||
// Load poster
|
||||
if (posterUrl) {
|
||||
setBannerUrl(posterUrl);
|
||||
const posterSuccess = await preloadImage(posterUrl);
|
||||
|
||||
if (posterSuccess) {
|
||||
setPosterLoaded(true);
|
||||
// Fade in poster
|
||||
posterOpacity.value = withTiming(1, {
|
||||
duration: 600,
|
||||
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
|
||||
});
|
||||
|
||||
// After poster loads, start showing content with slight delay
|
||||
contentOpacity.value = withDelay(150, withTiming(1, {
|
||||
duration: 400,
|
||||
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
|
||||
}));
|
||||
} else {
|
||||
setImageError(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Load logo if available
|
||||
// Load logo if available
|
||||
if (titleLogo) {
|
||||
setLogoUrl(titleLogo);
|
||||
const logoSuccess = await preloadImage(titleLogo);
|
||||
|
||||
if (logoSuccess) {
|
||||
setLogoLoaded(true);
|
||||
// Fade in logo with delay after poster
|
||||
logoOpacity.value = withDelay(300, withTiming(1, {
|
||||
duration: 500,
|
||||
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
|
||||
|
|
@ -145,37 +131,6 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
loadImages();
|
||||
}, [featuredContent?.id]);
|
||||
|
||||
// Preload next content
|
||||
useEffect(() => {
|
||||
if (!featuredContent || !posterLoaded) return;
|
||||
|
||||
// After current poster loads, prefetch for potential next items
|
||||
const preloadNextContent = async () => {
|
||||
// Simulate preloading next item (in a real app, you'd get this from allFeaturedContent)
|
||||
if (featuredContent.type === 'movie' && featuredContent.id) {
|
||||
// Try to preload related content by ID pattern
|
||||
const relatedIds = [
|
||||
`tmdb:${parseInt(featuredContent.id.split(':')[1] || '0') + 1}`,
|
||||
`tmdb:${parseInt(featuredContent.id.split(':')[1] || '0') + 2}`
|
||||
];
|
||||
|
||||
for (const id of relatedIds) {
|
||||
// This is just a simulation - in real app you'd have actual next content URLs
|
||||
const potentialNextPoster = featuredContent.poster?.replace(
|
||||
featuredContent.id,
|
||||
id
|
||||
);
|
||||
|
||||
if (potentialNextPoster) {
|
||||
await preloadImage(potentialNextPoster);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
preloadNextContent();
|
||||
}, [posterLoaded, featuredContent]);
|
||||
|
||||
if (!featuredContent) {
|
||||
return <SkeletonFeatured />;
|
||||
}
|
||||
|
|
@ -193,10 +148,9 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
>
|
||||
<Animated.View style={[styles.imageContainer, posterAnimatedStyle]}>
|
||||
<ImageBackground
|
||||
source={{ uri: bannerUrl || '' }}
|
||||
source={{ uri: bannerUrl || featuredContent.poster }}
|
||||
style={styles.featuredImage as ViewStyle}
|
||||
resizeMode="cover"
|
||||
imageStyle={{ opacity: imageError ? 0.5 : 1 }}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={[
|
||||
|
|
@ -214,7 +168,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
{featuredContent.logo ? (
|
||||
<Animated.View style={logoAnimatedStyle}>
|
||||
<ExpoImage
|
||||
source={{ uri: logoUrl }}
|
||||
source={{ uri: logoUrl || featuredContent.logo }}
|
||||
style={styles.featuredLogo as ImageStyle}
|
||||
contentFit="contain"
|
||||
cachePolicy="memory-disk"
|
||||
|
|
@ -234,51 +188,52 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
</React.Fragment>
|
||||
))}
|
||||
</View>
|
||||
<View style={styles.featuredButtons as ViewStyle}>
|
||||
<TouchableOpacity
|
||||
style={styles.myListButton as ViewStyle}
|
||||
onPress={handleSaveToLibrary}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={isSaved ? "bookmark" : "bookmark-border"}
|
||||
size={24}
|
||||
color={colors.white}
|
||||
/>
|
||||
<Text style={styles.myListButtonText as TextStyle}>
|
||||
{isSaved ? "Saved" : "Save"}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.playButton as ViewStyle}
|
||||
onPress={() => {
|
||||
if (featuredContent) {
|
||||
navigation.navigate('Streams', {
|
||||
id: featuredContent.id,
|
||||
type: featuredContent.type
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="play-arrow" size={24} color={colors.black} />
|
||||
<Text style={styles.playButtonText as TextStyle}>Play</Text>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.infoButton as ViewStyle}
|
||||
onPress={() => {
|
||||
if (featuredContent) {
|
||||
navigation.navigate('Metadata', {
|
||||
id: featuredContent.id,
|
||||
type: featuredContent.type
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="info-outline" size={24} color={colors.white} />
|
||||
<Text style={styles.infoButtonText as TextStyle}>Info</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Animated.View style={[styles.featuredButtons as ViewStyle, buttonsAnimatedStyle]}>
|
||||
<TouchableOpacity
|
||||
style={styles.myListButton as ViewStyle}
|
||||
onPress={handleSaveToLibrary}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={isSaved ? "bookmark" : "bookmark-border"}
|
||||
size={24}
|
||||
color={colors.white}
|
||||
/>
|
||||
<Text style={styles.myListButtonText as TextStyle}>
|
||||
{isSaved ? "Saved" : "Save"}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.playButton as ViewStyle}
|
||||
onPress={() => {
|
||||
if (featuredContent) {
|
||||
navigation.navigate('Streams', {
|
||||
id: featuredContent.id,
|
||||
type: featuredContent.type
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="play-arrow" size={24} color={colors.black} />
|
||||
<Text style={styles.playButtonText as TextStyle}>Play</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.infoButton as ViewStyle}
|
||||
onPress={() => {
|
||||
if (featuredContent) {
|
||||
navigation.navigate('Metadata', {
|
||||
id: featuredContent.id,
|
||||
type: featuredContent.type
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="info-outline" size={24} color={colors.white} />
|
||||
<Text style={styles.infoButtonText as TextStyle}>Info</Text>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
</LinearGradient>
|
||||
</ImageBackground>
|
||||
|
|
@ -290,7 +245,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
const styles = StyleSheet.create({
|
||||
featuredContainer: {
|
||||
width: '100%',
|
||||
height: height * 0.6,
|
||||
height: height * 0.5,
|
||||
marginTop: 0,
|
||||
marginBottom: 8,
|
||||
position: 'relative',
|
||||
|
|
@ -330,7 +285,7 @@ const styles = StyleSheet.create({
|
|||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 20,
|
||||
paddingBottom: 12,
|
||||
},
|
||||
featuredLogo: {
|
||||
width: width * 0.7,
|
||||
|
|
@ -353,7 +308,7 @@ const styles = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 16,
|
||||
marginBottom: 8,
|
||||
flexWrap: 'wrap',
|
||||
gap: 4,
|
||||
},
|
||||
|
|
@ -376,8 +331,8 @@ const styles = StyleSheet.create({
|
|||
justifyContent: 'space-evenly',
|
||||
width: '100%',
|
||||
flex: 1,
|
||||
maxHeight: 65,
|
||||
paddingTop: 16,
|
||||
maxHeight: 60,
|
||||
paddingTop: 0,
|
||||
},
|
||||
playButton: {
|
||||
flexDirection: 'row',
|
||||
|
|
|
|||
|
|
@ -176,6 +176,19 @@ export function useFeaturedContent() {
|
|||
}
|
||||
}, [cleanup, genreMap, loadingGenres, contentSource, selectedCatalogs]);
|
||||
|
||||
// Subscribe directly to settings emitter for immediate updates
|
||||
useEffect(() => {
|
||||
const handleSettingsChange = () => {
|
||||
// Force refresh when settings change
|
||||
loadFeaturedContent(true);
|
||||
};
|
||||
|
||||
// Subscribe to settings changes
|
||||
const unsubscribe = settingsEmitter.addListener(handleSettingsChange);
|
||||
|
||||
return unsubscribe;
|
||||
}, [loadFeaturedContent]);
|
||||
|
||||
// Load featured content initially and when content source changes
|
||||
useEffect(() => {
|
||||
const shouldForceRefresh = contentSource === 'tmdb' &&
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState, useRef } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
|
|
@ -12,9 +12,10 @@ import {
|
|||
useColorScheme,
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
Animated
|
||||
} from 'react-native';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useSettings, settingsEmitter } from '../hooks/useSettings';
|
||||
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { colors } from '../styles/colors';
|
||||
import { catalogService, StreamingAddon } from '../services/catalogService';
|
||||
|
|
@ -38,6 +39,58 @@ const HeroCatalogsScreen: React.FC = () => {
|
|||
const [catalogs, setCatalogs] = useState<CatalogItem[]>([]);
|
||||
const [selectedCatalogs, setSelectedCatalogs] = useState<string[]>(settings.selectedHeroCatalogs || []);
|
||||
const { getCustomName, isLoadingCustomNames } = useCustomCatalogNames();
|
||||
const [showSavedIndicator, setShowSavedIndicator] = useState(false);
|
||||
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||
|
||||
// Ensure selected catalogs state is refreshed whenever the screen gains focus
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setSelectedCatalogs(settings.selectedHeroCatalogs || []);
|
||||
}, [settings.selectedHeroCatalogs])
|
||||
);
|
||||
|
||||
// Subscribe to settings changes
|
||||
useEffect(() => {
|
||||
const unsubscribe = settingsEmitter.addListener(() => {
|
||||
// Refresh selected catalogs when settings change
|
||||
setSelectedCatalogs(settings.selectedHeroCatalogs || []);
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [settings.selectedHeroCatalogs]);
|
||||
|
||||
// Fade in/out animation for the "Changes saved" indicator
|
||||
useEffect(() => {
|
||||
if (showSavedIndicator) {
|
||||
Animated.sequence([
|
||||
Animated.timing(fadeAnim, {
|
||||
toValue: 1,
|
||||
duration: 300,
|
||||
useNativeDriver: true
|
||||
}),
|
||||
Animated.delay(1500),
|
||||
Animated.timing(fadeAnim, {
|
||||
toValue: 0,
|
||||
duration: 300,
|
||||
useNativeDriver: true
|
||||
})
|
||||
]).start(() => setShowSavedIndicator(false));
|
||||
}
|
||||
}, [showSavedIndicator, fadeAnim]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
// First update the settings
|
||||
updateSetting('selectedHeroCatalogs', selectedCatalogs);
|
||||
|
||||
// Show the confirmation indicator
|
||||
setShowSavedIndicator(true);
|
||||
|
||||
// Short delay before navigating back to allow settings to save
|
||||
// and the user to see the confirmation message
|
||||
setTimeout(() => {
|
||||
navigation.goBack();
|
||||
}, 800);
|
||||
}, [navigation, selectedCatalogs, updateSetting]);
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
navigation.goBack();
|
||||
|
|
@ -84,11 +137,6 @@ const HeroCatalogsScreen: React.FC = () => {
|
|||
setSelectedCatalogs([]);
|
||||
}, []);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
updateSetting('selectedHeroCatalogs', selectedCatalogs);
|
||||
navigation.goBack();
|
||||
}, [navigation, selectedCatalogs, updateSetting]);
|
||||
|
||||
const toggleCatalog = useCallback((catalogId: string) => {
|
||||
setSelectedCatalogs(prev => {
|
||||
if (prev.includes(catalogId)) {
|
||||
|
|
@ -127,6 +175,21 @@ const HeroCatalogsScreen: React.FC = () => {
|
|||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Saved indicator */}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.savedIndicator,
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
backgroundColor: isDarkMode ? 'rgba(0, 180, 150, 0.9)' : 'rgba(0, 180, 150, 0.9)'
|
||||
}
|
||||
]}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<MaterialIcons name="check-circle" size={20} color="#FFFFFF" />
|
||||
<Text style={styles.savedIndicatorText}>Settings Saved</Text>
|
||||
</Animated.View>
|
||||
|
||||
{loading || isLoadingCustomNames ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
|
|
@ -153,13 +216,14 @@ const HeroCatalogsScreen: React.FC = () => {
|
|||
style={[styles.saveButton, { backgroundColor: colors.primary }]}
|
||||
onPress={handleSave}
|
||||
>
|
||||
<MaterialIcons name="save" size={16} color={colors.white} style={styles.saveIcon} />
|
||||
<Text style={styles.saveButtonText}>Save</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.infoCard}>
|
||||
<Text style={[styles.infoText, { color: isDarkMode ? colors.mediumEmphasis : colors.textMutedDark }]}>
|
||||
Select which catalogs to display in the hero section. If none are selected, all catalogs will be used.
|
||||
Select which catalogs to display in the hero section. If none are selected, all catalogs will be used. Don't forget to press Save when you're done.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
|
@ -256,6 +320,7 @@ const styles = StyleSheet.create({
|
|||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
actionButton: {
|
||||
paddingHorizontal: 12,
|
||||
|
|
@ -271,12 +336,25 @@ const styles = StyleSheet.create({
|
|||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 8,
|
||||
backgroundColor: colors.primary,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minWidth: 100,
|
||||
elevation: 2,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 1.5,
|
||||
},
|
||||
saveButtonText: {
|
||||
color: colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
},
|
||||
saveIcon: {
|
||||
marginRight: 6,
|
||||
},
|
||||
infoCard: {
|
||||
marginHorizontal: 16,
|
||||
marginBottom: 16,
|
||||
|
|
@ -320,6 +398,28 @@ const styles = StyleSheet.create({
|
|||
fontSize: 14,
|
||||
marginTop: 2,
|
||||
},
|
||||
savedIndicator: {
|
||||
position: 'absolute',
|
||||
top: Platform.OS === 'android' ? (StatusBar.currentHeight || 0) + 60 : 90,
|
||||
alignSelf: 'center',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 24,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 1000,
|
||||
elevation: 5,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
},
|
||||
savedIndicatorText: {
|
||||
color: '#FFFFFF',
|
||||
marginLeft: 6,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
|
||||
export default HeroCatalogsScreen;
|
||||
|
|
@ -387,6 +387,24 @@ const HomeScreen = () => {
|
|||
setFeaturedContentSource(settings.featuredContentSource);
|
||||
}, [settings]);
|
||||
|
||||
// Subscribe directly to settings emitter for immediate updates
|
||||
useEffect(() => {
|
||||
const handleSettingsChange = () => {
|
||||
setShowHeroSection(settings.showHeroSection);
|
||||
setFeaturedContentSource(settings.featuredContentSource);
|
||||
|
||||
// If hero section is enabled, force a refresh of featured content
|
||||
if (settings.showHeroSection) {
|
||||
refreshFeatured();
|
||||
}
|
||||
};
|
||||
|
||||
// Subscribe to settings changes
|
||||
const unsubscribe = settingsEmitter.addListener(handleSettingsChange);
|
||||
|
||||
return unsubscribe;
|
||||
}, [refreshFeatured, settings]);
|
||||
|
||||
// Update the featured content refresh logic to handle persistence
|
||||
useEffect(() => {
|
||||
if (showHeroSection && featuredContentSource !== settings.featuredContentSource) {
|
||||
|
|
@ -558,12 +576,13 @@ const HomeScreen = () => {
|
|||
}
|
||||
contentContainerStyle={[
|
||||
homeStyles.scrollContent,
|
||||
{ paddingTop: Platform.OS === 'ios' ? 0 : 0 }
|
||||
{ paddingTop: Platform.OS === 'ios' ? 39 : 90 }
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{showHeroSection && (
|
||||
<FeaturedContent
|
||||
key={`featured-${showHeroSection}`}
|
||||
featuredContent={featuredContent}
|
||||
isSaved={isSaved}
|
||||
handleSaveToLibrary={handleSaveToLibrary}
|
||||
|
|
|
|||
|
|
@ -249,8 +249,9 @@ const HomeScreenSettings: React.FC = () => {
|
|||
icon="settings-input-component"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={() => <View />}
|
||||
isLast={!settings.showHeroSection || settings.featuredContentSource !== 'catalogs'}
|
||||
/>
|
||||
{settings.featuredContentSource === 'catalogs' && (
|
||||
{settings.showHeroSection && settings.featuredContentSource === 'catalogs' && (
|
||||
<SettingItem
|
||||
title="Select Catalogs"
|
||||
description={getSelectedCatalogsText()}
|
||||
|
|
@ -261,9 +262,6 @@ const HomeScreenSettings: React.FC = () => {
|
|||
isLast={true}
|
||||
/>
|
||||
)}
|
||||
{settings.featuredContentSource !== 'catalogs' && (
|
||||
<View style={{ height: 0 }} /> // Placeholder to maintain layout
|
||||
)}
|
||||
</SettingsCard>
|
||||
|
||||
{settings.showHeroSection && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue