From 6fcfae4cc0365bdd3b87546b7ba15b3e33476781 Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Wed, 14 Jan 2026 00:34:36 +0100 Subject: [PATCH] create for series --- components/AlbyInstaller.tsx | 13 +- components/ArticleCard.tsx | 5 +- components/ArticleFilters.tsx | 5 +- components/ArticleFormButtons.tsx | 15 ++- components/ArticlePreview.tsx | 8 +- components/ArticleReviews.tsx | 22 ++-- components/ArticlesList.tsx | 21 +--- components/AuthorCard.tsx | 15 ++- components/AuthorsList.tsx | 21 +--- components/ClearButton.tsx | 8 +- components/ConditionalPublishButton.tsx | 9 +- components/ConnectButton.tsx | 15 ++- components/PaymentModal.tsx | 16 +-- components/SearchBar.tsx | 23 ++-- components/SponsoringForm.tsx | 70 ++++++----- .../reviewForms/ConnectRequiredCard.tsx | 12 +- components/reviewForms/ReviewFormView.tsx | 24 ++-- components/reviewForms/ReviewTipFormView.tsx | 24 ++-- components/ui/MobileMenu.tsx | 77 +++++++----- components/ui/Modal.tsx | 114 +++++++++++++----- 20 files changed, 292 insertions(+), 225 deletions(-) diff --git a/components/AlbyInstaller.tsx b/components/AlbyInstaller.tsx index 60cca4c..40f4445 100644 --- a/components/AlbyInstaller.tsx +++ b/components/AlbyInstaller.tsx @@ -1,5 +1,6 @@ import { useEffect, useState, useCallback } from 'react' import { getAlbyService } from '@/lib/alby' +import { Button } from './ui' interface AlbyInstallerProps { onInstalled?: () => void @@ -43,18 +44,20 @@ function InstallerActions({ onInstalled, markInstalled }: InstallerActionsProps) href="https://getalby.com/" target="_blank" rel="noopener noreferrer" - className="inline-flex items-center justify-center px-4 py-2 border border-neon-cyan/50 text-sm font-medium rounded-lg text-neon-cyan bg-neon-cyan/20 hover:bg-neon-cyan/30 hover:shadow-glow-cyan focus:outline-none focus:ring-2 focus:ring-neon-cyan transition-all" > - Install Alby + - + ) diff --git a/components/ArticleCard.tsx b/components/ArticleCard.tsx index f34c58e..1e46112 100644 --- a/components/ArticleCard.tsx +++ b/components/ArticleCard.tsx @@ -3,6 +3,7 @@ import { useNostrAuth } from '@/hooks/useNostrAuth' import { useArticlePayment } from '@/hooks/useArticlePayment' import { ArticlePreview } from './ArticlePreview' import { PaymentModal } from './PaymentModal' +import { Card } from './ui' import { t } from '@/lib/i18n' import Link from 'next/link' @@ -69,7 +70,7 @@ export function ArticleCard({ article, onUnlock }: ArticleCardProps): React.Reac }, connect) return ( -
+
-
+ ) } diff --git a/components/ArticleFilters.tsx b/components/ArticleFilters.tsx index 8b03450..4a9b028 100644 --- a/components/ArticleFilters.tsx +++ b/components/ArticleFilters.tsx @@ -1,5 +1,6 @@ import React from 'react' import type { Article } from '@/types/nostr' +import { Card } from './ui' import { t } from '@/lib/i18n' import { AuthorFilter } from './AuthorFilter' @@ -123,9 +124,9 @@ export function ArticleFiltersComponent({ filters.category !== null return ( -
+ -
+ ) } diff --git a/components/ArticleFormButtons.tsx b/components/ArticleFormButtons.tsx index 434e968..3616c0e 100644 --- a/components/ArticleFormButtons.tsx +++ b/components/ArticleFormButtons.tsx @@ -1,3 +1,4 @@ +import { Button } from './ui' import { t } from '@/lib/i18n' interface ArticleFormButtonsProps { @@ -10,21 +11,23 @@ export function ArticleFormButtons({ loading, onCancel }: ArticleFormButtonsProp return (
- + {onCancel && ( - + )}
diff --git a/components/ArticlePreview.tsx b/components/ArticlePreview.tsx index ee06fdd..c3ee8ab 100644 --- a/components/ArticlePreview.tsx +++ b/components/ArticlePreview.tsx @@ -1,4 +1,5 @@ import type { Article } from '@/types/nostr' +import { Button } from './ui' import { ArticlePages } from './ArticlePages' interface ArticlePreviewProps { @@ -25,13 +26,14 @@ export function ArticlePreview({ article, loading, onUnlock }: ArticlePreviewPro

Contenu complet disponible après un zap de {article.zapAmount} sats

- + ) diff --git a/components/ArticleReviews.tsx b/components/ArticleReviews.tsx index f90a300..9184dc9 100644 --- a/components/ArticleReviews.tsx +++ b/components/ArticleReviews.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react' import type { Review, Article } from '@/types/nostr' import { getReviewsForArticle } from '@/lib/reviews' import { getReviewTipsForArticle } from '@/lib/reviewAggregation' +import { Card, ErrorState, Button } from './ui' import { ReviewForm } from './ReviewForm' import { ReviewTipForm } from './ReviewTipForm' import { t } from '@/lib/i18n' @@ -17,7 +18,7 @@ export function ArticleReviews({ article, authorPubkey }: ArticleReviewsProps): const tipSelection = useReviewTipSelection({ article, reviews: data.reviews, reload: data.reload }) return ( -
+ {reviewForm.show && ( )} {data.loading &&

{t('common.loading')}

} - {data.error &&

{data.error}

} + {data.error && } {!data.loading && !data.error && data.reviews.length === 0 && !reviewForm.show && (

{t('review.empty')}

)} {!data.loading && !data.error && } -
+ ) } @@ -163,12 +164,9 @@ function ArticleReviewsHeader({ tips, onAddReview }: { tips: number; onAddReview

{t('review.title')}

{t('review.tips.total', { amount: tips })} - +
) @@ -192,14 +190,16 @@ function ArticleReviewsList({ reviews, onTipReview }: { reviews: Review[]; onTip {t('review.reviewer')}: {formatPubkey(r.reviewerPubkey)} {formatDate(r.createdAt)} - + ))} diff --git a/components/ArticlesList.tsx b/components/ArticlesList.tsx index 1271035..4d19dcc 100644 --- a/components/ArticlesList.tsx +++ b/components/ArticlesList.tsx @@ -1,5 +1,6 @@ import type { Article } from '@/types/nostr' import { ArticleCard } from './ArticleCard' +import { ErrorState, EmptyState } from './ui' import { t } from '@/lib/i18n' interface ArticlesListProps { @@ -20,21 +21,11 @@ function LoadingState(): React.ReactElement { ) } -function ErrorState({ message }: { message: string }): React.ReactElement { +function ArticlesEmptyState({ hasAny }: { hasAny: boolean }): React.ReactElement { return ( -
-

{message}

-
- ) -} - -function EmptyState({ hasAny }: { hasAny: boolean }): React.ReactElement { - return ( -
-

- {hasAny ? t('common.empty.articles.filtered') : t('common.empty.articles')} -

-
+ ) } @@ -53,7 +44,7 @@ export function ArticlesList({ return } if (articles.length === 0) { - return 0} /> + return 0} /> } return ( diff --git a/components/AuthorCard.tsx b/components/AuthorCard.tsx index 91703de..be6016b 100644 --- a/components/AuthorCard.tsx +++ b/components/AuthorCard.tsx @@ -1,6 +1,7 @@ -import Link from 'next/link' +import { useRouter } from 'next/router' import Image from 'next/image' import type { Article } from '@/types/nostr' +import { Card } from './ui' import { t } from '@/lib/i18n' interface AuthorCardProps { @@ -8,14 +9,16 @@ interface AuthorCardProps { } export function AuthorCard({ presentation }: AuthorCardProps): React.ReactElement { + const router = useRouter() const authorName = presentation.title.replace(/^Présentation de /, '') || t('common.author') const totalBTC = (presentation.totalSponsoring ?? 0) / 100_000_000 + const handleClick = (): void => { + void router.push(`/author/${presentation.pubkey}`) + } + return ( - +
{presentation.bannerUrl && (
@@ -37,6 +40,6 @@ export function AuthorCard({ presentation }: AuthorCardProps): React.ReactElemen )}
- +
) } diff --git a/components/AuthorsList.tsx b/components/AuthorsList.tsx index 9140d8b..b99e781 100644 --- a/components/AuthorsList.tsx +++ b/components/AuthorsList.tsx @@ -1,5 +1,6 @@ import type { Article } from '@/types/nostr' import { AuthorCard } from './AuthorCard' +import { ErrorState, EmptyState } from './ui' import { t } from '@/lib/i18n' interface AuthorsListProps { @@ -17,21 +18,11 @@ function LoadingState(): React.ReactElement { ) } -function ErrorState({ message }: { message: string }): React.ReactElement { +function AuthorsEmptyState({ hasAny }: { hasAny: boolean }): React.ReactElement { return ( -
-

{message}

-
- ) -} - -function EmptyState({ hasAny }: { hasAny: boolean }): React.ReactElement { - return ( -
-

- {hasAny ? t('common.empty.authors.filtered') : t('common.empty.authors')} -

-
+ ) } @@ -43,7 +34,7 @@ export function AuthorsList({ authors, allAuthors, loading, error }: AuthorsList return } if (authors.length === 0) { - return 0} /> + return 0} /> } return ( diff --git a/components/ClearButton.tsx b/components/ClearButton.tsx index 970660d..0fade62 100644 --- a/components/ClearButton.tsx +++ b/components/ClearButton.tsx @@ -7,9 +7,13 @@ interface ClearButtonProps { export function ClearButton({ onClick }: ClearButtonProps): React.ReactElement { return ( - + {error &&

{error}

} ) diff --git a/components/PaymentModal.tsx b/components/PaymentModal.tsx index 46e8528..46a0276 100644 --- a/components/PaymentModal.tsx +++ b/components/PaymentModal.tsx @@ -3,7 +3,7 @@ import QRCode from 'react-qr-code' import type { AlbyInvoice } from '@/types/alby' import { getAlbyService, isWebLNAvailable } from '@/lib/alby' import { AlbyInstaller } from './AlbyInstaller' -import { Modal } from './ui/Modal' +import { Modal, Button } from './ui' import { t } from '@/lib/i18n' interface PaymentModalProps { @@ -94,20 +94,22 @@ function PaymentActions({ }): React.ReactElement { return (
- - +
) } diff --git a/components/SearchBar.tsx b/components/SearchBar.tsx index 8fb2fb4..62af3ad 100644 --- a/components/SearchBar.tsx +++ b/components/SearchBar.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from 'react' import { SearchIcon } from './SearchIcon' import { ClearButton } from './ClearButton' +import { Input } from './ui' import { t } from '@/lib/i18n' interface SearchBarProps { @@ -29,18 +30,14 @@ export function SearchBar({ value, onChange, placeholder }: SearchBarProps): Rea } return ( -
-
- -
- - {localValue && } -
+ } + rightIcon={localValue ? : undefined} + className="pr-10" + /> ) } diff --git a/components/SponsoringForm.tsx b/components/SponsoringForm.tsx index ee59ad5..c35d3c3 100644 --- a/components/SponsoringForm.tsx +++ b/components/SponsoringForm.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { nostrService } from '@/lib/nostr' import { useNostrAuth } from '@/hooks/useNostrAuth' +import { Button, Card, Textarea, ErrorState } from './ui' import { t } from '@/lib/i18n' import { sponsoringPaymentService } from '@/lib/sponsoringPayment' import type { AuthorPresentationArticle } from '@/types/nostr' @@ -129,36 +130,38 @@ async function submitSponsoring(params: { function SponsoringConnectRequired(params: { onConnect: () => void }): React.ReactElement { return ( -
+

{t('sponsoring.form.connectRequired')}

- -
+ + ) } function SponsoringNoAddress(): React.ReactElement { return ( -
+

{t('sponsoring.form.error.noAddress')}

-
+ ) } function SponsoringInstructions(params: { instructions: SponsoringInstructionsState; onClose: () => void; onCancel: () => void }): React.ReactElement { return ( -
-

{t('sponsoring.form.title')}

-

{t('sponsoring.form.instructions', { authorAddress: params.instructions.authorAddress, platformAddress: params.instructions.platformAddress, authorAmount: params.instructions.authorBtc, platformAmount: params.instructions.platformBtc })}

-
- - -
+
+ +

{t('sponsoring.form.title')}

+

{t('sponsoring.form.instructions', { authorAddress: params.instructions.authorAddress, platformAddress: params.instructions.platformAddress, authorAmount: params.instructions.authorBtc, platformAmount: params.instructions.platformBtc })}

+
+ + +
+
) } @@ -175,29 +178,24 @@ function SponsoringFormView(params: {

{t('sponsoring.form.title')}

{t('sponsoring.form.description', { amount: '0.046' })}

-
- -