From facffe0f19e5ad1be6a7ee96c5cceed2f6393c74 Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 13 Aug 2025 02:32:36 +0530 Subject: [PATCH] fixed continue watching --- assets/Dog Running.lottie | Bin 0 -> 5657 bytes .../home/ContinueWatchingSection.tsx | 21 ++------ src/services/storageService.ts | 50 +++++++++++++++--- 3 files changed, 48 insertions(+), 23 deletions(-) create mode 100644 assets/Dog Running.lottie diff --git a/assets/Dog Running.lottie b/assets/Dog Running.lottie new file mode 100644 index 0000000000000000000000000000000000000000..e44e106be36b093135669f0c57f891b4b38a4907 GIT binary patch literal 5657 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 P3&!7*@wbC1|MmJW=X((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 */