mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-02 13:44:54 +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,10 +44,12 @@ const DayItem = ({
|
||||||
isSelected,
|
isSelected,
|
||||||
hasEvents,
|
hasEvents,
|
||||||
onPress
|
onPress
|
||||||
}: DayItemProps) => (
|
}: DayItemProps) => {
|
||||||
|
const { currentTheme } = useTheme();
|
||||||
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.dayItem,
|
styles.dayButton,
|
||||||
today && styles.todayItem,
|
today && styles.todayItem,
|
||||||
isSelected && styles.selectedItem,
|
isSelected && styles.selectedItem,
|
||||||
hasEvents && styles.dayWithEvents
|
hasEvents && styles.dayWithEvents
|
||||||
|
|
@ -66,25 +58,26 @@ const DayItem = ({
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.dayText,
|
styles.dayText,
|
||||||
!isCurrentMonth && styles.otherMonthDay,
|
!isCurrentMonth && { color: currentTheme.colors.lightGray + '80' },
|
||||||
today && styles.todayText,
|
today && styles.todayText,
|
||||||
isSelected && styles.selectedDayText
|
isSelected && styles.selectedDayText
|
||||||
]}>
|
]}>
|
||||||
{date.getDate()}
|
{date.getDate()}
|
||||||
</Text>
|
</Text>
|
||||||
{hasEvents && (
|
{hasEvents && (
|
||||||
<View style={styles.eventIndicator} />
|
<View style={[styles.eventIndicator, { backgroundColor: currentTheme.colors.primary }]} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</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]);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate days for the current month view
|
const renderDays = () => {
|
||||||
const generateDaysForMonth = () => {
|
const start = startOfMonth(currentDate);
|
||||||
const monthStart = startOfMonth(currentDate);
|
const end = endOfMonth(currentDate);
|
||||||
const monthEnd = endOfMonth(currentDate);
|
const days = eachDayOfInterval({ start, end });
|
||||||
const startDate = new Date(monthStart);
|
|
||||||
|
|
||||||
// Adjust the start date to the beginning of the week
|
// Get the day of the week for the first day (0-6)
|
||||||
const dayOfWeek = getDay(startDate);
|
const firstDayOfWeek = start.getDay();
|
||||||
startDate.setDate(startDate.getDate() - dayOfWeek);
|
|
||||||
|
|
||||||
// Ensure we have 6 complete weeks in our view
|
// Add empty days at the start
|
||||||
const endDate = new Date(monthEnd);
|
const emptyDays = Array(firstDayOfWeek).fill(null);
|
||||||
const lastDayOfWeek = getDay(endDate);
|
|
||||||
if (lastDayOfWeek < 6) {
|
// Calculate remaining days to fill the last row
|
||||||
endDate.setDate(endDate.getDate() + (6 - lastDayOfWeek));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get dates for a complete 6-week calendar
|
return weeks.map((week, weekIndex) => (
|
||||||
const totalDaysNeeded = 42; // 6 weeks × 7 days
|
<View key={weekIndex} style={styles.weekRow}>
|
||||||
const daysInView = [];
|
{week.map((day, dayIndex) => {
|
||||||
|
if (!day) {
|
||||||
let currentDateInView = new Date(startDate);
|
return <View key={`empty-${dayIndex}`} style={styles.emptyDay} />;
|
||||||
for (let i = 0; i < totalDaysNeeded; i++) {
|
|
||||||
daysInView.push(new Date(currentDateInView));
|
|
||||||
currentDateInView.setDate(currentDateInView.getDate() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return daysInView;
|
const isCurrentMonth = isSameMonth(day, currentDate);
|
||||||
};
|
const isCurrentDay = isToday(day);
|
||||||
|
const isSelected = selectedDate && isSameDay(day, selectedDate);
|
||||||
const dayItems = generateDaysForMonth();
|
const hasEvents = datesWithEpisodes[format(day, 'yyyy-MM-dd')] || false;
|
||||||
|
|
||||||
// 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}>
|
<TouchableOpacity
|
||||||
<View style={styles.header}>
|
key={day.toISOString()}
|
||||||
<TouchableOpacity onPress={goToPreviousMonth} style={styles.headerButton}>
|
style={[
|
||||||
<MaterialIcons name="chevron-left" size={24} color={colors.text} />
|
styles.dayButton,
|
||||||
</TouchableOpacity>
|
isCurrentDay && [styles.todayItem, { backgroundColor: currentTheme.colors.primary + '30', borderColor: currentTheme.colors.primary }],
|
||||||
|
isSelected && [styles.selectedItem, { backgroundColor: currentTheme.colors.primary + '60', borderColor: currentTheme.colors.primary }],
|
||||||
<Text style={styles.monthTitle}>
|
hasEvents && styles.dayWithEvents
|
||||||
{format(currentDate, 'MMMM yyyy')}
|
]}
|
||||||
|
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>
|
</Text>
|
||||||
|
{hasEvents && (
|
||||||
<TouchableOpacity onPress={goToNextMonth} style={styles.headerButton}>
|
<View style={[styles.eventDot, { backgroundColor: currentTheme.colors.primary }]} />
|
||||||
<MaterialIcons name="chevron-right" size={24} color={colors.text} />
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.weekHeader}>
|
|
||||||
{weekDays.map((day, index) => (
|
|
||||||
<View key={index} style={styles.weekHeaderItem}>
|
|
||||||
<Text style={styles.weekDayText}>{day}</Text>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.calendarGrid}>
|
|
||||||
{rows.map((row, rowIndex) => (
|
|
||||||
<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>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||||
|
<View style={[styles.header, { borderBottomColor: currentTheme.colors.border }]}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={goToPreviousMonth}
|
||||||
|
style={styles.headerButton}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="chevron-left" size={24} color={currentTheme.colors.text} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>
|
||||||
|
{format(currentDate, 'MMMM yyyy')}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={goToNextMonth}
|
||||||
|
style={styles.headerButton}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="chevron-right" size={24} color={currentTheme.colors.text} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.weekDaysContainer}>
|
||||||
|
{weekDays.map((day, index) => (
|
||||||
|
<Text
|
||||||
|
key={index}
|
||||||
|
style={[styles.weekDayText, { color: currentTheme.colors.lightGray }]}
|
||||||
|
>
|
||||||
|
{day}
|
||||||
|
</Text>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</Animated.View>
|
|
||||||
|
<View style={styles.daysContainer}>
|
||||||
|
{renderDays()}
|
||||||
|
</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