import { useState } from 'react' import { uploadNip95Media } from '@/lib/nip95' import { Button } from './ui' import { t } from '@/lib/i18n' import Image from 'next/image' import { UnlockAccountModal } from './UnlockAccountModal' interface ImageUploadFieldProps { id: string label?: string | undefined value?: string | undefined onChange: (url: string) => void helpText?: string | undefined } function ImagePreview({ value }: { value: string }): React.ReactElement { return (
{t('presentation.field.picture')}
) } function UploadButtonLabel({ uploading, value }: { uploading: boolean; value: string | undefined }): React.ReactElement { if (uploading) { return {t('presentation.field.picture.uploading')} } return {value ? t('presentation.field.picture.change') : t('presentation.field.picture.upload')} } function RemoveButton({ value, onChange }: { value: string | undefined; onChange: (url: string) => void }): React.ReactElement | null { if (!value) { return null } return ( ) } function ImageUploadControls({ id, uploading, value, onChange, onFileSelect, }: { id: string uploading: boolean value: string | undefined onChange: (url: string) => void onFileSelect: (e: React.ChangeEvent) => Promise }): React.ReactElement { return (
{ void onFileSelect(e) }} disabled={uploading} />
) } async function processFileUpload(file: File, onChange: (url: string) => void, setError: (error: string | null) => void): Promise { const media = await uploadNip95Media(file) if (media.type === 'image') { onChange(media.url) } else { setError(t('presentation.field.picture.error.imagesOnly')) } } type ImageUploadState = { uploading: boolean error: string | null handleFileSelect: (e: React.ChangeEvent) => Promise showUnlockModal: boolean setShowUnlockModal: (show: boolean) => void handleUnlockSuccess: () => Promise } function useImageUpload(onChange: (url: string) => void): ImageUploadState { const [uploading, setUploading] = useState(false) const [error, setError] = useState(null) const [showUnlockModal, setShowUnlockModal] = useState(false) const [pendingFile, setPendingFile] = useState(null) const handleFileSelect = createHandleFileSelect({ onChange, setError, setUploading, setPendingFile, setShowUnlockModal, }) const handleUnlockSuccess = createHandleUnlockSuccess({ pendingFile, onChange, setError, setPendingFile, setShowUnlockModal, setUploading, }) return { uploading, error, handleFileSelect, showUnlockModal, setShowUnlockModal, handleUnlockSuccess } } function createHandleFileSelect(params: { onChange: (url: string) => void setError: (error: string | null) => void setUploading: (uploading: boolean) => void setPendingFile: (file: File | null) => void setShowUnlockModal: (show: boolean) => void }): (event: React.ChangeEvent) => Promise { return async (event: React.ChangeEvent): Promise => { const file = readFirstFile(event) if (!file) { return } params.setError(null) params.setUploading(true) try { await processFileUpload(file, params.onChange, params.setError) } catch (uploadError) { const uploadErr = normalizeError(uploadError) if (isUnlockRequiredError(uploadErr)) { params.setPendingFile(file) params.setShowUnlockModal(true) params.setError(null) } else { params.setError(uploadErr.message ?? t('presentation.field.picture.error.uploadFailed')) } } finally { params.setUploading(false) } } } function createHandleUnlockSuccess(params: { pendingFile: File | null onChange: (url: string) => void setError: (error: string | null) => void setPendingFile: (file: File | null) => void setShowUnlockModal: (show: boolean) => void setUploading: (uploading: boolean) => void }): () => Promise { return async (): Promise => { await retryPendingUpload(params) } } function readFirstFile(event: React.ChangeEvent): File | null { return event.target.files?.[0] ?? null } function normalizeError(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)) } function isUnlockRequiredError(error: Error): boolean { if (error.message === 'UNLOCK_REQUIRED') { return true } if (typeof error === 'object' && error !== null && 'unlockRequired' in error) { return (error as { unlockRequired?: boolean }).unlockRequired === true } return false } async function retryPendingUpload(params: { pendingFile: File | null onChange: (url: string) => void setError: (error: string | null) => void setPendingFile: (file: File | null) => void setShowUnlockModal: (show: boolean) => void setUploading: (uploading: boolean) => void }): Promise { params.setShowUnlockModal(false) if (!params.pendingFile) { return } params.setUploading(true) params.setError(null) try { await processFileUpload(params.pendingFile, params.onChange, params.setError) params.setPendingFile(null) } catch (retryError) { params.setError(retryError instanceof Error ? retryError.message : t('presentation.field.picture.error.uploadFailed')) } finally { params.setUploading(false) } } export function ImageUploadField({ id, label, value, onChange, helpText }: ImageUploadFieldProps): React.ReactElement { const { uploading, error, handleFileSelect, showUnlockModal, setShowUnlockModal, handleUnlockSuccess } = useImageUpload(onChange) const displayLabel = label ?? t('presentation.field.picture') const displayHelpText = helpText ?? t('presentation.field.picture.help') return ( <>
{value && } {error &&

{error}

} {displayHelpText &&

{displayHelpText}

}
{showUnlockModal && ( { void handleUnlockSuccess() }} onClose={() => { setShowUnlockModal(false) }} /> )} ) }