116 lines
3.4 KiB
TypeScript
116 lines
3.4 KiB
TypeScript
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
import { nostrService } from '@/lib/nostr'
|
|
import type { Article } from '@/types/nostr'
|
|
import { applyFiltersAndSort } from '@/lib/articleFiltering'
|
|
import type { ArticleFilters } from '@/components/ArticleFilters'
|
|
import { objectCache } from '@/lib/objectCache'
|
|
|
|
/**
|
|
* Hook to fetch articles published by a specific user
|
|
*/
|
|
export function useUserArticles(
|
|
userPubkey: string,
|
|
searchQuery: string = '',
|
|
filters: ArticleFilters | null = null
|
|
): {
|
|
articles: Article[]
|
|
allArticles: Article[]
|
|
loading: boolean
|
|
error: string | null
|
|
loadArticleContent: (articleId: string, authorPubkey: string) => Promise<Article | null>
|
|
} {
|
|
const [articles, setArticles] = useState<Article[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const hasArticlesRef = useRef(false)
|
|
|
|
useEffect(() => {
|
|
if (!userPubkey) {
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
// Read only from IndexedDB cache - no network subscription
|
|
void (async (): Promise<void> => {
|
|
try {
|
|
const allPublications = await objectCache.getAll('publication')
|
|
const allAuthors = await objectCache.getAll('author')
|
|
const allArticles = [...allPublications, ...allAuthors] as Article[]
|
|
|
|
// Filter by user pubkey
|
|
const userArticles = allArticles.filter((article) => article.pubkey === userPubkey)
|
|
|
|
// Sort by creation date descending
|
|
const sortedArticles = userArticles.sort((a, b) => b.createdAt - a.createdAt)
|
|
|
|
setArticles(sortedArticles)
|
|
hasArticlesRef.current = sortedArticles.length > 0
|
|
if (sortedArticles.length === 0) {
|
|
setError('Aucun contenu trouvé')
|
|
}
|
|
} catch (loadError) {
|
|
console.error('Error loading user articles from cache:', loadError)
|
|
setError('Erreur lors du chargement des articles')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
})()
|
|
|
|
return () => {
|
|
// No cleanup needed - no network subscription
|
|
}
|
|
}, [userPubkey])
|
|
|
|
// Apply filters and sorting
|
|
const filteredArticles = useMemo(() => {
|
|
const effectiveFilters =
|
|
filters ??
|
|
({
|
|
authorPubkey: null,
|
|
sortBy: 'newest',
|
|
category: 'all',
|
|
} as const)
|
|
|
|
if (!filters && !searchQuery.trim()) {
|
|
return articles
|
|
}
|
|
|
|
return applyFiltersAndSort(articles, searchQuery, effectiveFilters)
|
|
}, [articles, searchQuery, filters])
|
|
|
|
const loadArticleContent = async (articleId: string, authorPubkey: string): Promise<Article | null> => {
|
|
try {
|
|
const article = await nostrService.getArticleById(articleId)
|
|
if (article) {
|
|
// Try to decrypt article content using decryption key from private messages
|
|
const decryptedContent = await nostrService.getDecryptedArticleContent(articleId, authorPubkey)
|
|
if (decryptedContent) {
|
|
setArticles((prev) =>
|
|
prev.map((a) =>
|
|
(a.id === articleId
|
|
? { ...a, content: decryptedContent, paid: true }
|
|
: a)
|
|
)
|
|
)
|
|
}
|
|
return article
|
|
}
|
|
} catch (e) {
|
|
console.error('Error loading article content:', e)
|
|
setError(e instanceof Error ? e.message : 'Failed to load article')
|
|
}
|
|
return null
|
|
}
|
|
|
|
return {
|
|
articles: filteredArticles,
|
|
allArticles: articles,
|
|
loading,
|
|
error,
|
|
loadArticleContent,
|
|
}
|
|
}
|