mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
Refactor CalendarSection and CalendarScreen components to integrate theme context for improved UI consistency
This update enhances the CalendarSection and CalendarScreen components by incorporating the ThemeContext, allowing for dynamic theming throughout the calendar interface. Styles have been adjusted to reflect the current theme colors, improving visual consistency and user experience. Key changes include updates to button styles, text colors, and background settings, ensuring a cohesive interface that adapts to different themes. Additionally, the CalendarSection's date handling logic has been optimized for better performance.
This commit is contained in:
parent
9ab154f8b8
commit
29347ee028
3 changed files with 276 additions and 311 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -8,24 +8,14 @@ import {
|
||||||
Dimensions
|
Dimensions
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { colors } from '../../styles/colors';
|
import { format, addMonths, subMonths, startOfMonth, endOfMonth, eachDayOfInterval, isSameMonth, isToday, isSameDay } from 'date-fns';
|
||||||
import {
|
|
||||||
format,
|
|
||||||
addMonths,
|
|
||||||
subMonths,
|
|
||||||
startOfMonth,
|
|
||||||
endOfMonth,
|
|
||||||
isSameMonth,
|
|
||||||
isSameDay,
|
|
||||||
getDay,
|
|
||||||
isToday,
|
|
||||||
parseISO
|
|
||||||
} from 'date-fns';
|
|
||||||
import Animated, { FadeIn } from 'react-native-reanimated';
|
import Animated, { FadeIn } from 'react-native-reanimated';
|
||||||
|
import { useTheme } from '../../contexts/ThemeContext';
|
||||||
|
|
||||||
const { width } = Dimensions.get('window');
|
const { width } = Dimensions.get('window');
|
||||||
const COLUMN_COUNT = 7; // 7 days in a week
|
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 {
|
interface CalendarEpisode {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -54,37 +44,40 @@ const DayItem = ({
|
||||||
isSelected,
|
isSelected,
|
||||||
hasEvents,
|
hasEvents,
|
||||||
onPress
|
onPress
|
||||||
}: DayItemProps) => (
|
}: DayItemProps) => {
|
||||||
<TouchableOpacity
|
const { currentTheme } = useTheme();
|
||||||
style={[
|
return (
|
||||||
styles.dayItem,
|
<TouchableOpacity
|
||||||
today && styles.todayItem,
|
style={[
|
||||||
isSelected && styles.selectedItem,
|
styles.dayButton,
|
||||||
hasEvents && styles.dayWithEvents
|
today && styles.todayItem,
|
||||||
]}
|
isSelected && styles.selectedItem,
|
||||||
onPress={() => onPress(date)}
|
hasEvents && styles.dayWithEvents
|
||||||
>
|
]}
|
||||||
<Text style={[
|
onPress={() => onPress(date)}
|
||||||
styles.dayText,
|
>
|
||||||
!isCurrentMonth && styles.otherMonthDay,
|
<Text style={[
|
||||||
today && styles.todayText,
|
styles.dayText,
|
||||||
isSelected && styles.selectedDayText
|
!isCurrentMonth && { color: currentTheme.colors.lightGray + '80' },
|
||||||
]}>
|
today && styles.todayText,
|
||||||
{date.getDate()}
|
isSelected && styles.selectedDayText
|
||||||
</Text>
|
]}>
|
||||||
{hasEvents && (
|
{date.getDate()}
|
||||||
<View style={styles.eventIndicator} />
|
</Text>
|
||||||
)}
|
{hasEvents && (
|
||||||
</TouchableOpacity>
|
<View style={[styles.eventIndicator, { backgroundColor: currentTheme.colors.primary }]} />
|
||||||
);
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const CalendarSection: React.FC<CalendarSectionProps> = ({
|
export const CalendarSection: React.FC<CalendarSectionProps> = ({
|
||||||
episodes = [],
|
episodes = [],
|
||||||
onSelectDate
|
onSelectDate
|
||||||
}) => {
|
}) => {
|
||||||
console.log(`[CalendarSection] Rendering with ${episodes.length} episodes`);
|
const { currentTheme } = useTheme();
|
||||||
const [currentDate, setCurrentDate] = useState(new Date());
|
const [currentDate, setCurrentDate] = useState(new Date());
|
||||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
||||||
const scrollViewRef = useRef<ScrollView>(null);
|
const scrollViewRef = useRef<ScrollView>(null);
|
||||||
|
|
||||||
// Map of dates with episodes
|
// Map of dates with episodes
|
||||||
|
|
@ -97,7 +90,7 @@ export const CalendarSection: React.FC<CalendarSectionProps> = ({
|
||||||
|
|
||||||
episodes.forEach(episode => {
|
episodes.forEach(episode => {
|
||||||
if (episode.releaseDate) {
|
if (episode.releaseDate) {
|
||||||
const releaseDate = parseISO(episode.releaseDate);
|
const releaseDate = new Date(episode.releaseDate);
|
||||||
const dateKey = format(releaseDate, 'yyyy-MM-dd');
|
const dateKey = format(releaseDate, 'yyyy-MM-dd');
|
||||||
dateMap[dateKey] = true;
|
dateMap[dateKey] = true;
|
||||||
}
|
}
|
||||||
|
|
@ -107,201 +100,194 @@ export const CalendarSection: React.FC<CalendarSectionProps> = ({
|
||||||
setDatesWithEpisodes(dateMap);
|
setDatesWithEpisodes(dateMap);
|
||||||
}, [episodes]);
|
}, [episodes]);
|
||||||
|
|
||||||
const goToPreviousMonth = () => {
|
const goToPreviousMonth = useCallback(() => {
|
||||||
setCurrentDate(prevDate => subMonths(prevDate, 1));
|
setCurrentDate(prev => subMonths(prev, 1));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const goToNextMonth = () => {
|
const goToNextMonth = useCallback(() => {
|
||||||
setCurrentDate(prevDate => addMonths(prevDate, 1));
|
setCurrentDate(prev => addMonths(prev, 1));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleDayPress = (date: Date) => {
|
const handleDateSelect = useCallback((date: Date) => {
|
||||||
setSelectedDate(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) => (
|
||||||
|
<View key={weekIndex} style={styles.weekRow}>
|
||||||
|
{week.map((day, dayIndex) => {
|
||||||
|
if (!day) {
|
||||||
|
return <View key={`empty-${dayIndex}`} style={styles.emptyDay} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={day.toISOString()}
|
||||||
|
style={[
|
||||||
|
styles.dayButton,
|
||||||
|
isCurrentDay && [styles.todayItem, { backgroundColor: currentTheme.colors.primary + '30', borderColor: currentTheme.colors.primary }],
|
||||||
|
isSelected && [styles.selectedItem, { backgroundColor: currentTheme.colors.primary + '60', borderColor: currentTheme.colors.primary }],
|
||||||
|
hasEvents && styles.dayWithEvents
|
||||||
|
]}
|
||||||
|
onPress={() => handleDateSelect(day)}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.dayText,
|
||||||
|
{ color: currentTheme.colors.text },
|
||||||
|
!isCurrentMonth && { color: currentTheme.colors.lightGray + '80' },
|
||||||
|
isCurrentDay && [styles.todayText, { color: currentTheme.colors.primary }],
|
||||||
|
isSelected && [styles.selectedDayText, { color: currentTheme.colors.text }]
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{format(day, 'd')}
|
||||||
|
</Text>
|
||||||
|
{hasEvents && (
|
||||||
|
<View style={[styles.eventDot, { backgroundColor: currentTheme.colors.primary }]} />
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 (
|
return (
|
||||||
<Animated.View entering={FadeIn.duration(300)} style={styles.container}>
|
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
<View style={styles.header}>
|
<View style={[styles.header, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<TouchableOpacity onPress={goToPreviousMonth} style={styles.headerButton}>
|
<TouchableOpacity
|
||||||
<MaterialIcons name="chevron-left" size={24} color={colors.text} />
|
onPress={goToPreviousMonth}
|
||||||
|
style={styles.headerButton}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="chevron-left" size={24} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<Text style={styles.monthTitle}>
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>
|
||||||
{format(currentDate, 'MMMM yyyy')}
|
{format(currentDate, 'MMMM yyyy')}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<TouchableOpacity onPress={goToNextMonth} style={styles.headerButton}>
|
<TouchableOpacity
|
||||||
<MaterialIcons name="chevron-right" size={24} color={colors.text} />
|
onPress={goToNextMonth}
|
||||||
|
style={styles.headerButton}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="chevron-right" size={24} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.weekHeader}>
|
<View style={styles.weekDaysContainer}>
|
||||||
{weekDays.map((day, index) => (
|
{weekDays.map((day, index) => (
|
||||||
<View key={index} style={styles.weekHeaderItem}>
|
<Text
|
||||||
<Text style={styles.weekDayText}>{day}</Text>
|
key={index}
|
||||||
</View>
|
style={[styles.weekDayText, { color: currentTheme.colors.lightGray }]}
|
||||||
|
>
|
||||||
|
{day}
|
||||||
|
</Text>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.calendarGrid}>
|
<View style={styles.daysContainer}>
|
||||||
{rows.map((row, rowIndex) => (
|
{renderDays()}
|
||||||
<View key={rowIndex} style={styles.row}>
|
|
||||||
{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 (
|
|
||||||
<DayItem
|
|
||||||
key={cellIndex}
|
|
||||||
date={date}
|
|
||||||
isCurrentMonth={isCurrentMonthDay}
|
|
||||||
isToday={isSelectedToday}
|
|
||||||
isSelected={isDateSelected}
|
|
||||||
hasEvents={hasEvents}
|
|
||||||
onPress={handleDayPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
</View>
|
||||||
</Animated.View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: colors.darkBackground,
|
width: '100%',
|
||||||
marginBottom: 12,
|
|
||||||
borderRadius: 8,
|
|
||||||
overflow: 'hidden',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.border,
|
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
paddingVertical: 12,
|
alignItems: 'center',
|
||||||
paddingHorizontal: 16,
|
padding: 16,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border,
|
|
||||||
},
|
},
|
||||||
headerButton: {
|
headerButton: {
|
||||||
padding: 8,
|
padding: 8,
|
||||||
},
|
},
|
||||||
monthTitle: {
|
headerTitle: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: colors.text,
|
|
||||||
},
|
},
|
||||||
weekHeader: {
|
weekDaysContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-around',
|
||||||
padding: 8,
|
padding: 8,
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: colors.border,
|
|
||||||
},
|
|
||||||
weekHeaderItem: {
|
|
||||||
width: DAY_ITEM_SIZE,
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
},
|
||||||
weekDayText: {
|
weekDayText: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: colors.lightGray,
|
|
||||||
},
|
},
|
||||||
calendarGrid: {
|
daysContainer: {
|
||||||
padding: 8,
|
padding: 8,
|
||||||
},
|
},
|
||||||
row: {
|
weekRow: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-around',
|
justifyContent: 'space-around',
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
dayItem: {
|
dayButton: {
|
||||||
width: DAY_ITEM_SIZE,
|
width: 36,
|
||||||
height: DAY_ITEM_SIZE,
|
height: 36,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderRadius: DAY_ITEM_SIZE / 2,
|
borderRadius: 18,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'transparent',
|
||||||
},
|
},
|
||||||
dayText: {
|
dayText: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: colors.text,
|
|
||||||
},
|
},
|
||||||
otherMonthDay: {
|
emptyDay: {
|
||||||
color: colors.lightGray + '80', // 50% opacity
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
},
|
||||||
|
eventDot: {
|
||||||
|
width: 4,
|
||||||
|
height: 4,
|
||||||
|
borderRadius: 2,
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 6,
|
||||||
},
|
},
|
||||||
todayItem: {
|
todayItem: {
|
||||||
backgroundColor: colors.primary + '30', // 30% opacity
|
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.primary,
|
|
||||||
},
|
},
|
||||||
selectedItem: {
|
selectedItem: {
|
||||||
backgroundColor: colors.primary + '60', // 60% opacity
|
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.primary,
|
|
||||||
},
|
},
|
||||||
todayText: {
|
todayText: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: colors.primary,
|
|
||||||
},
|
},
|
||||||
selectedDayText: {
|
selectedDayText: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: colors.text,
|
|
||||||
},
|
},
|
||||||
dayWithEvents: {
|
dayWithEvents: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
|
@ -312,6 +298,5 @@ const styles = StyleSheet.create({
|
||||||
width: 4,
|
width: 4,
|
||||||
height: 4,
|
height: 4,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
backgroundColor: colors.primary,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
useColorScheme,
|
|
||||||
Dimensions,
|
Dimensions,
|
||||||
SectionList
|
SectionList
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
@ -18,7 +17,7 @@ import { NavigationProp } from '@react-navigation/native';
|
||||||
import { Image } from 'expo-image';
|
import { Image } from 'expo-image';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import { colors } from '../styles/colors';
|
import { useTheme } from '../contexts/ThemeContext';
|
||||||
import { RootStackParamList } from '../navigation/AppNavigator';
|
import { RootStackParamList } from '../navigation/AppNavigator';
|
||||||
import { stremioService } from '../services/stremioService';
|
import { stremioService } from '../services/stremioService';
|
||||||
import { useLibrary } from '../hooks/useLibrary';
|
import { useLibrary } from '../hooks/useLibrary';
|
||||||
|
|
@ -53,6 +52,7 @@ interface CalendarSection {
|
||||||
const CalendarScreen = () => {
|
const CalendarScreen = () => {
|
||||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||||
const { libraryItems, loading: libraryLoading } = useLibrary();
|
const { libraryItems, loading: libraryLoading } = useLibrary();
|
||||||
|
const { currentTheme } = useTheme();
|
||||||
logger.log(`[Calendar] Initial load - Library has ${libraryItems?.length || 0} items, loading: ${libraryLoading}`);
|
logger.log(`[Calendar] Initial load - Library has ${libraryItems?.length || 0} items, loading: ${libraryLoading}`);
|
||||||
const [calendarData, setCalendarData] = useState<CalendarSection[]>([]);
|
const [calendarData, setCalendarData] = useState<CalendarSection[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
@ -270,7 +270,7 @@ const CalendarScreen = () => {
|
||||||
return (
|
return (
|
||||||
<Animated.View entering={FadeIn.duration(300).delay(100)}>
|
<Animated.View entering={FadeIn.duration(300).delay(100)}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.episodeItem}
|
style={[styles.episodeItem, { borderBottomColor: currentTheme.colors.border + '20' }]}
|
||||||
onPress={() => handleEpisodePress(item)}
|
onPress={() => handleEpisodePress(item)}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
|
|
@ -287,18 +287,18 @@ const CalendarScreen = () => {
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<View style={styles.episodeDetails}>
|
<View style={styles.episodeDetails}>
|
||||||
<Text style={styles.seriesName} numberOfLines={1}>
|
<Text style={[styles.seriesName, { color: currentTheme.colors.text }]} numberOfLines={1}>
|
||||||
{item.seriesName}
|
{item.seriesName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{hasReleaseDate ? (
|
{hasReleaseDate ? (
|
||||||
<>
|
<>
|
||||||
<Text style={styles.episodeTitle} numberOfLines={2}>
|
<Text style={[styles.episodeTitle, { color: currentTheme.colors.lightGray }]} numberOfLines={2}>
|
||||||
S{item.season}:E{item.episode} - {item.title}
|
S{item.season}:E{item.episode} - {item.title}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{item.overview ? (
|
{item.overview ? (
|
||||||
<Text style={styles.overview} numberOfLines={2}>
|
<Text style={[styles.overview, { color: currentTheme.colors.lightGray }]} numberOfLines={2}>
|
||||||
{item.overview}
|
{item.overview}
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -308,9 +308,9 @@ const CalendarScreen = () => {
|
||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name={isFuture ? "event" : "event-available"}
|
name={isFuture ? "event" : "event-available"}
|
||||||
size={16}
|
size={16}
|
||||||
color={colors.lightGray}
|
color={currentTheme.colors.lightGray}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.date}>{formattedDate}</Text>
|
<Text style={[styles.date, { color: currentTheme.colors.lightGray }]}>{formattedDate}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{item.vote_average > 0 && (
|
{item.vote_average > 0 && (
|
||||||
|
|
@ -318,9 +318,9 @@ const CalendarScreen = () => {
|
||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name="star"
|
name="star"
|
||||||
size={16}
|
size={16}
|
||||||
color={colors.primary}
|
color={currentTheme.colors.primary}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.rating}>
|
<Text style={[styles.rating, { color: currentTheme.colors.primary }]}>
|
||||||
{item.vote_average.toFixed(1)}
|
{item.vote_average.toFixed(1)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -329,16 +329,16 @@ const CalendarScreen = () => {
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Text style={styles.noEpisodesText}>
|
<Text style={[styles.noEpisodesText, { color: currentTheme.colors.text }]}>
|
||||||
No scheduled episodes
|
No scheduled episodes
|
||||||
</Text>
|
</Text>
|
||||||
<View style={styles.dateContainer}>
|
<View style={styles.dateContainer}>
|
||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name="event-busy"
|
name="event-busy"
|
||||||
size={16}
|
size={16}
|
||||||
color={colors.lightGray}
|
color={currentTheme.colors.lightGray}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.date}>Check back later</Text>
|
<Text style={[styles.date, { color: currentTheme.colors.lightGray }]}>Check back later</Text>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -349,8 +349,13 @@ const CalendarScreen = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSectionHeader = ({ section }: { section: CalendarSection }) => (
|
const renderSectionHeader = ({ section }: { section: CalendarSection }) => (
|
||||||
<View style={styles.sectionHeader}>
|
<View style={[styles.sectionHeader, {
|
||||||
<Text style={styles.sectionTitle}>{section.title}</Text>
|
backgroundColor: currentTheme.colors.darkBackground,
|
||||||
|
borderBottomColor: currentTheme.colors.border
|
||||||
|
}]}>
|
||||||
|
<Text style={[styles.sectionTitle, { color: currentTheme.colors.text }]}>
|
||||||
|
{section.title}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -386,22 +391,22 @@ const CalendarScreen = () => {
|
||||||
|
|
||||||
if (libraryItems.length === 0 && !libraryLoading) {
|
if (libraryItems.length === 0 && !libraryLoading) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
<StatusBar barStyle="light-content" />
|
<StatusBar barStyle="light-content" />
|
||||||
|
|
||||||
<View style={styles.header}>
|
<View style={[styles.header, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.backButton}
|
style={styles.backButton}
|
||||||
onPress={() => navigation.goBack()}
|
onPress={() => navigation.goBack()}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="arrow-back" size={24} color={colors.text} />
|
<MaterialIcons name="arrow-back" size={24} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={styles.headerTitle}>Calendar</Text>
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>Calendar</Text>
|
||||||
<View style={{ width: 40 }} />
|
<View style={{ width: 40 }} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.emptyLibraryContainer}>
|
<View style={styles.emptyLibraryContainer}>
|
||||||
<MaterialIcons name="video-library" size={64} color={colors.lightGray} />
|
<MaterialIcons name="video-library" size={64} color={currentTheme.colors.lightGray} />
|
||||||
<Text style={styles.emptyText}>
|
<Text style={styles.emptyText}>
|
||||||
Your library is empty
|
Your library is empty
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -423,10 +428,10 @@ const CalendarScreen = () => {
|
||||||
|
|
||||||
if (loading && !refreshing) {
|
if (loading && !refreshing) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
<StatusBar barStyle="light-content" />
|
<StatusBar barStyle="light-content" />
|
||||||
<View style={styles.loadingContainer}>
|
<View style={styles.loadingContainer}>
|
||||||
<ActivityIndicator size="large" color={colors.primary} />
|
<ActivityIndicator size="large" color={currentTheme.colors.primary} />
|
||||||
<Text style={styles.loadingText}>Loading calendar...</Text>
|
<Text style={styles.loadingText}>Loading calendar...</Text>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|
@ -434,27 +439,27 @@ const CalendarScreen = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
<StatusBar barStyle="light-content" />
|
<StatusBar barStyle="light-content" />
|
||||||
|
|
||||||
<View style={styles.header}>
|
<View style={[styles.header, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.backButton}
|
style={styles.backButton}
|
||||||
onPress={() => navigation.goBack()}
|
onPress={() => navigation.goBack()}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="arrow-back" size={24} color={colors.text} />
|
<MaterialIcons name="arrow-back" size={24} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={styles.headerTitle}>Calendar</Text>
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>Calendar</Text>
|
||||||
<View style={{ width: 40 }} />
|
<View style={{ width: 40 }} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{selectedDate && filteredEpisodes.length > 0 && (
|
{selectedDate && filteredEpisodes.length > 0 && (
|
||||||
<View style={styles.filterInfoContainer}>
|
<View style={[styles.filterInfoContainer, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<Text style={styles.filterInfoText}>
|
<Text style={[styles.filterInfoText, { color: currentTheme.colors.text }]}>
|
||||||
Showing episodes for {format(selectedDate, 'MMMM d, yyyy')}
|
Showing episodes for {format(selectedDate, 'MMMM d, yyyy')}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity onPress={clearDateFilter} style={styles.clearFilterButton}>
|
<TouchableOpacity onPress={clearDateFilter} style={styles.clearFilterButton}>
|
||||||
<MaterialIcons name="close" size={18} color={colors.text} />
|
<MaterialIcons name="close" size={18} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
@ -474,22 +479,22 @@ const CalendarScreen = () => {
|
||||||
<RefreshControl
|
<RefreshControl
|
||||||
refreshing={refreshing}
|
refreshing={refreshing}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
tintColor={colors.primary}
|
tintColor={currentTheme.colors.primary}
|
||||||
colors={[colors.primary]}
|
colors={[currentTheme.colors.primary]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : selectedDate && filteredEpisodes.length === 0 ? (
|
) : selectedDate && filteredEpisodes.length === 0 ? (
|
||||||
<View style={styles.emptyFilterContainer}>
|
<View style={styles.emptyFilterContainer}>
|
||||||
<MaterialIcons name="event-busy" size={48} color={colors.lightGray} />
|
<MaterialIcons name="event-busy" size={48} color={currentTheme.colors.lightGray} />
|
||||||
<Text style={styles.emptyFilterText}>
|
<Text style={[styles.emptyFilterText, { color: currentTheme.colors.text }]}>
|
||||||
No episodes for {format(selectedDate, 'MMMM d, yyyy')}
|
No episodes for {format(selectedDate, 'MMMM d, yyyy')}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.clearFilterButtonLarge}
|
style={[styles.clearFilterButtonLarge, { backgroundColor: currentTheme.colors.primary }]}
|
||||||
onPress={clearDateFilter}
|
onPress={clearDateFilter}
|
||||||
>
|
>
|
||||||
<Text style={styles.clearFilterButtonText}>
|
<Text style={[styles.clearFilterButtonText, { color: currentTheme.colors.text }]}>
|
||||||
Show All Episodes
|
Show All Episodes
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
@ -505,18 +510,18 @@ const CalendarScreen = () => {
|
||||||
<RefreshControl
|
<RefreshControl
|
||||||
refreshing={refreshing}
|
refreshing={refreshing}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
tintColor={colors.primary}
|
tintColor={currentTheme.colors.primary}
|
||||||
colors={[colors.primary]}
|
colors={[currentTheme.colors.primary]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.emptyContainer}>
|
<View style={styles.emptyContainer}>
|
||||||
<MaterialIcons name="calendar-today" size={64} color={colors.lightGray} />
|
<MaterialIcons name="calendar-today" size={64} color={currentTheme.colors.lightGray} />
|
||||||
<Text style={styles.emptyText}>
|
<Text style={[styles.emptyText, { color: currentTheme.colors.text }]}>
|
||||||
No upcoming episodes found
|
No upcoming episodes found
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.emptySubtext}>
|
<Text style={[styles.emptySubtext, { color: currentTheme.colors.lightGray }]}>
|
||||||
Add series to your library to see their upcoming episodes here
|
Add series to your library to see their upcoming episodes here
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -528,7 +533,6 @@ const CalendarScreen = () => {
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.darkBackground,
|
|
||||||
},
|
},
|
||||||
listContent: {
|
listContent: {
|
||||||
paddingBottom: 20,
|
paddingBottom: 20,
|
||||||
|
|
@ -539,19 +543,15 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
loadingText: {
|
loadingText: {
|
||||||
color: colors.text,
|
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
sectionHeader: {
|
sectionHeader: {
|
||||||
backgroundColor: colors.darkBackground,
|
|
||||||
paddingVertical: 8,
|
paddingVertical: 8,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border,
|
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
|
|
@ -559,7 +559,6 @@ const styles = StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border + '20',
|
|
||||||
},
|
},
|
||||||
poster: {
|
poster: {
|
||||||
width: 120,
|
width: 120,
|
||||||
|
|
@ -572,18 +571,15 @@ const styles = StyleSheet.create({
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
seriesName: {
|
seriesName: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
episodeTitle: {
|
episodeTitle: {
|
||||||
color: colors.lightGray,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 20,
|
lineHeight: 20,
|
||||||
},
|
},
|
||||||
overview: {
|
overview: {
|
||||||
color: colors.lightGray,
|
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
lineHeight: 16,
|
lineHeight: 16,
|
||||||
|
|
@ -599,7 +595,6 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
color: colors.lightGray,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
},
|
},
|
||||||
|
|
@ -608,7 +603,6 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
rating: {
|
rating: {
|
||||||
color: colors.primary,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
|
@ -620,14 +614,12 @@ const styles = StyleSheet.create({
|
||||||
padding: 20,
|
padding: 20,
|
||||||
},
|
},
|
||||||
emptyText: {
|
emptyText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginTop: 16,
|
marginTop: 16,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
emptySubtext: {
|
emptySubtext: {
|
||||||
color: colors.lightGray,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
|
@ -638,10 +630,8 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border,
|
|
||||||
},
|
},
|
||||||
filterInfoText: {
|
filterInfoText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
|
|
@ -655,7 +645,6 @@ const styles = StyleSheet.create({
|
||||||
padding: 20,
|
padding: 20,
|
||||||
},
|
},
|
||||||
emptyFilterText: {
|
emptyFilterText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginTop: 16,
|
marginTop: 16,
|
||||||
|
|
@ -664,11 +653,9 @@ const styles = StyleSheet.create({
|
||||||
clearFilterButtonLarge: {
|
clearFilterButtonLarge: {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
backgroundColor: colors.primary,
|
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
},
|
},
|
||||||
clearFilterButtonText: {
|
clearFilterButtonText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
|
|
@ -681,7 +668,6 @@ const styles = StyleSheet.create({
|
||||||
padding: 8,
|
padding: 8,
|
||||||
},
|
},
|
||||||
headerTitle: {
|
headerTitle: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginLeft: 12,
|
marginLeft: 12,
|
||||||
|
|
@ -694,16 +680,13 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
discoverButton: {
|
discoverButton: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
backgroundColor: colors.primary,
|
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
},
|
},
|
||||||
discoverButtonText: {
|
discoverButtonText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
noEpisodesText: {
|
noEpisodesText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
StatusBar,
|
StatusBar,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { colors } from '../styles/colors';
|
import { useTheme } from '../contexts/ThemeContext';
|
||||||
import { notificationService, NotificationSettings } from '../services/notificationService';
|
import { notificationService, NotificationSettings } from '../services/notificationService';
|
||||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
@ -19,6 +19,7 @@ import { logger } from '../utils/logger';
|
||||||
|
|
||||||
const NotificationSettingsScreen = () => {
|
const NotificationSettingsScreen = () => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
const { currentTheme } = useTheme();
|
||||||
const [settings, setSettings] = useState<NotificationSettings>({
|
const [settings, setSettings] = useState<NotificationSettings>({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
newEpisodeNotifications: true,
|
newEpisodeNotifications: true,
|
||||||
|
|
@ -155,36 +156,36 @@ const NotificationSettingsScreen = () => {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
<View style={styles.header}>
|
<View style={[styles.header, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.backButton}
|
style={styles.backButton}
|
||||||
onPress={() => navigation.goBack()}
|
onPress={() => navigation.goBack()}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="arrow-back" size={24} color={colors.text} />
|
<MaterialIcons name="arrow-back" size={24} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={styles.headerTitle}>Notification Settings</Text>
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>Notification Settings</Text>
|
||||||
<View style={{ width: 40 }} />
|
<View style={{ width: 40 }} />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.loadingContainer}>
|
<View style={styles.loadingContainer}>
|
||||||
<Text style={styles.loadingText}>Loading settings...</Text>
|
<Text style={[styles.loadingText, { color: currentTheme.colors.text }]}>Loading settings...</Text>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
<StatusBar barStyle="light-content" />
|
<StatusBar barStyle="light-content" />
|
||||||
|
|
||||||
<View style={styles.header}>
|
<View style={[styles.header, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.backButton}
|
style={styles.backButton}
|
||||||
onPress={() => navigation.goBack()}
|
onPress={() => navigation.goBack()}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="arrow-back" size={24} color={colors.text} />
|
<MaterialIcons name="arrow-back" size={24} color={currentTheme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={styles.headerTitle}>Notification Settings</Text>
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>Notification Settings</Text>
|
||||||
<View style={{ width: 40 }} />
|
<View style={{ width: 40 }} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
@ -193,72 +194,72 @@ const NotificationSettingsScreen = () => {
|
||||||
entering={FadeIn.duration(300)}
|
entering={FadeIn.duration(300)}
|
||||||
exiting={FadeOut.duration(200)}
|
exiting={FadeOut.duration(200)}
|
||||||
>
|
>
|
||||||
<View style={styles.section}>
|
<View style={[styles.section, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<Text style={styles.sectionTitle}>General</Text>
|
<Text style={[styles.sectionTitle, { color: currentTheme.colors.text }]}>General</Text>
|
||||||
|
|
||||||
<View style={styles.settingItem}>
|
<View style={[styles.settingItem, { borderBottomColor: currentTheme.colors.border + '50' }]}>
|
||||||
<View style={styles.settingInfo}>
|
<View style={styles.settingInfo}>
|
||||||
<MaterialIcons name="notifications" size={24} color={colors.text} />
|
<MaterialIcons name="notifications" size={24} color={currentTheme.colors.text} />
|
||||||
<Text style={styles.settingText}>Enable Notifications</Text>
|
<Text style={[styles.settingText, { color: currentTheme.colors.text }]}>Enable Notifications</Text>
|
||||||
</View>
|
</View>
|
||||||
<Switch
|
<Switch
|
||||||
value={settings.enabled}
|
value={settings.enabled}
|
||||||
onValueChange={(value) => updateSetting('enabled', value)}
|
onValueChange={(value) => updateSetting('enabled', value)}
|
||||||
trackColor={{ false: colors.border, true: colors.primary + '80' }}
|
trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }}
|
||||||
thumbColor={settings.enabled ? colors.primary : colors.lightGray}
|
thumbColor={settings.enabled ? currentTheme.colors.primary : currentTheme.colors.lightGray}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{settings.enabled && (
|
{settings.enabled && (
|
||||||
<>
|
<>
|
||||||
<View style={styles.section}>
|
<View style={[styles.section, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<Text style={styles.sectionTitle}>Notification Types</Text>
|
<Text style={[styles.sectionTitle, { color: currentTheme.colors.text }]}>Notification Types</Text>
|
||||||
|
|
||||||
<View style={styles.settingItem}>
|
<View style={[styles.settingItem, { borderBottomColor: currentTheme.colors.border + '50' }]}>
|
||||||
<View style={styles.settingInfo}>
|
<View style={styles.settingInfo}>
|
||||||
<MaterialIcons name="new-releases" size={24} color={colors.text} />
|
<MaterialIcons name="new-releases" size={24} color={currentTheme.colors.text} />
|
||||||
<Text style={styles.settingText}>New Episodes</Text>
|
<Text style={[styles.settingText, { color: currentTheme.colors.text }]}>New Episodes</Text>
|
||||||
</View>
|
</View>
|
||||||
<Switch
|
<Switch
|
||||||
value={settings.newEpisodeNotifications}
|
value={settings.newEpisodeNotifications}
|
||||||
onValueChange={(value) => updateSetting('newEpisodeNotifications', value)}
|
onValueChange={(value) => updateSetting('newEpisodeNotifications', value)}
|
||||||
trackColor={{ false: colors.border, true: colors.primary + '80' }}
|
trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }}
|
||||||
thumbColor={settings.newEpisodeNotifications ? colors.primary : colors.lightGray}
|
thumbColor={settings.newEpisodeNotifications ? currentTheme.colors.primary : currentTheme.colors.lightGray}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.settingItem}>
|
<View style={[styles.settingItem, { borderBottomColor: currentTheme.colors.border + '50' }]}>
|
||||||
<View style={styles.settingInfo}>
|
<View style={styles.settingInfo}>
|
||||||
<MaterialIcons name="event" size={24} color={colors.text} />
|
<MaterialIcons name="event" size={24} color={currentTheme.colors.text} />
|
||||||
<Text style={styles.settingText}>Upcoming Shows</Text>
|
<Text style={[styles.settingText, { color: currentTheme.colors.text }]}>Upcoming Shows</Text>
|
||||||
</View>
|
</View>
|
||||||
<Switch
|
<Switch
|
||||||
value={settings.upcomingShowsNotifications}
|
value={settings.upcomingShowsNotifications}
|
||||||
onValueChange={(value) => updateSetting('upcomingShowsNotifications', value)}
|
onValueChange={(value) => updateSetting('upcomingShowsNotifications', value)}
|
||||||
trackColor={{ false: colors.border, true: colors.primary + '80' }}
|
trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }}
|
||||||
thumbColor={settings.upcomingShowsNotifications ? colors.primary : colors.lightGray}
|
thumbColor={settings.upcomingShowsNotifications ? currentTheme.colors.primary : currentTheme.colors.lightGray}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.settingItem}>
|
<View style={[styles.settingItem, { borderBottomColor: currentTheme.colors.border + '50' }]}>
|
||||||
<View style={styles.settingInfo}>
|
<View style={styles.settingInfo}>
|
||||||
<MaterialIcons name="alarm" size={24} color={colors.text} />
|
<MaterialIcons name="alarm" size={24} color={currentTheme.colors.text} />
|
||||||
<Text style={styles.settingText}>Reminders</Text>
|
<Text style={[styles.settingText, { color: currentTheme.colors.text }]}>Reminders</Text>
|
||||||
</View>
|
</View>
|
||||||
<Switch
|
<Switch
|
||||||
value={settings.reminderNotifications}
|
value={settings.reminderNotifications}
|
||||||
onValueChange={(value) => updateSetting('reminderNotifications', value)}
|
onValueChange={(value) => updateSetting('reminderNotifications', value)}
|
||||||
trackColor={{ false: colors.border, true: colors.primary + '80' }}
|
trackColor={{ false: currentTheme.colors.border, true: currentTheme.colors.primary + '80' }}
|
||||||
thumbColor={settings.reminderNotifications ? colors.primary : colors.lightGray}
|
thumbColor={settings.reminderNotifications ? currentTheme.colors.primary : currentTheme.colors.lightGray}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.section}>
|
<View style={[styles.section, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<Text style={styles.sectionTitle}>Notification Timing</Text>
|
<Text style={[styles.sectionTitle, { color: currentTheme.colors.text }]}>Notification Timing</Text>
|
||||||
|
|
||||||
<Text style={styles.settingDescription}>
|
<Text style={[styles.settingDescription, { color: currentTheme.colors.lightGray }]}>
|
||||||
When should you be notified before an episode airs?
|
When should you be notified before an episode airs?
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
|
@ -268,13 +269,24 @@ const NotificationSettingsScreen = () => {
|
||||||
key={hours}
|
key={hours}
|
||||||
style={[
|
style={[
|
||||||
styles.timingOption,
|
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)}
|
onPress={() => setTimeBeforeAiring(hours)}
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.timingText,
|
styles.timingText,
|
||||||
settings.timeBeforeAiring === hours && styles.selectedTimingText
|
{ color: currentTheme.colors.text },
|
||||||
|
settings.timeBeforeAiring === hours && {
|
||||||
|
color: currentTheme.colors.primary,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}
|
||||||
]}>
|
]}>
|
||||||
{hours === 1 ? '1 hour' : `${hours} hours`}
|
{hours === 1 ? '1 hour' : `${hours} hours`}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -283,27 +295,37 @@ const NotificationSettingsScreen = () => {
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.section}>
|
<View style={[styles.section, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
<Text style={styles.sectionTitle}>Advanced</Text>
|
<Text style={[styles.sectionTitle, { color: currentTheme.colors.text }]}>Advanced</Text>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.resetButton}
|
style={[
|
||||||
|
styles.resetButton,
|
||||||
|
{
|
||||||
|
backgroundColor: currentTheme.colors.error + '20',
|
||||||
|
borderColor: currentTheme.colors.error + '50'
|
||||||
|
}
|
||||||
|
]}
|
||||||
onPress={resetAllNotifications}
|
onPress={resetAllNotifications}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="refresh" size={24} color={colors.error} />
|
<MaterialIcons name="refresh" size={24} color={currentTheme.colors.error} />
|
||||||
<Text style={styles.resetButtonText}>Reset All Notifications</Text>
|
<Text style={[styles.resetButtonText, { color: currentTheme.colors.error }]}>Reset All Notifications</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.resetButton,
|
styles.resetButton,
|
||||||
{ marginTop: 12, backgroundColor: colors.primary + '20', borderColor: colors.primary + '50' }
|
{
|
||||||
|
marginTop: 12,
|
||||||
|
backgroundColor: currentTheme.colors.primary + '20',
|
||||||
|
borderColor: currentTheme.colors.primary + '50'
|
||||||
|
}
|
||||||
]}
|
]}
|
||||||
onPress={handleTestNotification}
|
onPress={handleTestNotification}
|
||||||
disabled={countdown !== null}
|
disabled={countdown !== null}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="bug-report" size={24} color={colors.primary} />
|
<MaterialIcons name="bug-report" size={24} color={currentTheme.colors.primary} />
|
||||||
<Text style={[styles.resetButtonText, { color: colors.primary }]}>
|
<Text style={[styles.resetButtonText, { color: currentTheme.colors.primary }]}>
|
||||||
{countdown !== null
|
{countdown !== null
|
||||||
? `Notification in ${countdown}s...`
|
? `Notification in ${countdown}s...`
|
||||||
: 'Test Notification (1min)'}
|
: 'Test Notification (1min)'}
|
||||||
|
|
@ -315,16 +337,16 @@ const NotificationSettingsScreen = () => {
|
||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name="timer"
|
name="timer"
|
||||||
size={16}
|
size={16}
|
||||||
color={colors.primary}
|
color={currentTheme.colors.primary}
|
||||||
style={styles.countdownIcon}
|
style={styles.countdownIcon}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.countdownText}>
|
<Text style={[styles.countdownText, { color: currentTheme.colors.primary }]}>
|
||||||
Notification will appear in {countdown} seconds
|
Notification will appear in {countdown} seconds
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Text style={styles.resetDescription}>
|
<Text style={[styles.resetDescription, { color: currentTheme.colors.lightGray }]}>
|
||||||
This will cancel all scheduled notifications. You'll need to re-enable them manually.
|
This will cancel all scheduled notifications. You'll need to re-enable them manually.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -339,7 +361,6 @@ const NotificationSettingsScreen = () => {
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.darkBackground,
|
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
@ -348,7 +369,6 @@ const styles = StyleSheet.create({
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border,
|
|
||||||
},
|
},
|
||||||
backButton: {
|
backButton: {
|
||||||
padding: 8,
|
padding: 8,
|
||||||
|
|
@ -356,7 +376,6 @@ const styles = StyleSheet.create({
|
||||||
headerTitle: {
|
headerTitle: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: colors.text,
|
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
@ -367,18 +386,15 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
loadingText: {
|
loadingText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
section: {
|
section: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border,
|
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: colors.text,
|
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
settingItem: {
|
settingItem: {
|
||||||
|
|
@ -387,7 +403,6 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.border + '50',
|
|
||||||
},
|
},
|
||||||
settingInfo: {
|
settingInfo: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
@ -395,12 +410,10 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
settingText: {
|
settingText: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: colors.text,
|
|
||||||
marginLeft: 12,
|
marginLeft: 12,
|
||||||
},
|
},
|
||||||
settingDescription: {
|
settingDescription: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: colors.lightGray,
|
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
timingOptions: {
|
timingOptions: {
|
||||||
|
|
@ -410,47 +423,32 @@ const styles = StyleSheet.create({
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
},
|
},
|
||||||
timingOption: {
|
timingOption: {
|
||||||
backgroundColor: colors.elevation1,
|
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.border,
|
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
width: '48%',
|
width: '48%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
selectedTimingOption: {
|
|
||||||
backgroundColor: colors.primary + '30',
|
|
||||||
borderColor: colors.primary,
|
|
||||||
},
|
|
||||||
timingText: {
|
timingText: {
|
||||||
color: colors.text,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
selectedTimingText: {
|
|
||||||
color: colors.primary,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
resetButton: {
|
resetButton: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: 12,
|
padding: 12,
|
||||||
backgroundColor: colors.error + '20',
|
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.error + '50',
|
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
resetButtonText: {
|
resetButtonText: {
|
||||||
color: colors.error,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
},
|
},
|
||||||
resetDescription: {
|
resetDescription: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: colors.lightGray,
|
|
||||||
fontStyle: 'italic',
|
fontStyle: 'italic',
|
||||||
},
|
},
|
||||||
countdownContainer: {
|
countdownContainer: {
|
||||||
|
|
@ -458,14 +456,13 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
padding: 8,
|
padding: 8,
|
||||||
backgroundColor: colors.primary + '10',
|
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
countdownIcon: {
|
countdownIcon: {
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
},
|
},
|
||||||
countdownText: {
|
countdownText: {
|
||||||
color: colors.primary,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue