story-research-zapwall/hooks/useUserArticles.ts
2026-01-10 09:41:57 +01:00

143 lines
4.3 KiB
TypeScript

import { useEffect, useMemo, useRef, useState, type Dispatch, type MutableRefObject, type SetStateAction } 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)
useLoadUserArticlesFromCache({
userPubkey,
setArticles,
setLoading,
setError,
hasArticlesRef,
})
// Apply filters and sorting
const filteredArticles = useMemo(() => {
const effectiveFilters = buildDefaultFilters(filters)
if (!filters && !searchQuery.trim()) {
return articles
}
return applyFiltersAndSort(articles, searchQuery, effectiveFilters)
}, [articles, searchQuery, filters])
const loadArticleContent = (articleId: string, authorPubkey: string): Promise<Article | null> =>
loadAndDecryptUserArticle({
articleId,
authorPubkey,
setArticles,
setError,
})
return {
articles: filteredArticles,
allArticles: articles,
loading,
error,
loadArticleContent,
}
}
function useLoadUserArticlesFromCache(params: {
userPubkey: string
setArticles: (value: Article[]) => void
setLoading: (value: boolean) => void
setError: (value: string | null) => void
hasArticlesRef: MutableRefObject<boolean>
}): void {
const { userPubkey, setArticles, setLoading, setError, hasArticlesRef } = params
useEffect(() => {
if (!userPubkey) {
setLoading(false)
return
}
void loadUserArticlesFromCache({ userPubkey, setArticles, setLoading, setError, hasArticlesRef })
}, [userPubkey, setArticles, setLoading, setError, hasArticlesRef])
}
async function loadUserArticlesFromCache(params: {
userPubkey: string
setArticles: (value: Article[]) => void
setLoading: (value: boolean) => void
setError: (value: string | null) => void
hasArticlesRef: MutableRefObject<boolean>
}): Promise<void> {
const {hasArticlesRef} = params
try {
params.setLoading(true)
params.setError(null)
const all = (await readArticlesFromCache()).filter((a) => a.pubkey === params.userPubkey)
const sorted = all.sort((a, b) => b.createdAt - a.createdAt)
params.setArticles(sorted)
hasArticlesRef.current = sorted.length > 0
if (sorted.length === 0) {
params.setError('Aucun contenu trouvé')
}
} catch (e) {
console.error('Error loading user articles from cache:', e)
params.setError('Erreur lors du chargement des articles')
} finally {
params.setLoading(false)
}
}
async function readArticlesFromCache(): Promise<Article[]> {
const [publications, authors] = await Promise.all([
objectCache.getAll('publication'),
objectCache.getAll('author'),
])
return [...publications, ...authors] as Article[]
}
function buildDefaultFilters(filters: ArticleFilters | null): ArticleFilters {
if (filters) {
return filters
}
return { authorPubkey: null, sortBy: 'newest', category: 'all' }
}
async function loadAndDecryptUserArticle(params: {
articleId: string
authorPubkey: string
setArticles: Dispatch<SetStateAction<Article[]>>
setError: (value: string | null) => void
}): Promise<Article | null> {
try {
const article = await nostrService.getArticleById(params.articleId)
if (!article) {
return null
}
const decrypted = await nostrService.getDecryptedArticleContent(params.articleId, params.authorPubkey)
if (decrypted) {
params.setArticles((prev) =>
prev.map((a) => (a.id === params.articleId ? { ...a, content: decrypted, paid: true } : a))
)
}
return article
} catch (e) {
console.error('Error loading article content:', e)
params.setError(e instanceof Error ? e.message : 'Failed to load article')
return null
}
}