CP, calender changes

This commit is contained in:
tapframe 2025-07-07 18:41:13 +05:30
parent 93e80d9720
commit fe8489fa18
4 changed files with 192 additions and 31 deletions

View file

@ -14,6 +14,7 @@ import { Image } from 'expo-image';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import { MaterialIcons } from '@expo/vector-icons'; import { MaterialIcons } from '@expo/vector-icons';
import { useTheme } from '../../contexts/ThemeContext'; import { useTheme } from '../../contexts/ThemeContext';
import { useTraktContext } from '../../contexts/TraktContext';
import { stremioService } from '../../services/stremioService'; import { stremioService } from '../../services/stremioService';
import { tmdbService } from '../../services/tmdbService'; import { tmdbService } from '../../services/tmdbService';
import { useLibrary } from '../../hooks/useLibrary'; import { useLibrary } from '../../hooks/useLibrary';
@ -44,23 +45,75 @@ interface ThisWeekEpisode {
export const ThisWeekSection = React.memo(() => { export const ThisWeekSection = React.memo(() => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>(); const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { libraryItems, loading: libraryLoading } = useLibrary(); const { libraryItems, loading: libraryLoading } = useLibrary();
const {
isAuthenticated: traktAuthenticated,
isLoading: traktLoading,
watchedShows,
watchlistShows,
continueWatching,
loadAllCollections
} = useTraktContext();
const [episodes, setEpisodes] = useState<ThisWeekEpisode[]>([]); const [episodes, setEpisodes] = useState<ThisWeekEpisode[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
const fetchThisWeekEpisodes = useCallback(async () => { const fetchThisWeekEpisodes = useCallback(async () => {
if (libraryItems.length === 0) {
setLoading(false);
return;
}
setLoading(true); setLoading(true);
try { try {
const seriesItems = libraryItems.filter(item => item.type === 'series'); // Combine library series with Trakt series
const librarySeries = libraryItems.filter(item => item.type === 'series');
let allSeries = [...librarySeries];
// Add Trakt watchlist and continue watching shows if authenticated
if (traktAuthenticated) {
const traktSeriesIds = new Set();
// Add watchlist shows
if (watchlistShows) {
for (const item of watchlistShows) {
if (item.show && item.show.ids.imdb) {
const imdbId = item.show.ids.imdb;
if (!librarySeries.some(s => s.id === imdbId)) {
traktSeriesIds.add(imdbId);
allSeries.push({
id: imdbId,
name: item.show.title,
type: 'series',
poster: '', // Will try to fetch from TMDB
year: item.show.year,
traktSource: 'watchlist'
});
}
}
}
}
// Add continue watching shows
if (continueWatching) {
for (const item of continueWatching) {
if (item.type === 'episode' && item.show && item.show.ids.imdb) {
const imdbId = item.show.ids.imdb;
if (!librarySeries.some(s => s.id === imdbId) && !traktSeriesIds.has(imdbId)) {
traktSeriesIds.add(imdbId);
allSeries.push({
id: imdbId,
name: item.show.title,
type: 'series',
poster: '', // Will try to fetch from TMDB
year: item.show.year,
traktSource: 'continue-watching'
});
}
}
}
}
}
console.log(`[ThisWeekSection] Checking ${allSeries.length} series (Library: ${librarySeries.length}, Trakt: ${allSeries.length - librarySeries.length})`);
let allEpisodes: ThisWeekEpisode[] = []; let allEpisodes: ThisWeekEpisode[] = [];
for (const series of seriesItems) { for (const series of allSeries) {
try { try {
const metadata = await stremioService.getMetaDetails(series.type, series.id); const metadata = await stremioService.getMetaDetails(series.type, series.id);
@ -125,15 +178,23 @@ export const ThisWeekSection = React.memo(() => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [libraryItems]); }, [libraryItems, traktAuthenticated, watchlistShows, continueWatching]);
// Load episodes when library items change // Load episodes when library items or Trakt data changes
useEffect(() => { useEffect(() => {
if (!libraryLoading) { if (!libraryLoading && !traktLoading) {
console.log('[ThisWeekSection] Library items changed, refreshing episodes. Items count:', libraryItems.length); if (traktAuthenticated && (!watchlistShows || !continueWatching)) {
console.log('[ThisWeekSection] Loading Trakt collections for this week data');
loadAllCollections();
} else {
console.log('[ThisWeekSection] Data ready, refreshing episodes. Library items:', libraryItems.length);
fetchThisWeekEpisodes();
}
} else if (!libraryLoading && !traktAuthenticated) {
console.log('[ThisWeekSection] Not authenticated with Trakt, using library only. Items count:', libraryItems.length);
fetchThisWeekEpisodes(); fetchThisWeekEpisodes();
} }
}, [libraryLoading, libraryItems, fetchThisWeekEpisodes]); }, [libraryLoading, traktLoading, traktAuthenticated, libraryItems, watchlistShows, continueWatching, fetchThisWeekEpisodes, loadAllCollections]);
const handleEpisodePress = (episode: ThisWeekEpisode) => { const handleEpisodePress = (episode: ThisWeekEpisode) => {
// For upcoming episodes, go to the metadata screen // For upcoming episodes, go to the metadata screen

View file

@ -22,6 +22,7 @@ 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';
import { useTraktContext } from '../contexts/TraktContext';
import { format, parseISO, isThisWeek, isAfter, startOfToday, addWeeks, isBefore, isSameDay } from 'date-fns'; import { format, parseISO, isThisWeek, isAfter, startOfToday, addWeeks, isBefore, isSameDay } from 'date-fns';
import Animated, { FadeIn } from 'react-native-reanimated'; import Animated, { FadeIn } from 'react-native-reanimated';
import { CalendarSection } from '../components/calendar/CalendarSection'; import { CalendarSection } from '../components/calendar/CalendarSection';
@ -55,6 +56,15 @@ 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(); const { currentTheme } = useTheme();
const {
isAuthenticated: traktAuthenticated,
isLoading: traktLoading,
watchedShows,
watchlistShows,
continueWatching,
loadAllCollections
} = useTraktContext();
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);
@ -67,15 +77,83 @@ const CalendarScreen = () => {
setLoading(true); setLoading(true);
try { try {
// Filter for only series in library // Combine library series with Trakt series
const seriesItems = libraryItems.filter(item => item.type === 'series'); const librarySeries = libraryItems.filter(item => item.type === 'series');
logger.log(`[Calendar] Library items: ${libraryItems.length}, Series items: ${seriesItems.length}`); let allSeries = [...librarySeries];
// Add Trakt watchlist and watched shows if authenticated
if (traktAuthenticated) {
const traktSeriesIds = new Set();
// Add watchlist shows
if (watchlistShows) {
for (const item of watchlistShows) {
if (item.show && item.show.ids.imdb) {
const imdbId = item.show.ids.imdb;
if (!librarySeries.some(s => s.id === imdbId)) {
traktSeriesIds.add(imdbId);
allSeries.push({
id: imdbId,
name: item.show.title,
type: 'series',
poster: '', // Will try to fetch from TMDB
year: item.show.year,
traktSource: 'watchlist'
});
}
}
}
}
// Add continue watching shows
if (continueWatching) {
for (const item of continueWatching) {
if (item.type === 'episode' && item.show && item.show.ids.imdb) {
const imdbId = item.show.ids.imdb;
if (!librarySeries.some(s => s.id === imdbId) && !traktSeriesIds.has(imdbId)) {
traktSeriesIds.add(imdbId);
allSeries.push({
id: imdbId,
name: item.show.title,
type: 'series',
poster: '', // Will try to fetch from TMDB
year: item.show.year,
traktSource: 'continue-watching'
});
}
}
}
}
// Add watched shows (only recent ones to avoid too much data)
if (watchedShows) {
const recentWatched = watchedShows.slice(0, 20); // Limit to recent 20
for (const item of recentWatched) {
if (item.show && item.show.ids.imdb) {
const imdbId = item.show.ids.imdb;
if (!librarySeries.some(s => s.id === imdbId) && !traktSeriesIds.has(imdbId)) {
traktSeriesIds.add(imdbId);
allSeries.push({
id: imdbId,
name: item.show.title,
type: 'series',
poster: '', // Will try to fetch from TMDB
year: item.show.year,
traktSource: 'watched'
});
}
}
}
}
}
logger.log(`[Calendar] Total series to check: ${allSeries.length} (Library: ${librarySeries.length}, Trakt: ${allSeries.length - librarySeries.length})`);
let allEpisodes: CalendarEpisode[] = []; let allEpisodes: CalendarEpisode[] = [];
let seriesWithoutEpisodes: CalendarEpisode[] = []; let seriesWithoutEpisodes: CalendarEpisode[] = [];
// For each series, fetch upcoming episodes // For each series, fetch upcoming episodes
for (const series of seriesItems) { for (const series of allSeries) {
try { try {
logger.log(`[Calendar] Fetching episodes for series: ${series.name} (${series.id})`); logger.log(`[Calendar] Fetching episodes for series: ${series.name} (${series.id})`);
const metadata = await stremioService.getMetaDetails(series.type, series.id); const metadata = await stremioService.getMetaDetails(series.type, series.id);
@ -215,17 +293,22 @@ const CalendarScreen = () => {
setLoading(false); setLoading(false);
setRefreshing(false); setRefreshing(false);
} }
}, [libraryItems]); }, [libraryItems, traktAuthenticated, watchlistShows, continueWatching, watchedShows]);
useEffect(() => { useEffect(() => {
if (libraryItems.length > 0 && !libraryLoading) { if (!libraryLoading && !traktLoading) {
logger.log(`[Calendar] Library loaded with ${libraryItems.length} items, fetching calendar data`); if (traktAuthenticated && (!watchlistShows || !continueWatching || !watchedShows)) {
logger.log(`[Calendar] Loading Trakt collections for calendar data`);
loadAllCollections();
} else {
logger.log(`[Calendar] Data ready, fetching calendar data - Library: ${libraryItems.length} items`);
fetchCalendarData();
}
} else if (!libraryLoading && !traktAuthenticated) {
logger.log(`[Calendar] Not authenticated with Trakt, using library only (${libraryItems.length} items)`);
fetchCalendarData(); fetchCalendarData();
} else if (!libraryLoading) {
logger.log(`[Calendar] Library loaded but empty (${libraryItems.length} items)`);
setLoading(false);
} }
}, [libraryItems, libraryLoading, fetchCalendarData]); }, [libraryItems, libraryLoading, traktLoading, traktAuthenticated, watchlistShows, continueWatching, watchedShows, fetchCalendarData, loadAllCollections]);
const onRefresh = useCallback(() => { const onRefresh = useCallback(() => {
setRefreshing(true); setRefreshing(true);

View file

@ -913,7 +913,20 @@ const LibraryScreen = () => {
<View style={styles.headerSpacer} /> <View style={styles.headerSpacer} />
</> </>
) : ( ) : (
<Text style={[styles.headerTitle, { color: currentTheme.colors.white }]}>Library</Text> <>
<Text style={[styles.headerTitle, { color: currentTheme.colors.white }]}>Library</Text>
<TouchableOpacity
style={[styles.calendarButton, { backgroundColor: currentTheme.colors.primary }]}
onPress={() => navigation.navigate('Calendar')}
activeOpacity={0.7}
>
<MaterialIcons
name="event"
size={24}
color={currentTheme.colors.white}
/>
</TouchableOpacity>
</>
)} )}
</View> </View>
</View> </View>
@ -1224,6 +1237,17 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
paddingHorizontal: 16, paddingHorizontal: 16,
}, },
calendarButton: {
width: 44,
height: 44,
borderRadius: 22,
justifyContent: 'center',
alignItems: 'center',
elevation: 3,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
},
}); });
export default LibraryScreen; export default LibraryScreen;

View file

@ -401,13 +401,6 @@ const SettingsScreen: React.FC = () => {
renderControl={ChevronRight} renderControl={ChevronRight}
onPress={() => navigation.navigate('PlayerSettings')} onPress={() => navigation.navigate('PlayerSettings')}
/> />
<SettingItem
title="Calendar"
description="Episode tracking"
icon="event"
renderControl={ChevronRight}
onPress={() => navigation.navigate('Calendar')}
/>
<SettingItem <SettingItem
title="Notifications" title="Notifications"
description="Episode reminders" description="Episode reminders"