#353 を再開できなかったので. Reviewed-on: #380 Co-authored-by: miteruzo <miteruzo@naver.com> Co-committed-by: miteruzo <miteruzo@naver.com>
このコミットはPull リクエスト #380 でマージされました.
このコミットが含まれているのは:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import PostFormTagsArea from '@/components/PostFormTagsArea'
|
||||
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
|
||||
@@ -9,29 +9,35 @@ import { Button } from '@/components/ui/button'
|
||||
import { toast } from '@/components/ui/use-toast'
|
||||
import { isApiError } from '@/lib/api'
|
||||
import { updatePost } from '@/lib/posts'
|
||||
import { inputClass } from '@/lib/utils'
|
||||
import { inputClass, msToTime } from '@/lib/utils'
|
||||
import { useValidationErrors } from '@/lib/useValidationErrors'
|
||||
|
||||
import type { FC, FormEvent } from 'react'
|
||||
|
||||
import type { Post, Tag } from '@/types'
|
||||
import type { Post, TagWithSections } from '@/types'
|
||||
|
||||
type PostFormField =
|
||||
'parentPostIds' | 'tags' | 'originalCreatedAt'
|
||||
'parentPostIds' | 'tags' | 'videoMs' | 'originalCreatedAt'
|
||||
|
||||
const videoMsToDurationValue = (videoMs: number | null): string =>
|
||||
videoMs == null ? '' : String (videoMs / 1_000)
|
||||
|
||||
|
||||
const tagsToStr = (tags: Tag[]): string => {
|
||||
const result: Tag[] = []
|
||||
const tagsToStr = (tags: TagWithSections[]): string => {
|
||||
const result: Omit<TagWithSections, 'children'>[] = []
|
||||
|
||||
const walk = (tag: Tag) => {
|
||||
const walk = (tag: TagWithSections) => {
|
||||
const { children, ...rest } = tag
|
||||
result.push (rest)
|
||||
children?.forEach (walk)
|
||||
children.forEach (walk)
|
||||
}
|
||||
|
||||
tags.filter (t => t.category !== 'nico').forEach (walk)
|
||||
|
||||
return [...(new Set (result.map (t => t.name)))].join (' ')
|
||||
return [...(new Set (result.map (t =>
|
||||
`${ t.name }${ t.sections
|
||||
.map (s => `[${ msToTime (s.beginMs) }-${ s.endMs == null ? '' : msToTime (s.endMs) }]`)
|
||||
.join ('') }`)))].join (' ')
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +47,7 @@ type Props = { post: Post
|
||||
|
||||
const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
const [disabled, setDisabled] = useState (false)
|
||||
const [duration, setDuration] = useState<string> (videoMsToDurationValue (post.videoMs))
|
||||
const { baseErrors, fieldErrors, clearValidationErrors, applyValidationError } =
|
||||
useValidationErrors<PostFormField> ()
|
||||
const [originalCreatedBefore, setOriginalCreatedBefore] =
|
||||
@@ -52,6 +59,10 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
const [tags, setTags] = useState<string> ('')
|
||||
const [title, setTitle] = useState (post.title)
|
||||
|
||||
const videoFlg =
|
||||
useMemo (() => tags.split (/\s+/).some (tag => tag.replace (/\[.*\]$/, '') === '動画'),
|
||||
[tags])
|
||||
|
||||
const dialogue = useDialogue ()
|
||||
|
||||
const update = async (...args: Parameters<typeof updatePost>) => {
|
||||
@@ -63,6 +74,7 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
onSave ({ ...post,
|
||||
versionNo: data.versionNo,
|
||||
title: data.title,
|
||||
videoMs: data.videoMs,
|
||||
tags: data.tags,
|
||||
parentPosts: data.parentPosts,
|
||||
childPosts: data.childPosts,
|
||||
@@ -102,6 +114,7 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
{
|
||||
// TODO: 差分 UI
|
||||
await update ({ id: post.id, title, tags, parentPostIds,
|
||||
duration: videoFlg ? duration : null,
|
||||
originalCreatedFrom, originalCreatedBefore },
|
||||
{ baseVersionNo: post.versionNo, merge: true })
|
||||
return
|
||||
@@ -110,6 +123,7 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
if (action === 'overwrite')
|
||||
{
|
||||
await update ({ id: post.id, title, tags, parentPostIds,
|
||||
duration: videoFlg ? duration : null,
|
||||
originalCreatedFrom, originalCreatedBefore },
|
||||
{ baseVersionNo: post.versionNo, force: true })
|
||||
return
|
||||
@@ -124,6 +138,7 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
try
|
||||
{
|
||||
await update ({ id: post.id, title, tags, parentPostIds,
|
||||
duration: videoFlg ? duration : null,
|
||||
originalCreatedFrom, originalCreatedBefore },
|
||||
{ baseVersionNo: post.versionNo })
|
||||
}
|
||||
@@ -134,7 +149,8 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
}
|
||||
|
||||
useEffect (() => {
|
||||
setTags(tagsToStr (post.tags))
|
||||
setTags (tagsToStr (post.tags))
|
||||
setDuration (videoMsToDurationValue (post.videoMs))
|
||||
}, [post])
|
||||
|
||||
return (
|
||||
@@ -149,7 +165,7 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
disabled={disabled}
|
||||
className={inputClass (invalid)}
|
||||
value={title ?? ''}
|
||||
onChange={ev => setTitle (ev.target.value)}/>)}
|
||||
onChange={e => setTitle (e.target.value)}/>)}
|
||||
</FormField>
|
||||
|
||||
{/* 親投稿 */}
|
||||
@@ -181,6 +197,20 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
setOriginalCreatedBefore={setOriginalCreatedBefore}
|
||||
errors={fieldErrors.originalCreatedAt}/>
|
||||
|
||||
{/* 動画時間 */}
|
||||
{videoFlg && (
|
||||
<FormField label="動画時間" messages={fieldErrors.videoMs}>
|
||||
{({ invalid }) => (
|
||||
<input
|
||||
type="number"
|
||||
min="0.001"
|
||||
step="0.001"
|
||||
disabled={disabled}
|
||||
className={inputClass (invalid)}
|
||||
value={duration}
|
||||
onChange={e => setDuration (e.target.value)}/>)}
|
||||
</FormField>)}
|
||||
|
||||
{/* 送信 */}
|
||||
<Button type="submit" disabled={disabled}>
|
||||
更新
|
||||
@@ -188,4 +218,5 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
</form>)
|
||||
}
|
||||
|
||||
|
||||
export default PostEditForm
|
||||
|
||||
新しい課題から参照
ユーザをブロックする