168 lines
4.4 KiB
TypeScript
168 lines
4.4 KiB
TypeScript
import { useState } from 'react'
|
|
import { publishReview } from '@/lib/articleMutations'
|
|
import { nostrService } from '@/lib/nostr'
|
|
import { t } from '@/lib/i18n'
|
|
import type { ReviewFormProps } from './reviewFormTypes'
|
|
import type { Article } from '@/types/nostr'
|
|
|
|
export interface ReviewFormController {
|
|
pubkey: string | null
|
|
content: string
|
|
title: string
|
|
text: string
|
|
loading: boolean
|
|
error: string | null
|
|
setContent: (value: string) => void
|
|
setTitle: (value: string) => void
|
|
setText: (value: string) => void
|
|
handleSubmit: (e: React.FormEvent) => Promise<void>
|
|
}
|
|
|
|
export function useReviewFormController(params: {
|
|
article: ReviewFormProps['article']
|
|
pubkey: string | null
|
|
onSuccess?: () => void
|
|
}): ReviewFormController {
|
|
const [content, setContent] = useState('')
|
|
const [title, setTitle] = useState('')
|
|
const [text, setText] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const handleSubmit = buildReviewSubmitHandler({
|
|
article: params.article,
|
|
pubkey: params.pubkey,
|
|
content,
|
|
title,
|
|
text,
|
|
setLoading,
|
|
setError,
|
|
reset: () => resetFields({ setContent, setTitle, setText }),
|
|
...(params.onSuccess ? { onSuccess: params.onSuccess } : {}),
|
|
})
|
|
|
|
return {
|
|
pubkey: params.pubkey,
|
|
content,
|
|
title,
|
|
text,
|
|
loading,
|
|
error,
|
|
setContent,
|
|
setTitle,
|
|
setText,
|
|
handleSubmit,
|
|
}
|
|
}
|
|
|
|
function buildReviewSubmitHandler(params: {
|
|
article: Article
|
|
pubkey: string | null
|
|
content: string
|
|
title: string
|
|
text: string
|
|
setLoading: (loading: boolean) => void
|
|
setError: (error: string | null) => void
|
|
reset: () => void
|
|
onSuccess?: () => void
|
|
}): (e: React.FormEvent) => Promise<void> {
|
|
return async (e: React.FormEvent): Promise<void> => {
|
|
e.preventDefault()
|
|
const pubkey = params.pubkey
|
|
if (!pubkey) {
|
|
return
|
|
}
|
|
const contentError = validateRequiredContent(params.content)
|
|
if (contentError) {
|
|
params.setError(contentError)
|
|
return
|
|
}
|
|
await submitReview({ ...params, pubkey })
|
|
}
|
|
}
|
|
|
|
async function submitReview(params: {
|
|
article: Article
|
|
pubkey: string
|
|
content: string
|
|
title: string
|
|
text: string
|
|
setLoading: (loading: boolean) => void
|
|
setError: (error: string | null) => void
|
|
reset: () => void
|
|
onSuccess?: () => void
|
|
}): Promise<void> {
|
|
params.setLoading(true)
|
|
params.setError(null)
|
|
try {
|
|
await publishReview(buildPublishReviewParams({ article: params.article, reviewerPubkey: params.pubkey, content: params.content, title: params.title, text: params.text }))
|
|
params.reset()
|
|
params.onSuccess?.()
|
|
} catch (submitError) {
|
|
params.setError(submitError instanceof Error ? submitError.message : t('review.form.error.publishFailed'))
|
|
} finally {
|
|
params.setLoading(false)
|
|
}
|
|
}
|
|
|
|
function validateRequiredContent(content: string): string | null {
|
|
if (!content.trim()) {
|
|
return t('review.form.error.contentRequired')
|
|
}
|
|
return null
|
|
}
|
|
|
|
function buildPublishReviewParams(params: {
|
|
article: Article
|
|
reviewerPubkey: string
|
|
content: string
|
|
title: string
|
|
text: string
|
|
}): {
|
|
articleId: string
|
|
seriesId: string
|
|
category: 'science-fiction' | 'scientific-research'
|
|
authorPubkey: string
|
|
reviewerPubkey: string
|
|
content: string
|
|
title?: string
|
|
text?: string
|
|
authorPrivateKey: string
|
|
} {
|
|
const privateKey = nostrService.getPrivateKey()
|
|
if (!privateKey) {
|
|
throw new Error(t('review.form.error.noPrivateKey'))
|
|
}
|
|
|
|
const category = normalizeArticleCategory(params.article)
|
|
const seriesId = params.article.seriesId ?? ''
|
|
|
|
return {
|
|
articleId: params.article.id,
|
|
seriesId,
|
|
category,
|
|
authorPubkey: params.article.pubkey,
|
|
reviewerPubkey: params.reviewerPubkey,
|
|
content: params.content.trim(),
|
|
...(params.title.trim() ? { title: params.title.trim() } : {}),
|
|
...(params.text.trim() ? { text: params.text.trim() } : {}),
|
|
authorPrivateKey: privateKey,
|
|
}
|
|
}
|
|
|
|
function normalizeArticleCategory(article: Article): 'science-fiction' | 'scientific-research' {
|
|
if (article.category === 'author-presentation') {
|
|
return 'science-fiction'
|
|
}
|
|
return article.category === 'scientific-research' ? 'scientific-research' : 'science-fiction'
|
|
}
|
|
|
|
function resetFields(params: {
|
|
setContent: (value: string) => void
|
|
setTitle: (value: string) => void
|
|
setText: (value: string) => void
|
|
}): void {
|
|
params.setContent('')
|
|
params.setTitle('')
|
|
params.setText('')
|
|
}
|