diff --git a/src/components/metadata/SeriesContent.tsx b/src/components/metadata/SeriesContent.tsx index a35c403cf..8868b283f 100644 --- a/src/components/metadata/SeriesContent.tsx +++ b/src/components/metadata/SeriesContent.tsx @@ -2,7 +2,9 @@ import React, { useEffect, useState, useRef } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ActivityIndicator, Dimensions, useWindowDimensions, useColorScheme } from 'react-native'; import { Image } from 'expo-image'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; +import { LinearGradient } from 'expo-linear-gradient'; import { useTheme } from '../../contexts/ThemeContext'; +import { useSettings } from '../../hooks/useSettings'; import { Episode } from '../../types/metadata'; import { tmdbService } from '../../services/tmdbService'; import { storageService } from '../../services/storageService'; @@ -34,6 +36,7 @@ export const SeriesContent: React.FC = ({ metadata }) => { const { currentTheme } = useTheme(); + const { settings } = useSettings(); const { width } = useWindowDimensions(); const isTablet = width > 768; const isDarkMode = useColorScheme() === 'dark'; @@ -159,6 +162,7 @@ export const SeriesContent: React.FC = ({ = ({ ); }; - const renderEpisodeCard = (episode: Episode) => { + // Vertical layout episode card (traditional) + const renderVerticalEpisodeCard = (episode: Episode) => { let episodeImage = EPISODE_PLACEHOLDER; if (episode.still_path) { const tmdbUrl = tmdbService.getImageUrl(episode.still_path, 'w500'); @@ -217,9 +222,9 @@ export const SeriesContent: React.FC = ({ onSelectEpisode(episode)} activeOpacity={0.7} @@ -291,6 +296,127 @@ export const SeriesContent: React.FC = ({ ); }; + // Horizontal layout episode card (Netflix-style) + const renderHorizontalEpisodeCard = (episode: Episode) => { + let episodeImage = EPISODE_PLACEHOLDER; + if (episode.still_path) { + const tmdbUrl = tmdbService.getImageUrl(episode.still_path, 'w500'); + if (tmdbUrl) episodeImage = tmdbUrl; + } else if (metadata?.poster) { + episodeImage = metadata.poster; + } + + const episodeNumber = typeof episode.episode_number === 'number' ? episode.episode_number.toString() : ''; + const seasonNumber = typeof episode.season_number === 'number' ? episode.season_number.toString() : ''; + const episodeString = seasonNumber && episodeNumber ? `EPISODE ${episodeNumber}` : ''; + + const formatRuntime = (runtime: number) => { + if (!runtime) return null; + const hours = Math.floor(runtime / 60); + const minutes = runtime % 60; + if (hours > 0) { + return `${hours}h ${minutes}m`; + } + return `${minutes}m`; + }; + + // Get episode progress + const episodeId = episode.stremioId || `${metadata?.id}:${episode.season_number}:${episode.episode_number}`; + const progress = episodeProgress[episodeId]; + const progressPercent = progress ? (progress.currentTime / progress.duration) * 100 : 0; + + // Don't show progress bar if episode is complete (>= 95%) + const showProgress = progress && progressPercent < 95; + + return ( + onSelectEpisode(episode)} + activeOpacity={0.85} + > + {/* Background Image */} + + + {/* Gradient Overlay */} + + {/* Content Container */} + + {/* Episode Number Badge */} + {episodeString} + + {/* Episode Title */} + + {episode.name} + + + {/* Episode Description */} + + {episode.overview || 'No description available'} + + + {/* Metadata Row */} + + {episode.runtime && ( + + {formatRuntime(episode.runtime)} + + )} + {episode.vote_average > 0 && ( + + + + {episode.vote_average.toFixed(1)} + + + )} + + + + {/* Progress Bar */} + {showProgress && ( + + + + )} + + {/* Completed Badge */} + {progressPercent >= 95 && ( + + + + )} + + {/* More Options */} + + + + + + ); + }; + const currentSeasonEpisodes = groupedEpisodes[selectedSeason] || []; return ( @@ -308,35 +434,62 @@ export const SeriesContent: React.FC = ({ {episodes.length} {episodes.length === 1 ? 'Episode' : 'Episodes'} - - {isTablet ? ( - - {currentSeasonEpisodes.map((episode, index) => ( + {settings.episodeLayoutStyle === 'horizontal' ? ( + // Horizontal Layout (Netflix-style) + + {currentSeasonEpisodes.map((episode, index) => ( + + {renderHorizontalEpisodeCard(episode)} + + ))} + + ) : ( + // Vertical Layout (Traditional) + + {isTablet ? ( + + {currentSeasonEpisodes.map((episode, index) => ( + + {renderVerticalEpisodeCard(episode)} + + ))} + + ) : ( + currentSeasonEpisodes.map((episode, index) => ( - {renderEpisodeCard(episode)} + {renderVerticalEpisodeCard(episode)} - ))} - - ) : ( - currentSeasonEpisodes.map((episode, index) => ( - - {renderEpisodeCard(episode)} - - )) - )} - + )) + )} + + )} ); @@ -366,18 +519,20 @@ const styles = StyleSheet.create({ episodeList: { flex: 1, }, - episodeListContent: { + + // Vertical Layout Styles + episodeListContentVertical: { paddingBottom: 20, }, - episodeListContentTablet: { + episodeListContentVerticalTablet: { paddingHorizontal: 8, }, - episodeGrid: { + episodeGridVertical: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', }, - episodeCard: { + episodeCardVertical: { flexDirection: 'row', borderRadius: 16, marginBottom: 16, @@ -385,13 +540,13 @@ const styles = StyleSheet.create({ elevation: 8, shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.35, - shadowRadius: 12, + shadowOpacity: 0.25, + shadowRadius: 8, borderWidth: 1, - borderColor: 'rgba(255,255,255,0.05)', + borderColor: 'rgba(255,255,255,0.1)', height: 120, }, - episodeCardTablet: { + episodeCardVerticalTablet: { width: '48%', flexDirection: 'column', height: 120, @@ -410,12 +565,12 @@ const styles = StyleSheet.create({ position: 'absolute', bottom: 8, right: 4, - backgroundColor: 'rgba(0,0,0,0.9)', + backgroundColor: 'rgba(0,0,0,0.85)', paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1, - borderColor: 'rgba(255,255,255,0.15)', + borderColor: 'rgba(255,255,255,0.2)', zIndex: 1, }, episodeNumberText: { @@ -446,7 +601,7 @@ const styles = StyleSheet.create({ ratingContainer: { flexDirection: 'row', alignItems: 'center', - backgroundColor: 'rgba(0,0,0,0.85)', + backgroundColor: 'rgba(0,0,0,0.7)', paddingHorizontal: 4, paddingVertical: 2, borderRadius: 4, @@ -461,6 +616,19 @@ const styles = StyleSheet.create({ fontWeight: '700', marginLeft: 4, }, + runtimeContainer: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.7)', + paddingHorizontal: 4, + paddingVertical: 2, + borderRadius: 4, + }, + runtimeText: { + fontSize: 13, + fontWeight: '600', + marginLeft: 4, + }, airDateText: { fontSize: 12, opacity: 0.8, @@ -469,6 +637,161 @@ const styles = StyleSheet.create({ fontSize: 13, lineHeight: 18, }, + progressBarContainer: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + height: 3, + backgroundColor: 'rgba(0,0,0,0.5)', + }, + progressBar: { + height: '100%', + }, + completedBadge: { + position: 'absolute', + bottom: 8, + right: 8, + width: 20, + height: 20, + borderRadius: 10, + alignItems: 'center', + justifyContent: 'center', + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.3)', + }, + + // Horizontal Layout Styles + episodeListContentHorizontal: { + paddingLeft: 0, + paddingRight: 16, + }, + episodeCardWrapperHorizontal: { + width: Dimensions.get('window').width * 0.85, + marginRight: 16, + }, + episodeCardWrapperHorizontalTablet: { + width: Dimensions.get('window').width * 0.4, + }, + episodeCardHorizontal: { + borderRadius: 16, + overflow: 'hidden', + elevation: 8, + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.35, + shadowRadius: 12, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.05)', + height: 200, + position: 'relative', + width: '100%', + }, + episodeCardHorizontalTablet: { + height: 180, + }, + episodeBackgroundImage: { + width: '100%', + height: '100%', + borderRadius: 16, + }, + episodeGradient: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + borderRadius: 16, + justifyContent: 'flex-end', + }, + episodeContent: { + padding: 16, + paddingBottom: 20, + }, + episodeNumberHorizontal: { + color: 'rgba(255,255,255,0.8)', + fontSize: 11, + fontWeight: '600', + letterSpacing: 1, + textTransform: 'uppercase', + marginBottom: 4, + }, + episodeTitleHorizontal: { + color: '#fff', + fontSize: 18, + fontWeight: '700', + letterSpacing: -0.3, + marginBottom: 8, + lineHeight: 22, + }, + episodeDescriptionHorizontal: { + color: 'rgba(255,255,255,0.85)', + fontSize: 13, + lineHeight: 18, + marginBottom: 12, + opacity: 0.9, + }, + episodeMetadataRowHorizontal: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + }, + runtimeTextHorizontal: { + color: 'rgba(255,255,255,0.8)', + fontSize: 12, + fontWeight: '500', + }, + ratingContainerHorizontal: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.4)', + paddingHorizontal: 6, + paddingVertical: 3, + borderRadius: 4, + gap: 3, + }, + ratingTextHorizontal: { + color: '#FFD700', + fontSize: 12, + fontWeight: '600', + }, + progressBarContainerHorizontal: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + height: 3, + backgroundColor: 'rgba(255,255,255,0.2)', + }, + progressBarHorizontal: { + height: '100%', + borderRadius: 2, + }, + completedBadgeHorizontal: { + position: 'absolute', + bottom: 12, + right: 12, + width: 24, + height: 24, + borderRadius: 12, + alignItems: 'center', + justifyContent: 'center', + borderWidth: 2, + borderColor: '#fff', + }, + moreButton: { + position: 'absolute', + top: 12, + right: 12, + width: 32, + height: 32, + borderRadius: 16, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'rgba(0,0,0,0.3)', + }, + + // Season Selector Styles seasonSelectorWrapper: { marginBottom: 20, }, @@ -517,54 +840,4 @@ const styles = StyleSheet.create({ selectedSeasonButtonText: { fontWeight: '700', }, - progressBarContainer: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - height: 3, - backgroundColor: 'rgba(0,0,0,0.5)', - }, - progressBar: { - height: '100%', - }, - progressTextContainer: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: 'rgba(0,0,0,0.7)', - paddingHorizontal: 6, - paddingVertical: 3, - borderRadius: 4, - marginRight: 8, - }, - progressText: { - fontSize: 12, - fontWeight: '600', - marginLeft: 4, - }, - completedBadge: { - position: 'absolute', - bottom: 8, - right: 8, - width: 20, - height: 20, - borderRadius: 10, - alignItems: 'center', - justifyContent: 'center', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.3)', - }, - runtimeContainer: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: 'rgba(0,0,0,0.85)', - paddingHorizontal: 4, - paddingVertical: 2, - borderRadius: 4, - }, - runtimeText: { - fontSize: 13, - fontWeight: '600', - marginLeft: 4, - }, }); \ No newline at end of file diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index aa63dce10..bb1354683 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -35,6 +35,7 @@ export interface AppSettings { logoSourcePreference: 'metahub' | 'tmdb'; // Preferred source for title logos tmdbLanguagePreference: string; // Preferred language for TMDB logos (ISO 639-1 code) enableInternalProviders: boolean; // Toggle for internal providers like HDRezka + episodeLayoutStyle: 'vertical' | 'horizontal'; // Layout style for episode cards } export const DEFAULT_SETTINGS: AppSettings = { @@ -52,6 +53,7 @@ export const DEFAULT_SETTINGS: AppSettings = { logoSourcePreference: 'metahub', // Default to Metahub as first source tmdbLanguagePreference: 'en', // Default to English enableInternalProviders: true, // Enable internal providers by default + episodeLayoutStyle: 'horizontal', // Default to the new horizontal layout }; const SETTINGS_STORAGE_KEY = 'app_settings'; diff --git a/src/screens/AddonsScreen.tsx b/src/screens/AddonsScreen.tsx index 1761a9a5d..c9b39bb57 100644 --- a/src/screens/AddonsScreen.tsx +++ b/src/screens/AddonsScreen.tsx @@ -29,7 +29,9 @@ import { NavigationProp } from '@react-navigation/native'; import { RootStackParamList } from '../navigation/AppNavigator'; import { logger } from '../utils/logger'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { BlurView } from 'expo-blur'; +import { BlurView as ExpoBlurView } from 'expo-blur'; +import { BlurView as CommunityBlurView } from '@react-native-community/blur'; +import Constants, { ExecutionEnvironment } from 'expo-constants'; import axios from 'axios'; import { useTheme } from '../contexts/ThemeContext'; @@ -552,6 +554,36 @@ const createStyles = (colors: any) => StyleSheet.create({ flexDirection: 'row', alignItems: 'center', }, + blurOverlay: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: 'rgba(0,0,0,0.4)', + }, + androidBlurContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + androidBlur: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + androidFallbackBlur: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: 'black', + }, }); const AddonsScreen = () => { @@ -1233,7 +1265,24 @@ const AddonsScreen = () => { setAddonDetails(null); }} > - + + {Platform.OS === 'ios' ? ( + + ) : ( + Constants.executionEnvironment === ExecutionEnvironment.StoreClient ? ( + + ) : ( + + + + ) + )} {addonDetails && ( <> @@ -1332,7 +1381,7 @@ const AddonsScreen = () => { )} - + ); diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index bc7471cad..9b1dcf166 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -367,6 +367,51 @@ const SettingsScreen: React.FC = () => { icon="palette" renderControl={ChevronRight} onPress={() => navigation.navigate('ThemeSettings')} + /> + ( + + updateSetting('episodeLayoutStyle', 'vertical')} + > + Vertical + + updateSetting('episodeLayoutStyle', 'horizontal')} + > + Horizontal + + + )} isLast={true} /> diff --git a/src/screens/TMDBSettingsScreen.tsx b/src/screens/TMDBSettingsScreen.tsx index f65540552..660ecd39e 100644 --- a/src/screens/TMDBSettingsScreen.tsx +++ b/src/screens/TMDBSettingsScreen.tsx @@ -26,10 +26,10 @@ import { tmdbService } from '../services/tmdbService'; import { useSettings } from '../hooks/useSettings'; import { logger } from '../utils/logger'; import { useTheme } from '../contexts/ThemeContext'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; const TMDB_API_KEY_STORAGE_KEY = 'tmdb_api_key'; const USE_CUSTOM_TMDB_API_KEY = 'use_custom_tmdb_api_key'; -const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0; const TMDBSettingsScreen = () => { const navigation = useNavigation(); @@ -41,6 +41,7 @@ const TMDBSettingsScreen = () => { const [isInputFocused, setIsInputFocused] = useState(false); const apiKeyInputRef = useRef(null); const { currentTheme } = useTheme(); + const insets = useSafeAreaInsets(); useEffect(() => { logger.log('[TMDBSettingsScreen] Component mounted'); @@ -222,277 +223,66 @@ const TMDBSettingsScreen = () => { }); }; - const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: currentTheme.colors.darkBackground, - }, - loadingContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - loadingText: { - marginTop: 12, - fontSize: 16, - color: currentTheme.colors.white, - }, - header: { - flexDirection: 'row', - alignItems: 'center', - paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 8 : 8, - paddingHorizontal: 16, - paddingBottom: 16, - }, - backButton: { - flexDirection: 'row', - alignItems: 'center', - }, - backText: { - color: currentTheme.colors.primary, - fontSize: 16, - fontWeight: '500', - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingBottom: 40, - }, - titleContainer: { - paddingTop: 8, - }, - title: { - fontSize: 28, - fontWeight: 'bold', - color: currentTheme.colors.white, - marginHorizontal: 16, - marginBottom: 16, - }, - switchCard: { - backgroundColor: currentTheme.colors.elevation2, - borderRadius: 12, - marginHorizontal: 16, - marginBottom: 16, - padding: 16, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - switchTextContainer: { - flex: 1, - marginRight: 12, - }, - switchTitle: { - fontSize: 16, - fontWeight: '500', - color: currentTheme.colors.white, - }, - switchDescription: { - fontSize: 14, - color: currentTheme.colors.mediumEmphasis, - lineHeight: 20, - }, - statusCard: { - flexDirection: 'row', - backgroundColor: currentTheme.colors.elevation2, - borderRadius: 12, - marginHorizontal: 16, - marginBottom: 16, - padding: 16, - }, - statusIconContainer: { - marginRight: 12, - }, - statusTextContainer: { - flex: 1, - }, - statusTitle: { - fontSize: 16, - fontWeight: '500', - color: currentTheme.colors.white, - marginBottom: 4, - }, - statusDescription: { - fontSize: 14, - color: currentTheme.colors.mediumEmphasis, - }, - card: { - backgroundColor: currentTheme.colors.elevation2, - borderRadius: 12, - marginHorizontal: 16, - marginBottom: 16, - padding: 16, - }, - cardTitle: { - fontSize: 16, - fontWeight: '500', - color: currentTheme.colors.white, - marginBottom: 16, - }, - inputContainer: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 16, - }, - input: { - flex: 1, - backgroundColor: currentTheme.colors.elevation1, - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 10, - color: currentTheme.colors.white, - fontSize: 15, - borderWidth: 1, - borderColor: 'transparent', - }, - inputFocused: { - borderColor: currentTheme.colors.primary, - }, - pasteButton: { - position: 'absolute', - right: 8, - padding: 4, - }, - buttonRow: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - button: { - backgroundColor: currentTheme.colors.primary, - borderRadius: 8, - paddingVertical: 12, - paddingHorizontal: 16, - alignItems: 'center', - flex: 1, - marginRight: 8, - }, - clearButton: { - backgroundColor: 'transparent', - borderWidth: 1, - borderColor: currentTheme.colors.error, - marginRight: 0, - marginLeft: 8, - flex: 0, - }, - buttonText: { - color: currentTheme.colors.white, - fontWeight: '500', - fontSize: 15, - }, - clearButtonText: { - color: currentTheme.colors.error, - }, - resultMessage: { - borderRadius: 8, - padding: 12, - marginTop: 16, - flexDirection: 'row', - alignItems: 'center', - }, - successMessage: { - backgroundColor: currentTheme.colors.success + '1A', // 10% opacity - }, - errorMessage: { - backgroundColor: currentTheme.colors.error + '1A', // 10% opacity - }, - resultIcon: { - marginRight: 8, - }, - resultText: { - flex: 1, - }, - successText: { - color: currentTheme.colors.success, - }, - errorText: { - color: currentTheme.colors.error, - }, - helpLink: { - flexDirection: 'row', - alignItems: 'center', - marginTop: 8, - }, - helpIcon: { - marginRight: 4, - }, - helpText: { - color: currentTheme.colors.primary, - fontSize: 14, - }, - infoCard: { - backgroundColor: currentTheme.colors.elevation1, - borderRadius: 12, - marginHorizontal: 16, - marginBottom: 16, - padding: 16, - flexDirection: 'row', - alignItems: 'flex-start', - }, - infoIcon: { - marginRight: 8, - marginTop: 2, - }, - infoText: { - color: currentTheme.colors.mediumEmphasis, - fontSize: 14, - flex: 1, - lineHeight: 20, - }, - }); + const headerBaseHeight = Platform.OS === 'android' ? 80 : 60; + const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top; + const headerHeight = headerBaseHeight + topSpacing; if (isLoading) { return ( - + - Loading Settings... + Loading Settings... - + ); } return ( - + - - navigation.goBack()} - > - - Settings - + + + navigation.goBack()} + > + + Settings + + + + TMDb Settings + - TMDb Settings - + - Use Custom TMDb API Key + Use Custom TMDb API Key + + Enable to use your own TMDb API key instead of the built-in one. + Using your own API key may provide better performance and higher rate limits. + - - Enable to use your own TMDb API key instead of the built-in one. - Using your own API key may provide better performance and higher rate limits. - - {useCustomKey && ( <> - + { style={styles.statusIconContainer} /> - + {isKeySet ? "API Key Active" : "API Key Required"} - + {isKeySet ? "Your custom TMDb API key is set and active." : "Add your TMDb API key below."} @@ -511,19 +301,26 @@ const TMDBSettingsScreen = () => { - - API Key + + API Key { setApiKey(text); if (testResult) setTestResult(null); }} placeholder="Paste your TMDb API key (v3)" - placeholderTextColor={currentTheme.colors.mediumGray} + placeholderTextColor={currentTheme.colors.mediumEmphasis} autoCapitalize="none" autoCorrect={false} spellCheck={false} @@ -540,18 +337,18 @@ const TMDBSettingsScreen = () => { - Save API Key + Save API Key {isKeySet && ( - Clear + Clear )} @@ -559,7 +356,7 @@ const TMDBSettingsScreen = () => { {testResult && ( { /> {testResult.message} @@ -581,15 +378,15 @@ const TMDBSettingsScreen = () => { onPress={openTMDBWebsite} > - + How to get a TMDb API key? - + - + To get your own TMDb API key (v3), you need to create a TMDb account and request an API key from their website. Using your own API key gives you dedicated quota and may improve app performance. @@ -598,17 +395,226 @@ const TMDBSettingsScreen = () => { )} {!useCustomKey && ( - + - + Currently using the built-in TMDb API key. This key is shared among all users. For better performance and reliability, consider using your own API key. )} - + ); }; +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + loadingText: { + marginTop: 12, + fontSize: 16, + }, + headerContainer: { + paddingHorizontal: 20, + paddingBottom: 8, + backgroundColor: 'transparent', + zIndex: 2, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }, + backButton: { + flexDirection: 'row', + alignItems: 'center', + }, + backText: { + fontSize: 16, + fontWeight: '500', + marginLeft: 4, + }, + headerTitle: { + fontSize: 32, + fontWeight: '800', + letterSpacing: 0.3, + paddingLeft: 4, + }, + scrollView: { + flex: 1, + zIndex: 1, + }, + scrollContent: { + paddingHorizontal: 16, + paddingBottom: 40, + }, + switchCard: { + borderRadius: 16, + marginBottom: 16, + padding: 16, + flexDirection: 'row', + alignItems: 'flex-start', + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + switchTextContainer: { + flex: 1, + marginRight: 16, + }, + switchTitle: { + fontSize: 16, + fontWeight: '600', + marginBottom: 4, + }, + switchDescription: { + fontSize: 14, + lineHeight: 20, + opacity: 0.8, + }, + statusCard: { + flexDirection: 'row', + borderRadius: 16, + marginBottom: 16, + padding: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + statusIconContainer: { + marginRight: 12, + }, + statusTextContainer: { + flex: 1, + }, + statusTitle: { + fontSize: 16, + fontWeight: '600', + marginBottom: 4, + }, + statusDescription: { + fontSize: 14, + opacity: 0.8, + }, + card: { + borderRadius: 16, + marginBottom: 16, + padding: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + cardTitle: { + fontSize: 16, + fontWeight: '600', + marginBottom: 16, + }, + inputContainer: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 16, + }, + input: { + flex: 1, + borderRadius: 12, + paddingHorizontal: 16, + paddingVertical: 14, + fontSize: 15, + borderWidth: 2, + }, + pasteButton: { + position: 'absolute', + right: 12, + padding: 8, + }, + buttonRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + button: { + borderRadius: 12, + paddingVertical: 14, + paddingHorizontal: 20, + alignItems: 'center', + flex: 1, + marginRight: 8, + }, + clearButton: { + backgroundColor: 'transparent', + borderWidth: 2, + marginRight: 0, + marginLeft: 8, + flex: 0, + paddingHorizontal: 16, + }, + buttonText: { + fontWeight: '600', + fontSize: 15, + }, + resultMessage: { + borderRadius: 12, + padding: 16, + marginTop: 16, + flexDirection: 'row', + alignItems: 'center', + }, + resultIcon: { + marginRight: 12, + }, + resultText: { + flex: 1, + fontSize: 14, + fontWeight: '500', + }, + helpLink: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 16, + paddingVertical: 8, + }, + helpIcon: { + marginRight: 8, + }, + helpText: { + fontSize: 14, + fontWeight: '500', + }, + infoCard: { + borderRadius: 16, + marginBottom: 16, + padding: 16, + flexDirection: 'row', + alignItems: 'flex-start', + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 2, + elevation: 1, + }, + infoIcon: { + marginRight: 12, + marginTop: 2, + }, + infoText: { + fontSize: 14, + flex: 1, + lineHeight: 20, + opacity: 0.8, + }, +}); + export default TMDBSettingsScreen; \ No newline at end of file