e03cc01109
#171 #171 #171 #171 #171 #171 #171 Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: #345
168 lines
4.6 KiB
TypeScript
168 lines
4.6 KiB
TypeScript
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 { updatePost } from '@/lib/posts'
|
||
|
||
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)))].join (' ')
|
||
}
|
||
|
||
|
||
type Props = { post: Post
|
||
onSave: (newPost: Post) => void }
|
||
|
||
|
||
export default (({ post, onSave }: Props) => {
|
||
const [disabled, setDisabled] = useState (false)
|
||
const [originalCreatedBefore, setOriginalCreatedBefore] =
|
||
useState<string | null> (post.originalCreatedBefore)
|
||
const [originalCreatedFrom, setOriginalCreatedFrom] =
|
||
useState<string | null> (post.originalCreatedFrom)
|
||
const [parentPostIds, setParentPostIds] =
|
||
useState ((post.parentPosts ?? []).map (p => p.id).join (' '))
|
||
const [tags, setTags] = useState<string> ('')
|
||
const [title, setTitle] = useState (post.title)
|
||
|
||
const dialogue = useDialogue ()
|
||
|
||
const update = async (...args: Parameters<typeof updatePost>) => {
|
||
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 = (e as any)?.response
|
||
|
||
if (response?.status !== 409)
|
||
{
|
||
toast ({ description: '更新はできなかったよ……' })
|
||
return
|
||
}
|
||
|
||
const action = await dialogue.choice ({
|
||
title: '競合が発生しました.',
|
||
description: (
|
||
<div>
|
||
<p>ほかの耕作員が先に更新してゐます.</p>
|
||
<p>現在の変更をどう扱ひますか?</p>
|
||
</div>),
|
||
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 (
|
||
<form onSubmit={handleSubmit} className="max-w-xl pt-2 space-y-4">
|
||
{/* タイトル */}
|
||
<div>
|
||
<Label>タイトル</Label>
|
||
<input
|
||
type="text"
|
||
disabled={disabled}
|
||
className="w-full border rounded p-2"
|
||
value={title ?? ''}
|
||
onChange={ev => setTitle (ev.target.value)}/>
|
||
</div>
|
||
|
||
{/* 親投稿 */}
|
||
<div>
|
||
<Label>親投稿</Label>
|
||
<input
|
||
type="text"
|
||
disabled={disabled}
|
||
value={parentPostIds}
|
||
onChange={e => setParentPostIds (e.target.value)}
|
||
className="w-full border p-2 rounded"/>
|
||
</div>
|
||
|
||
{/* タグ */}
|
||
<PostFormTagsArea
|
||
disabled={disabled}
|
||
tags={tags}
|
||
setTags={setTags}/>
|
||
|
||
{/* オリジナルの作成日時 */}
|
||
<PostOriginalCreatedTimeField
|
||
disabled={disabled}
|
||
originalCreatedFrom={originalCreatedFrom}
|
||
setOriginalCreatedFrom={setOriginalCreatedFrom}
|
||
originalCreatedBefore={originalCreatedBefore}
|
||
setOriginalCreatedBefore={setOriginalCreatedBefore}/>
|
||
|
||
{/* 送信 */}
|
||
<Button
|
||
type="submit"
|
||
disabled={disabled}>
|
||
更新
|
||
</Button>
|
||
</form>)
|
||
}) satisfies FC<Props>
|