mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 06:34:24 +01:00
debug(reading): add comprehensive logging for position restore and save
Add detailed console logs to trace: - Position collection and stabilization - Save scheduling, suppression, and execution - Restore calculations and decisions - Scroll deltas and thresholds Logs use [reading-position] prefix with emoji indicators for easy filtering and visual scanning.
This commit is contained in:
@@ -162,20 +162,24 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
// Callback to save reading position
|
||||
const handleSavePosition = useCallback(async (position: number) => {
|
||||
if (!activeAccount || !relayPool || !eventStore || !articleIdentifier) {
|
||||
console.log('[reading-position] ❌ Cannot save: missing dependencies')
|
||||
return
|
||||
}
|
||||
if (!settings?.syncReadingPosition) {
|
||||
console.log('[reading-position] ⚠️ Save skipped: sync disabled in settings')
|
||||
return
|
||||
}
|
||||
|
||||
// Check if content is long enough to track reading progress
|
||||
if (!shouldTrackReadingProgress(html, markdown)) {
|
||||
console.log('[reading-position] ⚠️ Save skipped: content too short')
|
||||
return
|
||||
}
|
||||
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||
|
||||
try {
|
||||
console.log('[reading-position] 🚀 Publishing position', Math.round(position * 100) + '% to relays...')
|
||||
const factory = new EventFactory({ signer: activeAccount })
|
||||
await saveReadingPosition(
|
||||
relayPool,
|
||||
@@ -188,8 +192,9 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
scrollTop
|
||||
}
|
||||
)
|
||||
console.log('[reading-position] ✅ Position published successfully')
|
||||
} catch (error) {
|
||||
console.error('[progress] ❌ ContentPanel: Failed to save reading position:', error)
|
||||
console.error('[reading-position] ❌ Failed to save reading position:', error)
|
||||
}
|
||||
}, [activeAccount, relayPool, eventStore, articleIdentifier, settings?.syncReadingPosition, html, markdown])
|
||||
|
||||
@@ -217,12 +222,15 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
// Load saved reading position when article loads (stabilized one-shot restore)
|
||||
useEffect(() => {
|
||||
if (!isTextContent || !activeAccount || !relayPool || !eventStore || !articleIdentifier) {
|
||||
console.log('[reading-position] ⏭️ Restore skipped: missing dependencies or not text content')
|
||||
return
|
||||
}
|
||||
if (settings?.syncReadingPosition === false) {
|
||||
console.log('[reading-position] ⏭️ Restore skipped: sync disabled in settings')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[reading-position] 🔄 Initiating restore for article:', articleIdentifier)
|
||||
const collector = collectReadingPositionsOnce({
|
||||
relayPool,
|
||||
eventStore,
|
||||
@@ -232,7 +240,12 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
})
|
||||
|
||||
collector.onStable((bestPosition) => {
|
||||
if (!bestPosition) return
|
||||
if (!bestPosition) {
|
||||
console.log('[reading-position] ℹ️ No position to restore')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[reading-position] 🎯 Stable position received:', Math.round(bestPosition.position * 100) + '%')
|
||||
|
||||
// Wait for content to be fully rendered
|
||||
setTimeout(() => {
|
||||
@@ -242,13 +255,25 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
const currentTop = window.pageYOffset || document.documentElement.scrollTop
|
||||
const targetTop = bestPosition.position * maxScroll
|
||||
|
||||
console.log('[reading-position] 📐 Restore calculation:', {
|
||||
docHeight: docH,
|
||||
winHeight: winH,
|
||||
maxScroll,
|
||||
currentTop,
|
||||
targetTop,
|
||||
targetPercent: Math.round(bestPosition.position * 100) + '%'
|
||||
})
|
||||
|
||||
// Skip if delta is too small (< 48px or < 5%)
|
||||
const deltaPx = Math.abs(targetTop - currentTop)
|
||||
const deltaPct = maxScroll > 0 ? Math.abs((targetTop - currentTop) / maxScroll) : 0
|
||||
if (deltaPx < 48 || deltaPct < 0.05) {
|
||||
console.log('[reading-position] ⏭️ Restore skipped: delta too small (', deltaPx, 'px,', Math.round(deltaPct * 100) + '%)')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[reading-position] 📜 Restoring scroll position (delta:', deltaPx, 'px,', Math.round(deltaPct * 100) + '%)')
|
||||
|
||||
// Suppress saves briefly to avoid feedback loop
|
||||
if (suppressSavesFor) {
|
||||
suppressSavesFor(1500)
|
||||
@@ -259,10 +284,14 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
top: targetTop,
|
||||
behavior: 'auto'
|
||||
})
|
||||
console.log('[reading-position] ✅ Scroll restored to', Math.round(bestPosition.position * 100) + '%')
|
||||
}, 500) // Give content time to render
|
||||
})
|
||||
|
||||
return () => collector.stop()
|
||||
return () => {
|
||||
console.log('[reading-position] 🛑 Stopping restore collector')
|
||||
collector.stop()
|
||||
}
|
||||
}, [isTextContent, activeAccount, relayPool, eventStore, articleIdentifier, settings?.syncReadingPosition, selectedUrl, suppressSavesFor])
|
||||
|
||||
// Save position before unmounting or changing article
|
||||
|
||||
@@ -34,7 +34,9 @@ export const useReadingPosition = ({
|
||||
|
||||
// Suppress auto-saves for a given duration (used after programmatic restore)
|
||||
const suppressSavesFor = useCallback((ms: number) => {
|
||||
suppressUntilRef.current = Date.now() + ms
|
||||
const until = Date.now() + ms
|
||||
suppressUntilRef.current = until
|
||||
console.log('[reading-position] 🛡️ Suppressing saves for', ms, 'ms until', new Date(until).toISOString())
|
||||
}, [])
|
||||
|
||||
// Debounced save function
|
||||
@@ -49,6 +51,7 @@ export const useReadingPosition = ({
|
||||
clearTimeout(saveTimerRef.current)
|
||||
saveTimerRef.current = null
|
||||
}
|
||||
console.log('[reading-position] 💾 Instant save at 100% completion')
|
||||
lastSavedPosition.current = 1
|
||||
hasSavedOnce.current = true
|
||||
lastSavedAtRef.current = Date.now()
|
||||
@@ -80,7 +83,9 @@ export const useReadingPosition = ({
|
||||
}
|
||||
const remaining = Math.max(0, MIN_INTERVAL_MS - (nowMs - lastSavedAtRef.current))
|
||||
const delay = Math.max(autoSaveInterval, remaining)
|
||||
console.log('[reading-position] ⏱️ Rescheduling save in', delay, 'ms (pos:', Math.round(currentPosition * 100) + '%)')
|
||||
saveTimerRef.current = setTimeout(() => {
|
||||
console.log('[reading-position] 💾 Auto-save (rescheduled) at', Math.round(currentPosition * 100) + '%')
|
||||
lastSavedPosition.current = currentPosition
|
||||
hasSavedOnce.current = true
|
||||
lastSavedAtRef.current = Date.now()
|
||||
@@ -96,7 +101,9 @@ export const useReadingPosition = ({
|
||||
|
||||
// Schedule new save using the larger of autoSaveInterval and MIN_INTERVAL_MS
|
||||
const delay = Math.max(autoSaveInterval, MIN_INTERVAL_MS)
|
||||
console.log('[reading-position] ⏱️ Scheduling save in', delay, 'ms (pos:', Math.round(currentPosition * 100) + '%)')
|
||||
saveTimerRef.current = setTimeout(() => {
|
||||
console.log('[reading-position] 💾 Auto-save at', Math.round(currentPosition * 100) + '%')
|
||||
lastSavedPosition.current = currentPosition
|
||||
hasSavedOnce.current = true
|
||||
lastSavedAtRef.current = Date.now()
|
||||
@@ -111,6 +118,7 @@ export const useReadingPosition = ({
|
||||
clearTimeout(saveTimerRef.current)
|
||||
saveTimerRef.current = null
|
||||
}
|
||||
console.log('[reading-position] 💾 saveNow() called at', Math.round(position * 100) + '%')
|
||||
lastSavedPosition.current = position
|
||||
hasSavedOnce.current = true
|
||||
lastSavedAtRef.current = Date.now()
|
||||
@@ -145,6 +153,9 @@ export const useReadingPosition = ({
|
||||
// Schedule auto-save if sync is enabled (unless suppressed)
|
||||
if (Date.now() >= suppressUntilRef.current) {
|
||||
scheduleSave(clampedProgress)
|
||||
} else {
|
||||
const remainingMs = suppressUntilRef.current - Date.now()
|
||||
console.log('[reading-position] 🛡️ Save suppressed (', remainingMs, 'ms remaining) at', Math.round(clampedProgress * 100) + '%')
|
||||
}
|
||||
|
||||
// Completion detection with 2s hold at 100%
|
||||
|
||||
@@ -203,10 +203,14 @@ export function collectReadingPositionsOnce(params: {
|
||||
hasEmitted = true
|
||||
|
||||
if (candidates.length === 0) {
|
||||
console.log('[reading-position] 📊 No candidates collected during stabilization window')
|
||||
stableCallback(null)
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[reading-position] 📊 Collected', candidates.length, 'position candidates:',
|
||||
candidates.map(c => `${Math.round(c.position * 100)}% @${new Date(c.timestamp * 1000).toLocaleTimeString()}`).join(', '))
|
||||
|
||||
// Sort: newest first, then highest progress
|
||||
candidates.sort((a, b) => {
|
||||
const timeDiff = b.timestamp - a.timestamp
|
||||
@@ -214,10 +218,13 @@ export function collectReadingPositionsOnce(params: {
|
||||
return b.position - a.position
|
||||
})
|
||||
|
||||
console.log('[reading-position] ✅ Best position selected:', Math.round(candidates[0].position * 100) + '%',
|
||||
'from', new Date(candidates[0].timestamp * 1000).toLocaleTimeString())
|
||||
stableCallback(candidates[0])
|
||||
}
|
||||
|
||||
// Start streaming and collecting
|
||||
console.log('[reading-position] 🎯 Starting stabilized position collector (window:', windowMs, 'ms)')
|
||||
streamStop = startReadingPositionStream(
|
||||
relayPool,
|
||||
eventStore,
|
||||
@@ -225,13 +232,22 @@ export function collectReadingPositionsOnce(params: {
|
||||
articleIdentifier,
|
||||
(pos) => {
|
||||
if (hasEmitted) return
|
||||
if (!pos) return
|
||||
if (pos.position <= 0.05 || pos.position >= 1) return
|
||||
if (!pos) {
|
||||
console.log('[reading-position] 📥 Received null position')
|
||||
return
|
||||
}
|
||||
if (pos.position <= 0.05 || pos.position >= 1) {
|
||||
console.log('[reading-position] 🚫 Ignoring position', Math.round(pos.position * 100) + '% (outside 5%-100% range)')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[reading-position] 📥 Received position candidate:', Math.round(pos.position * 100) + '%',
|
||||
'from', new Date(pos.timestamp * 1000).toLocaleTimeString())
|
||||
candidates.push(pos)
|
||||
|
||||
// Schedule one-shot emission if not already scheduled
|
||||
if (!timer) {
|
||||
console.log('[reading-position] ⏰ Starting', windowMs, 'ms stabilization timer')
|
||||
timer = setTimeout(() => {
|
||||
emitStable()
|
||||
if (streamStop) streamStop()
|
||||
|
||||
Reference in New Issue
Block a user