import { useEffect, useState } from 'react' import PostFormTagsArea from '@/components/PostFormTagsArea' import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField' import Label from '@/components/common/Label' import { useDialogue } from '@/components/dialogues/DialogueProvider' import { Button } from '@/components/ui/button' import { toast } from '@/components/ui/use-toast' import { isApiError } from '@/lib/api' import { updatePost } from '@/lib/posts' import { msToTime } from '@/lib/utils' import type { FC, FormEvent } from 'react' import type { Post, Tag } from '@/types' const tagsToStr = (tags: Tag[]): string => { const result: Tag[] = [] const walk = (tag: Tag) => { const { children, ...rest } = tag result.push (rest) children?.forEach (walk) } tags.filter (t => t.category !== 'nico').forEach (walk) return [...(new Set (result.map (t => `${ t.name }${ t.sections.map (s => `[${ msToTime (s.beginMs) }-${ msToTime (s.endMs) }]`).join ('') }`)))].join (' ') } type Props = { post: Post onSave: (newPost: Post) => void } const PostEditForm: FC = ({ post, onSave }) => { const [disabled, setDisabled] = useState (false) const [originalCreatedBefore, setOriginalCreatedBefore] = useState (post.originalCreatedBefore) const [originalCreatedFrom, setOriginalCreatedFrom] = useState (post.originalCreatedFrom) const [parentPostIds, setParentPostIds] = useState ((post.parentPosts ?? []).map (p => p.id).join (' ')) const [tags, setTags] = useState ('') const [title, setTitle] = useState (post.title) const dialogue = useDialogue () const update = async (...args: Parameters) => { try { const data = await updatePost (...args) onSave ({ ...post, versionNo: data.versionNo, title: data.title, tags: data.tags, parentPosts: data.parentPosts, childPosts: data.childPosts, siblingPosts: data.siblingPosts, originalCreatedFrom: data.originalCreatedFrom, originalCreatedBefore: data.originalCreatedBefore } as Post) toast ({ description: '更新しました.' }) } catch (e) { const response = isApiError<{ mergeable?: boolean }> (e) ? e.response : undefined if (response?.status !== 409) { toast ({ description: '更新はできなかったよ……' }) return } const action = await dialogue.choice ({ title: '競合が発生しました.', description: (

ほかの耕作員が先に更新してゐます.

現在の変更をどう扱ひますか?

), choices: [...(response?.data?.mergeable ? [{ value: 'merge', label: '差分をマージ' }] : []), { value: 'overwrite', label: '強制上書き', variant: 'danger' }] }) if (action === 'merge') { // TODO: 差分 UI await update ({ id: post.id, title, tags, parentPostIds, originalCreatedFrom, originalCreatedBefore }, { baseVersionNo: post.versionNo, merge: true }) return } if (action === 'overwrite') { await update ({ id: post.id, title, tags, parentPostIds, originalCreatedFrom, originalCreatedBefore }, { baseVersionNo: post.versionNo, force: true }) return } } } const handleSubmit = async (e: FormEvent) => { e.preventDefault () setDisabled (true) try { await update ({ id: post.id, title, tags, parentPostIds, originalCreatedFrom, originalCreatedBefore }, { baseVersionNo: post.versionNo }) } finally { setDisabled (false) } } useEffect (() => { setTags(tagsToStr (post.tags)) }, [post]) return (
{/* タイトル */}
setTitle (ev.target.value)}/>
{/* 親投稿 */}
setParentPostIds (e.target.value)} className="w-full border p-2 rounded"/>
{/* タグ */} {/* オリジナルの作成日時 */} {/* 送信 */} ) } export default PostEditForm