154 lines
4.8 KiB
TypeScript
154 lines
4.8 KiB
TypeScript
import { useState } from 'react'
|
|
import type { ArticleDraft } from '@/lib/articlePublisher'
|
|
import type { ArticleUpdateResult } from '@/lib/articleMutations'
|
|
import { publishArticleUpdate, deleteArticleEvent, getStoredContent } from '@/lib/articleMutations'
|
|
import { nostrService } from '@/lib/nostr'
|
|
import type { Article } from '@/types/nostr'
|
|
|
|
interface EditState {
|
|
draft: ArticleDraft | null
|
|
articleId: string | null
|
|
}
|
|
|
|
type UseArticleEditingResult = {
|
|
editingDraft: ArticleDraft | null
|
|
editingArticleId: string | null
|
|
loading: boolean
|
|
error: string | null
|
|
startEditing: (article: Article) => Promise<void>
|
|
cancelEditing: () => void
|
|
submitEdit: () => Promise<ArticleUpdateResult | null>
|
|
deleteArticle: (articleId: string) => Promise<boolean>
|
|
updateDraft: (draft: ArticleDraft | null) => void
|
|
}
|
|
|
|
export function useArticleEditing(authorPubkey: string | null): UseArticleEditingResult {
|
|
const [state, setState] = useState<EditState>({ draft: null, articleId: null })
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const updateDraft = (draft: ArticleDraft | null): void => setState((prev) => ({ ...prev, draft }))
|
|
|
|
const startEditing = (article: Article): Promise<void> =>
|
|
startEditingArticle({ authorPubkey, article, setState, setLoading, setError })
|
|
|
|
const cancelEditing = (): void => resetEditingState({ setState, setError })
|
|
|
|
const submitEdit = (): Promise<ArticleUpdateResult | null> =>
|
|
submitArticleEdit({ authorPubkey, state, setState, setLoading, setError })
|
|
|
|
const deleteArticle = (articleId: string): Promise<boolean> =>
|
|
deleteArticleById({ authorPubkey, articleId, setLoading, setError })
|
|
|
|
return {
|
|
editingDraft: state.draft,
|
|
editingArticleId: state.articleId,
|
|
loading,
|
|
error,
|
|
startEditing,
|
|
cancelEditing,
|
|
submitEdit,
|
|
deleteArticle,
|
|
updateDraft,
|
|
}
|
|
}
|
|
|
|
function resetEditingState(params: {
|
|
setState: (value: EditState) => void
|
|
setError: (value: string | null) => void
|
|
}): void {
|
|
params.setState({ draft: null, articleId: null })
|
|
params.setError(null)
|
|
}
|
|
|
|
async function startEditingArticle(params: {
|
|
authorPubkey: string | null
|
|
article: Article
|
|
setState: (value: EditState) => void
|
|
setLoading: (value: boolean) => void
|
|
setError: (value: string | null) => void
|
|
}): Promise<void> {
|
|
if (!params.authorPubkey) {
|
|
params.setError('Connect your Nostr wallet to edit')
|
|
return
|
|
}
|
|
params.setLoading(true)
|
|
params.setError(null)
|
|
try {
|
|
const stored = await getStoredContent(params.article.id)
|
|
if (!stored) {
|
|
params.setError('Private content not available locally. Please republish from original device.')
|
|
return
|
|
}
|
|
params.setState({ articleId: params.article.id, draft: buildDraftForEdit(params.article, stored.content) })
|
|
} catch (e) {
|
|
params.setError(e instanceof Error ? e.message : 'Failed to load draft')
|
|
} finally {
|
|
params.setLoading(false)
|
|
}
|
|
}
|
|
|
|
function buildDraftForEdit(article: Article, content: string): ArticleDraft {
|
|
return {
|
|
title: article.title,
|
|
preview: article.preview,
|
|
content,
|
|
zapAmount: article.zapAmount,
|
|
...(article.category === 'science-fiction' || article.category === 'scientific-research' ? { category: article.category } : {}),
|
|
}
|
|
}
|
|
|
|
async function submitArticleEdit(params: {
|
|
authorPubkey: string | null
|
|
state: EditState
|
|
setState: (value: EditState) => void
|
|
setLoading: (value: boolean) => void
|
|
setError: (value: string | null) => void
|
|
}): Promise<ArticleUpdateResult | null> {
|
|
if (!params.authorPubkey || !params.state.articleId || !params.state.draft) {
|
|
params.setError('Missing data for update')
|
|
return null
|
|
}
|
|
params.setLoading(true)
|
|
params.setError(null)
|
|
try {
|
|
const privateKey = nostrService.getPrivateKey() ?? undefined
|
|
const result = await publishArticleUpdate(params.state.articleId, params.state.draft, params.authorPubkey, privateKey)
|
|
if (!result.success) {
|
|
params.setError(result.error ?? 'Update failed')
|
|
return null
|
|
}
|
|
return result
|
|
} catch (e) {
|
|
params.setError(e instanceof Error ? e.message : 'Update failed')
|
|
return null
|
|
} finally {
|
|
params.setLoading(false)
|
|
params.setState({ draft: null, articleId: null })
|
|
}
|
|
}
|
|
|
|
async function deleteArticleById(params: {
|
|
authorPubkey: string | null
|
|
articleId: string
|
|
setLoading: (value: boolean) => void
|
|
setError: (value: string | null) => void
|
|
}): Promise<boolean> {
|
|
if (!params.authorPubkey) {
|
|
params.setError('Connect your Nostr wallet to delete')
|
|
return false
|
|
}
|
|
params.setLoading(true)
|
|
params.setError(null)
|
|
try {
|
|
const privateKey = nostrService.getPrivateKey() ?? undefined
|
|
await deleteArticleEvent(params.articleId, params.authorPubkey, privateKey)
|
|
return true
|
|
} catch (e) {
|
|
params.setError(e instanceof Error ? e.message : 'Delete failed')
|
|
return false
|
|
} finally {
|
|
params.setLoading(false)
|
|
}
|
|
}
|