このコミットが含まれているのは:
2026-07-17 23:14:37 +09:00
コミット 0ae41b6266
19個のファイルの変更455行の追加216行の削除
+47 -64
ファイルの表示
@@ -29,6 +29,7 @@ import {
clearPostImportSession,
clearPostImportSourceDraft,
generatePostImportSessionId,
hasThumbnailBaseValue,
initialisePreviewRows,
isCompletedReviewRow,
isExistingSkipRow,
@@ -41,6 +42,7 @@ import {
reviewSummaryCounts,
retryImportRow,
loadPostImportSession,
serialisedPostImportRowsEqual,
savePostImportSession,
} from '@/lib/postImportSession'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
@@ -125,7 +127,7 @@ const buildDryRunFormData = (row: PostImportRow): FormData => {
String (row.attributes.originalCreatedBefore ?? ''))
formData.append ('video_ms', String (row.attributes.videoMs ?? ''))
formData.append ('duration', String (row.attributes.duration ?? ''))
if (!(row.attributes.thumbnailBase) && row.thumbnailFile != null)
if (!(hasThumbnailBaseValue (row.attributes.thumbnailBase)) && row.thumbnailFile != null)
formData.append ('thumbnail', row.thumbnailFile)
return formData
}
@@ -174,6 +176,12 @@ const indexedBulkResults = (
): PostImportResultRow[] =>
rows.map ((row, index) => {
const result = results[index]
const errors =
result?.baseErrors?.length
? {
...(result.errors ?? { }),
base: [...new Set ([...(result.errors?.base ?? []), ...result.baseErrors])] }
: result?.errors
if (result?.status === 'created' && result.post != null)
{
return {
@@ -182,7 +190,7 @@ const indexedBulkResults = (
post: result.post,
fieldWarnings: result.fieldWarnings,
baseWarnings: result.baseWarnings,
errors: result.errors }
errors }
}
if (result?.status === 'skipped' && result.existingPost != null)
{
@@ -193,52 +201,28 @@ const indexedBulkResults = (
existingPost: result.existingPost,
fieldWarnings: result.fieldWarnings,
baseWarnings: result.baseWarnings,
errors: result.errors }
errors }
}
return {
sourceRow: row.sourceRow,
status: 'failed',
fieldWarnings: result?.fieldWarnings,
baseWarnings: result?.baseWarnings,
errors: result?.errors,
errors,
recoverable: result?.recoverable }
})
const serialisableRowJson = (row: PostImportRow): string =>
JSON.stringify ({
url: row.url,
sourceRow: row.sourceRow,
attributes: row.attributes,
fieldWarnings: row.fieldWarnings,
baseWarnings: row.baseWarnings,
validationErrors: row.validationErrors,
importErrors: row.importErrors,
provenance: row.provenance,
tagSources: row.tagSources,
status: row.status,
skipReason: row.skipReason,
existingPostId: row.existingPostId,
existingPost: row.existingPost,
metadataUrl: row.metadataUrl,
resetSnapshot: row.resetSnapshot,
createdPostId: row.createdPostId,
importStatus: row.importStatus,
recoverable: row.recoverable })
const serialisableRowsEqual = (
left: PostImportRow[],
right: PostImportRow[],
): boolean =>
left.length === right.length
&& left.every ((row, index) =>
serialisableRowJson (row) === serialisableRowJson (right[index]))
const mergePreviewRow = (
currentRow: PostImportRow,
preview: PreviewResponse,
): PostImportRow => {
const nextRow: PostImportRow = {
...currentRow,
attributes: { ...currentRow.attributes },
provenance: { ...currentRow.provenance },
tagSources: {
automatic: currentRow.tagSources?.automatic ?? '',
manual: currentRow.tagSources?.manual ?? '' },
url: preview.url,
fieldWarnings: preview.fieldWarnings ?? { },
baseWarnings: preview.baseWarnings ?? [],
@@ -267,9 +251,7 @@ const mergePreviewRow = (
if (currentRow.provenance.tags !== 'manual')
{
nextRow.attributes.tags = preview.tags ?? ''
nextRow.tagSources = {
automatic: preview.tags ?? '',
manual: currentRow.tagSources?.manual ?? '' }
nextRow.tagSources.automatic = preview.tags ?? ''
}
if (currentRow.provenance.videoMs !== 'manual')
nextRow.attributes.videoMs = preview.videoMs ?? ''
@@ -296,7 +278,7 @@ const buildBulkFormData = (rows: PostImportRow[]): FormData => {
video_ms: row.attributes.videoMs ?? '',
duration: row.attributes.duration ?? '' }))))
rows.forEach ((row, index) => {
if (!(row.attributes.thumbnailBase) && row.thumbnailFile != null)
if (!(hasThumbnailBaseValue (row.attributes.thumbnailBase)) && row.thumbnailFile != null)
formData.append (`thumbnails[${ index }]`, row.thumbnailFile)
})
return formData
@@ -380,10 +362,10 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
const canRecoverFromUrlOnly = serialised.complete
if (!(canRecoverFromUrlOnly)
&& (!(saved))
&& !(serialisableRowsEqual (loadedSession?.rows ?? [], nextSession.rows)))
&& !(serialisedPostImportRowsEqual (loadedSession?.rows ?? [], nextSession.rows)))
return null
if (saved
&& !(serialisableRowsEqual (loadedSession?.rows ?? [], nextSession.rows)))
&& !(serialisedPostImportRowsEqual (loadedSession?.rows ?? [], nextSession.rows)))
return null
const persistedSession = buildSession (nextSession.rows)
@@ -425,6 +407,11 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
return
const baseRow = baseSession.rows[index]
if (baseRow.skipReason === 'manual'
|| baseRow.importStatus === 'created'
|| baseRow.importStatus === 'skipped'
|| isNonRecoverableFailedRow (baseRow))
continue
const controller = new AbortController ()
controllers.add (controller)
try
@@ -441,10 +428,8 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
if (currentRow == null)
continue
const mergedRow = mergePreviewRow (currentRow, preview)
const nextRows = replaceImportRow (latestSession.rows, mergedRow)
const nextSession = buildSession (nextRows)
sessionRef.current = nextSession
setSession (nextSession)
await persistSession (buildSession (
replaceImportRow (latestSession.rows, mergedRow)))
}
catch (requestError)
{
@@ -457,15 +442,6 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
row => row.sourceRow === baseRow.sourceRow)
if (currentRow == null)
continue
const nextRows = replaceImportRow (
latestSession.rows,
applyThumbnailWarning ({
...currentRow,
existingPostId: undefined,
existingPost: undefined }))
const nextSession = buildSession (nextRows)
sessionRef.current = nextSession
setSession (nextSession)
if (!(isApiError (requestError)))
continue
}
@@ -481,24 +457,25 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
}
const hydrate = async () => {
const parsedSessionId = parsePostNewSessionId (location.search)
const sessionId = parsedSessionId ?? generatePostImportSessionId ()
const loadedSession = loadPostImportSession (sessionId)
const parsed = await parsePostNewState (location.search)
if (!(active))
return
if (parsed == null)
if (parsed == null && loadedSession == null)
{
navigate ('/posts/new', { replace: true })
return
}
const parsedSessionId = parsePostNewSessionId (location.search)
const sessionId = parsedSessionId ?? generatePostImportSessionId ()
sessionIdRef.current = sessionId
const loaded = loadPostImportSession (sessionId) ?? buildSession (parsed.rows)
const loaded = loadedSession ?? buildSession (parsed?.rows ?? [])
persistedSearchRef.current = location.search
sessionRef.current = loaded
setSession (loaded)
if (parsed.shortcut)
if (loadedSession == null && parsed?.shortcut)
{
try
{
@@ -580,7 +557,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
return
}
if (parsedSessionId == null)
if (loadedSession == null)
{
await persistSession (loaded)
}
@@ -672,21 +649,28 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
.map (([key, values]) => [key, [...values]])),
baseWarnings: [...row.resetSnapshot.baseWarnings],
metadataUrl: row.resetSnapshot.metadataUrl,
thumbnailFile: undefined,
thumbnailObjectUrl: undefined }
thumbnailFile: undefined }
: row
const urlChanged = draft.url !== baseRow.url
const nextRow = buildNextEditedRow (baseRow, draft, urlChanged)
let candidateRow = nextRow
try
{
candidateRow =
urlChanged
? mergePreviewRow (
nextRow,
await apiGet<PreviewResponse> ('/posts/preview', {
params: { url: nextRow.url } }))
: nextRow
const dryRun = await apiPost<PreviewResponse> (
'/posts?dry=1',
buildDryRunFormData (nextRow))
buildDryRunFormData (candidateRow))
const latestSession = sessionRef.current
if (latestSession == null)
return { saved: false, row: null }
const mergedRow = mergeDryRunRow (nextRow, dryRun)
const mergedRow = mergeDryRunRow (candidateRow, dryRun)
const mergedRows = replaceImportRow (latestSession.rows, mergedRow)
const nextSession = await persistSession (buildSession (mergedRows))
if (nextSession == null)
@@ -708,7 +692,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
&& requestError.response?.status === 422)
{
const errorRow = {
...nextRow,
...candidateRow,
validationErrors: requestError.response.data.errors ?? { },
baseWarnings: [],
fieldWarnings: { },
@@ -731,7 +715,6 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
await dialogue.form ({
title: '投稿を編輯',
description: `投稿 ${ row.sourceRow } の内容を確認し、必要な項目を編輯してください.`,
cancelText: '取消',
size: 'large',
body: controls => (