diff --git a/src/components/Bookmarks.tsx b/src/components/Bookmarks.tsx index 0c408a48..54dc9252 100644 --- a/src/components/Bookmarks.tsx +++ b/src/components/Bookmarks.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from 'react' import { useParams } from 'react-router-dom' -import { nip19 } from 'nostr-tools' import { Hooks } from 'applesauce-react' import { useEventStore } from 'applesauce-react/hooks' import { RelayPool } from 'applesauce-relay' @@ -11,11 +10,12 @@ import { fetchBookmarks } from '../services/bookmarkService' import { fetchHighlights, fetchHighlightsForArticle } from '../services/highlightService' import ContentPanel from './ContentPanel' import { HighlightsPanel } from './HighlightsPanel' -import { fetchReadableContent, ReadableContent } from '../services/readerService' -import { fetchArticleByNaddr } from '../services/articleService' +import { ReadableContent } from '../services/readerService' import Settings from './Settings' import Toast from './Toast' import { useSettings } from '../hooks/useSettings' +import { useArticleLoader } from '../hooks/useArticleLoader' +import { loadContent, BookmarkReference } from '../utils/contentLoader' export type ViewMode = 'compact' | 'cards' | 'large' interface BookmarksProps { @@ -51,68 +51,19 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => { }) // Load article if naddr is in URL - useEffect(() => { - if (!relayPool || !naddr) return - - const loadArticle = async () => { - setReaderLoading(true) - setReaderContent(undefined) - setSelectedUrl(`nostr:${naddr}`) // Use naddr as the URL identifier - setIsCollapsed(true) - setIsHighlightsCollapsed(false) // Show highlights for the article - - try { - const article = await fetchArticleByNaddr(relayPool, naddr) - setReaderContent({ - title: article.title, - markdown: article.markdown, - image: article.image, - url: `nostr:${naddr}` - }) - - // Fetch highlights for this article using its address coordinate - // Extract the d-tag identifier from the article event - const dTag = article.event.tags.find(t => t[0] === 'd')?.[1] || '' - const articleCoordinate = `${article.event.kind}:${article.author}:${dTag}` - - // Store article info for refresh functionality - setCurrentArticleCoordinate(articleCoordinate) - setCurrentArticleEventId(article.event.id) - - console.log('📰 Article details:') - console.log(' - Event ID:', article.event.id) - console.log(' - Author:', article.author) - console.log(' - Kind:', article.event.kind) - console.log(' - D-tag:', dTag) - console.log(' - Coordinate:', articleCoordinate) - console.log(' - Title:', article.title) - - try { - setHighlightsLoading(true) - // Pass both the article coordinate and event ID for comprehensive highlight fetching - const fetchedHighlights = await fetchHighlightsForArticle(relayPool, articleCoordinate, article.event.id) - console.log(`📌 Found ${fetchedHighlights.length} highlights for article ${articleCoordinate}`) - setHighlights(fetchedHighlights) - } catch (err) { - console.error('Failed to fetch highlights:', err) - } finally { - setHighlightsLoading(false) - } - } catch (err) { - console.error('Failed to load article:', err) - setReaderContent({ - title: 'Error Loading Article', - html: `

Failed to load article: ${err instanceof Error ? err.message : 'Unknown error'}

`, - url: `nostr:${naddr}` - }) - setReaderLoading(false) - } finally { - setReaderLoading(false) - } - } - - loadArticle() - }, [naddr, relayPool]) + useArticleLoader({ + naddr, + relayPool, + setSelectedUrl, + setReaderContent, + setReaderLoading, + setIsCollapsed, + setIsHighlightsCollapsed, + setHighlights, + setHighlightsLoading, + setCurrentArticleCoordinate, + setCurrentArticleEventId + }) // Load initial data on login useEffect(() => { @@ -162,7 +113,7 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => { } } - const handleSelectUrl = async (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => { + const handleSelectUrl = async (url: string, bookmark?: BookmarkReference) => { if (!relayPool) return setSelectedUrl(url) @@ -172,33 +123,8 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => { if (settings.collapseOnArticleOpen !== false) setIsCollapsed(true) try { - // Check if this is a kind:30023 article - if (bookmark && bookmark.kind === 30023) { - // For articles, construct an naddr and fetch using article service - const dTag = bookmark.tags.find(t => t[0] === 'd')?.[1] || '' - - if (dTag !== undefined && bookmark.pubkey) { - const pointer = { - identifier: dTag, - kind: 30023, - pubkey: bookmark.pubkey, - } - const naddr = nip19.naddrEncode(pointer) - const article = await fetchArticleByNaddr(relayPool, naddr) - setReaderContent({ - title: article.title, - markdown: article.markdown, - image: article.image, - url: `nostr:${naddr}` - }) - } else { - throw new Error('Invalid article reference - missing d tag or pubkey') - } - } else { - // For regular URLs, fetch readable content - const content = await fetchReadableContent(url) - setReaderContent(content) - } + const content = await loadContent(url, relayPool, bookmark) + setReaderContent(content) } catch (err) { console.warn('Failed to fetch content:', err) } finally { diff --git a/src/hooks/useArticleLoader.ts b/src/hooks/useArticleLoader.ts new file mode 100644 index 00000000..4e891816 --- /dev/null +++ b/src/hooks/useArticleLoader.ts @@ -0,0 +1,92 @@ +import { useEffect } from 'react' +import { RelayPool } from 'applesauce-relay' +import { fetchArticleByNaddr } from '../services/articleService' +import { fetchHighlightsForArticle } from '../services/highlightService' +import { ReadableContent } from '../services/readerService' +import { Highlight } from '../types/highlights' + +interface UseArticleLoaderProps { + naddr: string | undefined + relayPool: RelayPool | null + setSelectedUrl: (url: string) => void + setReaderContent: (content: ReadableContent | undefined) => void + setReaderLoading: (loading: boolean) => void + setIsCollapsed: (collapsed: boolean) => void + setIsHighlightsCollapsed: (collapsed: boolean) => void + setHighlights: (highlights: Highlight[]) => void + setHighlightsLoading: (loading: boolean) => void + setCurrentArticleCoordinate: (coord: string | undefined) => void + setCurrentArticleEventId: (id: string | undefined) => void +} + +export function useArticleLoader({ + naddr, + relayPool, + setSelectedUrl, + setReaderContent, + setReaderLoading, + setIsCollapsed, + setIsHighlightsCollapsed, + setHighlights, + setHighlightsLoading, + setCurrentArticleCoordinate, + setCurrentArticleEventId +}: UseArticleLoaderProps) { + useEffect(() => { + if (!relayPool || !naddr) return + + const loadArticle = async () => { + setReaderLoading(true) + setReaderContent(undefined) + setSelectedUrl(`nostr:${naddr}`) + setIsCollapsed(true) + setIsHighlightsCollapsed(false) + + try { + const article = await fetchArticleByNaddr(relayPool, naddr) + setReaderContent({ + title: article.title, + markdown: article.markdown, + image: article.image, + url: `nostr:${naddr}` + }) + + const dTag = article.event.tags.find(t => t[0] === 'd')?.[1] || '' + const articleCoordinate = `${article.event.kind}:${article.author}:${dTag}` + + setCurrentArticleCoordinate(articleCoordinate) + setCurrentArticleEventId(article.event.id) + + console.log('📰 Article loaded:', article.title) + console.log('📍 Coordinate:', articleCoordinate) + + try { + setHighlightsLoading(true) + const fetchedHighlights = await fetchHighlightsForArticle( + relayPool, + articleCoordinate, + article.event.id + ) + console.log(`📌 Found ${fetchedHighlights.length} highlights`) + setHighlights(fetchedHighlights) + } catch (err) { + console.error('Failed to fetch highlights:', err) + } finally { + setHighlightsLoading(false) + } + } catch (err) { + console.error('Failed to load article:', err) + setReaderContent({ + title: 'Error Loading Article', + html: `

Failed to load article: ${err instanceof Error ? err.message : 'Unknown error'}

`, + url: `nostr:${naddr}` + }) + setReaderLoading(false) + } finally { + setReaderLoading(false) + } + } + + loadArticle() + }, [naddr, relayPool]) +} diff --git a/src/utils/contentLoader.ts b/src/utils/contentLoader.ts new file mode 100644 index 00000000..54c390b1 --- /dev/null +++ b/src/utils/contentLoader.ts @@ -0,0 +1,44 @@ +import { nip19 } from 'nostr-tools' +import { RelayPool } from 'applesauce-relay' +import { fetchArticleByNaddr } from '../services/articleService' +import { fetchReadableContent, ReadableContent } from '../services/readerService' + +export interface BookmarkReference { + id: string + kind: number + tags: string[][] + pubkey: string +} + +export async function loadContent( + url: string, + relayPool: RelayPool, + bookmark?: BookmarkReference +): Promise { + // Check if this is a kind:30023 article + if (bookmark && bookmark.kind === 30023) { + const dTag = bookmark.tags.find(t => t[0] === 'd')?.[1] || '' + + if (dTag !== undefined && bookmark.pubkey) { + const pointer = { + identifier: dTag, + kind: 30023, + pubkey: bookmark.pubkey, + } + const naddr = nip19.naddrEncode(pointer) + const article = await fetchArticleByNaddr(relayPool, naddr) + + return { + title: article.title, + markdown: article.markdown, + image: article.image, + url: `nostr:${naddr}` + } + } else { + throw new Error('Invalid article reference - missing d tag or pubkey') + } + } else { + // For regular URLs, fetch readable content + return await fetchReadableContent(url) + } +}