diff --git a/src/hooks/useArticleLoader.ts b/src/hooks/useArticleLoader.ts index 149d04c3..74b25d74 100644 --- a/src/hooks/useArticleLoader.ts +++ b/src/hooks/useArticleLoader.ts @@ -74,19 +74,18 @@ export function useArticleLoader({ try { setHighlightsLoading(true) setHighlights([]) // Clear old highlights - const highlightsMap = new Map() await fetchHighlightsForArticle( relayPool, articleCoordinate, article.event.id, (highlight) => { - // Deduplicate highlights by ID as they arrive - if (!highlightsMap.has(highlight.id)) { - highlightsMap.set(highlight.id, highlight) - const highlightsList = Array.from(highlightsMap.values()) - setHighlights(highlightsList.sort((a, b) => b.created_at - a.created_at)) - } + // Merge streaming results with existing UI state to preserve locally created highlights + setHighlights((prev) => { + if (prev.some(h => h.id === highlight.id)) return prev + const next = [highlight, ...prev] + return next.sort((a, b) => b.created_at - a.created_at) + }) }, settings ) diff --git a/src/hooks/useExternalUrlLoader.ts b/src/hooks/useExternalUrlLoader.ts index ecd103fe..fe792ff9 100644 --- a/src/hooks/useExternalUrlLoader.ts +++ b/src/hooks/useExternalUrlLoader.ts @@ -87,7 +87,13 @@ export function useExternalUrlLoader({ // Seed with cached highlights first if (cachedUrlHighlights.length > 0) { - setHighlights(cachedUrlHighlights.sort((a, b) => b.created_at - a.created_at)) + setHighlights((prev) => { + // Seed with cache but keep any locally created highlights already in state + const seen = new Set(cachedUrlHighlights.map(h => h.id)) + const localOnly = prev.filter(h => !seen.has(h.id)) + const next = [...cachedUrlHighlights, ...localOnly] + return next.sort((a, b) => b.created_at - a.created_at) + }) } else { setHighlights([]) } @@ -106,7 +112,7 @@ export function useExternalUrlLoader({ seen.add(highlight.id) setHighlights((prev) => { if (prev.some(h => h.id === highlight.id)) return prev - const next = [...prev, highlight] + const next = [highlight, ...prev] return next.sort((a, b) => b.created_at - a.created_at) }) }, diff --git a/src/hooks/useHighlightInteractions.ts b/src/hooks/useHighlightInteractions.ts index b0be1bc7..024db59a 100644 --- a/src/hooks/useHighlightInteractions.ts +++ b/src/hooks/useHighlightInteractions.ts @@ -113,32 +113,6 @@ export const useHighlightInteractions = ({ }, 10) }, [onTextSelection, onClearSelection]) - // Listen to document-level selection changes to catch keyboard/touch/ios cases - useEffect(() => { - const handler = () => { - // Defer to allow DOM selection to settle - setTimeout(() => { - const selection = window.getSelection() - if (!selection || selection.rangeCount === 0) { - onClearSelection?.() - return - } - - const range = selection.getRangeAt(0) - const text = selection.toString().trim() - - if (text.length > 0 && contentRef.current?.contains(range.commonAncestorContainer)) { - onTextSelection?.(text) - } else { - onClearSelection?.() - } - }, 10) - } - - document.addEventListener('selectionchange', handler) - return () => document.removeEventListener('selectionchange', handler) - }, [onTextSelection, onClearSelection]) - return { contentRef, handleSelectionEnd } }