mirror of
https://github.com/dergigi/boris.git
synced 2025-12-20 16:14:20 +01:00
fix: move progress indicator outside reader and fix position tracking
- Move ReadingProgressIndicator outside reader div for true fixed positioning - Replace position-indicator library with custom scroll tracking - Track document scroll position instead of content scroll - Remove unused position-indicator dependency - Ensure progress indicator is always visible and shows correct percentage
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -22,7 +22,6 @@
|
|||||||
"applesauce-relay": "^4.0.0",
|
"applesauce-relay": "^4.0.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"nostr-tools": "^2.4.0",
|
"nostr-tools": "^2.4.0",
|
||||||
"position-indicator": "^0.0.12",
|
|
||||||
"prismjs": "^1.30.0",
|
"prismjs": "^1.30.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@@ -8973,12 +8972,6 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/position-indicator": {
|
|
||||||
"version": "0.0.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/position-indicator/-/position-indicator-0.0.12.tgz",
|
|
||||||
"integrity": "sha512-qHQejEylblB7rZ3MfXSI5hu1+Dq7EBv1BYwUIVWzJ3nZ8d6V7LFBi1zC5/XwT/01Wxddf9kaFoOy3L70/5tC+A==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/possible-typed-array-names": {
|
"node_modules/possible-typed-array-names": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
"applesauce-relay": "^4.0.0",
|
"applesauce-relay": "^4.0.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"nostr-tools": "^2.4.0",
|
"nostr-tools": "^2.4.0",
|
||||||
"position-indicator": "^0.0.12",
|
|
||||||
"prismjs": "^1.30.0",
|
"prismjs": "^1.30.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@@ -374,8 +374,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
const highlightRgb = hexToRgb(highlightColor)
|
const highlightRgb = hexToRgb(highlightColor)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="reader" style={{ '--highlight-rgb': highlightRgb } as React.CSSProperties}>
|
<>
|
||||||
{/* Reading Progress Indicator */}
|
{/* Reading Progress Indicator - Outside reader for fixed positioning */}
|
||||||
{isTextContent && (
|
{isTextContent && (
|
||||||
<ReadingProgressIndicator
|
<ReadingProgressIndicator
|
||||||
progress={progressPercentage}
|
progress={progressPercentage}
|
||||||
@@ -384,6 +384,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="reader" style={{ '--highlight-rgb': highlightRgb } as React.CSSProperties}>
|
||||||
{/* Hidden markdown preview to convert markdown to HTML */}
|
{/* Hidden markdown preview to convert markdown to HTML */}
|
||||||
{markdown && (
|
{markdown && (
|
||||||
<div ref={markdownPreviewRef} style={{ display: 'none' }}>
|
<div ref={markdownPreviewRef} style={{ display: 'none' }}>
|
||||||
@@ -591,6 +592,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,8 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
// @ts-ignore - position-indicator types issue
|
|
||||||
import { createPositionIndicator } from 'position-indicator'
|
|
||||||
|
|
||||||
interface ReadingPositionData {
|
|
||||||
position: number // 0 to 1
|
|
||||||
prevPosition: number
|
|
||||||
hasUpdated: boolean
|
|
||||||
hasScroll: boolean
|
|
||||||
eventType: 'scroll' | 'resize' | 'heightChange' | 'init'
|
|
||||||
eventDate: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UseReadingPositionOptions {
|
interface UseReadingPositionOptions {
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
onPositionChange?: (data: ReadingPositionData) => void
|
onPositionChange?: (position: number) => void
|
||||||
onReadingComplete?: () => void
|
onReadingComplete?: () => void
|
||||||
readingCompleteThreshold?: number // Default 0.9 (90%)
|
readingCompleteThreshold?: number // Default 0.9 (90%)
|
||||||
}
|
}
|
||||||
@@ -26,44 +15,45 @@ export const useReadingPosition = ({
|
|||||||
}: UseReadingPositionOptions = {}) => {
|
}: UseReadingPositionOptions = {}) => {
|
||||||
const [position, setPosition] = useState(0)
|
const [position, setPosition] = useState(0)
|
||||||
const [isReadingComplete, setIsReadingComplete] = useState(false)
|
const [isReadingComplete, setIsReadingComplete] = useState(false)
|
||||||
const positionIndicatorRef = useRef<any>(null)
|
|
||||||
const hasTriggeredComplete = useRef(false)
|
const hasTriggeredComplete = useRef(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!enabled) return
|
if (!enabled) return
|
||||||
|
|
||||||
const handleInit = (data: ReadingPositionData) => {
|
const handleScroll = () => {
|
||||||
setPosition(data.position)
|
// Get the main content area (reader content)
|
||||||
onPositionChange?.(data)
|
const readerContent = document.querySelector('.reader-html, .reader-markdown')
|
||||||
}
|
if (!readerContent) return
|
||||||
|
|
||||||
const handleUpdate = (data: ReadingPositionData) => {
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||||
setPosition(data.position)
|
const windowHeight = window.innerHeight
|
||||||
onPositionChange?.(data)
|
const documentHeight = document.documentElement.scrollHeight
|
||||||
|
|
||||||
|
// Calculate position based on how much of the content has been scrolled through
|
||||||
|
const scrollProgress = Math.min(scrollTop / (documentHeight - windowHeight), 1)
|
||||||
|
const clampedProgress = Math.max(0, Math.min(1, scrollProgress))
|
||||||
|
|
||||||
|
setPosition(clampedProgress)
|
||||||
|
onPositionChange?.(clampedProgress)
|
||||||
|
|
||||||
// Check if reading is complete
|
// Check if reading is complete
|
||||||
if (data.position >= readingCompleteThreshold && !hasTriggeredComplete.current) {
|
if (clampedProgress >= readingCompleteThreshold && !hasTriggeredComplete.current) {
|
||||||
setIsReadingComplete(true)
|
setIsReadingComplete(true)
|
||||||
hasTriggeredComplete.current = true
|
hasTriggeredComplete.current = true
|
||||||
onReadingComplete?.()
|
onReadingComplete?.()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const positionIndicator = createPositionIndicator({
|
// Initial calculation
|
||||||
onInit: handleInit,
|
handleScroll()
|
||||||
onUpdate: handleUpdate,
|
|
||||||
useResizeListener: true,
|
|
||||||
useResizeObserver: true
|
|
||||||
})
|
|
||||||
|
|
||||||
positionIndicator.init()
|
// Add scroll listener
|
||||||
positionIndicatorRef.current = positionIndicator
|
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||||
|
window.addEventListener('resize', handleScroll, { passive: true })
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (positionIndicatorRef.current) {
|
window.removeEventListener('scroll', handleScroll)
|
||||||
positionIndicatorRef.current.destroy()
|
window.removeEventListener('resize', handleScroll)
|
||||||
positionIndicatorRef.current = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [enabled, onPositionChange, onReadingComplete, readingCompleteThreshold])
|
}, [enabled, onPositionChange, onReadingComplete, readingCompleteThreshold])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user