fix(highlights): scroll to highlight when clicked from /me/highlights

Pass highlightId and openHighlights in navigation state when clicking
highlights from the highlights list. This triggers the scroll behavior
in Bookmarks.tsx that was already implemented but not being used.

The useHighlightInteractions hook automatically scrolls to the selected
highlight once the article loads and the highlight mark is found in the DOM.
This commit is contained in:
Gigi
2025-10-23 00:27:35 +02:00
parent 8c79b5fd75
commit 51c0f7d923
2 changed files with 28 additions and 42 deletions

View File

@@ -212,12 +212,23 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({
pubkey,
identifier
})
navigate(`/a/${naddr}`)
// Pass highlight ID in navigation state to trigger scroll
navigate(`/a/${naddr}`, {
state: {
highlightId: highlight.id,
openHighlights: true
}
})
}
}
} else if (highlight.urlReference) {
// Navigate to external URL
navigate(`/r/${encodeURIComponent(highlight.urlReference)}`)
// Navigate to external URL with highlight ID to trigger scroll
navigate(`/r/${encodeURIComponent(highlight.urlReference)}`, {
state: {
highlightId: highlight.id,
openHighlights: true
}
})
}
}

View File

@@ -29,15 +29,6 @@ export const useReadingPosition = ({
const completionTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const lastSavedAtRef = useRef<number>(0)
const suppressUntilRef = useRef<number>(0)
const syncEnabledRef = useRef(syncEnabled)
const onSaveRef = useRef(onSave)
const scheduleSaveRef = useRef<((pos: number) => void) | null>(null)
// Keep refs in sync with props
useEffect(() => {
syncEnabledRef.current = syncEnabled
onSaveRef.current = onSave
}, [syncEnabled, onSave])
// Suppress auto-saves for a given duration (used after programmatic restore)
const suppressSavesFor = useCallback((ms: number) => {
@@ -48,7 +39,7 @@ export const useReadingPosition = ({
// Debounced save function - simple 2s debounce
const scheduleSave = useCallback((currentPosition: number) => {
if (!syncEnabledRef.current || !onSaveRef.current) {
if (!syncEnabled || !onSave) {
return
}
@@ -62,7 +53,7 @@ export const useReadingPosition = ({
lastSavedPosition.current = 1
hasSavedOnce.current = true
lastSavedAtRef.current = Date.now()
onSaveRef.current(1)
onSave(1)
return
}
@@ -85,26 +76,19 @@ export const useReadingPosition = ({
lastSavedPosition.current = currentPosition
hasSavedOnce.current = true
lastSavedAtRef.current = Date.now()
if (onSaveRef.current) {
onSaveRef.current(currentPosition)
}
onSave(currentPosition)
saveTimerRef.current = null
}, DEBOUNCE_MS)
}, [])
// Store scheduleSave in ref for use in scroll handler
useEffect(() => {
scheduleSaveRef.current = scheduleSave
}, [scheduleSave])
}, [syncEnabled, onSave])
// Immediate save function
const saveNow = useCallback(() => {
if (!syncEnabledRef.current || !onSaveRef.current) return
if (!syncEnabled || !onSave) return
// Check suppression even for saveNow (e.g., during restore)
if (Date.now() < suppressUntilRef.current) {
const remainingMs = suppressUntilRef.current - Date.now()
console.log(`[reading-position] [${new Date().toISOString()}] ⏭️ saveNow() suppressed (${remainingMs}ms remaining) at ${Math.round(positionRef.current * 100)}%`)
console.log(`[reading-position] [${new Date().toISOString()}] ⏭️ saveNow() suppressed (${remainingMs}ms remaining) at ${Math.round(position * 100)}%`)
return
}
@@ -112,12 +96,12 @@ export const useReadingPosition = ({
clearTimeout(saveTimerRef.current)
saveTimerRef.current = null
}
console.log(`[reading-position] [${new Date().toISOString()}] 💾 saveNow() called at ${Math.round(positionRef.current * 100)}%`)
lastSavedPosition.current = positionRef.current
console.log(`[reading-position] [${new Date().toISOString()}] 💾 saveNow() called at ${Math.round(position * 100)}%`)
lastSavedPosition.current = position
hasSavedOnce.current = true
lastSavedAtRef.current = Date.now()
onSaveRef.current(positionRef.current)
}, [])
onSave(position)
}, [syncEnabled, onSave, position])
useEffect(() => {
if (!enabled) return
@@ -149,7 +133,7 @@ export const useReadingPosition = ({
// Schedule auto-save if sync is enabled (unless suppressed)
if (Date.now() >= suppressUntilRef.current) {
scheduleSaveRef.current?.(clampedProgress)
scheduleSave(clampedProgress)
} else {
const remainingMs = suppressUntilRef.current - Date.now()
console.log(`[reading-position] [${new Date().toISOString()}] 🛡️ Save suppressed (${remainingMs}ms remaining) at ${Math.round(clampedProgress * 100)}%`)
@@ -196,24 +180,15 @@ export const useReadingPosition = ({
window.removeEventListener('scroll', handleScroll)
window.removeEventListener('resize', handleScroll)
// Flush pending save before unmount (don't lose progress if navigating away during debounce window)
if (saveTimerRef.current && syncEnabledRef.current && onSaveRef.current) {
// Clear save timer on unmount
if (saveTimerRef.current) {
clearTimeout(saveTimerRef.current)
saveTimerRef.current = null
// Only flush if we have unsaved progress (position differs from last saved)
const hasUnsavedProgress = Math.abs(positionRef.current - lastSavedPosition.current) >= 0.05
if (hasUnsavedProgress && Date.now() >= suppressUntilRef.current) {
console.log(`[reading-position] [${new Date().toISOString()}] 💾 Flushing pending save on unmount at ${Math.round(positionRef.current * 100)}%`)
onSaveRef.current(positionRef.current)
}
}
if (completionTimerRef.current) {
clearTimeout(completionTimerRef.current)
}
}
}, [enabled, onPositionChange, onReadingComplete, readingCompleteThreshold, completionHoldMs])
}, [enabled, onPositionChange, onReadingComplete, readingCompleteThreshold, scheduleSave, completionHoldMs])
// Reset reading complete state when enabled changes
useEffect(() => {