このコミットが含まれているのは:
2026-07-17 23:14:37 +09:00
コミット 0ae41b6266
19個のファイルの変更455行の追加216行の削除
+66 -40
ファイルの表示
@@ -24,6 +24,7 @@ import { countImportSourceLines,
loadPostImportSession,
loadPostImportSourceDraft,
resultRepairMode,
serialisedPostImportRowsEqual,
savePostImportSession,
savePostImportSourceDraft,
validateImportSource } from '@/lib/postImportSession'
@@ -143,8 +144,16 @@ const fetchPreviewRows = async (
rows: Array<{ sourceRow: number
url: string }>,
signal: AbortSignal,
): Promise<PostImportRow[]> => {
const results: PostImportRow[] = new Array (rows.length)
): Promise<{
rows: PostImportRow[]
issues: Array<{ sourceRow: number
message: string
url: string }>
}> => {
const results: Array<PostImportRow | null> = new Array (rows.length).fill (null)
const issues: Array<{ sourceRow: number
message: string
url: string }> = []
let nextIndex = 0
const worker = async () => {
while (nextIndex < rows.length)
@@ -152,44 +161,43 @@ const fetchPreviewRows = async (
const index = nextIndex
++nextIndex
const row = rows[index]
const result = await apiGet<PreviewResponse> ('/posts/preview', {
params: { url: row.url },
signal })
results[index] = previewRowFromResult (row.sourceRow, result)
try
{
const result = await apiGet<PreviewResponse> ('/posts/preview', {
params: { url: row.url },
signal })
results[index] = previewRowFromResult (row.sourceRow, result)
}
catch (requestError)
{
if (signal.aborted)
return
const apiMessage =
isApiError<{
message?: string
errors?: Record<string, string[]>
baseErrors?: string[]
}> (requestError)
? (requestError.response?.data?.errors?.url?.[0]
?? requestError.response?.data?.message
?? requestError.response?.data?.baseErrors?.[0])
: undefined
issues.push ({
sourceRow: row.sourceRow,
message: apiMessage ?? '入力を確認してください.',
url: row.url })
}
}
}
await Promise.all (
Array.from ({ length: Math.min (4, rows.length) }, () => worker ()))
return results
return {
rows: results.filter ((row): row is PostImportRow => row != null),
issues }
}
const serialisableRowJson = (row: PostImportRow): string =>
JSON.stringify ({
sourceRow: row.sourceRow,
url: row.url,
attributes: row.attributes,
fieldWarnings: row.fieldWarnings,
baseWarnings: row.baseWarnings,
validationErrors: row.validationErrors,
provenance: row.provenance,
tagSources: row.tagSources,
status: row.status,
skipReason: row.skipReason,
existingPostId: row.existingPostId,
existingPost: row.existingPost,
resetSnapshot: row.resetSnapshot,
importStatus: row.importStatus,
recoverable: row.recoverable })
const sameRows = (
left: PostImportRow[],
right: PostImportRow[],
): boolean =>
left.length === right.length
&& left.every ((row, index) =>
serialisableRowJson (row) === serialisableRowJson (right[index]))
const PostImportSourcePage: FC<Props> = ({ user }) => {
const editable = canEditContent (user)
@@ -269,7 +277,7 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
previewController.current?.abort ()
const controller = new AbortController ()
previewController.current = controller
const rows = await fetchPreviewRows (
const previewed = await fetchPreviewRows (
source
.split (/\r\n|\n|\r/)
.map ((line, index) => ({
@@ -277,13 +285,31 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
url: line.trim () }))
.filter (row => row.url !== ''),
controller.signal)
const urlIssues = urlIssuesFromRows (rows, source)
if (urlIssues.length > 0)
if (previewed.issues.length > 0)
{
setSourceIssues (urlIssues)
setSourceIssues (previewed.issues)
return
}
const nextRows = initialisePreviewRows (rows)
const urlIssues = urlIssuesFromRows (previewed.rows, source)
const duplicateIssues = Object.entries (
previewed.rows.reduce<Record<string, PostImportRow[]>> ((acc, row) => {
acc[row.url] ||= []
acc[row.url].push (row)
return acc
}, { }))
.flatMap (([, rows]) =>
rows.length < 2
? []
: rows.map (row => ({
sourceRow: row.sourceRow,
message: 'URL が重複しています.',
url: row.url })))
if (urlIssues.length > 0 || duplicateIssues.length > 0)
{
setSourceIssues ([...urlIssues, ...duplicateIssues])
return
}
const nextRows = initialisePreviewRows (previewed.rows)
const serialised = await serialisePostNewState (nextRows)
if (serialised == null)
return
@@ -297,9 +323,9 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
const sessionLoaded = loadPostImportSession (sessionId)
if (!(serialised.complete)
&& (!(saved))
&& !(sameRows (sessionLoaded?.rows ?? [], nextRows)))
&& !(serialisedPostImportRowsEqual (sessionLoaded?.rows ?? [], nextRows)))
return
if (saved && !(sameRows (sessionLoaded?.rows ?? [], nextRows)))
if (saved && !(serialisedPostImportRowsEqual (sessionLoaded?.rows ?? [], nextRows)))
return
navigate (appendPostNewSessionId (serialised.path, sessionId))