2026-01-15 12:12:05 +01:00

176 lines
5.5 KiB
TypeScript

import Head from 'next/head'
import type { Article } from '@/types/nostr'
import { ArticleFiltersComponent, type ArticleFilters } from '@/components/ArticleFilters'
import { CategoryTabs } from '@/components/CategoryTabs'
import { SearchBar } from '@/components/SearchBar'
import { ArticlesList } from '@/components/ArticlesList'
import { AuthorsList } from '@/components/AuthorsList'
import { PageHeader } from '@/components/PageHeader'
import { Footer } from '@/components/Footer'
import { SkipLinks } from '@/components/SkipLinks'
import { OnboardingTour } from '@/components/OnboardingTour'
import type { Dispatch, SetStateAction } from 'react'
import { t } from '@/lib/i18n'
interface HomeViewProps {
searchQuery: string
setSearchQuery: Dispatch<SetStateAction<string>>
selectedCategory: ArticleFilters['category']
setSelectedCategory: Dispatch<SetStateAction<ArticleFilters['category']>>
filters: ArticleFilters
setFilters: Dispatch<SetStateAction<ArticleFilters>>
articles: Article[]
allArticles: Article[]
authors: Article[]
allAuthors: Article[]
loading: boolean
error: string | null
onUnlock: (article: Article) => void
unlockedArticles: Set<string>
}
function HomeHead(): React.ReactElement {
return (
<Head>
<title>zapwall.fr</title>
<meta
name="description"
content="Plateforme de publication d'articles scientifiques et de science-fiction avec sponsoring et rémunération des avis"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
</Head>
)
}
function ArticlesHero({
searchQuery,
setSearchQuery,
selectedCategory,
setSelectedCategory,
}: Pick<HomeViewProps, 'searchQuery' | 'setSearchQuery' | 'selectedCategory' | 'setSelectedCategory'>): React.ReactElement {
return (
<div className="mb-8">
<CategoryTabs selectedCategory={selectedCategory} onCategoryChange={setSelectedCategory} />
<div className="mb-4">
<SearchBar value={searchQuery} onChange={setSearchQuery} />
</div>
</div>
)
}
function HomeContent(props: HomeViewProps): React.ReactElement {
const shouldShowFilters = !props.loading && props.allArticles.length > 0
const shouldShowAuthors = props.selectedCategory !== null && props.selectedCategory !== 'all'
// At startup, we don't know yet if we're loading articles or authors
// Use a generic loading message until we have content
const isInitialLoad = props.loading && props.allArticles.length === 0 && props.allAuthors.length === 0
return (
<div className="w-full px-4 py-8" id="main-content" tabIndex={-1}>
<ArticlesHero
searchQuery={props.searchQuery}
setSearchQuery={props.setSearchQuery}
selectedCategory={props.selectedCategory}
setSelectedCategory={props.setSelectedCategory}
/>
{shouldShowFilters && !shouldShowAuthors && (
<aside id="filters-section" role="complementary" aria-label={t('navigation.filtersSection')} tabIndex={-1}>
<ArticleFiltersComponent filters={props.filters} onFiltersChange={props.setFilters} articles={props.allArticles} />
</aside>
)}
<HomeMainList
isInitialLoad={isInitialLoad}
shouldShowAuthors={shouldShowAuthors}
articlesListProps={buildArticlesListProps({
articles: props.articles,
allArticles: props.allArticles,
loading: props.loading,
isInitialLoad,
error: props.error,
onUnlock: props.onUnlock,
unlockedArticles: props.unlockedArticles,
})}
authorsListProps={buildAuthorsListProps({
authors: props.authors,
allAuthors: props.allAuthors,
loading: props.loading,
isInitialLoad,
error: props.error,
})}
/>
</div>
)
}
function HomeMainList(params: {
isInitialLoad: boolean
shouldShowAuthors: boolean
articlesListProps: Parameters<typeof ArticlesList>[0]
authorsListProps: Parameters<typeof AuthorsList>[0]
}): React.ReactElement {
if (params.isInitialLoad) {
return (
<div className="text-center py-12">
<p className="text-cyber-accent/70">{t('common.loading')}</p>
</div>
)
}
if (params.shouldShowAuthors) {
return <AuthorsList {...params.authorsListProps} />
}
return <ArticlesList {...params.articlesListProps} />
}
function buildArticlesListProps(params: {
articles: Article[]
allArticles: Article[]
loading: boolean
isInitialLoad: boolean
error: string | null
onUnlock: (article: Article) => void
unlockedArticles: Set<string>
}): Parameters<typeof ArticlesList>[0] {
return {
articles: params.articles,
allArticles: params.allArticles,
loading: params.loading && !params.isInitialLoad,
error: params.error,
onUnlock: params.onUnlock,
unlockedArticles: params.unlockedArticles,
}
}
function buildAuthorsListProps(params: {
authors: Article[]
allAuthors: Article[]
loading: boolean
isInitialLoad: boolean
error: string | null
}): Parameters<typeof AuthorsList>[0] {
return {
authors: params.authors,
allAuthors: params.allAuthors,
loading: params.loading && !params.isInitialLoad,
error: params.error,
}
}
export function HomeView(props: HomeViewProps): React.ReactElement {
return (
<>
<HomeHead />
<SkipLinks />
<main role="main" className="min-h-screen bg-cyber-darker">
<PageHeader />
<HomeContent {...props} />
<OnboardingTour />
<Footer />
</main>
</>
)
}