2025-12-22 09:48:57 +01:00

130 lines
4.4 KiB
TypeScript

import { useState } from 'react'
import Head from 'next/head'
import { ConnectButton } from '@/components/ConnectButton'
import { ArticleCard } from '@/components/ArticleCard'
import { SearchBar } from '@/components/SearchBar'
import { ArticleFiltersComponent, type ArticleFilters } from '@/components/ArticleFilters'
import { useArticles } from '@/hooks/useArticles'
import type { Article } from '@/types/nostr'
export default function Home() {
const [searchQuery, setSearchQuery] = useState('')
const [filters, setFilters] = useState<ArticleFilters>({
authorPubkey: null,
minPrice: null,
maxPrice: null,
sortBy: 'newest',
})
const { articles, allArticles, loading, error, loadArticleContent } = useArticles(
searchQuery,
filters
)
const [unlockedArticles, setUnlockedArticles] = useState<Set<string>>(new Set())
const handleUnlock = async (article: Article) => {
const fullArticle = await loadArticleContent(article.id, article.pubkey)
if (fullArticle && fullArticle.paid) {
setUnlockedArticles((prev) => new Set([...prev, article.id]))
}
}
return (
<>
<Head>
<title>Nostr Paywall - Articles with Lightning Payments</title>
<meta name="description" content="Read article previews for free, unlock full content with Lightning zaps" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="min-h-screen bg-gray-50">
<header className="bg-white shadow-sm">
<div className="max-w-4xl mx-auto px-4 py-4 flex justify-between items-center">
<h1 className="text-2xl font-bold text-gray-900">Nostr Paywall</h1>
<div className="flex items-center gap-4">
<a
href="/docs"
className="px-4 py-2 text-gray-600 hover:text-gray-800 text-sm font-medium transition-colors"
>
Documentation
</a>
<a
href="/publish"
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium transition-colors"
>
Publish Article
</a>
<ConnectButton />
</div>
</div>
</header>
<div className="max-w-4xl mx-auto px-4 py-8">
<div className="mb-8">
<h2 className="text-3xl font-bold mb-4">Articles</h2>
<p className="text-gray-600 mb-4">
Read previews for free, unlock full content with {800} sats Lightning zaps
</p>
{/* Search Bar */}
<div className="mb-4">
<SearchBar value={searchQuery} onChange={setSearchQuery} />
</div>
</div>
{/* Filters */}
{!loading && allArticles.length > 0 && (
<ArticleFiltersComponent
filters={filters}
onFiltersChange={setFilters}
articles={allArticles}
/>
)}
{loading && (
<div className="text-center py-12">
<p className="text-gray-500">Loading articles...</p>
</div>
)}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
<p className="text-red-800">{error}</p>
</div>
)}
{!loading && articles.length === 0 && (
<div className="text-center py-12">
<p className="text-gray-500">
{allArticles.length === 0
? 'No articles found. Check back later!'
: 'No articles match your search or filters.'}
</p>
</div>
)}
{!loading && articles.length > 0 && (
<div className="mb-4 text-sm text-gray-600">
Showing {articles.length} of {allArticles.length} article{allArticles.length !== 1 ? 's' : ''}
</div>
)}
<div className="space-y-6">
{articles.map((article) => (
<ArticleCard
key={article.id}
article={{
...article,
paid: unlockedArticles.has(article.id) || article.paid,
}}
onUnlock={handleUnlock}
/>
))}
</div>
</div>
</main>
</>
)
}