このコミットが含まれているのは:
2026-07-17 23:14:37 +09:00
コミット 0ae41b6266
19個のファイルの変更455行の追加216行の削除
+46 -37
ファイルの表示
@@ -3,8 +3,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import FieldError from '@/components/common/FieldError'
import FieldWarning from '@/components/common/FieldWarning'
import PostCreationDataFields from '@/components/posts/PostCreationDataFields'
import PostDurationField from '@/components/posts/PostDurationField'
import PostTextField from '@/components/posts/PostTextField'
import PostImportThumbnailPreview from '@/components/posts/import/PostImportThumbnailPreview'
import { hasThumbnailBaseValue, hasVideoTag } from '@/lib/postImportRows'
import type { FC } from 'react'
@@ -21,6 +23,8 @@ type Props = {
saved: boolean
row: PostImportRow | null }> }
const THUMBNAIL_MISSING_WARNING = 'サムネールなし'
const buildDraft = (row: PostImportRow): Draft => ({
url: row.url,
title: String (row.attributes.title ?? ''),
@@ -29,6 +33,7 @@ const buildDraft = (row: PostImportRow): Draft => ({
originalCreatedBefore: String (row.attributes.originalCreatedBefore ?? ''),
tags: String (row.attributes.tags ?? ''),
parentPostIds: String (row.attributes.parentPostIds ?? ''),
duration: String (row.attributes.duration ?? ''),
thumbnailFile: row.thumbnailFile })
const buildResetDraft = (row: PostImportRow): Draft => ({
@@ -39,6 +44,7 @@ const buildResetDraft = (row: PostImportRow): Draft => ({
originalCreatedBefore: String (row.resetSnapshot.attributes.originalCreatedBefore ?? ''),
tags: String (row.resetSnapshot.attributes.tags ?? ''),
parentPostIds: String (row.resetSnapshot.attributes.parentPostIds ?? ''),
duration: String (row.resetSnapshot.attributes.duration ?? ''),
thumbnailFile: undefined })
const groupedMessages = (...values: (string[] | undefined)[]): string[] =>
@@ -52,6 +58,8 @@ const sameDraft = (left: Draft, right: Draft): boolean =>
&& left.originalCreatedBefore === right.originalCreatedBefore
&& left.tags === right.tags
&& left.parentPostIds === right.parentPostIds
&& left.duration === right.duration
&& left.thumbnailFile === right.thumbnailFile
const sameProvenance = (
current: PostImportRow['provenance'],
@@ -73,6 +81,17 @@ const sameWarnings = (
JSON.stringify (current.fieldWarnings) === JSON.stringify (reset.fieldWarnings)
&& JSON.stringify (current.baseWarnings) === JSON.stringify (reset.baseWarnings)
const thumbnailWarnings = (
messages: string[] | undefined,
thumbnailBase: string,
thumbnailFile: File | undefined,
): string[] => {
const others = (messages ?? []).filter (message => message !== THUMBNAIL_MISSING_WARNING)
return hasThumbnailBaseValue (thumbnailBase) || thumbnailFile != null
? others
: [...new Set ([...others, THUMBNAIL_MISSING_WARNING])]
}
const PostImportRowForm: FC<Props> = (
{ row,
@@ -85,8 +104,6 @@ const PostImportRowForm: FC<Props> = (
const [resetRequested, setResetRequested] = useState (false)
const [committedThumbnailBase, setCommittedThumbnailBase] = useState (
() => String (row.attributes.thumbnailBase ?? ''))
const [thumbnailObjectUrl, setThumbnailObjectUrl] = useState<string | undefined> (
() => row.thumbnailObjectUrl)
useEffect (() => {
const nextDraft = buildDraft (row)
@@ -94,18 +111,17 @@ const PostImportRowForm: FC<Props> = (
setMessageRow (null)
setResetRequested (false)
setCommittedThumbnailBase (String (row.attributes.thumbnailBase ?? ''))
setThumbnailObjectUrl (row.thumbnailObjectUrl)
}, [row])
useEffect (() => () => {
if (thumbnailObjectUrl != null)
URL.revokeObjectURL (thumbnailObjectUrl)
}, [thumbnailObjectUrl])
const displayRow = messageRow ?? row
const resetDraft = useMemo (
() => buildResetDraft (row),
[row])
const durationVisible = hasVideoTag (draft.tags)
const currentThumbnailWarnings = thumbnailWarnings (
displayRow.fieldWarnings.thumbnailBase,
draft.thumbnailBase,
draft.thumbnailFile)
const resetDisabled =
saving
|| (sameDraft (draft, resetDraft)
@@ -129,7 +145,6 @@ const PostImportRowForm: FC<Props> = (
const confirmed = await controls.confirm ({
title: '変更をリセットしますか?',
description: '現在の URL に対する自動取得直後の内容へ戻します.',
confirmText: 'リセット',
cancelText: '取消',
variant: 'danger' })
@@ -140,11 +155,8 @@ const PostImportRowForm: FC<Props> = (
setResetRequested (true)
setMessageRow (null)
setCommittedThumbnailBase (resetDraft.thumbnailBase)
if (thumbnailObjectUrl != null)
URL.revokeObjectURL (thumbnailObjectUrl)
setThumbnailObjectUrl (undefined)
return false
}, [controls, resetDisabled, resetDraft, thumbnailObjectUrl])
}, [controls, resetDisabled, resetDraft])
const save = useCallback (async (): Promise<boolean> => {
setSaving (true)
@@ -184,7 +196,11 @@ const PostImportRowForm: FC<Props> = (
<div className="grid gap-6 md:grid-cols-[7rem_minmax(0,1fr)]">
<div className="space-y-3 md:sticky md:top-0 md:self-start">
<PostImportThumbnailPreview
url={committedThumbnailBase || thumbnailObjectUrl || ''}
url={committedThumbnailBase}
file={
hasThumbnailBaseValue (committedThumbnailBase)
? undefined
: draft.thumbnailFile}
className="h-28 w-28"/>
</div>
@@ -204,40 +220,22 @@ const PostImportRowForm: FC<Props> = (
label="サムネール"
value={draft.thumbnailBase}
disabled={saving}
warnings={displayRow.fieldWarnings.thumbnailBase}
warnings={currentThumbnailWarnings}
errors={groupedMessages (
displayRow.validationErrors.thumbnailBase,
displayRow.importErrors?.thumbnailBase)}
onBlur={() => {
if (draft.thumbnailBase !== committedThumbnailBase)
if (draft.thumbnailBase.trim () !== committedThumbnailBase.trim ())
setCommittedThumbnailBase (draft.thumbnailBase)
}}
onChange={value => {
if (value && thumbnailObjectUrl != null)
{
URL.revokeObjectURL (thumbnailObjectUrl)
setThumbnailObjectUrl (undefined)
update ('thumbnailFile', undefined)
}
update ('thumbnailBase', value)
}}/>
{!(draft.thumbnailBase) && (
onChange={value => update ('thumbnailBase', value)}/>
{!(hasThumbnailBaseValue (draft.thumbnailBase)) && (
<input
type="file"
accept="image/*"
disabled={saving}
onChange={event => {
const file = event.target.files?.[0]
if (thumbnailObjectUrl != null)
URL.revokeObjectURL (thumbnailObjectUrl)
if (file == null)
{
setThumbnailObjectUrl (undefined)
update ('thumbnailFile', undefined)
return
}
const objectUrl = URL.createObjectURL (file)
setThumbnailObjectUrl (objectUrl)
update ('thumbnailFile', file)
}}/>)}
</>}
@@ -283,7 +281,18 @@ const PostImportRowForm: FC<Props> = (
disabled: saving,
errors: groupedMessages (
displayRow.validationErrors.parentPostIds,
displayRow.importErrors?.parentPostIds) } }}/>
displayRow.importErrors?.parentPostIds) } }}
extraFields={
durationVisible
? (
<PostDurationField
value={draft.duration}
onChange={value => update ('duration', value)}
disabled={saving}
errors={groupedMessages (
displayRow.validationErrors.videoMs,
displayRow.importErrors?.videoMs)}/>)
: null}/>
<FieldWarning messages={displayRow.baseWarnings}/>
<FieldError messages={displayRow.validationErrors.base}/>
<FieldError messages={displayRow.importErrors?.base}/>