NuvioStreaming/src/navigation/AppNavigator.tsx
2025-07-30 00:29:11 +05:30

1052 lines
No EOL
34 KiB
TypeScript

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, AppState, Easing } 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';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
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 { useTheme } from '../contexts/ThemeContext';
// Import screens with their proper types
import HomeScreen from '../screens/HomeScreen';
import LibraryScreen from '../screens/LibraryScreen';
import SettingsScreen from '../screens/SettingsScreen';
import MetadataScreen from '../screens/MetadataScreen';
import VideoPlayer from '../components/player/VideoPlayer';
import CatalogScreen from '../screens/CatalogScreen';
import AddonsScreen from '../screens/AddonsScreen';
import SearchScreen from '../screens/SearchScreen';
import ShowRatingsScreen from '../screens/ShowRatingsScreen';
import CatalogSettingsScreen from '../screens/CatalogSettingsScreen';
import StreamsScreen from '../screens/StreamsScreen';
import CalendarScreen from '../screens/CalendarScreen';
import NotificationSettingsScreen from '../screens/NotificationSettingsScreen';
import MDBListSettingsScreen from '../screens/MDBListSettingsScreen';
import TMDBSettingsScreen from '../screens/TMDBSettingsScreen';
import HomeScreenSettings from '../screens/HomeScreenSettings';
import HeroCatalogsScreen from '../screens/HeroCatalogsScreen';
import TraktSettingsScreen from '../screens/TraktSettingsScreen';
import PlayerSettingsScreen from '../screens/PlayerSettingsScreen';
import LogoSourceSettings from '../screens/LogoSourceSettings';
import ThemeScreen from '../screens/ThemeScreen';
import ProfilesScreen from '../screens/ProfilesScreen';
import OnboardingScreen from '../screens/OnboardingScreen';
import PluginsScreen from '../screens/PluginsScreen';
// Stack navigator types
export type RootStackParamList = {
Onboarding: undefined;
MainTabs: undefined;
Home: undefined;
Library: undefined;
Settings: undefined;
Search: undefined;
Calendar: undefined;
Metadata: {
id: string;
type: string;
episodeId?: string;
addonId?: string;
};
Streams: {
id: string;
type: string;
episodeId?: string;
episodeThumbnail?: string;
};
VideoPlayer: {
id: string;
type: string;
stream: Stream;
episodeId?: string;
backdrop?: string;
};
Player: {
uri: string;
title?: string;
season?: number;
episode?: number;
episodeTitle?: string;
quality?: string;
year?: number;
streamProvider?: string;
streamName?: string;
headers?: { [key: string]: string };
id?: string;
type?: string;
episodeId?: string;
imdbId?: string;
availableStreams?: { [providerId: string]: { streams: any[]; addonName: string } };
backdrop?: string;
};
Catalog: { id: string; type: string; addonId?: string; name?: string; genreFilter?: string };
Credits: { mediaId: string; mediaType: string };
ShowRatings: { showId: number };
Account: undefined;
Payment: undefined;
PrivacyPolicy: undefined;
About: undefined;
Addons: undefined;
CatalogSettings: undefined;
NotificationSettings: undefined;
MDBListSettings: undefined;
TMDBSettings: undefined;
HomeScreenSettings: undefined;
HeroCatalogs: undefined;
TraktSettings: undefined;
PlayerSettings: undefined;
LogoSourceSettings: undefined;
ThemeSettings: undefined;
ProfilesSettings: undefined;
ScraperSettings: undefined;
};
export type RootStackNavigationProp = NativeStackNavigationProp<RootStackParamList>;
// Tab navigator types
export type MainTabParamList = {
Home: undefined;
Library: undefined;
Search: undefined;
Settings: undefined;
};
// Custom fonts that satisfy both theme types
const fonts = {
regular: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
},
medium: {
fontFamily: 'sans-serif-medium',
fontWeight: '500' as const,
},
bold: {
fontFamily: 'sans-serif',
fontWeight: '700' as const,
},
heavy: {
fontFamily: 'sans-serif',
fontWeight: '900' as const,
},
// MD3 specific fonts
displayLarge: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 64,
fontSize: 57,
},
displayMedium: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 52,
fontSize: 45,
},
displaySmall: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 44,
fontSize: 36,
},
headlineLarge: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 40,
fontSize: 32,
},
headlineMedium: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 36,
fontSize: 28,
},
headlineSmall: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 32,
fontSize: 24,
},
titleLarge: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0,
lineHeight: 28,
fontSize: 22,
},
titleMedium: {
fontFamily: 'sans-serif-medium',
fontWeight: '500' as const,
letterSpacing: 0.15,
lineHeight: 24,
fontSize: 16,
},
titleSmall: {
fontFamily: 'sans-serif-medium',
fontWeight: '500' as const,
letterSpacing: 0.1,
lineHeight: 20,
fontSize: 14,
},
labelLarge: {
fontFamily: 'sans-serif-medium',
fontWeight: '500' as const,
letterSpacing: 0.1,
lineHeight: 20,
fontSize: 14,
},
labelMedium: {
fontFamily: 'sans-serif-medium',
fontWeight: '500' as const,
letterSpacing: 0.5,
lineHeight: 16,
fontSize: 12,
},
labelSmall: {
fontFamily: 'sans-serif-medium',
fontWeight: '500' as const,
letterSpacing: 0.5,
lineHeight: 16,
fontSize: 11,
},
bodyLarge: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0.15,
lineHeight: 24,
fontSize: 16,
},
bodyMedium: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0.25,
lineHeight: 20,
fontSize: 14,
},
bodySmall: {
fontFamily: 'sans-serif',
fontWeight: '400' as const,
letterSpacing: 0.4,
lineHeight: 16,
fontSize: 12,
},
} as const;
// Create navigators
const Stack = createNativeStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<MainTabParamList>();
// Create custom paper themes
export const CustomLightTheme: MD3Theme = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
primary: colors.primary,
},
fonts: MD3LightTheme.fonts,
};
export const CustomDarkTheme: MD3Theme = {
...MD3DarkTheme,
colors: {
...MD3DarkTheme.colors,
primary: colors.primary,
},
fonts: MD3DarkTheme.fonts,
};
// Create custom navigation theme
const { LightTheme, DarkTheme } = adaptNavigationTheme({
reactNavigationLight: NavigationDefaultTheme,
reactNavigationDark: NavigationDarkTheme,
});
// Add fonts to navigation themes
export const CustomNavigationLightTheme: Theme = {
...LightTheme,
colors: {
...LightTheme.colors,
background: colors.white,
card: colors.white,
text: colors.textDark,
border: colors.border,
},
fonts,
};
export const CustomNavigationDarkTheme: Theme = {
...DarkTheme,
colors: {
...DarkTheme.colors,
background: colors.darkBackground,
card: colors.darkBackground,
text: colors.text,
border: colors.border,
},
fonts,
};
type IconNameType = 'home' | 'home-outline' | 'compass' | 'compass-outline' |
'play-box-multiple' | 'play-box-multiple-outline' |
'puzzle' | 'puzzle-outline' |
'cog' | 'cog-outline' | 'feature-search' | 'feature-search-outline';
// Add TabIcon component
const TabIcon = React.memo(({ focused, color, iconName }: {
focused: boolean;
color: string;
iconName: IconNameType;
}) => {
const scaleAnim = useRef(new Animated.Value(1)).current;
useEffect(() => {
Animated.spring(scaleAnim, {
toValue: focused ? 1.1 : 1,
useNativeDriver: true,
friction: 8,
tension: 100
}).start();
}, [focused]);
const finalIconName = focused ? iconName : `${iconName}-outline` as IconNameType;
return (
<Animated.View style={{
alignItems: 'center',
justifyContent: 'center',
transform: [{ scale: scaleAnim }]
}}>
<MaterialCommunityIcons
name={finalIconName}
size={24}
color={color}
/>
</Animated.View>
);
});
// 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 = () => {
const { currentTheme } = useTheme();
const renderTabBar = (props: BottomTabBarProps) => {
return (
<View style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 85,
backgroundColor: 'transparent',
overflow: 'hidden',
}}>
{Platform.OS === 'ios' ? (
<BlurView
tint="dark"
intensity={75}
style={{
position: 'absolute',
height: '100%',
width: '100%',
borderTopColor: currentTheme.colors.border,
borderTopWidth: 0.5,
shadowColor: currentTheme.colors.black,
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%',
}}
/>
)}
<View
style={{
height: '100%',
paddingBottom: 20,
paddingTop: 12,
backgroundColor: 'transparent',
}}
>
<View style={{ flexDirection: 'row', paddingTop: 4 }}>
{props.state.routes.map((route, index) => {
const { options } = props.descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = props.state.index === index;
const onPress = () => {
const event = props.navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
props.navigation.navigate(route.name);
}
};
let iconName: IconNameType = 'home';
switch (route.name) {
case 'Home':
iconName = 'home';
break;
case 'Library':
iconName = 'play-box-multiple';
break;
case 'Search':
iconName = 'feature-search';
break;
case 'Settings':
iconName = 'cog';
break;
}
return (
<TouchableOpacity
key={route.key}
activeOpacity={0.7}
onPress={onPress}
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent',
}}
>
<TabIcon
focused={isFocused}
color={isFocused ? currentTheme.colors.primary : currentTheme.colors.white}
iconName={iconName}
/>
<Text
style={{
fontSize: 12,
fontWeight: '600',
marginTop: 4,
color: isFocused ? currentTheme.colors.primary : currentTheme.colors.white,
opacity: isFocused ? 1 : 0.7,
}}
>
{typeof label === 'string' ? label : ''}
</Text>
</TouchableOpacity>
);
})}
</View>
</View>
</View>
);
};
return (
<View style={{ flex: 1, backgroundColor: currentTheme.colors.darkBackground }}>
{/* Common StatusBar for all tabs */}
<StatusBar
translucent
barStyle="light-content"
backgroundColor="transparent"
/>
<Tab.Navigator
tabBar={renderTabBar}
screenOptions={({ route, navigation, theme }) => ({
transitionSpec: {
animation: 'timing',
config: {
duration: 200,
easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
},
},
sceneStyleInterpolator: ({ current }) => ({
sceneStyle: {
opacity: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0, 1, 0],
}),
transform: [
{
scale: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0.95, 1, 0.95],
}),
},
{
translateY: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [8, 0, 8],
}),
},
],
},
}),
header: () => (route.name === 'Home' ? <NuvioHeader /> : null),
headerShown: route.name === 'Home',
tabBarShowLabel: false,
tabBarStyle: {
position: 'absolute',
borderTopWidth: 0,
elevation: 0,
backgroundColor: currentTheme.colors.darkBackground,
},
detachInactiveScreens: false,
})}
>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color, size, focused }) => (
<MaterialCommunityIcons name={focused ? 'home' : 'home-outline'} size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Library"
component={LibraryScreen}
options={{
tabBarLabel: 'Library',
tabBarIcon: ({ color, size, focused }) => (
<MaterialCommunityIcons name={focused ? 'play-box-multiple' : 'play-box-multiple-outline'} size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Search"
component={SearchScreen}
options={{
tabBarLabel: 'Search',
tabBarIcon: ({ color, size, focused }) => (
<MaterialCommunityIcons name={focused ? 'feature-search' : 'feature-search-outline'} size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
tabBarLabel: 'Settings',
tabBarIcon: ({ color, size, focused }) => (
<MaterialCommunityIcons name={focused ? 'cog' : 'cog-outline'} size={size} color={color} />
),
}}
/>
</Tab.Navigator>
</View>
);
};
// Create custom fade animation interpolator for MetadataScreen
const customFadeInterpolator = ({ current, layouts }: any) => {
return {
cardStyle: {
opacity: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
transform: [
{
scale: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0.95, 1],
}),
},
],
},
overlayStyle: {
opacity: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.3],
}),
},
};
};
// Stack Navigator
const AppNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootStackParamList }) => {
const { currentTheme } = useTheme();
// Handle Android-specific optimizations
useEffect(() => {
if (Platform.OS === 'android') {
// Ensure consistent background color for Android
StatusBar.setBackgroundColor('transparent', true);
StatusBar.setTranslucent(true);
}
}, []);
return (
<SafeAreaProvider>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="light-content"
/>
<PaperProvider theme={CustomDarkTheme}>
<View style={{
flex: 1,
backgroundColor: currentTheme.colors.darkBackground,
...(Platform.OS === 'android' && {
// Prevent white flashes on Android
opacity: 1,
})
}}>
<Stack.Navigator
initialRouteName={initialRouteName || 'MainTabs'}
screenOptions={{
headerShown: false,
// Use slide_from_right for consistency and smooth transitions
animation: Platform.OS === 'android' ? 'slide_from_right' : 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 250 : 300,
// Ensure consistent background during transitions
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
// Improve Android performance with custom interpolator
...(Platform.OS === 'android' && {
cardStyleInterpolator: ({ current, layouts }: any) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
backgroundColor: currentTheme.colors.darkBackground,
},
};
},
}),
}}
>
<Stack.Screen
name="Onboarding"
component={OnboardingScreen}
options={{
headerShown: false,
animation: 'fade',
animationDuration: 300,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="MainTabs"
component={MainTabs as any}
options={{
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="Metadata"
component={MetadataScreen}
options={{
headerShown: false,
animation: Platform.OS === 'android' ? 'none' : 'fade',
animationDuration: Platform.OS === 'android' ? 0 : 300,
...(Platform.OS === 'ios' && {
cardStyleInterpolator: customFadeInterpolator,
animationTypeForReplace: 'push',
gestureEnabled: true,
gestureDirection: 'horizontal',
}),
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="Streams"
component={StreamsScreen as any}
options={{
headerShown: false,
animation: Platform.OS === 'ios' ? 'slide_from_bottom' : 'none',
animationDuration: Platform.OS === 'android' ? 0 : 300,
gestureEnabled: true,
gestureDirection: Platform.OS === 'ios' ? 'vertical' : 'horizontal',
...(Platform.OS === 'ios' && { presentation: 'modal' }),
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="Player"
component={VideoPlayer as any}
options={{
animation: 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 200 : 300,
// Force fullscreen presentation on iPad
presentation: Platform.OS === 'ios' ? 'fullScreenModal' : 'card',
// Disable gestures during video playback
gestureEnabled: false,
// Ensure proper orientation handling
orientation: 'landscape',
contentStyle: {
backgroundColor: '#000000', // Pure black for video player
},
// iPad-specific fullscreen options
...(Platform.OS === 'ios' && {
statusBarHidden: true,
statusBarAnimation: 'none',
}),
}}
/>
<Stack.Screen
name="Catalog"
component={CatalogScreen as any}
options={{
animation: 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 250 : 300,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="Addons"
component={AddonsScreen as any}
options={{
animation: 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 250 : 300,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="Search"
component={SearchScreen as any}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 350,
gestureEnabled: true,
gestureDirection: 'horizontal',
...(Platform.OS === 'android' && {
cardStyleInterpolator: ({ current, layouts }: any) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
opacity: current.progress.interpolate({
inputRange: [0, 0.3, 1],
outputRange: [0, 0.85, 1],
}),
},
};
},
}),
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="CatalogSettings"
component={CatalogSettingsScreen as any}
options={{
animation: 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 250 : 300,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="HomeScreenSettings"
component={HomeScreenSettings}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="HeroCatalogs"
component={HeroCatalogsScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="ShowRatings"
component={ShowRatingsScreen}
options={{
animation: Platform.OS === 'android' ? 'fade_from_bottom' : 'fade',
animationDuration: Platform.OS === 'android' ? 200 : 200,
...(Platform.OS === 'ios' && { presentation: 'modal' }),
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: 'transparent',
},
}}
/>
<Stack.Screen
name="Calendar"
component={CalendarScreen as any}
options={{
animation: 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 250 : 300,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="NotificationSettings"
component={NotificationSettingsScreen as any}
options={{
animation: 'slide_from_right',
animationDuration: Platform.OS === 'android' ? 250 : 300,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="MDBListSettings"
component={MDBListSettingsScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="TMDBSettings"
component={TMDBSettingsScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="TraktSettings"
component={TraktSettingsScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="PlayerSettings"
component={PlayerSettingsScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="LogoSourceSettings"
component={LogoSourceSettings}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="ThemeSettings"
component={ThemeScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="ProfilesSettings"
component={ProfilesScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
<Stack.Screen
name="ScraperSettings"
component={PluginsScreen}
options={{
animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
animationDuration: Platform.OS === 'android' ? 250 : 200,
presentation: 'card',
gestureEnabled: true,
gestureDirection: 'horizontal',
headerShown: false,
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
}}
/>
</Stack.Navigator>
</View>
</PaperProvider>
</SafeAreaProvider>
);
};
export default AppNavigator;