story-research-zapwall/components/createSeriesModal/useCreateSeriesModalController.ts
2026-01-08 23:53:05 +01:00

155 lines
4.8 KiB
TypeScript

import { useState } from 'react'
import { publishSeries } from '@/lib/articleMutations'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { nostrService } from '@/lib/nostr'
import { t } from '@/lib/i18n'
import type { SeriesDraft, SeriesAggregates } from './createSeriesModalTypes'
export interface CreateSeriesModalController {
draft: SeriesDraft
setDraft: (draft: SeriesDraft) => void
loading: boolean
error: string | null
canPublish: boolean
handleClose: () => void
handleSubmit: (e: React.FormEvent) => Promise<void>
}
export function useCreateSeriesModalController(params: {
authorPubkey: string
onClose: () => void
onSuccess: () => void
isOpen: boolean
}): CreateSeriesModalController {
const { pubkey, isUnlocked } = useNostrAuth()
const [draft, setDraft] = useState<SeriesDraft>(createInitialDraft())
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const aggregates = computeAggregates({ pubkey, isUnlocked, authorPubkey: params.authorPubkey })
const handleClose = createHandleClose({ loading, setDraft, setError, onClose: params.onClose })
const handleSubmit = createHandleSubmit({
authorPubkey: params.authorPubkey,
onClose: params.onClose,
onSuccess: params.onSuccess,
draft,
aggregates,
setDraft,
setLoading,
setError,
})
return { draft, setDraft, loading, error, canPublish: aggregates.canPublish, handleClose, handleSubmit }
}
function createInitialDraft(): SeriesDraft {
return { title: '', description: '', preview: '', coverUrl: '', category: 'science-fiction' }
}
function resetForm(params: { setDraft: (draft: SeriesDraft) => void; setError: (error: string | null) => void }): void {
params.setDraft(createInitialDraft())
params.setError(null)
}
function computeAggregates(params: { pubkey: string | null; isUnlocked: boolean; authorPubkey: string }): SeriesAggregates {
const privateKey = nostrService.getPrivateKey()
const canPublish = params.pubkey === params.authorPubkey && params.isUnlocked && privateKey !== null
return { canPublish, privateKey }
}
function getCanSubmitError(params: { canPublish: boolean; draft: SeriesDraft; privateKey: string | null }): string | null {
if (!params.canPublish || !params.privateKey) {
return t('series.create.error.notAuthor')
}
if (!params.draft.title.trim() || !params.draft.description.trim() || !params.draft.preview.trim()) {
return t('series.create.error.missingFields')
}
return null
}
function createHandleClose(params: {
loading: boolean
setDraft: (draft: SeriesDraft) => void
setError: (error: string | null) => void
onClose: () => void
}): () => void {
return (): void => {
if (params.loading) {
return
}
resetForm({ setDraft: params.setDraft, setError: params.setError })
params.onClose()
}
}
function createHandleSubmit(params: {
authorPubkey: string
onClose: () => void
onSuccess: () => void
draft: SeriesDraft
aggregates: SeriesAggregates
setDraft: (draft: SeriesDraft) => void
setLoading: (loading: boolean) => void
setError: (error: string | null) => void
}): (e: React.FormEvent) => Promise<void> {
return async (e: React.FormEvent): Promise<void> => submitSeriesForm(params, e)
}
async function submitSeriesForm(
params: {
authorPubkey: string
onClose: () => void
onSuccess: () => void
draft: SeriesDraft
aggregates: SeriesAggregates
setDraft: (draft: SeriesDraft) => void
setLoading: (loading: boolean) => void
setError: (error: string | null) => void
},
e: React.FormEvent
): Promise<void> {
e.preventDefault()
const canSubmitError = getCanSubmitError({ canPublish: params.aggregates.canPublish, draft: params.draft, privateKey: params.aggregates.privateKey })
if (canSubmitError) {
params.setError(canSubmitError)
return
}
params.setLoading(true)
params.setError(null)
try {
await publishSeries(buildPublishSeriesParams(params))
resetForm({ setDraft: params.setDraft, setError: params.setError })
params.onSuccess()
params.onClose()
} catch (submitError) {
params.setError(submitError instanceof Error ? submitError.message : t('series.create.error.publishFailed'))
} finally {
params.setLoading(false)
}
}
function buildPublishSeriesParams(params: {
authorPubkey: string
draft: SeriesDraft
aggregates: SeriesAggregates
}): {
title: string
description: string
preview: string
coverUrl?: string
category: SeriesDraft['category']
authorPubkey: string
authorPrivateKey: string
} {
return {
title: params.draft.title,
description: params.draft.description,
preview: params.draft.preview,
...(params.draft.coverUrl ? { coverUrl: params.draft.coverUrl } : {}),
category: params.draft.category,
authorPubkey: params.authorPubkey,
authorPrivateKey: params.aggregates.privateKey ?? '',
}
}