137 lines
4.3 KiB
TypeScript
137 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
|
|
}
|
|
}
|