import { useState, useCallback, useEffect, type FormEvent } from 'react' import { useNostrAuth } from '@/hooks/useNostrAuth' import { useAuthorPresentation } from '@/hooks/useAuthorPresentation' import { ArticleField } from './ArticleField' import { ArticleFormButtons } from './ArticleFormButtons' import { CreateAccountModal } from './CreateAccountModal' import { RecoveryStep } from './CreateAccountModalSteps' import { UnlockAccountModal } from './UnlockAccountModal' import { ImageUploadField } from './ImageUploadField' import { PresentationFormHeader } from './PresentationFormHeader' import { t } from '@/lib/i18n' interface AuthorPresentationDraft { authorName: string presentation: string contentDescription: string mainnetAddress: string pictureUrl?: string } const ADDRESS_PATTERN = /^(1|3|bc1)[a-zA-Z0-9]{25,62}$/ function SuccessNotice() { return (

{t('presentation.success')}

{t('presentation.successMessage')}

) } function ValidationError({ message }: { message: string | null }) { if (!message) { return null } return (

{message}

) } function PresentationField({ draft, onChange, }: { draft: AuthorPresentationDraft onChange: (next: AuthorPresentationDraft) => void }) { return ( onChange({ ...draft, presentation: value as string })} required type="textarea" rows={6} placeholder={t('presentation.field.presentation.placeholder')} helpText={t('presentation.field.presentation.help')} /> ) } function ContentDescriptionField({ draft, onChange, }: { draft: AuthorPresentationDraft onChange: (next: AuthorPresentationDraft) => void }) { return ( onChange({ ...draft, contentDescription: value as string })} required type="textarea" rows={6} placeholder={t('presentation.field.contentDescription.placeholder')} helpText={t('presentation.field.contentDescription.help')} /> ) } function MainnetAddressField({ draft, onChange, }: { draft: AuthorPresentationDraft onChange: (next: AuthorPresentationDraft) => void }) { return ( onChange({ ...draft, mainnetAddress: value as string })} required type="text" placeholder={t('presentation.field.mainnetAddress.placeholder')} helpText={t('presentation.field.mainnetAddress.help')} /> ) } function AuthorNameField({ draft, onChange, }: { draft: AuthorPresentationDraft onChange: (next: AuthorPresentationDraft) => void }) { return ( onChange({ ...draft, authorName: value as string })} required type="text" placeholder={t('presentation.field.authorName.placeholder')} helpText={t('presentation.field.authorName.help')} /> ) } function PictureField({ draft, onChange, }: { draft: AuthorPresentationDraft onChange: (next: AuthorPresentationDraft) => void }) { return ( onChange({ ...draft, pictureUrl: url })} /> ) } const PresentationFields = ({ draft, onChange, }: { draft: AuthorPresentationDraft onChange: (next: AuthorPresentationDraft) => void }) => (
) function PresentationForm({ draft, setDraft, validationError, error, loading, handleSubmit, userName, }: { draft: AuthorPresentationDraft setDraft: (next: AuthorPresentationDraft) => void validationError: string | null error: string | null loading: boolean handleSubmit: (e: FormEvent) => Promise userName: string }) { return (
) => { void handleSubmit(e) }} className="border border-neon-cyan/20 rounded-lg p-6 bg-cyber-dark space-y-4" > ) } function useAuthorPresentationState(pubkey: string | null, existingAuthorName?: string) { const { loading, error, success, publishPresentation } = useAuthorPresentation(pubkey) const [draft, setDraft] = useState({ authorName: existingAuthorName ?? '', presentation: '', contentDescription: '', mainnetAddress: '', }) const [validationError, setValidationError] = useState(null) // Update authorName when profile changes useEffect(() => { if (existingAuthorName && existingAuthorName !== draft.authorName) { setDraft((prev) => ({ ...prev, authorName: existingAuthorName })) } }, [existingAuthorName]) const handleSubmit = useCallback( async (e: FormEvent) => { e.preventDefault() const address = draft.mainnetAddress.trim() if (!ADDRESS_PATTERN.test(address)) { setValidationError(t('presentation.validation.invalidAddress')) return } if (!draft.authorName.trim()) { setValidationError('Author name is required') return } setValidationError(null) await publishPresentation(draft) }, [draft, publishPresentation] ) return { loading, error, success, draft, setDraft, validationError, handleSubmit } } function NoAccountActionButtons({ onGenerate, onImport, }: { onGenerate: () => void onImport: () => void }) { return (
) } function NoAccountView() { const [showImportModal, setShowImportModal] = useState(false) const [showRecoveryStep, setShowRecoveryStep] = useState(false) const [showUnlockModal, setShowUnlockModal] = useState(false) const [recoveryPhrase, setRecoveryPhrase] = useState([]) const [npub, setNpub] = useState('') const [generating, setGenerating] = useState(false) const [error, setError] = useState(null) const handleGenerate = async () => { setGenerating(true) setError(null) try { const { nostrAuthService } = await import('@/lib/nostrAuth') const result = await nostrAuthService.createAccount() setRecoveryPhrase(result.recoveryPhrase) setNpub(result.npub) setShowRecoveryStep(true) } catch (e) { setError(e instanceof Error ? e.message : 'Failed to create account') } finally { setGenerating(false) } } const handleRecoveryContinue = () => { setShowRecoveryStep(false) setShowUnlockModal(true) } const handleUnlockSuccess = () => { setShowUnlockModal(false) setRecoveryPhrase([]) setNpub('') } return (

Créez un compte ou importez votre clé secrète pour commencer

{error &&

{error}

} setShowImportModal(true)} /> {generating && (

Génération du compte...

)} {showImportModal && ( { setShowImportModal(false) setShowUnlockModal(true) }} onClose={() => setShowImportModal(false)} initialStep="import" /> )} {showRecoveryStep && ( )} {showUnlockModal && ( setShowUnlockModal(false)} /> )}
) } function AuthorPresentationFormView({ pubkey, profile, }: { pubkey: string | null profile: { name?: string; pubkey: string } | null }) { const state = useAuthorPresentationState(pubkey, profile?.name) if (!pubkey) { return } if (state.success) { return } // Get user name or fallback to shortened pubkey const userName = profile?.name ?? (pubkey ? `${pubkey.substring(0, 16)}...` : t('presentation.fallback.user')) return ( ) } function useAutoLoadPubkey(accountExists: boolean | null, pubkey: string | null, connect: () => Promise) { useEffect(() => { if (accountExists === true && !pubkey) { void connect() } }, [accountExists, pubkey, connect]) } export function AuthorPresentationEditor() { const { pubkey, profile, accountExists, connect } = useNostrAuth() useAutoLoadPubkey(accountExists, pubkey ?? null, connect) return }