mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 14:44:26 +01:00
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:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
},
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user