diff --git a/src/components/calendar/CalendarSection.tsx b/src/components/calendar/CalendarSection.tsx index 7bafa911..7050b903 100644 --- a/src/components/calendar/CalendarSection.tsx +++ b/src/components/calendar/CalendarSection.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useEffect, useCallback } from 'react'; import { View, Text, @@ -8,24 +8,14 @@ import { Dimensions } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; -import { colors } from '../../styles/colors'; -import { - format, - addMonths, - subMonths, - startOfMonth, - endOfMonth, - isSameMonth, - isSameDay, - getDay, - isToday, - parseISO -} from 'date-fns'; +import { format, addMonths, subMonths, startOfMonth, endOfMonth, eachDayOfInterval, isSameMonth, isToday, isSameDay } from 'date-fns'; import Animated, { FadeIn } from 'react-native-reanimated'; +import { useTheme } from '../../contexts/ThemeContext'; const { width } = Dimensions.get('window'); const COLUMN_COUNT = 7; // 7 days in a week -const DAY_ITEM_SIZE = width / 9; // Slightly smaller than 1/7 to fit all days +const DAY_ITEM_SIZE = (width - 32 - 56) / 7; // Slightly smaller than 1/7 to fit all days +const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; interface CalendarEpisode { id: string; @@ -54,37 +44,40 @@ const DayItem = ({ isSelected, hasEvents, onPress -}: DayItemProps) => ( - onPress(date)} - > - - {date.getDate()} - - {hasEvents && ( - - )} - -); +}: DayItemProps) => { + const { currentTheme } = useTheme(); + return ( + onPress(date)} + > + + {date.getDate()} + + {hasEvents && ( + + )} + + ); +}; export const CalendarSection: React.FC = ({ episodes = [], onSelectDate }) => { - console.log(`[CalendarSection] Rendering with ${episodes.length} episodes`); + const { currentTheme } = useTheme(); const [currentDate, setCurrentDate] = useState(new Date()); - const [selectedDate, setSelectedDate] = useState(new Date()); + const [selectedDate, setSelectedDate] = useState(null); const scrollViewRef = useRef(null); // Map of dates with episodes @@ -97,7 +90,7 @@ export const CalendarSection: React.FC = ({ episodes.forEach(episode => { if (episode.releaseDate) { - const releaseDate = parseISO(episode.releaseDate); + const releaseDate = new Date(episode.releaseDate); const dateKey = format(releaseDate, 'yyyy-MM-dd'); dateMap[dateKey] = true; } @@ -107,201 +100,194 @@ export const CalendarSection: React.FC = ({ setDatesWithEpisodes(dateMap); }, [episodes]); - const goToPreviousMonth = () => { - setCurrentDate(prevDate => subMonths(prevDate, 1)); - }; + const goToPreviousMonth = useCallback(() => { + setCurrentDate(prev => subMonths(prev, 1)); + }, []); - const goToNextMonth = () => { - setCurrentDate(prevDate => addMonths(prevDate, 1)); - }; + const goToNextMonth = useCallback(() => { + setCurrentDate(prev => addMonths(prev, 1)); + }, []); - const handleDayPress = (date: Date) => { + const handleDateSelect = useCallback((date: Date) => { setSelectedDate(date); - if (onSelectDate) { - onSelectDate(date); + onSelectDate?.(date); + }, [onSelectDate]); + + const renderDays = () => { + const start = startOfMonth(currentDate); + const end = endOfMonth(currentDate); + const days = eachDayOfInterval({ start, end }); + + // Get the day of the week for the first day (0-6) + const firstDayOfWeek = start.getDay(); + + // Add empty days at the start + const emptyDays = Array(firstDayOfWeek).fill(null); + + // Calculate remaining days to fill the last row + const totalDays = emptyDays.length + days.length; + const remainingDays = 7 - (totalDays % 7); + const endEmptyDays = remainingDays === 7 ? [] : Array(remainingDays).fill(null); + + const allDays = [...emptyDays, ...days, ...endEmptyDays]; + const weeks = []; + + for (let i = 0; i < allDays.length; i += 7) { + weeks.push(allDays.slice(i, i + 7)); } + + return weeks.map((week, weekIndex) => ( + + {week.map((day, dayIndex) => { + if (!day) { + return ; + } + + const isCurrentMonth = isSameMonth(day, currentDate); + const isCurrentDay = isToday(day); + const isSelected = selectedDate && isSameDay(day, selectedDate); + const hasEvents = datesWithEpisodes[format(day, 'yyyy-MM-dd')] || false; + + return ( + handleDateSelect(day)} + > + + {format(day, 'd')} + + {hasEvents && ( + + )} + + ); + })} + + )); }; - // Generate days for the current month view - const generateDaysForMonth = () => { - const monthStart = startOfMonth(currentDate); - const monthEnd = endOfMonth(currentDate); - const startDate = new Date(monthStart); - - // Adjust the start date to the beginning of the week - const dayOfWeek = getDay(startDate); - startDate.setDate(startDate.getDate() - dayOfWeek); - - // Ensure we have 6 complete weeks in our view - const endDate = new Date(monthEnd); - const lastDayOfWeek = getDay(endDate); - if (lastDayOfWeek < 6) { - endDate.setDate(endDate.getDate() + (6 - lastDayOfWeek)); - } - - // Get dates for a complete 6-week calendar - const totalDaysNeeded = 42; // 6 weeks × 7 days - const daysInView = []; - - let currentDateInView = new Date(startDate); - for (let i = 0; i < totalDaysNeeded; i++) { - daysInView.push(new Date(currentDateInView)); - currentDateInView.setDate(currentDateInView.getDate() + 1); - } - - return daysInView; - }; - - const dayItems = generateDaysForMonth(); - - // Break days into rows (6 rows of 7 days each) - const rows = []; - for (let i = 0; i < dayItems.length; i += COLUMN_COUNT) { - rows.push(dayItems.slice(i, i + COLUMN_COUNT)); - } - - // Get weekday names for header - const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; - return ( - - - - + + + + - + {format(currentDate, 'MMMM yyyy')} - - + + - - + + {weekDays.map((day, index) => ( - - {day} - + + {day} + ))} - - - {rows.map((row, rowIndex) => ( - - {row.map((date, cellIndex) => { - const isCurrentMonthDay = isSameMonth(date, currentDate); - const isSelectedToday = isToday(date); - const isDateSelected = isSameDay(date, selectedDate); - - // Check if this date has episodes - const dateKey = format(date, 'yyyy-MM-dd'); - const hasEvents = datesWithEpisodes[dateKey] || false; - - // Log every 7 days to avoid console spam - if (cellIndex === 0 && rowIndex === 0) { - console.log(`[CalendarSection] Sample date check - ${dateKey}: hasEvents=${hasEvents}`); - } - - return ( - - ); - })} - - ))} + + + {renderDays()} - + ); }; const styles = StyleSheet.create({ container: { - backgroundColor: colors.darkBackground, - marginBottom: 12, - borderRadius: 8, - overflow: 'hidden', - borderWidth: 1, - borderColor: colors.border, + width: '100%', }, header: { flexDirection: 'row', - alignItems: 'center', justifyContent: 'space-between', - paddingVertical: 12, - paddingHorizontal: 16, + alignItems: 'center', + padding: 16, borderBottomWidth: 1, - borderBottomColor: colors.border, }, headerButton: { padding: 8, }, - monthTitle: { + headerTitle: { fontSize: 18, fontWeight: 'bold', - color: colors.text, }, - weekHeader: { + weekDaysContainer: { flexDirection: 'row', + justifyContent: 'space-around', padding: 8, - borderBottomWidth: 1, - borderBottomColor: colors.border, - }, - weekHeaderItem: { - width: DAY_ITEM_SIZE, - alignItems: 'center', }, weekDayText: { fontSize: 12, - color: colors.lightGray, }, - calendarGrid: { + daysContainer: { padding: 8, }, - row: { + weekRow: { flexDirection: 'row', justifyContent: 'space-around', marginBottom: 8, }, - dayItem: { - width: DAY_ITEM_SIZE, - height: DAY_ITEM_SIZE, + dayButton: { + width: 36, + height: 36, justifyContent: 'center', alignItems: 'center', - borderRadius: DAY_ITEM_SIZE / 2, + borderRadius: 18, + borderWidth: 1, + borderColor: 'transparent', }, dayText: { fontSize: 14, - color: colors.text, }, - otherMonthDay: { - color: colors.lightGray + '80', // 50% opacity + emptyDay: { + width: 36, + height: 36, + }, + eventDot: { + width: 4, + height: 4, + borderRadius: 2, + position: 'absolute', + bottom: 6, }, todayItem: { - backgroundColor: colors.primary + '30', // 30% opacity borderWidth: 1, - borderColor: colors.primary, }, selectedItem: { - backgroundColor: colors.primary + '60', // 60% opacity borderWidth: 1, - borderColor: colors.primary, }, todayText: { fontWeight: 'bold', - color: colors.primary, }, selectedDayText: { fontWeight: 'bold', - color: colors.text, }, dayWithEvents: { position: 'relative', @@ -312,6 +298,5 @@ const styles = StyleSheet.create({ width: 4, height: 4, borderRadius: 2, - backgroundColor: colors.primary, }, }); \ No newline at end of file diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx index 55a3050a..dd7b0c25 100644 --- a/src/screens/CalendarScreen.tsx +++ b/src/screens/CalendarScreen.tsx @@ -9,7 +9,6 @@ import { RefreshControl, SafeAreaView, StatusBar, - useColorScheme, Dimensions, SectionList } from 'react-native'; @@ -18,7 +17,7 @@ import { NavigationProp } from '@react-navigation/native'; import { Image } from 'expo-image'; import { MaterialIcons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; -import { colors } from '../styles/colors'; +import { useTheme } from '../contexts/ThemeContext'; import { RootStackParamList } from '../navigation/AppNavigator'; import { stremioService } from '../services/stremioService'; import { useLibrary } from '../hooks/useLibrary'; @@ -53,6 +52,7 @@ interface CalendarSection { const CalendarScreen = () => { const navigation = useNavigation>(); const { libraryItems, loading: libraryLoading } = useLibrary(); + const { currentTheme } = useTheme(); logger.log(`[Calendar] Initial load - Library has ${libraryItems?.length || 0} items, loading: ${libraryLoading}`); const [calendarData, setCalendarData] = useState([]); const [loading, setLoading] = useState(true); @@ -270,7 +270,7 @@ const CalendarScreen = () => { return ( handleEpisodePress(item)} activeOpacity={0.7} > @@ -287,18 +287,18 @@ const CalendarScreen = () => { - + {item.seriesName} {hasReleaseDate ? ( <> - + S{item.season}:E{item.episode} - {item.title} {item.overview ? ( - + {item.overview} ) : null} @@ -308,9 +308,9 @@ const CalendarScreen = () => { - {formattedDate} + {formattedDate} {item.vote_average > 0 && ( @@ -318,9 +318,9 @@ const CalendarScreen = () => { - + {item.vote_average.toFixed(1)} @@ -329,16 +329,16 @@ const CalendarScreen = () => { ) : ( <> - + No scheduled episodes - Check back later + Check back later )} @@ -349,8 +349,13 @@ const CalendarScreen = () => { }; const renderSectionHeader = ({ section }: { section: CalendarSection }) => ( - - {section.title} + + + {section.title} + ); @@ -386,22 +391,22 @@ const CalendarScreen = () => { if (libraryItems.length === 0 && !libraryLoading) { return ( - + - + navigation.goBack()} > - + - Calendar + Calendar - + Your library is empty @@ -423,10 +428,10 @@ const CalendarScreen = () => { if (loading && !refreshing) { return ( - + - + Loading calendar... @@ -434,27 +439,27 @@ const CalendarScreen = () => { } return ( - + - + navigation.goBack()} > - + - Calendar + Calendar {selectedDate && filteredEpisodes.length > 0 && ( - - + + Showing episodes for {format(selectedDate, 'MMMM d, yyyy')} - + )} @@ -474,22 +479,22 @@ const CalendarScreen = () => { } /> ) : selectedDate && filteredEpisodes.length === 0 ? ( - - + + No episodes for {format(selectedDate, 'MMMM d, yyyy')} - + Show All Episodes @@ -505,18 +510,18 @@ const CalendarScreen = () => { } /> ) : ( - - + + No upcoming episodes found - + Add series to your library to see their upcoming episodes here @@ -528,7 +533,6 @@ const CalendarScreen = () => { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: colors.darkBackground, }, listContent: { paddingBottom: 20, @@ -539,19 +543,15 @@ const styles = StyleSheet.create({ alignItems: 'center', }, loadingText: { - color: colors.text, marginTop: 10, fontSize: 16, }, sectionHeader: { - backgroundColor: colors.darkBackground, paddingVertical: 8, paddingHorizontal: 16, borderBottomWidth: 1, - borderBottomColor: colors.border, }, sectionTitle: { - color: colors.text, fontSize: 18, fontWeight: 'bold', }, @@ -559,7 +559,6 @@ const styles = StyleSheet.create({ flexDirection: 'row', padding: 12, borderBottomWidth: 1, - borderBottomColor: colors.border + '20', }, poster: { width: 120, @@ -572,18 +571,15 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', }, seriesName: { - color: colors.text, fontSize: 16, fontWeight: 'bold', marginBottom: 4, }, episodeTitle: { - color: colors.lightGray, fontSize: 14, lineHeight: 20, }, overview: { - color: colors.lightGray, fontSize: 12, marginTop: 4, lineHeight: 16, @@ -599,7 +595,6 @@ const styles = StyleSheet.create({ alignItems: 'center', }, date: { - color: colors.lightGray, fontSize: 14, marginLeft: 4, }, @@ -608,7 +603,6 @@ const styles = StyleSheet.create({ alignItems: 'center', }, rating: { - color: colors.primary, fontSize: 14, marginLeft: 4, fontWeight: 'bold', @@ -620,14 +614,12 @@ const styles = StyleSheet.create({ padding: 20, }, emptyText: { - color: colors.text, fontSize: 18, fontWeight: 'bold', marginTop: 16, textAlign: 'center', }, emptySubtext: { - color: colors.lightGray, fontSize: 14, marginTop: 8, textAlign: 'center', @@ -638,10 +630,8 @@ const styles = StyleSheet.create({ alignItems: 'center', padding: 12, borderBottomWidth: 1, - borderBottomColor: colors.border, }, filterInfoText: { - color: colors.text, fontSize: 16, fontWeight: 'bold', }, @@ -655,7 +645,6 @@ const styles = StyleSheet.create({ padding: 20, }, emptyFilterText: { - color: colors.text, fontSize: 18, fontWeight: 'bold', marginTop: 16, @@ -664,11 +653,9 @@ const styles = StyleSheet.create({ clearFilterButtonLarge: { marginTop: 20, padding: 16, - backgroundColor: colors.primary, borderRadius: 8, }, clearFilterButtonText: { - color: colors.text, fontSize: 16, fontWeight: 'bold', }, @@ -681,7 +668,6 @@ const styles = StyleSheet.create({ padding: 8, }, headerTitle: { - color: colors.text, fontSize: 18, fontWeight: 'bold', marginLeft: 12, @@ -694,16 +680,13 @@ const styles = StyleSheet.create({ }, discoverButton: { padding: 16, - backgroundColor: colors.primary, borderRadius: 8, }, discoverButtonText: { - color: colors.text, fontSize: 16, fontWeight: 'bold', }, noEpisodesText: { - color: colors.text, fontSize: 14, marginBottom: 4, }, diff --git a/src/screens/NotificationSettingsScreen.tsx b/src/screens/NotificationSettingsScreen.tsx index 68eb422a..360dfb4b 100644 --- a/src/screens/NotificationSettingsScreen.tsx +++ b/src/screens/NotificationSettingsScreen.tsx @@ -11,7 +11,7 @@ import { StatusBar, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; -import { colors } from '../styles/colors'; +import { useTheme } from '../contexts/ThemeContext'; import { notificationService, NotificationSettings } from '../services/notificationService'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; import { useNavigation } from '@react-navigation/native'; @@ -19,6 +19,7 @@ import { logger } from '../utils/logger'; const NotificationSettingsScreen = () => { const navigation = useNavigation(); + const { currentTheme } = useTheme(); const [settings, setSettings] = useState({ enabled: true, newEpisodeNotifications: true, @@ -155,36 +156,36 @@ const NotificationSettingsScreen = () => { if (loading) { return ( - - + + navigation.goBack()} > - + - Notification Settings + Notification Settings - Loading settings... + Loading settings... ); } return ( - + - + navigation.goBack()} > - + - Notification Settings + Notification Settings @@ -193,72 +194,72 @@ const NotificationSettingsScreen = () => { entering={FadeIn.duration(300)} exiting={FadeOut.duration(200)} > - - General + + General - + - - Enable Notifications + + Enable Notifications updateSetting('enabled', value)} - trackColor={{ false: colors.border, true: colors.primary + '80' }} - thumbColor={settings.enabled ? colors.primary : colors.lightGray} + trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }} + thumbColor={settings.enabled ? currentTheme.colors.primary : currentTheme.colors.lightGray} /> {settings.enabled && ( <> - - Notification Types + + Notification Types - + - - New Episodes + + New Episodes updateSetting('newEpisodeNotifications', value)} - trackColor={{ false: colors.border, true: colors.primary + '80' }} - thumbColor={settings.newEpisodeNotifications ? colors.primary : colors.lightGray} + trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }} + thumbColor={settings.newEpisodeNotifications ? currentTheme.colors.primary : currentTheme.colors.lightGray} /> - + - - Upcoming Shows + + Upcoming Shows updateSetting('upcomingShowsNotifications', value)} - trackColor={{ false: colors.border, true: colors.primary + '80' }} - thumbColor={settings.upcomingShowsNotifications ? colors.primary : colors.lightGray} + trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }} + thumbColor={settings.upcomingShowsNotifications ? currentTheme.colors.primary : currentTheme.colors.lightGray} /> - + - - Reminders + + Reminders updateSetting('reminderNotifications', value)} - trackColor={{ false: colors.border, true: colors.primary + '80' }} - thumbColor={settings.reminderNotifications ? colors.primary : colors.lightGray} + trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }} + thumbColor={settings.reminderNotifications ? currentTheme.colors.primary : currentTheme.colors.lightGray} /> - - Notification Timing + + Notification Timing - + When should you be notified before an episode airs? @@ -268,13 +269,24 @@ const NotificationSettingsScreen = () => { key={hours} style={[ styles.timingOption, - settings.timeBeforeAiring === hours && styles.selectedTimingOption + { + backgroundColor: currentTheme.colors.elevation1, + borderColor: currentTheme.colors.border + }, + settings.timeBeforeAiring === hours && { + backgroundColor: currentTheme.colors.primary + '30', + borderColor: currentTheme.colors.primary, + } ]} onPress={() => setTimeBeforeAiring(hours)} > {hours === 1 ? '1 hour' : `${hours} hours`} @@ -283,27 +295,37 @@ const NotificationSettingsScreen = () => { - - Advanced + + Advanced - - Reset All Notifications + + Reset All Notifications - - + + {countdown !== null ? `Notification in ${countdown}s...` : 'Test Notification (1min)'} @@ -315,16 +337,16 @@ const NotificationSettingsScreen = () => { - + Notification will appear in {countdown} seconds )} - + This will cancel all scheduled notifications. You'll need to re-enable them manually. @@ -339,7 +361,6 @@ const NotificationSettingsScreen = () => { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: colors.darkBackground, }, header: { flexDirection: 'row', @@ -348,7 +369,6 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, - borderBottomColor: colors.border, }, backButton: { padding: 8, @@ -356,7 +376,6 @@ const styles = StyleSheet.create({ headerTitle: { fontSize: 18, fontWeight: 'bold', - color: colors.text, }, content: { flex: 1, @@ -367,18 +386,15 @@ const styles = StyleSheet.create({ alignItems: 'center', }, loadingText: { - color: colors.text, fontSize: 16, }, section: { padding: 16, borderBottomWidth: 1, - borderBottomColor: colors.border, }, sectionTitle: { fontSize: 16, fontWeight: 'bold', - color: colors.text, marginBottom: 16, }, settingItem: { @@ -387,7 +403,6 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1, - borderBottomColor: colors.border + '50', }, settingInfo: { flexDirection: 'row', @@ -395,12 +410,10 @@ const styles = StyleSheet.create({ }, settingText: { fontSize: 16, - color: colors.text, marginLeft: 12, }, settingDescription: { fontSize: 14, - color: colors.lightGray, marginBottom: 16, }, timingOptions: { @@ -410,47 +423,32 @@ const styles = StyleSheet.create({ marginTop: 8, }, timingOption: { - backgroundColor: colors.elevation1, paddingVertical: 10, paddingHorizontal: 16, borderRadius: 8, borderWidth: 1, - borderColor: colors.border, marginBottom: 8, width: '48%', alignItems: 'center', }, - selectedTimingOption: { - backgroundColor: colors.primary + '30', - borderColor: colors.primary, - }, timingText: { - color: colors.text, fontSize: 14, }, - selectedTimingText: { - color: colors.primary, - fontWeight: 'bold', - }, resetButton: { flexDirection: 'row', alignItems: 'center', padding: 12, - backgroundColor: colors.error + '20', borderRadius: 8, borderWidth: 1, - borderColor: colors.error + '50', marginBottom: 8, }, resetButtonText: { - color: colors.error, fontSize: 16, fontWeight: 'bold', marginLeft: 8, }, resetDescription: { fontSize: 12, - color: colors.lightGray, fontStyle: 'italic', }, countdownContainer: { @@ -458,14 +456,13 @@ const styles = StyleSheet.create({ alignItems: 'center', marginTop: 8, padding: 8, - backgroundColor: colors.primary + '10', + backgroundColor: 'rgba(0, 0, 0, 0.1)', borderRadius: 4, }, countdownIcon: { marginRight: 8, }, countdownText: { - color: colors.primary, fontSize: 14, }, });