fix(highlights): preserve immediate UI highlight after creation by merging streaming results instead of overwriting in article and external URL loaders

This commit is contained in:
Gigi
2025-10-20 09:07:42 +02:00
parent 8ff3f08d8c
commit d2181ad772
3 changed files with 14 additions and 35 deletions

View File

@@ -74,19 +74,18 @@ export function useArticleLoader({
try {
setHighlightsLoading(true)
setHighlights([]) // Clear old highlights
const highlightsMap = new Map<string, Highlight>()
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
)

View File

@@ -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<string>(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)
})
},

View File

@@ -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 }
}