mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 06:34:24 +01:00
Merge pull request #36 from dergigi/mobile-highlighting
Fix mobile text selection detection for highlighting
This commit is contained in:
@@ -133,7 +133,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
return selectedUrl || `${title || ''}:${(markdown || html || '').length}`
|
return selectedUrl || `${title || ''}:${(markdown || html || '').length}`
|
||||||
}, [selectedUrl, title, markdown, html])
|
}, [selectedUrl, title, markdown, html])
|
||||||
|
|
||||||
const { contentRef, handleSelectionEnd } = useHighlightInteractions({
|
const { contentRef } = useHighlightInteractions({
|
||||||
onHighlightClick,
|
onHighlightClick,
|
||||||
selectedHighlightId,
|
selectedHighlightId,
|
||||||
onTextSelection,
|
onTextSelection,
|
||||||
@@ -815,8 +815,6 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
html={finalHtml}
|
html={finalHtml}
|
||||||
renderVideoLinksAsEmbeds={settings?.renderVideoLinksAsEmbeds === true}
|
renderVideoLinksAsEmbeds={settings?.renderVideoLinksAsEmbeds === true}
|
||||||
className="reader-markdown"
|
className="reader-markdown"
|
||||||
onMouseUp={handleSelectionEnd}
|
|
||||||
onTouchEnd={handleSelectionEnd}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="reader-markdown">
|
<div className="reader-markdown">
|
||||||
@@ -830,8 +828,6 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
html={finalHtml || html || ''}
|
html={finalHtml || html || ''}
|
||||||
renderVideoLinksAsEmbeds={settings?.renderVideoLinksAsEmbeds === true}
|
renderVideoLinksAsEmbeds={settings?.renderVideoLinksAsEmbeds === true}
|
||||||
className="reader-html"
|
className="reader-html"
|
||||||
onMouseUp={handleSelectionEnd}
|
|
||||||
onTouchEnd={handleSelectionEnd}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo, forwardRef } from 'react'
|
import { useMemo, forwardRef } from 'react'
|
||||||
import ReactPlayer from 'react-player'
|
import ReactPlayer from 'react-player'
|
||||||
import { classifyUrl } from '../utils/helpers'
|
import { classifyUrl } from '../utils/helpers'
|
||||||
|
|
||||||
@@ -6,8 +6,6 @@ interface VideoEmbedProcessorProps {
|
|||||||
html: string
|
html: string
|
||||||
renderVideoLinksAsEmbeds: boolean
|
renderVideoLinksAsEmbeds: boolean
|
||||||
className?: string
|
className?: string
|
||||||
onMouseUp?: (e: React.MouseEvent) => void
|
|
||||||
onTouchEnd?: (e: React.TouchEvent) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,9 +15,7 @@ interface VideoEmbedProcessorProps {
|
|||||||
const VideoEmbedProcessor = forwardRef<HTMLDivElement, VideoEmbedProcessorProps>(({
|
const VideoEmbedProcessor = forwardRef<HTMLDivElement, VideoEmbedProcessorProps>(({
|
||||||
html,
|
html,
|
||||||
renderVideoLinksAsEmbeds,
|
renderVideoLinksAsEmbeds,
|
||||||
className,
|
className
|
||||||
onMouseUp,
|
|
||||||
onTouchEnd
|
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
// Process HTML and extract video URLs in a single pass to keep them in sync
|
// Process HTML and extract video URLs in a single pass to keep them in sync
|
||||||
const { processedHtml, videoUrls } = useMemo(() => {
|
const { processedHtml, videoUrls } = useMemo(() => {
|
||||||
@@ -109,8 +105,6 @@ const VideoEmbedProcessor = forwardRef<HTMLDivElement, VideoEmbedProcessorProps>
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={className}
|
className={className}
|
||||||
dangerouslySetInnerHTML={{ __html: processedHtml }}
|
dangerouslySetInnerHTML={{ __html: processedHtml }}
|
||||||
onMouseUp={onMouseUp}
|
|
||||||
onTouchEnd={onTouchEnd}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -119,7 +113,7 @@ const VideoEmbedProcessor = forwardRef<HTMLDivElement, VideoEmbedProcessorProps>
|
|||||||
const parts = processedHtml.split(/(__VIDEO_EMBED_\d+__)/)
|
const parts = processedHtml.split(/(__VIDEO_EMBED_\d+__)/)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className={className} onMouseUp={onMouseUp} onTouchEnd={onTouchEnd}>
|
<div ref={ref} className={className}>
|
||||||
{parts.map((part, index) => {
|
{parts.map((part, index) => {
|
||||||
const videoMatch = part.match(/^__VIDEO_EMBED_(\d+)__$/)
|
const videoMatch = part.match(/^__VIDEO_EMBED_(\d+)__$/)
|
||||||
if (videoMatch) {
|
if (videoMatch) {
|
||||||
|
|||||||
@@ -93,9 +93,8 @@ export const useHighlightInteractions = ({
|
|||||||
return () => clearTimeout(timeoutId)
|
return () => clearTimeout(timeoutId)
|
||||||
}, [selectedHighlightId, contentVersion])
|
}, [selectedHighlightId, contentVersion])
|
||||||
|
|
||||||
// Handle text selection (works for both mouse and touch)
|
// Shared function to check and handle text selection
|
||||||
const handleSelectionEnd = useCallback(() => {
|
const checkSelection = useCallback(() => {
|
||||||
setTimeout(() => {
|
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
if (!selection || selection.rangeCount === 0) {
|
if (!selection || selection.rangeCount === 0) {
|
||||||
onClearSelection?.()
|
onClearSelection?.()
|
||||||
@@ -110,9 +109,21 @@ export const useHighlightInteractions = ({
|
|||||||
} else {
|
} else {
|
||||||
onClearSelection?.()
|
onClearSelection?.()
|
||||||
}
|
}
|
||||||
}, 10)
|
|
||||||
}, [onTextSelection, onClearSelection])
|
}, [onTextSelection, onClearSelection])
|
||||||
|
|
||||||
return { contentRef, handleSelectionEnd }
|
// Listen to selectionchange events for immediate detection (works reliably on mobile)
|
||||||
|
useEffect(() => {
|
||||||
|
const handleSelectionChange = () => {
|
||||||
|
// Use requestAnimationFrame to ensure selection is checked after browser updates
|
||||||
|
requestAnimationFrame(checkSelection)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('selectionchange', handleSelectionChange)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('selectionchange', handleSelectionChange)
|
||||||
|
}
|
||||||
|
}, [checkSelection])
|
||||||
|
|
||||||
|
return { contentRef }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user