diff --git a/assets/Dog Running.lottie b/assets/Dog Running.lottie new file mode 100644 index 00000000..e44e106b Binary files /dev/null and b/assets/Dog Running.lottie differ diff --git a/src/components/home/ContinueWatchingSection.tsx b/src/components/home/ContinueWatchingSection.tsx index 63af0c30..76433535 100644 --- a/src/components/home/ContinueWatchingSection.tsx +++ b/src/components/home/ContinueWatchingSection.tsx @@ -475,14 +475,8 @@ const ContinueWatchingSection = React.forwardRef((props, re // Trigger haptic feedback for confirmation Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); - // Remove the watch progress - await storageService.removeWatchProgress( - item.id, - item.type, - item.type === 'series' && item.season && item.episode - ? `${item.season}:${item.episode}` - : undefined - ); + // Remove all watch progress for this content (all episodes if series) + await storageService.removeAllWatchProgressForContent(item.id, item.type, { addBaseTombstone: true }); // Also remove from Trakt playback queue if authenticated const traktService = TraktService.getInstance(); @@ -491,18 +485,13 @@ const ContinueWatchingSection = React.forwardRef((props, re await traktService.deletePlaybackForContent( item.id, item.type as 'movie' | 'series', - item.season, - item.episode + undefined, + undefined ); } // Update the list by filtering out the deleted item - setContinueWatchingItems(prev => - prev.filter(i => i.id !== item.id || - (i.type === 'series' && item.type === 'series' && - (i.season !== item.season || i.episode !== item.episode)) - ) - ); + setContinueWatchingItems(prev => prev.filter(i => i.id !== item.id)); } catch (error) { logger.error('Failed to remove watch progress:', error); } finally { diff --git a/src/services/storageService.ts b/src/services/storageService.ts index 5686774b..229de7db 100644 --- a/src/services/storageService.ts +++ b/src/services/storageService.ts @@ -172,9 +172,12 @@ class StorageService { // Do not resurrect if tombstone exists and is newer than this progress try { const tombstones = await this.getWatchProgressTombstones(); - const tombKey = this.buildWpKeyString(id, type, episodeId); - const tombAt = tombstones[tombKey]; - if (tombAt && (progress.lastUpdated == null || progress.lastUpdated <= tombAt)) { + const exactKey = this.buildWpKeyString(id, type, episodeId); + const baseKey = this.buildWpKeyString(id, type, undefined); + const exactTombAt = tombstones[exactKey]; + const baseTombAt = tombstones[baseKey]; + const newestTombAt = Math.max(exactTombAt || 0, baseTombAt || 0); + if (newestTombAt && (progress.lastUpdated == null || progress.lastUpdated <= newestTombAt)) { return; } } catch {} @@ -355,6 +358,7 @@ class StorageService { }>> { try { const allProgress = await this.getAllWatchProgress(); + const tombstones = await this.getWatchProgressTombstones(); const unsynced: Array<{ key: string; id: string; @@ -364,10 +368,13 @@ class StorageService { }> = []; for (const [key, progress] of Object.entries(allProgress)) { - // Skip if tombstoned and tombstone is newer - const tombstones = await this.getWatchProgressTombstones(); - const tombAt = tombstones[key]; - if (tombAt && (progress.lastUpdated == null || progress.lastUpdated <= tombAt)) { + // Skip if tombstoned (either exact entry or base content) and tombstone is newer + const parts = key.split(':'); + const baseKey = `${parts[0]}:${parts[1]}`; + const exactTombAt = tombstones[key]; + const baseTombAt = tombstones[baseKey]; + const newestTombAt = Math.max(exactTombAt || 0, baseTombAt || 0); + if (newestTombAt && (progress.lastUpdated == null || progress.lastUpdated <= newestTombAt)) { continue; } // Check if needs sync (either never synced or local progress is newer) @@ -398,6 +405,35 @@ class StorageService { } } + /** + * Remove all watch progress entries for a given content id and type. + * Optionally add a base tombstone to prevent reappearance. + */ + public async removeAllWatchProgressForContent( + id: string, + type: string, + options?: { addBaseTombstone?: boolean } + ): Promise { + try { + const all = await this.getAllWatchProgress(); + const prefix = `${type}:${id}`; + const removals: Array> = []; + for (const key of Object.keys(all)) { + if (key === prefix || key.startsWith(`${prefix}:`)) { + // Compute episodeId if present + const episodeId = key.length > prefix.length + 1 ? key.slice(prefix.length + 1) : undefined; + removals.push(this.removeWatchProgress(id, type, episodeId)); + } + } + await Promise.allSettled(removals); + if (options?.addBaseTombstone) { + await this.addWatchProgressTombstone(id, type); + } + } catch (error) { + logger.error('Error removing all watch progress for content:', error); + } + } + /** * Merge Trakt progress with local progress using exact time when available */