From e5b15949332b0d2dc0f8395a68b790528631aade Mon Sep 17 00:00:00 2001 From: Gigi Date: Mon, 20 Oct 2025 00:34:38 +0200 Subject: [PATCH] feat: add listener for markedAsReadChanged events Implemented event listener pattern in readingProgressController: - Added onMarkedAsReadChanged() method for subscribers - Added emitMarkedAsReadChanged() to notify when marked IDs update - Call emitMarkedAsReadChanged() after loading reactions In Me.tsx: - Subscribe to onMarkedAsReadChanged() in new useEffect - When fired, rebuild reads list with new marked-as-read items - Include marked-only items (no progress event) Now when reactions finish loading in background, /me/reads/completed will update automatically with newly marked articles. --- src/components/Me.tsx | 40 +++++++++++++++++++++++ src/services/readingProgressController.ts | 13 ++++++++ 2 files changed, 53 insertions(+) diff --git a/src/components/Me.tsx b/src/components/Me.tsx index 098b5a91..1581ae54 100644 --- a/src/components/Me.tsx +++ b/src/components/Me.tsx @@ -192,6 +192,46 @@ const Me: React.FC = ({ unsubProgress() } }, []) + + // Subscribe to marked-as-read changes and rebuild reads list + useEffect(() => { + const unsubMarkedAsRead = readingProgressController.onMarkedAsReadChanged(() => { + // Rebuild reads list including marked-as-read-only items + const progressMap = readingProgressController.getProgressMap() + const readItems: ReadItem[] = Array.from(progressMap.entries()).map(([id, progress]) => ({ + id, + source: 'reading-progress', + type: 'article', + readingProgress: progress, + markedAsRead: readingProgressController.isMarkedAsRead(id), + readingTimestamp: Math.floor(Date.now() / 1000) + })) + + // Include items that are only marked-as-read (no progress event yet) + const markedIds = readingProgressController.getMarkedAsReadIds() + for (const id of markedIds) { + if (!readItems.find(i => i.id === id)) { + const isArticle = id.startsWith('naddr1') + readItems.push({ + id, + source: 'marked-as-read', + type: isArticle ? 'article' : 'external', + url: isArticle ? undefined : id, + markedAsRead: true, + readingTimestamp: Math.floor(Date.now() / 1000) + }) + } + } + + const readsMap = new Map(readItems.map(item => [item.id, item])) + setReadsMap(readsMap) + setReads(readItems) + }) + + return () => { + unsubMarkedAsRead() + } + }, []) // Load reading progress data for writings tab useEffect(() => { diff --git a/src/services/readingProgressController.ts b/src/services/readingProgressController.ts index 526e1b63..b33742ff 100644 --- a/src/services/readingProgressController.ts +++ b/src/services/readingProgressController.ts @@ -24,6 +24,7 @@ const PROGRESS_CACHE_KEY = 'reading_progress_cache_v1' class ReadingProgressController { private progressListeners: ProgressMapCallback[] = [] private loadingListeners: LoadingCallback[] = [] + private markedAsReadListeners: (() => void)[] = [] private currentProgressMap: Map = new Map() private markedAsReadIds: Set = new Set() @@ -46,6 +47,13 @@ class ReadingProgressController { } } + onMarkedAsReadChanged(cb: () => void): () => void { + this.markedAsReadListeners.push(cb) + return () => { + this.markedAsReadListeners = this.markedAsReadListeners.filter(l => l !== cb) + } + } + private setLoading(loading: boolean): void { this.loadingListeners.forEach(cb => cb(loading)) } @@ -54,6 +62,10 @@ class ReadingProgressController { this.progressListeners.forEach(cb => cb(new Map(progressMap))) } + private emitMarkedAsReadChanged(): void { + this.markedAsReadListeners.forEach(cb => cb()) + } + /** * Get current reading progress map without triggering a reload */ @@ -378,6 +390,7 @@ class ReadingProgressController { } console.log('[readingProgress] Mark-as-read reactions complete. Total:', Array.from(this.markedAsReadIds).length) + this.emitMarkedAsReadChanged() } catch (err) { console.warn('[readingProgress] Failed to load mark-as-read reactions:', err) }