mirror of
https://github.com/dergigi/boris.git
synced 2025-12-18 23:24:22 +01:00
fix: revert to inline mount tracking with useRef
- Replace useMountedState custom hook with inline useRef approach - Set mountedRef.current = true at start of each effect run - Ensures proper reset when navigating between articles - Simpler and more reliable than custom hook approach
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, Dispatch, SetStateAction } from 'react'
|
import { useEffect, useRef, Dispatch, SetStateAction } from 'react'
|
||||||
import { RelayPool } from 'applesauce-relay'
|
import { RelayPool } from 'applesauce-relay'
|
||||||
import { fetchArticleByNaddr } from '../services/articleService'
|
import { fetchArticleByNaddr } from '../services/articleService'
|
||||||
import { fetchHighlightsForArticle } from '../services/highlightService'
|
import { fetchHighlightsForArticle } from '../services/highlightService'
|
||||||
@@ -6,7 +6,6 @@ import { ReadableContent } from '../services/readerService'
|
|||||||
import { Highlight } from '../types/highlights'
|
import { Highlight } from '../types/highlights'
|
||||||
import { NostrEvent } from 'nostr-tools'
|
import { NostrEvent } from 'nostr-tools'
|
||||||
import { UserSettings } from '../services/settingsService'
|
import { UserSettings } from '../services/settingsService'
|
||||||
import { useMountedState } from './useMountedState'
|
|
||||||
|
|
||||||
interface UseArticleLoaderProps {
|
interface UseArticleLoaderProps {
|
||||||
naddr: string | undefined
|
naddr: string | undefined
|
||||||
@@ -37,13 +36,15 @@ export function useArticleLoader({
|
|||||||
setCurrentArticle,
|
setCurrentArticle,
|
||||||
settings
|
settings
|
||||||
}: UseArticleLoaderProps) {
|
}: UseArticleLoaderProps) {
|
||||||
const isMounted = useMountedState()
|
const mountedRef = useRef(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
mountedRef.current = true
|
||||||
|
|
||||||
if (!relayPool || !naddr) return
|
if (!relayPool || !naddr) return
|
||||||
|
|
||||||
const loadArticle = async () => {
|
const loadArticle = async () => {
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setReaderLoading(true)
|
setReaderLoading(true)
|
||||||
setReaderContent(undefined)
|
setReaderContent(undefined)
|
||||||
@@ -53,7 +54,7 @@ export function useArticleLoader({
|
|||||||
try {
|
try {
|
||||||
const article = await fetchArticleByNaddr(relayPool, naddr, false, settings)
|
const article = await fetchArticleByNaddr(relayPool, naddr, false, settings)
|
||||||
|
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setReaderContent({
|
setReaderContent({
|
||||||
title: article.title,
|
title: article.title,
|
||||||
@@ -74,7 +75,7 @@ export function useArticleLoader({
|
|||||||
|
|
||||||
// Fetch highlights asynchronously without blocking article display
|
// Fetch highlights asynchronously without blocking article display
|
||||||
try {
|
try {
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setHighlightsLoading(true)
|
setHighlightsLoading(true)
|
||||||
setHighlights([])
|
setHighlights([])
|
||||||
@@ -84,7 +85,7 @@ export function useArticleLoader({
|
|||||||
articleCoordinate,
|
articleCoordinate,
|
||||||
article.event.id,
|
article.event.id,
|
||||||
(highlight) => {
|
(highlight) => {
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setHighlights((prev: Highlight[]) => {
|
setHighlights((prev: Highlight[]) => {
|
||||||
if (prev.some((h: Highlight) => h.id === highlight.id)) return prev
|
if (prev.some((h: Highlight) => h.id === highlight.id)) return prev
|
||||||
@@ -97,13 +98,13 @@ export function useArticleLoader({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch highlights:', err)
|
console.error('Failed to fetch highlights:', err)
|
||||||
} finally {
|
} finally {
|
||||||
if (isMounted()) {
|
if (mountedRef.current) {
|
||||||
setHighlightsLoading(false)
|
setHighlightsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load article:', err)
|
console.error('Failed to load article:', err)
|
||||||
if (isMounted()) {
|
if (mountedRef.current) {
|
||||||
setReaderContent({
|
setReaderContent({
|
||||||
title: 'Error Loading Article',
|
title: 'Error Loading Article',
|
||||||
html: `<p>Failed to load article: ${err instanceof Error ? err.message : 'Unknown error'}</p>`,
|
html: `<p>Failed to load article: ${err instanceof Error ? err.message : 'Unknown error'}</p>`,
|
||||||
@@ -115,8 +116,11 @@ export function useArticleLoader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadArticle()
|
loadArticle()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mountedRef.current = false
|
||||||
|
}
|
||||||
// Intentionally excluding setter functions from dependencies to prevent race conditions
|
// Intentionally excluding setter functions from dependencies to prevent race conditions
|
||||||
// isMounted is a stable function and doesn't need to be in dependencies
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [naddr, relayPool, settings])
|
}, [naddr, relayPool, settings])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect, useRef, useMemo } from 'react'
|
||||||
import { RelayPool } from 'applesauce-relay'
|
import { RelayPool } from 'applesauce-relay'
|
||||||
import { IEventStore } from 'applesauce-core'
|
import { IEventStore } from 'applesauce-core'
|
||||||
import { fetchReadableContent, ReadableContent } from '../services/readerService'
|
import { fetchReadableContent, ReadableContent } from '../services/readerService'
|
||||||
@@ -7,7 +7,6 @@ import { Highlight } from '../types/highlights'
|
|||||||
import { useStoreTimeline } from './useStoreTimeline'
|
import { useStoreTimeline } from './useStoreTimeline'
|
||||||
import { eventToHighlight } from '../services/highlightEventProcessor'
|
import { eventToHighlight } from '../services/highlightEventProcessor'
|
||||||
import { KINDS } from '../config/kinds'
|
import { KINDS } from '../config/kinds'
|
||||||
import { useMountedState } from './useMountedState'
|
|
||||||
|
|
||||||
// Helper to extract filename from URL
|
// Helper to extract filename from URL
|
||||||
function getFilenameFromUrl(url: string): string {
|
function getFilenameFromUrl(url: string): string {
|
||||||
@@ -49,7 +48,7 @@ export function useExternalUrlLoader({
|
|||||||
setCurrentArticleCoordinate,
|
setCurrentArticleCoordinate,
|
||||||
setCurrentArticleEventId
|
setCurrentArticleEventId
|
||||||
}: UseExternalUrlLoaderProps) {
|
}: UseExternalUrlLoaderProps) {
|
||||||
const isMounted = useMountedState()
|
const mountedRef = useRef(true)
|
||||||
|
|
||||||
// Load cached URL-specific highlights from event store
|
// Load cached URL-specific highlights from event store
|
||||||
const urlFilter = useMemo(() => {
|
const urlFilter = useMemo(() => {
|
||||||
@@ -66,10 +65,12 @@ export function useExternalUrlLoader({
|
|||||||
|
|
||||||
// Load content and start streaming highlights when URL changes
|
// Load content and start streaming highlights when URL changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
mountedRef.current = true
|
||||||
|
|
||||||
if (!relayPool || !url) return
|
if (!relayPool || !url) return
|
||||||
|
|
||||||
const loadExternalUrl = async () => {
|
const loadExternalUrl = async () => {
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setReaderLoading(true)
|
setReaderLoading(true)
|
||||||
setReaderContent(undefined)
|
setReaderContent(undefined)
|
||||||
@@ -81,14 +82,14 @@ export function useExternalUrlLoader({
|
|||||||
try {
|
try {
|
||||||
const content = await fetchReadableContent(url)
|
const content = await fetchReadableContent(url)
|
||||||
|
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setReaderContent(content)
|
setReaderContent(content)
|
||||||
setReaderLoading(false)
|
setReaderLoading(false)
|
||||||
|
|
||||||
// Fetch highlights for this URL asynchronously
|
// Fetch highlights for this URL asynchronously
|
||||||
try {
|
try {
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
setHighlightsLoading(true)
|
setHighlightsLoading(true)
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ export function useExternalUrlLoader({
|
|||||||
relayPool,
|
relayPool,
|
||||||
url,
|
url,
|
||||||
(highlight) => {
|
(highlight) => {
|
||||||
if (!isMounted()) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
if (seen.has(highlight.id)) return
|
if (seen.has(highlight.id)) return
|
||||||
seen.add(highlight.id)
|
seen.add(highlight.id)
|
||||||
@@ -130,13 +131,13 @@ export function useExternalUrlLoader({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch highlights:', err)
|
console.error('Failed to fetch highlights:', err)
|
||||||
} finally {
|
} finally {
|
||||||
if (isMounted()) {
|
if (mountedRef.current) {
|
||||||
setHighlightsLoading(false)
|
setHighlightsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load external URL:', err)
|
console.error('Failed to load external URL:', err)
|
||||||
if (isMounted()) {
|
if (mountedRef.current) {
|
||||||
const filename = getFilenameFromUrl(url)
|
const filename = getFilenameFromUrl(url)
|
||||||
setReaderContent({
|
setReaderContent({
|
||||||
title: filename,
|
title: filename,
|
||||||
@@ -149,8 +150,11 @@ export function useExternalUrlLoader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadExternalUrl()
|
loadExternalUrl()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mountedRef.current = false
|
||||||
|
}
|
||||||
// Intentionally excluding setter functions from dependencies to prevent race conditions
|
// Intentionally excluding setter functions from dependencies to prevent race conditions
|
||||||
// isMounted is a stable function and doesn't need to be in dependencies
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [url, relayPool, eventStore, cachedUrlHighlights])
|
}, [url, relayPool, eventStore, cachedUrlHighlights])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user