diff --git a/src/components/player/KSPlayerCore.tsx b/src/components/player/KSPlayerCore.tsx index 79bf3877..41384800 100644 --- a/src/components/player/KSPlayerCore.tsx +++ b/src/components/player/KSPlayerCore.tsx @@ -340,7 +340,10 @@ const KSPlayerCore: React.FC = () => { const resumeTarget = routeInitialPosition || watchProgress.initialPosition || watchProgress.initialSeekTargetRef?.current; if (resumeTarget && resumeTarget > 0 && !watchProgress.showResumeOverlay && data.duration > 0) { setTimeout(() => { - controls.seekToTime(resumeTarget); + if (ksPlayerRef.current) { + logger.debug(`[KSPlayerCore] Auto-resuming to ${resumeTarget}`); + ksPlayerRef.current.seek(resumeTarget); + } }, 500); } @@ -373,15 +376,17 @@ const KSPlayerCore: React.FC = () => { modals.setShowErrorModal(true); }; - const handleClose = async () => { + const handleClose = useCallback(() => { if (isSyncingBeforeClose.current) return; isSyncingBeforeClose.current = true; - await traktAutosync.handleProgressUpdate(currentTime, duration, true); - await traktAutosync.handlePlaybackEnd(currentTime, duration, 'user_close'); + // Fire and forget - don't block navigation on async operations + // The useWatchProgress and useTraktAutosync hooks handle cleanup on unmount + traktAutosync.handleProgressUpdate(currentTime, duration, true); + traktAutosync.handlePlaybackEnd(currentTime, duration, 'user_close'); navigation.goBack(); - }; + }, [navigation, currentTime, duration, traktAutosync]); // Stream selection handler const handleSelectStream = async (newStream: any) => { diff --git a/src/components/player/hooks/useWatchProgress.ts b/src/components/player/hooks/useWatchProgress.ts index 506d84ca..3f602fed 100644 --- a/src/components/player/hooks/useWatchProgress.ts +++ b/src/components/player/hooks/useWatchProgress.ts @@ -102,13 +102,17 @@ export const useWatchProgress = ( } }, [id, type, paused, currentTime, duration]); - // Unmount Save + // Unmount Save - deferred to allow navigation to complete first useEffect(() => { return () => { - if (id && type && durationRef.current > 0) { - saveWatchProgress(); - traktAutosync.handlePlaybackEnd(currentTimeRef.current, durationRef.current, 'unmount'); - } + // Use setTimeout(0) to defer save operations to next event loop tick + // This allows navigation animations to complete smoothly + setTimeout(() => { + if (id && type && durationRef.current > 0) { + saveWatchProgress(); + traktAutosync.handlePlaybackEnd(currentTimeRef.current, durationRef.current, 'unmount'); + } + }, 0); }; }, [id, type]); diff --git a/src/components/player/modals/EpisodesModal.tsx b/src/components/player/modals/EpisodesModal.tsx index 5bb8b6a2..81dba416 100644 --- a/src/components/player/modals/EpisodesModal.tsx +++ b/src/components/player/modals/EpisodesModal.tsx @@ -55,12 +55,24 @@ export const EpisodesModal: React.FC = ({ if (showEpisodesModal && metadata?.id) { setIsLoadingProgress(true); try { - const progress = await storageService.getShowProgress(metadata.id); - setEpisodeProgress(progress || {}); + // Get all watch progress and filter for this show's episodes + const allProgress = await storageService.getAllWatchProgress(); + const showPrefix = `series:${metadata.id}:`; + const progress: { [key: string]: any } = {}; + + for (const [key, value] of Object.entries(allProgress)) { + if (key.startsWith(showPrefix)) { + // Extract episode id from key (format: series:showId:episodeId) + const episodeId = key.replace(showPrefix, ''); + progress[episodeId] = value; + } + } + + setEpisodeProgress(progress); // Trakt sync logic preserved - if (await TraktService.isAuthenticated()) { - // Optional: background sync logic + if (await TraktService.getInstance().isAuthenticated()) { + // Optional: background sync logic } } catch (err) { logger.error('Failed to fetch episode progress', err); @@ -84,7 +96,7 @@ export const EpisodesModal: React.FC = ({ const currentSeasonEpisodes = groupedEpisodes[selectedSeason] || []; return ( - + setShowEpisodesModal(false)}> @@ -110,31 +122,31 @@ export const EpisodesModal: React.FC = ({ {[...seasons] - .sort((a, b) => { - if (a === 0) return 1; - if (b === 0) return -1; - return a - b; - }).map((season) => ( - setSelectedSeason(season)} - style={{ - paddingHorizontal: 16, - paddingVertical: 8, - borderRadius: 20, - backgroundColor: selectedSeason === season ? 'white' : 'rgba(255,255,255,0.06)', - borderWidth: 1, - borderColor: selectedSeason === season ? 'white' : 'rgba(255,255,255,0.1)', - }} - > - - {season === 0 ? 'Specials' : `Season ${season}`} - - - ))} + .sort((a, b) => { + if (a === 0) return 1; + if (b === 0) return -1; + return a - b; + }).map((season) => ( + setSelectedSeason(season)} + style={{ + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 20, + backgroundColor: selectedSeason === season ? 'white' : 'rgba(255,255,255,0.06)', + borderWidth: 1, + borderColor: selectedSeason === season ? 'white' : 'rgba(255,255,255,0.1)', + }} + > + + {season === 0 ? 'Specials' : `Season ${season}`} + + + ))}