102 lines
4.0 KiB
TypeScript
102 lines
4.0 KiB
TypeScript
import { useMemo, useState } from 'react'
|
|
import type { Article } from '@/types/nostr'
|
|
import { useArticleEditing } from '@/hooks/useArticleEditing'
|
|
import type { ArticleDraft } from '@/lib/articlePublisherTypes'
|
|
import type { UserArticlesController, UserArticlesControllerParams } from './types'
|
|
|
|
export function useUserArticlesController(params: UserArticlesControllerParams): UserArticlesController {
|
|
const [deletedArticleIds, setDeletedArticleIds] = useState<Set<string>>(new Set())
|
|
const [articleOverridesById, setArticleOverridesById] = useState<Map<string, Article>>(new Map())
|
|
const [unlockedArticles, setUnlockedArticles] = useState<Set<string>>(new Set())
|
|
const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null)
|
|
const editingCtx = useArticleEditing(params.currentPubkey)
|
|
|
|
const localArticles = useMemo((): Article[] => {
|
|
return params.articles.filter((a) => !deletedArticleIds.has(a.id)).map((a) => articleOverridesById.get(a.id) ?? a)
|
|
}, [articleOverridesById, deletedArticleIds, params.articles])
|
|
|
|
return {
|
|
localArticles,
|
|
unlockedArticles,
|
|
pendingDeleteId,
|
|
requestDelete: (id: string) => setPendingDeleteId(id),
|
|
handleUnlock: createHandleUnlock(params.onLoadContent, setUnlockedArticles),
|
|
handleDelete: createHandleDelete(editingCtx.deleteArticle, setDeletedArticleIds, setPendingDeleteId),
|
|
handleEditSubmit: createHandleEditSubmit(editingCtx.submitEdit, editingCtx.editingDraft, params.currentPubkey, setArticleOverridesById),
|
|
...editingCtx,
|
|
}
|
|
}
|
|
|
|
function createHandleUnlock(
|
|
onLoadContent: (id: string, pubkey: string) => Promise<Article | null>,
|
|
setUnlocked: React.Dispatch<React.SetStateAction<Set<string>>>
|
|
): (article: Article) => Promise<void> {
|
|
return async (article: Article): Promise<void> => {
|
|
const full = await onLoadContent(article.id, article.pubkey)
|
|
if (full?.paid) {
|
|
setUnlocked((prev) => new Set([...prev, article.id]))
|
|
}
|
|
}
|
|
}
|
|
|
|
function createHandleDelete(
|
|
deleteArticle: (id: string) => Promise<boolean>,
|
|
setDeletedArticleIds: React.Dispatch<React.SetStateAction<Set<string>>>,
|
|
setPendingDeleteId: React.Dispatch<React.SetStateAction<string | null>>
|
|
): (article: Article) => Promise<void> {
|
|
return async (article: Article): Promise<void> => {
|
|
const ok = await deleteArticle(article.id)
|
|
if (ok) {
|
|
setDeletedArticleIds((prev) => new Set([...prev, article.id]))
|
|
}
|
|
setPendingDeleteId(null)
|
|
}
|
|
}
|
|
|
|
function createHandleEditSubmit(
|
|
submitEdit: () => Promise<import('@/lib/articleMutations').ArticleUpdateResult | null>,
|
|
draft: ReturnType<typeof useArticleEditing>['editingDraft'],
|
|
currentPubkey: string | null,
|
|
setArticleOverridesById: React.Dispatch<React.SetStateAction<Map<string, Article>>>
|
|
): () => Promise<void> {
|
|
return async (): Promise<void> => {
|
|
const result = await submitEdit()
|
|
if (result && draft) {
|
|
const updated = buildUpdatedArticle(draft, currentPubkey ?? '', result.articleId)
|
|
setArticleOverridesById((prev) => {
|
|
const next = new Map(prev)
|
|
next.set(result.originalArticleId, { ...updated, id: result.originalArticleId })
|
|
return next
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
function buildUpdatedArticle(draft: ArticleDraft, pubkey: string, newId: string): Article {
|
|
const parts = newId.split('_')
|
|
const hash = parts[0] ?? ''
|
|
const index = Number.parseInt(parts[1] ?? '0', 10)
|
|
const version = Number.parseInt(parts[2] ?? '0', 10)
|
|
return {
|
|
id: newId,
|
|
hash,
|
|
version,
|
|
index,
|
|
pubkey,
|
|
title: draft.title,
|
|
preview: draft.preview,
|
|
content: '',
|
|
description: draft.preview,
|
|
contentDescription: draft.preview,
|
|
createdAt: Math.floor(Date.now() / 1000),
|
|
zapAmount: draft.zapAmount,
|
|
paid: false,
|
|
thumbnailUrl: draft.bannerUrl ?? '',
|
|
...(draft.category ? { category: draft.category } : {}),
|
|
...(draft.seriesId ? { seriesId: draft.seriesId } : {}),
|
|
...(draft.bannerUrl ? { bannerUrl: draft.bannerUrl } : {}),
|
|
...(draft.media ? { media: draft.media } : {}),
|
|
...(draft.pages ? { pages: draft.pages } : {}),
|
|
}
|
|
}
|