mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
Enhance navigation and layout consistency across the app; integrate native screens for improved performance, update header visibility logic in NuvioHeader, and implement fixed layout dimensions in AppNavigator. Refactor screens to ensure consistent status bar settings and header spacing, while optimizing content rendering in DiscoverScreen, LibraryScreen, and SettingsScreen for better user experience.
This commit is contained in:
parent
b1e1017288
commit
78583c8e80
7 changed files with 637 additions and 401 deletions
10
App.tsx
10
App.tsx
|
|
@ -14,6 +14,7 @@ import { NavigationContainer } from '@react-navigation/native';
|
|||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import { Provider as PaperProvider } from 'react-native-paper';
|
||||
import { enableScreens } from 'react-native-screens';
|
||||
import AppNavigator, {
|
||||
CustomNavigationDarkTheme,
|
||||
CustomDarkTheme
|
||||
|
|
@ -23,6 +24,9 @@ import { CatalogProvider } from './src/contexts/CatalogContext';
|
|||
import { GenreProvider } from './src/contexts/GenreContext';
|
||||
import { TraktProvider } from './src/contexts/TraktContext';
|
||||
|
||||
// This fixes many navigation layout issues by using native screen containers
|
||||
enableScreens(true);
|
||||
|
||||
function App(): React.JSX.Element {
|
||||
// Always use dark mode
|
||||
const isDarkMode = true;
|
||||
|
|
@ -33,7 +37,11 @@ function App(): React.JSX.Element {
|
|||
<CatalogProvider>
|
||||
<TraktProvider>
|
||||
<PaperProvider theme={CustomDarkTheme}>
|
||||
<NavigationContainer theme={CustomNavigationDarkTheme}>
|
||||
<NavigationContainer
|
||||
theme={CustomNavigationDarkTheme}
|
||||
// Disable automatic linking which can cause layout issues
|
||||
linking={undefined}
|
||||
>
|
||||
<View style={[styles.container, { backgroundColor: '#000000' }]}>
|
||||
<StatusBar
|
||||
style="light"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { View, TouchableOpacity, Platform, StyleSheet, Image } from 'react-native';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { colors } from '../styles/colors';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import type { RootStackParamList } from '../navigation/AppNavigator';
|
||||
import { BlurView as ExpoBlurView } from 'expo-blur';
|
||||
|
|
@ -13,6 +13,12 @@ type NavigationProp = NativeStackNavigationProp<RootStackParamList>;
|
|||
|
||||
export const NuvioHeader = () => {
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const route = useRoute();
|
||||
|
||||
// Only render the header if the current route is 'Home'
|
||||
if (route.name !== 'Home') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine if running in Expo Go
|
||||
const isExpoGo = Constants.executionEnvironment === ExecutionEnvironment.StoreClient;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
|
|||
import { NavigationContainer, DefaultTheme as NavigationDefaultTheme, DarkTheme as NavigationDarkTheme, Theme, NavigationProp } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator, NativeStackNavigationOptions, NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { createBottomTabNavigator, BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
|
||||
import { useColorScheme, Platform, Animated, StatusBar, TouchableOpacity, View, Text } from 'react-native';
|
||||
import { useColorScheme, Platform, Animated, StatusBar, TouchableOpacity, View, Text, AppState } from 'react-native';
|
||||
import { PaperProvider, MD3DarkTheme, MD3LightTheme, adaptNavigationTheme } from 'react-native-paper';
|
||||
import type { MD3Theme } from 'react-native-paper';
|
||||
import type { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
||||
|
|
@ -12,6 +12,7 @@ import { BlurView } from 'expo-blur';
|
|||
import { colors } from '../styles/colors';
|
||||
import { NuvioHeader } from '../components/NuvioHeader';
|
||||
import { Stream } from '../types/streams';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
|
||||
// Import screens with their proper types
|
||||
import HomeScreen from '../screens/HomeScreen';
|
||||
|
|
@ -320,6 +321,65 @@ const TabIcon = React.memo(({ focused, color, iconName }: {
|
|||
);
|
||||
});
|
||||
|
||||
// Update the TabScreenWrapper component with fixed layout dimensions
|
||||
const TabScreenWrapper: React.FC<{children: React.ReactNode}> = ({ children }) => {
|
||||
// Force consistent status bar settings
|
||||
useEffect(() => {
|
||||
const applyStatusBarConfig = () => {
|
||||
StatusBar.setBarStyle('light-content');
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBackgroundColor('transparent');
|
||||
};
|
||||
|
||||
applyStatusBarConfig();
|
||||
|
||||
// Apply status bar config on every focus
|
||||
const subscription = Platform.OS === 'android'
|
||||
? AppState.addEventListener('change', (state) => {
|
||||
if (state === 'active') {
|
||||
applyStatusBarConfig();
|
||||
}
|
||||
})
|
||||
: { remove: () => {} };
|
||||
|
||||
return () => {
|
||||
subscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
flex: 1,
|
||||
backgroundColor: colors.darkBackground,
|
||||
// Lock the layout to prevent shifts
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
{/* Reserve consistent space for the header area on all screens */}
|
||||
<View style={{
|
||||
height: Platform.OS === 'android' ? 80 : 60,
|
||||
width: '100%',
|
||||
backgroundColor: colors.darkBackground,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: -1
|
||||
}} />
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
// Add this component to wrap each screen in the tab navigator
|
||||
const WrappedScreen: React.FC<{Screen: React.ComponentType<any>}> = ({ Screen }) => {
|
||||
return (
|
||||
<TabScreenWrapper>
|
||||
<Screen />
|
||||
</TabScreenWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
// Tab Navigator
|
||||
const MainTabs = () => {
|
||||
// Always use dark mode
|
||||
|
|
@ -454,112 +514,138 @@ const MainTabs = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
tabBar={renderTabBar}
|
||||
screenOptions={({ route }) => ({
|
||||
tabBarIcon: ({ focused, color, size }) => {
|
||||
let iconName: IconNameType = 'home';
|
||||
|
||||
switch (route.name) {
|
||||
case 'Home':
|
||||
iconName = 'home';
|
||||
break;
|
||||
case 'Discover':
|
||||
iconName = 'compass';
|
||||
break;
|
||||
case 'Library':
|
||||
iconName = 'play-box-multiple';
|
||||
break;
|
||||
case 'Settings':
|
||||
iconName = 'cog';
|
||||
break;
|
||||
}
|
||||
|
||||
return <TabIcon focused={focused} color={color} iconName={iconName} />;
|
||||
},
|
||||
tabBarActiveTintColor: colors.primary,
|
||||
tabBarInactiveTintColor: '#FFFFFF',
|
||||
tabBarStyle: {
|
||||
position: 'absolute',
|
||||
backgroundColor: 'transparent',
|
||||
borderTopWidth: 0,
|
||||
elevation: 0,
|
||||
height: 85,
|
||||
paddingBottom: 20,
|
||||
paddingTop: 12,
|
||||
},
|
||||
tabBarLabelStyle: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
marginTop: 0,
|
||||
},
|
||||
tabBarBackground: () => (
|
||||
Platform.OS === 'ios' ? (
|
||||
<BlurView
|
||||
tint="dark"
|
||||
intensity={75}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
borderTopColor: 'rgba(255,255,255,0.2)',
|
||||
borderTopWidth: 0.5,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: -2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 3,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LinearGradient
|
||||
colors={[
|
||||
'rgba(0, 0, 0, 0)',
|
||||
'rgba(0, 0, 0, 0.65)',
|
||||
'rgba(0, 0, 0, 0.85)',
|
||||
'rgba(0, 0, 0, 0.98)',
|
||||
]}
|
||||
locations={[0, 0.2, 0.4, 0.8]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
),
|
||||
header: () => route.name === 'Home' ? <NuvioHeader /> : null,
|
||||
headerShown: route.name === 'Home',
|
||||
})}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Home"
|
||||
component={HomeScreen as any}
|
||||
options={{
|
||||
tabBarLabel: 'Home',
|
||||
}}
|
||||
<View style={{ flex: 1, backgroundColor: colors.darkBackground }}>
|
||||
{/* Common StatusBar for all tabs */}
|
||||
<StatusBar
|
||||
translucent
|
||||
barStyle="light-content"
|
||||
backgroundColor="transparent"
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Discover"
|
||||
component={DiscoverScreen as any}
|
||||
options={{
|
||||
tabBarLabel: 'Discover'
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Library"
|
||||
component={LibraryScreen as any}
|
||||
options={{
|
||||
tabBarLabel: 'Library'
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Settings"
|
||||
component={SettingsScreen as any}
|
||||
options={{
|
||||
tabBarLabel: 'Settings'
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
|
||||
<Tab.Navigator
|
||||
tabBar={renderTabBar}
|
||||
screenOptions={({ route }) => ({
|
||||
tabBarIcon: ({ focused, color, size }) => {
|
||||
let iconName: IconNameType = 'home';
|
||||
|
||||
switch (route.name) {
|
||||
case 'Home':
|
||||
iconName = 'home';
|
||||
break;
|
||||
case 'Discover':
|
||||
iconName = 'compass';
|
||||
break;
|
||||
case 'Library':
|
||||
iconName = 'play-box-multiple';
|
||||
break;
|
||||
case 'Settings':
|
||||
iconName = 'cog';
|
||||
break;
|
||||
}
|
||||
|
||||
return <TabIcon focused={focused} color={color} iconName={iconName} />;
|
||||
},
|
||||
tabBarActiveTintColor: colors.primary,
|
||||
tabBarInactiveTintColor: '#FFFFFF',
|
||||
tabBarStyle: {
|
||||
position: 'absolute',
|
||||
backgroundColor: 'transparent',
|
||||
borderTopWidth: 0,
|
||||
elevation: 0,
|
||||
height: 85,
|
||||
paddingBottom: 20,
|
||||
paddingTop: 12,
|
||||
},
|
||||
tabBarLabelStyle: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
marginTop: 0,
|
||||
},
|
||||
// Completely disable animations between tabs for better performance
|
||||
animationEnabled: false,
|
||||
// Keep all screens mounted and active
|
||||
lazy: false,
|
||||
freezeOnBlur: false,
|
||||
detachPreviousScreen: false,
|
||||
// Configure how the screen renders
|
||||
detachInactiveScreens: false,
|
||||
tabBarBackground: () => (
|
||||
Platform.OS === 'ios' ? (
|
||||
<BlurView
|
||||
tint="dark"
|
||||
intensity={75}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
borderTopColor: 'rgba(255,255,255,0.2)',
|
||||
borderTopWidth: 0.5,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: -2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 3,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LinearGradient
|
||||
colors={[
|
||||
'rgba(0, 0, 0, 0)',
|
||||
'rgba(0, 0, 0, 0.65)',
|
||||
'rgba(0, 0, 0, 0.85)',
|
||||
'rgba(0, 0, 0, 0.98)',
|
||||
]}
|
||||
locations={[0, 0.2, 0.4, 0.8]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
),
|
||||
header: () => route.name === 'Home' ? <NuvioHeader /> : null,
|
||||
headerShown: route.name === 'Home',
|
||||
// Add fixed screen styling to help with consistency
|
||||
contentStyle: {
|
||||
backgroundColor: colors.darkBackground,
|
||||
},
|
||||
})}
|
||||
// Global configuration for the tab navigator
|
||||
detachInactiveScreens={false}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Home"
|
||||
component={HomeScreen}
|
||||
options={{
|
||||
tabBarLabel: 'Home',
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Discover"
|
||||
component={DiscoverScreen}
|
||||
options={{
|
||||
tabBarLabel: 'Discover',
|
||||
headerShown: false
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Library"
|
||||
component={LibraryScreen}
|
||||
options={{
|
||||
tabBarLabel: 'Library',
|
||||
headerShown: false
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Settings"
|
||||
component={SettingsScreen}
|
||||
options={{
|
||||
tabBarLabel: 'Settings',
|
||||
headerShown: false
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -569,7 +655,7 @@ const AppNavigator = () => {
|
|||
const isDarkMode = true;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SafeAreaProvider>
|
||||
<StatusBar
|
||||
translucent
|
||||
backgroundColor="transparent"
|
||||
|
|
@ -579,7 +665,12 @@ const AppNavigator = () => {
|
|||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
animation: Platform.OS === 'android' ? 'fade_from_bottom' : 'default',
|
||||
// Disable animations for smoother transitions
|
||||
animation: 'none',
|
||||
// Ensure content is not popping in and out
|
||||
contentStyle: {
|
||||
backgroundColor: colors.darkBackground,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
|
|
@ -734,7 +825,7 @@ const AppNavigator = () => {
|
|||
/>
|
||||
</Stack.Navigator>
|
||||
</PaperProvider>
|
||||
</>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { LinearGradient } from 'expo-linear-gradient';
|
|||
import { RootStackParamList } from '../navigation/AppNavigator';
|
||||
import { logger } from '../utils/logger';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
interface Category {
|
||||
id: string;
|
||||
|
|
@ -281,10 +282,24 @@ const useStyles = () => {
|
|||
flex: 1,
|
||||
backgroundColor: colors.darkBackground,
|
||||
},
|
||||
headerBackground: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: colors.darkBackground,
|
||||
zIndex: 1,
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.darkBackground,
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 16,
|
||||
paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 16 : 16,
|
||||
justifyContent: 'flex-end',
|
||||
paddingBottom: 8,
|
||||
backgroundColor: 'transparent',
|
||||
zIndex: 2,
|
||||
},
|
||||
headerContent: {
|
||||
flexDirection: 'row',
|
||||
|
|
@ -487,6 +502,24 @@ const DiscoverScreen = () => {
|
|||
const [allContent, setAllContent] = useState<StreamingContent[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const styles = useStyles();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
// Force consistent status bar settings
|
||||
useEffect(() => {
|
||||
const applyStatusBarConfig = () => {
|
||||
StatusBar.setBarStyle('light-content');
|
||||
if (Platform.OS === 'android') {
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBackgroundColor('transparent');
|
||||
}
|
||||
};
|
||||
|
||||
applyStatusBarConfig();
|
||||
|
||||
// Re-apply on focus
|
||||
const unsubscribe = navigation.addListener('focus', applyStatusBarConfig);
|
||||
return unsubscribe;
|
||||
}, [navigation]);
|
||||
|
||||
// Load content when category or genre changes
|
||||
useEffect(() => {
|
||||
|
|
@ -580,17 +613,18 @@ const DiscoverScreen = () => {
|
|||
// Memoize list key extractor
|
||||
const catalogKeyExtractor = useCallback((item: GenreCatalog) => item.genre, []);
|
||||
|
||||
const headerBaseHeight = Platform.OS === 'android' ? 80 : 60;
|
||||
const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top;
|
||||
const headerHeight = headerBaseHeight + topSpacing;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar
|
||||
barStyle="light-content"
|
||||
backgroundColor="transparent"
|
||||
translucent
|
||||
/>
|
||||
<View style={styles.container}>
|
||||
{/* Fixed position header background to prevent shifts */}
|
||||
<View style={[styles.headerBackground, { height: headerHeight }]} />
|
||||
|
||||
<View style={{ flex: 1 }}>
|
||||
{/* Header Section */}
|
||||
<View style={styles.header}>
|
||||
{/* Header Section with proper top spacing */}
|
||||
<View style={[styles.header, { height: headerHeight, paddingTop: topSpacing }]}>
|
||||
<View style={styles.headerContent}>
|
||||
<Text style={styles.headerTitle}>Discover</Text>
|
||||
<TouchableOpacity
|
||||
|
|
@ -607,66 +641,69 @@ const DiscoverScreen = () => {
|
|||
</View>
|
||||
</View>
|
||||
|
||||
{/* Categories Section */}
|
||||
<View style={styles.categoryContainer}>
|
||||
<View style={styles.categoriesContent}>
|
||||
{CATEGORIES.map((category) => (
|
||||
<CategoryButton
|
||||
key={category.id}
|
||||
category={category}
|
||||
isSelected={selectedCategory.id === category.id}
|
||||
onPress={() => handleCategoryPress(category)}
|
||||
/>
|
||||
))}
|
||||
{/* Rest of the content */}
|
||||
<View style={styles.contentContainer}>
|
||||
{/* Categories Section */}
|
||||
<View style={styles.categoryContainer}>
|
||||
<View style={styles.categoriesContent}>
|
||||
{CATEGORIES.map((category) => (
|
||||
<CategoryButton
|
||||
key={category.id}
|
||||
category={category}
|
||||
isSelected={selectedCategory.id === category.id}
|
||||
onPress={() => handleCategoryPress(category)}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Genres Section */}
|
||||
<View style={styles.genreContainer}>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.genresScrollView}
|
||||
decelerationRate="fast"
|
||||
snapToInterval={10}
|
||||
>
|
||||
{COMMON_GENRES.map(genre => (
|
||||
<GenreButton
|
||||
key={genre}
|
||||
genre={genre}
|
||||
isSelected={selectedGenre === genre}
|
||||
onPress={() => handleGenrePress(genre)}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* Content Section */}
|
||||
{loading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
</View>
|
||||
) : catalogs.length > 0 ? (
|
||||
<FlatList
|
||||
data={catalogs}
|
||||
renderItem={renderCatalogItem}
|
||||
keyExtractor={catalogKeyExtractor}
|
||||
contentContainerStyle={styles.catalogsContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
initialNumToRender={3}
|
||||
maxToRenderPerBatch={3}
|
||||
windowSize={5}
|
||||
removeClippedSubviews={Platform.OS === 'android'}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyText}>
|
||||
No content found for {selectedGenre !== 'All' ? selectedGenre : 'these filters'}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Genres Section */}
|
||||
<View style={styles.genreContainer}>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.genresScrollView}
|
||||
decelerationRate="fast"
|
||||
snapToInterval={10}
|
||||
>
|
||||
{COMMON_GENRES.map(genre => (
|
||||
<GenreButton
|
||||
key={genre}
|
||||
genre={genre}
|
||||
isSelected={selectedGenre === genre}
|
||||
onPress={() => handleGenrePress(genre)}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* Content Section */}
|
||||
{loading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
</View>
|
||||
) : catalogs.length > 0 ? (
|
||||
<FlatList
|
||||
data={catalogs}
|
||||
renderItem={renderCatalogItem}
|
||||
keyExtractor={catalogKeyExtractor}
|
||||
contentContainerStyle={styles.catalogsContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
initialNumToRender={3}
|
||||
maxToRenderPerBatch={3}
|
||||
windowSize={5}
|
||||
removeClippedSubviews={Platform.OS === 'android'}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyText}>
|
||||
No content found for {selectedGenre !== 'All' ? selectedGenre : 'these filters'}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -417,8 +417,8 @@ const HomeScreen = () => {
|
|||
useCallback(() => {
|
||||
const statusBarConfig = () => {
|
||||
StatusBar.setBarStyle("light-content");
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBackgroundColor('transparent');
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBackgroundColor('transparent');
|
||||
};
|
||||
|
||||
statusBarConfig();
|
||||
|
|
@ -745,9 +745,9 @@ const HomeScreen = () => {
|
|||
</Animated.View>
|
||||
|
||||
{hasContinueWatching && (
|
||||
<Animated.View entering={FadeIn.duration(400).delay(250)}>
|
||||
<ContinueWatchingSection ref={continueWatchingRef} />
|
||||
</Animated.View>
|
||||
<Animated.View entering={FadeIn.duration(400).delay(250)}>
|
||||
<ContinueWatchingSection ref={continueWatchingRef} />
|
||||
</Animated.View>
|
||||
)}
|
||||
|
||||
{catalogs.length > 0 ? (
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { catalogService } from '../services/catalogService';
|
|||
import type { StreamingContent } from '../services/catalogService';
|
||||
import { RootStackParamList } from '../navigation/AppNavigator';
|
||||
import { logger } from '../utils/logger';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
// Types
|
||||
interface LibraryItem extends StreamingContent {
|
||||
|
|
@ -97,6 +98,24 @@ const LibraryScreen = () => {
|
|||
const [loading, setLoading] = useState(true);
|
||||
const [libraryItems, setLibraryItems] = useState<LibraryItem[]>([]);
|
||||
const [filter, setFilter] = useState<'all' | 'movies' | 'series'>('all');
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
// Force consistent status bar settings
|
||||
useEffect(() => {
|
||||
const applyStatusBarConfig = () => {
|
||||
StatusBar.setBarStyle('light-content');
|
||||
if (Platform.OS === 'android') {
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBackgroundColor('transparent');
|
||||
}
|
||||
};
|
||||
|
||||
applyStatusBarConfig();
|
||||
|
||||
// Re-apply on focus
|
||||
const unsubscribe = navigation.addListener('focus', applyStatusBarConfig);
|
||||
return unsubscribe;
|
||||
}, [navigation]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadLibrary = async () => {
|
||||
|
|
@ -216,64 +235,71 @@ const LibraryScreen = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const headerBaseHeight = Platform.OS === 'android' ? 80 : 60;
|
||||
const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top;
|
||||
const headerHeight = headerBaseHeight + topSpacing;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar
|
||||
barStyle="light-content"
|
||||
backgroundColor="transparent"
|
||||
translucent
|
||||
/>
|
||||
<View style={styles.container}>
|
||||
{/* Fixed position header background to prevent shifts */}
|
||||
<View style={[styles.headerBackground, { height: headerHeight }]} />
|
||||
|
||||
<View style={styles.header}>
|
||||
<View style={styles.headerContent}>
|
||||
<Text style={styles.headerTitle}>Library</Text>
|
||||
<View style={{ flex: 1 }}>
|
||||
{/* Header Section with proper top spacing */}
|
||||
<View style={[styles.header, { height: headerHeight, paddingTop: topSpacing }]}>
|
||||
<View style={styles.headerContent}>
|
||||
<Text style={styles.headerTitle}>Library</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Content Container */}
|
||||
<View style={styles.contentContainer}>
|
||||
<View style={styles.filtersContainer}>
|
||||
{renderFilter('all', 'All', 'apps')}
|
||||
{renderFilter('movies', 'Movies', 'movie')}
|
||||
{renderFilter('series', 'TV Shows', 'live-tv')}
|
||||
</View>
|
||||
|
||||
{loading ? (
|
||||
<SkeletonLoader />
|
||||
) : filteredItems.length === 0 ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<MaterialIcons
|
||||
name="video-library"
|
||||
size={80}
|
||||
color={colors.mediumGray}
|
||||
style={{ opacity: 0.7 }}
|
||||
/>
|
||||
<Text style={styles.emptyText}>Your library is empty</Text>
|
||||
<Text style={styles.emptySubtext}>
|
||||
Add content to your library to keep track of what you're watching
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.exploreButton}
|
||||
onPress={() => navigation.navigate('Discover')}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.exploreButtonText}>Explore Content</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
data={filteredItems}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item.id}
|
||||
numColumns={2}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
initialNumToRender={6}
|
||||
maxToRenderPerBatch={6}
|
||||
windowSize={5}
|
||||
removeClippedSubviews={Platform.OS === 'android'}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.filtersContainer}>
|
||||
{renderFilter('all', 'All', 'apps')}
|
||||
{renderFilter('movies', 'Movies', 'movie')}
|
||||
{renderFilter('series', 'TV Shows', 'live-tv')}
|
||||
</View>
|
||||
|
||||
{loading ? (
|
||||
<SkeletonLoader />
|
||||
) : filteredItems.length === 0 ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<MaterialIcons
|
||||
name="video-library"
|
||||
size={80}
|
||||
color={colors.mediumGray}
|
||||
style={{ opacity: 0.7 }}
|
||||
/>
|
||||
<Text style={styles.emptyText}>Your library is empty</Text>
|
||||
<Text style={styles.emptySubtext}>
|
||||
Add content to your library to keep track of what you're watching
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.exploreButton}
|
||||
onPress={() => navigation.navigate('Discover')}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.exploreButtonText}>Explore Content</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
data={filteredItems}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item.id}
|
||||
numColumns={2}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
initialNumToRender={6}
|
||||
maxToRenderPerBatch={6}
|
||||
windowSize={5}
|
||||
removeClippedSubviews={Platform.OS === 'android'}
|
||||
/>
|
||||
)}
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -282,10 +308,24 @@ const styles = StyleSheet.create({
|
|||
flex: 1,
|
||||
backgroundColor: colors.darkBackground,
|
||||
},
|
||||
headerBackground: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: colors.darkBackground,
|
||||
zIndex: 1,
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.darkBackground,
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 16,
|
||||
paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 16 : 16,
|
||||
justifyContent: 'flex-end',
|
||||
paddingBottom: 8,
|
||||
backgroundColor: 'transparent',
|
||||
zIndex: 2,
|
||||
},
|
||||
headerContent: {
|
||||
flexDirection: 'row',
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { stremioService } from '../services/stremioService';
|
|||
import { useCatalogContext } from '../contexts/CatalogContext';
|
||||
import { useTraktContext } from '../contexts/TraktContext';
|
||||
import { catalogService, DataSource } from '../services/catalogService';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
|
||||
|
|
@ -125,6 +126,7 @@ const SettingsScreen: React.FC = () => {
|
|||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { lastUpdate } = useCatalogContext();
|
||||
const { isAuthenticated, userProfile } = useTraktContext();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
// States for dynamic content
|
||||
const [addonCount, setAddonCount] = useState<number>(0);
|
||||
|
|
@ -132,6 +134,23 @@ const SettingsScreen: React.FC = () => {
|
|||
const [mdblistKeySet, setMdblistKeySet] = useState<boolean>(false);
|
||||
const [discoverDataSource, setDiscoverDataSource] = useState<DataSource>(DataSource.STREMIO_ADDONS);
|
||||
|
||||
// Force consistent status bar settings
|
||||
useEffect(() => {
|
||||
const applyStatusBarConfig = () => {
|
||||
StatusBar.setBarStyle('light-content');
|
||||
if (Platform.OS === 'android') {
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBackgroundColor('transparent');
|
||||
}
|
||||
};
|
||||
|
||||
applyStatusBarConfig();
|
||||
|
||||
// Re-apply on focus
|
||||
const unsubscribe = navigation.addListener('focus', applyStatusBarConfig);
|
||||
return unsubscribe;
|
||||
}, [navigation]);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
// Load addon count and get their catalogs
|
||||
|
|
@ -231,166 +250,182 @@ const SettingsScreen: React.FC = () => {
|
|||
await catalogService.setDataSourcePreference(dataSource);
|
||||
}, []);
|
||||
|
||||
const headerBaseHeight = Platform.OS === 'android' ? 80 : 60;
|
||||
const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top;
|
||||
const headerHeight = headerBaseHeight + topSpacing;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={[
|
||||
<View style={[
|
||||
styles.container,
|
||||
{ backgroundColor: isDarkMode ? colors.darkBackground : '#F2F2F7' }
|
||||
]}>
|
||||
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||
<View style={styles.header}>
|
||||
<Text style={[styles.headerTitle, { color: isDarkMode ? colors.highEmphasis : colors.textDark }]}>
|
||||
Settings
|
||||
</Text>
|
||||
<TouchableOpacity onPress={handleResetSettings} style={styles.resetButton}>
|
||||
<Text style={[styles.resetButtonText, {color: colors.primary}]}>Reset</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
>
|
||||
<SettingsCard isDarkMode={isDarkMode} title="User & Account">
|
||||
<SettingItem
|
||||
title="Trakt"
|
||||
description={isAuthenticated ? `Connected as ${userProfile?.username || 'User'}` : "Not Connected"}
|
||||
icon="person"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('TraktSettings')}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Features">
|
||||
<SettingItem
|
||||
title="Calendar"
|
||||
description="Manage your show calendar settings"
|
||||
icon="calendar-today"
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('Calendar')}
|
||||
isDarkMode={isDarkMode}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Notifications"
|
||||
description="Configure episode notifications and reminders"
|
||||
icon="notifications"
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('NotificationSettings')}
|
||||
isDarkMode={isDarkMode}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Content">
|
||||
<SettingItem
|
||||
title="Addons"
|
||||
description="Manage your installed addons"
|
||||
icon="extension"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('Addons')}
|
||||
badge={addonCount}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Catalogs"
|
||||
description="Configure content sources"
|
||||
icon="view-list"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('CatalogSettings')}
|
||||
badge={catalogCount}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Home Screen"
|
||||
description="Customize layout and content"
|
||||
icon="home"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('HomeScreenSettings')}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Ratings Source"
|
||||
description={mdblistKeySet ? "MDBList API Configured" : "Configure MDBList API"}
|
||||
icon="info-outline"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('MDBListSettings')}
|
||||
/>
|
||||
<SettingItem
|
||||
title="TMDB"
|
||||
description="API & Metadata Settings"
|
||||
icon="movie-filter"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('TMDBSettings')}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Playback">
|
||||
<SettingItem
|
||||
title="Video Player"
|
||||
description={Platform.OS === 'ios'
|
||||
? (settings.preferredPlayer === 'internal'
|
||||
? 'Built-in Player'
|
||||
: settings.preferredPlayer
|
||||
? settings.preferredPlayer.toUpperCase()
|
||||
: 'Built-in Player')
|
||||
: (settings.useExternalPlayer ? 'External Player' : 'Built-in Player')
|
||||
}
|
||||
icon="play-arrow"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('PlayerSettings')}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Discover">
|
||||
<SettingItem
|
||||
title="Content Source"
|
||||
description="Choose where to get content for the Discover screen"
|
||||
icon="explore"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={() => (
|
||||
<View style={styles.selectorContainer}>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.selectorButton,
|
||||
discoverDataSource === DataSource.STREMIO_ADDONS && styles.selectorButtonActive
|
||||
]}
|
||||
onPress={() => handleDiscoverDataSourceChange(DataSource.STREMIO_ADDONS)}
|
||||
>
|
||||
<Text style={[
|
||||
styles.selectorText,
|
||||
discoverDataSource === DataSource.STREMIO_ADDONS && styles.selectorTextActive
|
||||
]}>Addons</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.selectorButton,
|
||||
discoverDataSource === DataSource.TMDB && styles.selectorButtonActive
|
||||
]}
|
||||
onPress={() => handleDiscoverDataSourceChange(DataSource.TMDB)}
|
||||
>
|
||||
<Text style={[
|
||||
styles.selectorText,
|
||||
discoverDataSource === DataSource.TMDB && styles.selectorTextActive
|
||||
]}>TMDB</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<View style={styles.versionContainer}>
|
||||
<Text style={[styles.versionText, {color: isDarkMode ? colors.mediumEmphasis : colors.textMutedDark}]}>
|
||||
Version 1.0.0
|
||||
{/* Fixed position header background to prevent shifts */}
|
||||
<View style={[
|
||||
styles.headerBackground,
|
||||
{ height: headerHeight, backgroundColor: isDarkMode ? colors.darkBackground : '#F2F2F7' }
|
||||
]} />
|
||||
|
||||
<View style={{ flex: 1 }}>
|
||||
{/* Header Section with proper top spacing */}
|
||||
<View style={[styles.header, { height: headerHeight, paddingTop: topSpacing }]}>
|
||||
<Text style={[styles.headerTitle, { color: isDarkMode ? colors.highEmphasis : colors.textDark }]}>
|
||||
Settings
|
||||
</Text>
|
||||
<TouchableOpacity onPress={handleResetSettings} style={styles.resetButton}>
|
||||
<Text style={[styles.resetButtonText, {color: colors.primary}]}>Reset</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
|
||||
{/* Content Container */}
|
||||
<View style={styles.contentContainer}>
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
>
|
||||
<SettingsCard isDarkMode={isDarkMode} title="User & Account">
|
||||
<SettingItem
|
||||
title="Trakt"
|
||||
description={isAuthenticated ? `Connected as ${userProfile?.username || 'User'}` : "Not Connected"}
|
||||
icon="person"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('TraktSettings')}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Features">
|
||||
<SettingItem
|
||||
title="Calendar"
|
||||
description="Manage your show calendar settings"
|
||||
icon="calendar-today"
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('Calendar')}
|
||||
isDarkMode={isDarkMode}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Notifications"
|
||||
description="Configure episode notifications and reminders"
|
||||
icon="notifications"
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('NotificationSettings')}
|
||||
isDarkMode={isDarkMode}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Content">
|
||||
<SettingItem
|
||||
title="Addons"
|
||||
description="Manage your installed addons"
|
||||
icon="extension"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('Addons')}
|
||||
badge={addonCount}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Catalogs"
|
||||
description="Configure content sources"
|
||||
icon="view-list"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('CatalogSettings')}
|
||||
badge={catalogCount}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Home Screen"
|
||||
description="Customize layout and content"
|
||||
icon="home"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('HomeScreenSettings')}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Ratings Source"
|
||||
description={mdblistKeySet ? "MDBList API Configured" : "Configure MDBList API"}
|
||||
icon="info-outline"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('MDBListSettings')}
|
||||
/>
|
||||
<SettingItem
|
||||
title="TMDB"
|
||||
description="API & Metadata Settings"
|
||||
icon="movie-filter"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('TMDBSettings')}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Playback">
|
||||
<SettingItem
|
||||
title="Video Player"
|
||||
description={Platform.OS === 'ios'
|
||||
? (settings.preferredPlayer === 'internal'
|
||||
? 'Built-in Player'
|
||||
: settings.preferredPlayer
|
||||
? settings.preferredPlayer.toUpperCase()
|
||||
: 'Built-in Player')
|
||||
: (settings.useExternalPlayer ? 'External Player' : 'Built-in Player')
|
||||
}
|
||||
icon="play-arrow"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('PlayerSettings')}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard isDarkMode={isDarkMode} title="Discover">
|
||||
<SettingItem
|
||||
title="Content Source"
|
||||
description="Choose where to get content for the Discover screen"
|
||||
icon="explore"
|
||||
isDarkMode={isDarkMode}
|
||||
renderControl={() => (
|
||||
<View style={styles.selectorContainer}>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.selectorButton,
|
||||
discoverDataSource === DataSource.STREMIO_ADDONS && styles.selectorButtonActive
|
||||
]}
|
||||
onPress={() => handleDiscoverDataSourceChange(DataSource.STREMIO_ADDONS)}
|
||||
>
|
||||
<Text style={[
|
||||
styles.selectorText,
|
||||
discoverDataSource === DataSource.STREMIO_ADDONS && styles.selectorTextActive
|
||||
]}>Addons</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.selectorButton,
|
||||
discoverDataSource === DataSource.TMDB && styles.selectorButtonActive
|
||||
]}
|
||||
onPress={() => handleDiscoverDataSourceChange(DataSource.TMDB)}
|
||||
>
|
||||
<Text style={[
|
||||
styles.selectorText,
|
||||
discoverDataSource === DataSource.TMDB && styles.selectorTextActive
|
||||
]}>TMDB</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<View style={styles.versionContainer}>
|
||||
<Text style={[styles.versionText, {color: isDarkMode ? colors.mediumEmphasis : colors.textMutedDark}]}>
|
||||
Version 1.0.0
|
||||
</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -398,34 +433,51 @@ const styles = StyleSheet.create({
|
|||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
headerBackground: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 1,
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
zIndex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 12 : 8,
|
||||
paddingHorizontal: 20,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
alignItems: 'flex-end',
|
||||
paddingBottom: 8,
|
||||
backgroundColor: 'transparent',
|
||||
zIndex: 2,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 32,
|
||||
fontWeight: '700',
|
||||
letterSpacing: 0.5,
|
||||
fontWeight: '800',
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
resetButton: {
|
||||
paddingVertical: 6,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
resetButtonText: {
|
||||
fontSize: 15,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
width: '100%',
|
||||
paddingBottom: 32,
|
||||
},
|
||||
cardContainer: {
|
||||
width: '100%',
|
||||
marginBottom: 20,
|
||||
},
|
||||
cardTitle: {
|
||||
|
|
@ -444,6 +496,7 @@ const styles = StyleSheet.create({
|
|||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
width: undefined, // Let it fill the container width
|
||||
},
|
||||
settingItem: {
|
||||
flexDirection: 'row',
|
||||
|
|
@ -452,6 +505,7 @@ const styles = StyleSheet.create({
|
|||
paddingHorizontal: 16,
|
||||
borderBottomWidth: 0.5,
|
||||
minHeight: 58,
|
||||
width: '100%',
|
||||
},
|
||||
settingItemBorder: {
|
||||
// Border styling handled directly in the component with borderBottomWidth
|
||||
|
|
|
|||
Loading…
Reference in a new issue