mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-29 13:59:58 +00:00
Enhance HeroSection and watch progress management with Trakt integration
This update improves the HeroSection component by adding functionality to determine if content has been watched based on Trakt progress. The ActionButtons now reflect the watched state with updated styles and indicators. Additionally, the useWatchProgress hook has been modified to always show current episode progress, ensuring accurate display of watched status. The useTraktIntegration hook has also been enhanced to fetch and merge watched movies, improving synchronization with Trakt.
This commit is contained in:
parent
cdec184c14
commit
671861c207
3 changed files with 277 additions and 99 deletions
|
|
@ -77,7 +77,9 @@ const ActionButtons = React.memo(({
|
|||
id,
|
||||
navigation,
|
||||
playButtonText,
|
||||
animatedStyle
|
||||
animatedStyle,
|
||||
isWatched,
|
||||
watchProgress
|
||||
}: {
|
||||
handleShowStreams: () => void;
|
||||
toggleLibrary: () => void;
|
||||
|
|
@ -87,6 +89,8 @@ const ActionButtons = React.memo(({
|
|||
navigation: any;
|
||||
playButtonText: string;
|
||||
animatedStyle: any;
|
||||
isWatched: boolean;
|
||||
watchProgress: any;
|
||||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
|
||||
|
|
@ -122,19 +126,48 @@ const ActionButtons = React.memo(({
|
|||
}
|
||||
}, [id, navigation]);
|
||||
|
||||
// Determine play button style and text based on watched status
|
||||
const playButtonStyle = useMemo(() => {
|
||||
if (isWatched) {
|
||||
return [styles.actionButton, styles.playButton, styles.watchedPlayButton];
|
||||
}
|
||||
return [styles.actionButton, styles.playButton];
|
||||
}, [isWatched]);
|
||||
|
||||
const playButtonTextStyle = useMemo(() => {
|
||||
if (isWatched) {
|
||||
return [styles.playButtonText, styles.watchedPlayButtonText];
|
||||
}
|
||||
return styles.playButtonText;
|
||||
}, [isWatched]);
|
||||
|
||||
const finalPlayButtonText = useMemo(() => {
|
||||
if (isWatched) {
|
||||
return 'Watch Again';
|
||||
}
|
||||
return playButtonText;
|
||||
}, [isWatched, playButtonText]);
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.actionButtons, animatedStyle]}>
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, styles.playButton]}
|
||||
style={playButtonStyle}
|
||||
onPress={handleShowStreams}
|
||||
activeOpacity={0.85}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={playButtonText === 'Resume' ? "play-circle-outline" : "play-arrow"}
|
||||
name={isWatched ? "replay" : (playButtonText === 'Resume' ? "play-circle-outline" : "play-arrow")}
|
||||
size={24}
|
||||
color="#000"
|
||||
color={isWatched ? "#fff" : "#000"}
|
||||
/>
|
||||
<Text style={styles.playButtonText}>{playButtonText}</Text>
|
||||
<Text style={playButtonTextStyle}>{finalPlayButtonText}</Text>
|
||||
|
||||
{/* Subtle watched indicator in play button */}
|
||||
{isWatched && (
|
||||
<View style={styles.watchedIndicator}>
|
||||
<MaterialIcons name="check" size={12} color="#fff" />
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
|
|
@ -199,12 +232,13 @@ const ActionButtons = React.memo(({
|
|||
);
|
||||
});
|
||||
|
||||
// Enhanced WatchProgress Component with Trakt integration
|
||||
// Enhanced WatchProgress Component with Trakt integration and watched status
|
||||
const WatchProgressDisplay = React.memo(({
|
||||
watchProgress,
|
||||
type,
|
||||
getEpisodeDetails,
|
||||
animatedStyle,
|
||||
isWatched
|
||||
}: {
|
||||
watchProgress: {
|
||||
currentTime: number;
|
||||
|
|
@ -217,15 +251,68 @@ const WatchProgressDisplay = React.memo(({
|
|||
type: 'movie' | 'series';
|
||||
getEpisodeDetails: (episodeId: string) => { seasonNumber: string; episodeNumber: string; episodeName: string } | null;
|
||||
animatedStyle: any;
|
||||
isWatched: boolean;
|
||||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const { isAuthenticated: isTraktAuthenticated } = useTraktContext();
|
||||
const { isAuthenticated: isTraktAuthenticated, forceSyncTraktProgress } = useTraktContext();
|
||||
|
||||
// Handle manual Trakt sync
|
||||
const handleTraktSync = useMemo(() => async () => {
|
||||
if (isTraktAuthenticated && forceSyncTraktProgress) {
|
||||
logger.log('[HeroSection] Manual Trakt sync requested');
|
||||
try {
|
||||
const success = await forceSyncTraktProgress();
|
||||
logger.log(`[HeroSection] Manual Trakt sync ${success ? 'successful' : 'failed'}`);
|
||||
} catch (error) {
|
||||
logger.error('[HeroSection] Manual Trakt sync error:', error);
|
||||
}
|
||||
}
|
||||
}, [isTraktAuthenticated, forceSyncTraktProgress]);
|
||||
|
||||
// Memoized progress calculation with Trakt integration
|
||||
const progressData = useMemo(() => {
|
||||
// If content is fully watched, show watched status instead of progress
|
||||
if (isWatched) {
|
||||
let episodeInfo = '';
|
||||
if (type === 'series' && watchProgress?.episodeId) {
|
||||
const details = getEpisodeDetails(watchProgress.episodeId);
|
||||
if (details) {
|
||||
episodeInfo = ` • S${details.seasonNumber}:E${details.episodeNumber}${details.episodeName ? ` - ${details.episodeName}` : ''}`;
|
||||
}
|
||||
}
|
||||
|
||||
const watchedDate = watchProgress?.lastUpdated
|
||||
? new Date(watchProgress.lastUpdated).toLocaleDateString()
|
||||
: new Date().toLocaleDateString();
|
||||
|
||||
// Determine if watched via Trakt or local
|
||||
const watchedViaTrakt = isTraktAuthenticated &&
|
||||
watchProgress?.traktProgress !== undefined &&
|
||||
watchProgress.traktProgress >= 95;
|
||||
|
||||
return {
|
||||
progressPercent: 100,
|
||||
formattedTime: watchedDate,
|
||||
episodeInfo,
|
||||
displayText: watchedViaTrakt ? 'Watched on Trakt' : 'Watched',
|
||||
syncStatus: isTraktAuthenticated && watchProgress?.traktSynced ? '' : '', // Clean look for watched
|
||||
isTraktSynced: watchProgress?.traktSynced && isTraktAuthenticated,
|
||||
isWatched: true
|
||||
};
|
||||
}
|
||||
|
||||
if (!watchProgress || watchProgress.duration === 0) return null;
|
||||
|
||||
const progressPercent = (watchProgress.currentTime / watchProgress.duration) * 100;
|
||||
// Determine which progress to show - prioritize Trakt if available and authenticated
|
||||
let progressPercent;
|
||||
let isUsingTraktProgress = false;
|
||||
|
||||
if (isTraktAuthenticated && watchProgress.traktProgress !== undefined) {
|
||||
progressPercent = watchProgress.traktProgress;
|
||||
isUsingTraktProgress = true;
|
||||
} else {
|
||||
progressPercent = (watchProgress.currentTime / watchProgress.duration) * 100;
|
||||
}
|
||||
const formattedTime = new Date(watchProgress.lastUpdated).toLocaleDateString();
|
||||
let episodeInfo = '';
|
||||
|
||||
|
|
@ -242,7 +329,12 @@ const WatchProgressDisplay = React.memo(({
|
|||
|
||||
// Show Trakt sync status if user is authenticated
|
||||
if (isTraktAuthenticated) {
|
||||
if (watchProgress.traktSynced) {
|
||||
if (isUsingTraktProgress) {
|
||||
syncStatus = ' • Using Trakt progress';
|
||||
if (watchProgress.traktSynced) {
|
||||
syncStatus = ' • Synced with Trakt';
|
||||
}
|
||||
} else if (watchProgress.traktSynced) {
|
||||
syncStatus = ' • Synced with Trakt';
|
||||
// If we have specific Trakt progress that differs from local, mention it
|
||||
if (watchProgress.traktProgress !== undefined &&
|
||||
|
|
@ -260,9 +352,10 @@ const WatchProgressDisplay = React.memo(({
|
|||
episodeInfo,
|
||||
displayText,
|
||||
syncStatus,
|
||||
isTraktSynced: watchProgress.traktSynced && isTraktAuthenticated
|
||||
isTraktSynced: watchProgress.traktSynced && isTraktAuthenticated,
|
||||
isWatched: false
|
||||
};
|
||||
}, [watchProgress, type, getEpisodeDetails, isTraktAuthenticated]);
|
||||
}, [watchProgress, type, getEpisodeDetails, isTraktAuthenticated, isWatched]);
|
||||
|
||||
if (!progressData) return null;
|
||||
|
||||
|
|
@ -274,14 +367,26 @@ const WatchProgressDisplay = React.memo(({
|
|||
styles.watchProgressFill,
|
||||
{
|
||||
width: `${progressData.progressPercent}%`,
|
||||
backgroundColor: progressData.isTraktSynced
|
||||
? '#E50914' // Netflix red for Trakt synced content
|
||||
: currentTheme.colors.primary
|
||||
backgroundColor: progressData.isWatched
|
||||
? '#666' // Subtle gray for completed
|
||||
: progressData.isTraktSynced
|
||||
? '#E50914' // Netflix red for Trakt synced content
|
||||
: currentTheme.colors.primary
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{/* Trakt sync indicator */}
|
||||
{progressData.isTraktSynced && (
|
||||
{/* Subtle watched indicator */}
|
||||
{progressData.isWatched && (
|
||||
<View style={styles.watchedProgressIndicator}>
|
||||
<MaterialIcons
|
||||
name="check"
|
||||
size={6}
|
||||
color="rgba(255,255,255,0.8)"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{/* Trakt sync indicator for non-watched content */}
|
||||
{progressData.isTraktSynced && !progressData.isWatched && (
|
||||
<View style={styles.traktSyncIndicator}>
|
||||
<MaterialIcons
|
||||
name="sync"
|
||||
|
|
@ -291,10 +396,30 @@ const WatchProgressDisplay = React.memo(({
|
|||
</View>
|
||||
)}
|
||||
</View>
|
||||
<Text style={[styles.watchProgressText, { color: currentTheme.colors.textMuted }]}>
|
||||
{progressData.displayText}{progressData.episodeInfo} • Last watched on {progressData.formattedTime}
|
||||
{progressData.syncStatus}
|
||||
</Text>
|
||||
<View style={styles.watchProgressTextContainer}>
|
||||
<Text style={[styles.watchProgressText, {
|
||||
color: progressData.isWatched ? 'rgba(255,255,255,0.6)' : currentTheme.colors.textMuted,
|
||||
fontSize: progressData.isWatched ? 10 : 11
|
||||
}]}>
|
||||
{progressData.displayText}{progressData.episodeInfo} • Last watched on {progressData.formattedTime}
|
||||
{progressData.syncStatus}
|
||||
</Text>
|
||||
|
||||
{/* Manual Trakt sync button */}
|
||||
{isTraktAuthenticated && forceSyncTraktProgress && (
|
||||
<TouchableOpacity
|
||||
style={styles.traktSyncButton}
|
||||
onPress={handleTraktSync}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="refresh"
|
||||
size={14}
|
||||
color={currentTheme.colors.textMuted}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
});
|
||||
|
|
@ -452,6 +577,25 @@ const HeroSection: React.FC<HeroSectionProps> = ({
|
|||
// Memoized play button text
|
||||
const playButtonText = useMemo(() => getPlayButtonText(), [getPlayButtonText]);
|
||||
|
||||
// Calculate if content is watched (>=95% progress) - check both local and Trakt progress
|
||||
const isWatched = useMemo(() => {
|
||||
if (!watchProgress) return false;
|
||||
|
||||
// Check Trakt progress first if available and user is authenticated
|
||||
if (isTraktAuthenticated && watchProgress.traktProgress !== undefined) {
|
||||
const traktWatched = watchProgress.traktProgress >= 95;
|
||||
logger.log(`[HeroSection] Trakt authenticated: ${isTraktAuthenticated}, Trakt progress: ${watchProgress.traktProgress}%, Watched: ${traktWatched}`);
|
||||
return traktWatched;
|
||||
}
|
||||
|
||||
// Fall back to local progress
|
||||
if (watchProgress.duration === 0) return false;
|
||||
const progressPercent = (watchProgress.currentTime / watchProgress.duration) * 100;
|
||||
const localWatched = progressPercent >= 95;
|
||||
logger.log(`[HeroSection] Local progress: ${progressPercent.toFixed(1)}%, Watched: ${localWatched}`);
|
||||
return localWatched;
|
||||
}, [watchProgress, isTraktAuthenticated]);
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.heroSection, heroAnimatedStyle]}>
|
||||
{/* Optimized Background */}
|
||||
|
|
@ -521,6 +665,7 @@ const HeroSection: React.FC<HeroSectionProps> = ({
|
|||
type={type}
|
||||
getEpisodeDetails={getEpisodeDetails}
|
||||
animatedStyle={watchProgressAnimatedStyle}
|
||||
isWatched={isWatched}
|
||||
/>
|
||||
|
||||
{/* Optimized Genres */}
|
||||
|
|
@ -540,6 +685,8 @@ const HeroSection: React.FC<HeroSectionProps> = ({
|
|||
navigation={navigation}
|
||||
playButtonText={playButtonText}
|
||||
animatedStyle={buttonsAnimatedStyle}
|
||||
isWatched={isWatched}
|
||||
watchProgress={watchProgress}
|
||||
/>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
|
|
@ -623,6 +770,7 @@ const styles = StyleSheet.create({
|
|||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
},
|
||||
actionButton: {
|
||||
flexDirection: 'row',
|
||||
|
|
@ -697,11 +845,32 @@ const styles = StyleSheet.create({
|
|||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
watchedProgressIndicator: {
|
||||
position: 'absolute',
|
||||
right: 2,
|
||||
top: -1,
|
||||
bottom: -1,
|
||||
width: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
watchProgressTextContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
watchProgressText: {
|
||||
fontSize: 11,
|
||||
textAlign: 'center',
|
||||
opacity: 0.85,
|
||||
letterSpacing: 0.1
|
||||
letterSpacing: 0.1,
|
||||
flex: 1,
|
||||
},
|
||||
traktSyncButton: {
|
||||
padding: 4,
|
||||
borderRadius: 12,
|
||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||
},
|
||||
blurBackground: {
|
||||
position: 'absolute',
|
||||
|
|
@ -737,6 +906,33 @@ const styles = StyleSheet.create({
|
|||
borderRadius: 25,
|
||||
backgroundColor: 'rgba(255,255,255,0.15)',
|
||||
},
|
||||
watchedIndicator: {
|
||||
position: 'absolute',
|
||||
top: 4,
|
||||
right: 4,
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
borderRadius: 8,
|
||||
width: 16,
|
||||
height: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
watchedPlayButton: {
|
||||
backgroundColor: '#1e1e1e',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.3)',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
},
|
||||
watchedPlayButtonText: {
|
||||
color: '#fff',
|
||||
fontWeight: '700',
|
||||
marginLeft: 6,
|
||||
fontSize: 15,
|
||||
},
|
||||
});
|
||||
|
||||
export default React.memo(HeroSection);
|
||||
|
|
@ -281,15 +281,16 @@ export function useTraktIntegration() {
|
|||
}
|
||||
|
||||
try {
|
||||
logger.log('[useTraktIntegration] Fetching Trakt playback progress...');
|
||||
const traktProgress = await getTraktPlaybackProgress();
|
||||
logger.log(`[useTraktIntegration] Retrieved ${traktProgress.length} Trakt progress items`);
|
||||
// Fetch both playback progress and recently watched movies
|
||||
logger.log('[useTraktIntegration] Fetching Trakt playback progress and watched movies...');
|
||||
const [traktProgress, watchedMovies] = await Promise.all([
|
||||
getTraktPlaybackProgress(),
|
||||
traktService.getWatchedMovies()
|
||||
]);
|
||||
|
||||
if (traktProgress.length === 0) {
|
||||
logger.log('[useTraktIntegration] No Trakt progress found - user may not have any content in progress');
|
||||
return true; // Not an error, just no data
|
||||
}
|
||||
logger.log(`[useTraktIntegration] Retrieved ${traktProgress.length} Trakt progress items, ${watchedMovies.length} watched movies`);
|
||||
|
||||
// Process playback progress (in-progress items)
|
||||
for (const item of traktProgress) {
|
||||
try {
|
||||
let id: string;
|
||||
|
|
@ -299,14 +300,14 @@ export function useTraktIntegration() {
|
|||
if (item.type === 'movie' && item.movie) {
|
||||
id = item.movie.ids.imdb;
|
||||
type = 'movie';
|
||||
logger.log(`[useTraktIntegration] Processing Trakt movie: ${item.movie.title} (${id}) - ${item.progress}%`);
|
||||
logger.log(`[useTraktIntegration] Processing Trakt movie progress: ${item.movie.title} (${id}) - ${item.progress}%`);
|
||||
} else if (item.type === 'episode' && item.show && item.episode) {
|
||||
id = item.show.ids.imdb;
|
||||
type = 'series';
|
||||
episodeId = `${id}:${item.episode.season}:${item.episode.number}`;
|
||||
logger.log(`[useTraktIntegration] Processing Trakt episode: ${item.show.title} S${item.episode.season}E${item.episode.number} (${id}) - ${item.progress}%`);
|
||||
logger.log(`[useTraktIntegration] Processing Trakt episode progress: ${item.show.title} S${item.episode.season}E${item.episode.number} (${id}) - ${item.progress}%`);
|
||||
} else {
|
||||
logger.warn(`[useTraktIntegration] Skipping invalid Trakt item:`, item);
|
||||
logger.warn(`[useTraktIntegration] Skipping invalid Trakt progress item:`, item);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +324,27 @@ export function useTraktIntegration() {
|
|||
}
|
||||
}
|
||||
|
||||
logger.log(`[useTraktIntegration] Successfully merged ${traktProgress.length} Trakt progress entries`);
|
||||
// Process watched movies (100% completed)
|
||||
for (const movie of watchedMovies) {
|
||||
try {
|
||||
if (movie.movie?.ids?.imdb) {
|
||||
const id = movie.movie.ids.imdb;
|
||||
const watchedAt = movie.last_watched_at;
|
||||
logger.log(`[useTraktIntegration] Processing watched movie: ${movie.movie.title} (${id}) - 100% watched on ${watchedAt}`);
|
||||
|
||||
await storageService.mergeWithTraktProgress(
|
||||
id,
|
||||
'movie',
|
||||
100, // 100% progress for watched items
|
||||
watchedAt
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[useTraktIntegration] Error merging watched movie:', error);
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(`[useTraktIntegration] Successfully merged ${traktProgress.length} progress items + ${watchedMovies.length} watched movies`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('[useTraktIntegration] Error fetching and merging Trakt progress:', error);
|
||||
|
|
@ -362,6 +383,24 @@ export function useTraktIntegration() {
|
|||
}
|
||||
}, [isAuthenticated, fetchAndMergeTraktProgress]);
|
||||
|
||||
// Periodic sync - check for updates every 2 minutes when authenticated
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) return;
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
logger.log('[useTraktIntegration] Periodic Trakt sync check');
|
||||
fetchAndMergeTraktProgress().then((success) => {
|
||||
if (success) {
|
||||
logger.log('[useTraktIntegration] Periodic sync completed successfully');
|
||||
}
|
||||
}).catch(error => {
|
||||
logger.error('[useTraktIntegration] Periodic sync failed:', error);
|
||||
});
|
||||
}, 2 * 60 * 1000); // 2 minutes
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [isAuthenticated, fetchAndMergeTraktProgress]);
|
||||
|
||||
// Trigger sync when auth status is manually refreshed (for login scenarios)
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
|
|
|
|||
|
|
@ -91,62 +91,8 @@ export const useWatchProgress = (
|
|||
if (episodeId) {
|
||||
const progress = await storageService.getWatchProgress(id, type, episodeId);
|
||||
if (progress) {
|
||||
const progressPercent = (progress.currentTime / progress.duration) * 100;
|
||||
|
||||
// If current episode is finished (≥95%), try to find next unwatched episode
|
||||
if (progressPercent >= 95) {
|
||||
const currentEpNum = getEpisodeNumber(episodeId);
|
||||
if (currentEpNum && episodes.length > 0) {
|
||||
// Find the next episode
|
||||
const nextEpisode = episodes.find(ep => {
|
||||
// First check in same season
|
||||
if (ep.season_number === currentEpNum.season && ep.episode_number > currentEpNum.episode) {
|
||||
const epId = ep.stremioId || `${id}:${ep.season_number}:${ep.episode_number}`;
|
||||
const epProgress = seriesProgresses.find(p => p.episodeId === epId);
|
||||
if (!epProgress) return true;
|
||||
const percent = (epProgress.progress.currentTime / epProgress.progress.duration) * 100;
|
||||
return percent < 95;
|
||||
}
|
||||
// Then check next seasons
|
||||
if (ep.season_number > currentEpNum.season) {
|
||||
const epId = ep.stremioId || `${id}:${ep.season_number}:${ep.episode_number}`;
|
||||
const epProgress = seriesProgresses.find(p => p.episodeId === epId);
|
||||
if (!epProgress) return true;
|
||||
const percent = (epProgress.progress.currentTime / epProgress.progress.duration) * 100;
|
||||
return percent < 95;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (nextEpisode) {
|
||||
const nextEpisodeId = nextEpisode.stremioId ||
|
||||
`${id}:${nextEpisode.season_number}:${nextEpisode.episode_number}`;
|
||||
const nextProgress = await storageService.getWatchProgress(id, type, nextEpisodeId);
|
||||
if (nextProgress) {
|
||||
setWatchProgress({
|
||||
...nextProgress,
|
||||
episodeId: nextEpisodeId,
|
||||
traktSynced: nextProgress.traktSynced,
|
||||
traktProgress: nextProgress.traktProgress
|
||||
});
|
||||
} else {
|
||||
setWatchProgress({
|
||||
currentTime: 0,
|
||||
duration: 0,
|
||||
lastUpdated: Date.now(),
|
||||
episodeId: nextEpisodeId,
|
||||
traktSynced: false
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If no next episode found or current episode is finished, show no progress
|
||||
setWatchProgress(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// If current episode is not finished, show its progress
|
||||
// Always show the current episode progress when viewing it specifically
|
||||
// This allows HeroSection to properly display watched state
|
||||
setWatchProgress({
|
||||
...progress,
|
||||
episodeId,
|
||||
|
|
@ -194,17 +140,14 @@ export const useWatchProgress = (
|
|||
// For movies
|
||||
const progress = await storageService.getWatchProgress(id, type, episodeId);
|
||||
if (progress && progress.currentTime > 0) {
|
||||
const progressPercent = (progress.currentTime / progress.duration) * 100;
|
||||
if (progressPercent >= 95) {
|
||||
setWatchProgress(null);
|
||||
} else {
|
||||
setWatchProgress({
|
||||
...progress,
|
||||
episodeId,
|
||||
traktSynced: progress.traktSynced,
|
||||
traktProgress: progress.traktProgress
|
||||
});
|
||||
}
|
||||
// Always show progress data, even if watched (≥95%)
|
||||
// The HeroSection will handle the "watched" state display
|
||||
setWatchProgress({
|
||||
...progress,
|
||||
episodeId,
|
||||
traktSynced: progress.traktSynced,
|
||||
traktProgress: progress.traktProgress
|
||||
});
|
||||
} else {
|
||||
setWatchProgress(null);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue