mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 14:44:26 +01:00
fix: reduce markdown reprocessing to prevent flicker
- Use stable string keys instead of Map objects as dependencies - Only clear rendered HTML when markdown content actually changes - Use refs to access latest Map values without triggering re-renders - Prevents excessive markdown reprocessing on every profile update - Should significantly reduce screen flickering during profile resolution
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react'
|
import React, { useState, useEffect, useRef, useMemo } from 'react'
|
||||||
import { RelayPool } from 'applesauce-relay'
|
import { RelayPool } from 'applesauce-relay'
|
||||||
import { extractNaddrUris, replaceNostrUrisInMarkdownWithProfileLabels } from '../utils/nostrUriResolver'
|
import { extractNaddrUris, replaceNostrUrisInMarkdownWithProfileLabels } from '../utils/nostrUriResolver'
|
||||||
import { fetchArticleTitles } from '../services/articleTitleResolver'
|
import { fetchArticleTitles } from '../services/articleTitleResolver'
|
||||||
@@ -24,6 +24,35 @@ export const useMarkdownToHTML = (
|
|||||||
// Resolve profile labels progressively as profiles load
|
// Resolve profile labels progressively as profiles load
|
||||||
const { labels: profileLabels, loading: profileLoading } = useProfileLabels(markdown || '', relayPool)
|
const { labels: profileLabels, loading: profileLoading } = useProfileLabels(markdown || '', relayPool)
|
||||||
|
|
||||||
|
// Create stable dependencies based on Map contents, not Map objects
|
||||||
|
// This prevents unnecessary reprocessing when Maps are recreated with same content
|
||||||
|
const profileLabelsKey = useMemo(() => {
|
||||||
|
return Array.from(profileLabels.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`).join('|')
|
||||||
|
}, [profileLabels])
|
||||||
|
|
||||||
|
const profileLoadingKey = useMemo(() => {
|
||||||
|
return Array.from(profileLoading.entries())
|
||||||
|
.filter(([, loading]) => loading)
|
||||||
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
|
.map(([k]) => k)
|
||||||
|
.join('|')
|
||||||
|
}, [profileLoading])
|
||||||
|
|
||||||
|
const articleTitlesKey = useMemo(() => {
|
||||||
|
return Array.from(articleTitles.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`).join('|')
|
||||||
|
}, [articleTitles])
|
||||||
|
|
||||||
|
// Keep refs to latest Maps for processing without causing re-renders
|
||||||
|
const profileLabelsRef = useRef(profileLabels)
|
||||||
|
const profileLoadingRef = useRef(profileLoading)
|
||||||
|
const articleTitlesRef = useRef(articleTitles)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
profileLabelsRef.current = profileLabels
|
||||||
|
profileLoadingRef.current = profileLoading
|
||||||
|
articleTitlesRef.current = articleTitles
|
||||||
|
}, [profileLabels, profileLoading, articleTitles])
|
||||||
|
|
||||||
// Fetch article titles
|
// Fetch article titles
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!markdown || !relayPool) {
|
if (!markdown || !relayPool) {
|
||||||
@@ -54,15 +83,27 @@ export const useMarkdownToHTML = (
|
|||||||
return () => { isCancelled = true }
|
return () => { isCancelled = true }
|
||||||
}, [markdown, relayPool])
|
}, [markdown, relayPool])
|
||||||
|
|
||||||
// Process markdown with progressive profile labels and article titles
|
// Track previous markdown and processed state to detect actual content changes
|
||||||
|
const previousMarkdownRef = useRef<string | undefined>(markdown)
|
||||||
|
const processedMarkdownRef = useRef<string>(processedMarkdown)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(`[profile-loading-debug][markdown-to-html] Processing markdown, profileLabels=${profileLabels.size}, profileLoading=${profileLoading.size}, articleTitles=${articleTitles.size}`)
|
processedMarkdownRef.current = processedMarkdown
|
||||||
console.log(`[profile-loading-debug][markdown-to-html] Clearing rendered HTML and processed markdown`)
|
}, [processedMarkdown])
|
||||||
// Always clear previous render immediately to avoid showing stale content while processing
|
|
||||||
setRenderedHtml('')
|
// Process markdown with progressive profile labels and article titles
|
||||||
setProcessedMarkdown('')
|
// Use stable string keys instead of Map objects to prevent excessive reprocessing
|
||||||
|
useEffect(() => {
|
||||||
|
const labelsSize = profileLabelsRef.current.size
|
||||||
|
const loadingSize = profileLoadingRef.current.size
|
||||||
|
const titlesSize = articleTitlesRef.current.size
|
||||||
|
console.log(`[profile-loading-debug][markdown-to-html] Processing markdown, profileLabels=${labelsSize}, profileLoading=${loadingSize}, articleTitles=${titlesSize}`)
|
||||||
|
|
||||||
if (!markdown) {
|
if (!markdown) {
|
||||||
|
setRenderedHtml('')
|
||||||
|
setProcessedMarkdown('')
|
||||||
|
previousMarkdownRef.current = markdown
|
||||||
|
processedMarkdownRef.current = ''
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,21 +112,27 @@ export const useMarkdownToHTML = (
|
|||||||
const processMarkdown = () => {
|
const processMarkdown = () => {
|
||||||
try {
|
try {
|
||||||
// Replace nostr URIs with profile labels (progressive) and article titles
|
// Replace nostr URIs with profile labels (progressive) and article titles
|
||||||
|
// Use refs to get latest values without causing dependency changes
|
||||||
const processed = replaceNostrUrisInMarkdownWithProfileLabels(
|
const processed = replaceNostrUrisInMarkdownWithProfileLabels(
|
||||||
markdown,
|
markdown,
|
||||||
profileLabels,
|
profileLabelsRef.current,
|
||||||
articleTitles,
|
articleTitlesRef.current,
|
||||||
profileLoading
|
profileLoadingRef.current
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isCancelled) return
|
if (isCancelled) return
|
||||||
|
|
||||||
console.log(`[profile-loading-debug][markdown-to-html] Processed markdown, loading states:`, Array.from(profileLoading.entries()).filter(([, l]) => l).map(([e]) => e.slice(0, 16) + '...'))
|
const loadingStates = Array.from(profileLoadingRef.current.entries())
|
||||||
|
.filter(([, l]) => l)
|
||||||
|
.map(([e]) => e.slice(0, 16) + '...')
|
||||||
|
console.log(`[profile-loading-debug][markdown-to-html] Processed markdown, loading states:`, loadingStates)
|
||||||
setProcessedMarkdown(processed)
|
setProcessedMarkdown(processed)
|
||||||
|
processedMarkdownRef.current = processed
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[markdown-to-html] Error processing markdown:`, error)
|
console.error(`[markdown-to-html] Error processing markdown:`, error)
|
||||||
if (!isCancelled) {
|
if (!isCancelled) {
|
||||||
setProcessedMarkdown(markdown) // Fallback to original
|
setProcessedMarkdown(markdown) // Fallback to original
|
||||||
|
processedMarkdownRef.current = markdown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,12 +148,24 @@ export const useMarkdownToHTML = (
|
|||||||
return () => cancelAnimationFrame(rafId)
|
return () => cancelAnimationFrame(rafId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only clear previous content if this is the first processing or markdown changed
|
||||||
|
// For profile updates, just reprocess without clearing to avoid flicker
|
||||||
|
const isMarkdownChange = previousMarkdownRef.current !== markdown
|
||||||
|
previousMarkdownRef.current = markdown
|
||||||
|
|
||||||
|
if (isMarkdownChange || !processedMarkdownRef.current) {
|
||||||
|
console.log(`[profile-loading-debug][markdown-to-html] Clearing rendered HTML and processed markdown (markdown changed: ${isMarkdownChange})`)
|
||||||
|
setRenderedHtml('')
|
||||||
|
setProcessedMarkdown('')
|
||||||
|
processedMarkdownRef.current = ''
|
||||||
|
}
|
||||||
|
|
||||||
processMarkdown()
|
processMarkdown()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isCancelled = true
|
isCancelled = true
|
||||||
}
|
}
|
||||||
}, [markdown, profileLabels, profileLoading, articleTitles])
|
}, [markdown, profileLabelsKey, profileLoadingKey, articleTitlesKey])
|
||||||
|
|
||||||
return { renderedHtml, previewRef, processedMarkdown }
|
return { renderedHtml, previewRef, processedMarkdown }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user