- Fix unused function warnings by renaming to _unusedExtractTags - Fix type errors in nostrTagSystem.ts for includes() calls - Fix type errors in reviews.ts for filter kinds array - Fix ArrayBuffer type errors in articleEncryption.ts - Remove unused imports (DecryptionKey, decryptArticleContent, extractTagsFromEvent) - All TypeScript checks now pass without disabling any controls
106 lines
2.8 KiB
TypeScript
106 lines
2.8 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'
|
|
|
|
/**
|
|
* Hook to fetch articles published by a specific user
|
|
*/
|
|
export function useUserArticles(
|
|
userPubkey: string,
|
|
searchQuery: string = '',
|
|
filters: ArticleFilters | null = 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)
|
|
|
|
const unsubscribe = nostrService.subscribeToArticles(
|
|
(article) => {
|
|
if (article.pubkey === userPubkey) {
|
|
setArticles((prev) => {
|
|
if (prev.some((a) => a.id === article.id)) {
|
|
return prev
|
|
}
|
|
const next = [article, ...prev].sort((a, b) => b.createdAt - a.createdAt)
|
|
hasArticlesRef.current = next.length > 0
|
|
return next
|
|
})
|
|
setLoading(false)
|
|
}
|
|
},
|
|
100
|
|
)
|
|
|
|
// Timeout after 10 seconds
|
|
const timeout = setTimeout(() => {
|
|
setLoading(false)
|
|
}, 10000)
|
|
|
|
return () => {
|
|
unsubscribe()
|
|
clearTimeout(timeout)
|
|
}
|
|
}, [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) => {
|
|
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,
|
|
}
|
|
}
|