このコミットが含まれているのは:
@@ -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))
|
||||
|
||||
新しい課題から参照
ユーザをブロックする