広場投稿追加画面の刷新 (#399) #413
@@ -226,12 +226,14 @@ class Post < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.sanitised_svg_bytes(bytes)
|
def self.sanitised_svg_bytes(bytes)
|
||||||
|
parse_options =
|
||||||
|
Nokogiri::XML::ParseOptions::STRICT |
|
||||||
|
Nokogiri::XML::ParseOptions::NONET
|
||||||
document = Nokogiri::XML(
|
document = Nokogiri::XML(
|
||||||
bytes,
|
bytes,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
Nokogiri::XML::ParseOptions::STRICT
|
parse_options)
|
||||||
| Nokogiri::XML::ParseOptions::NONET)
|
|
||||||
root = document.root
|
root = document.root
|
||||||
raise MiniMagick::Error, 'SVG が不正です.' if root == nil || root.name != 'svg'
|
raise MiniMagick::Error, 'SVG が不正です.' if root == nil || root.name != 'svg'
|
||||||
raise MiniMagick::Error, 'SVG が不正です.' if document.internal_subset != nil
|
raise MiniMagick::Error, 'SVG が不正です.' if document.internal_subset != nil
|
||||||
|
|||||||
@@ -427,7 +427,8 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
: { duration: .2, ease: 'easeOut' as const }
|
: { duration: .2, ease: 'easeOut' as const }
|
||||||
|
|
||||||
const [session, setSession] = useState<PostImportSession | null> (null)
|
const [session, setSession] = useState<PostImportSession | null> (null)
|
||||||
const [loading, setLoading] = useState (false)
|
const [metadataLoading, setMetadataLoading] = useState (false)
|
||||||
|
const [submitting, setSubmitting] = useState (false)
|
||||||
const [loadingRow, setLoadingRow] = useState<number | null> (null)
|
const [loadingRow, setLoadingRow] = useState<number | null> (null)
|
||||||
const [editingRow, setEditingRow] = useState<PostImportRow | null> (null)
|
const [editingRow, setEditingRow] = useState<PostImportRow | null> (null)
|
||||||
const [showExistingRows, setShowExistingRows] = useState (false)
|
const [showExistingRows, setShowExistingRows] = useState (false)
|
||||||
@@ -490,7 +491,10 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const refreshRows = async (baseSession: PostImportSession) => {
|
const refreshRows = async (baseSession: PostImportSession) => {
|
||||||
setLoading (true)
|
if (active)
|
||||||
|
setMetadataLoading (true)
|
||||||
|
try
|
||||||
|
{
|
||||||
let nextIndex = 0
|
let nextIndex = 0
|
||||||
const previewErrors = new Map<number, Record<string, string[]>> ()
|
const previewErrors = new Map<number, Record<string, string[]>> ()
|
||||||
let workingRows = [...(sessionRef.current ?? baseSession).rows]
|
let workingRows = [...(sessionRef.current ?? baseSession).rows]
|
||||||
@@ -569,10 +573,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
await Promise.all (
|
await Promise.all (
|
||||||
Array.from ({ length: Math.min (4, baseSession.rows.length) }, () => worker ()))
|
Array.from ({ length: Math.min (4, baseSession.rows.length) }, () => worker ()))
|
||||||
if (!(active) || previewSequenceRef.current !== previewSequence)
|
if (!(active) || previewSequenceRef.current !== previewSequence)
|
||||||
{
|
|
||||||
setLoading (false)
|
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
const nextRows = applyDuplicateUrlErrors (workingRows.map (row => {
|
const nextRows = applyDuplicateUrlErrors (workingRows.map (row => {
|
||||||
const rowErrors = previewErrors.get (row.sourceRow)
|
const rowErrors = previewErrors.get (row.sourceRow)
|
||||||
@@ -584,7 +585,12 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
status: 'error' as const }
|
status: 'error' as const }
|
||||||
}))
|
}))
|
||||||
await persistSession (buildSession (nextRows))
|
await persistSession (buildSession (nextRows))
|
||||||
setLoading (false)
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
setMetadataLoading (false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hydrate = async () => {
|
const hydrate = async () => {
|
||||||
@@ -643,7 +649,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
: rows
|
: rows
|
||||||
const existingRows = sortedRows.filter (row => isExistingSkipRow (row))
|
const existingRows = sortedRows.filter (row => isExistingSkipRow (row))
|
||||||
const reviewRows = sortedRows.filter (row => !(isExistingSkipRow (row)))
|
const reviewRows = sortedRows.filter (row => !(isExistingSkipRow (row)))
|
||||||
const busy = loading || loadingRow != null
|
const busy = metadataLoading || submitting || loadingRow != null
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
rows.every (row => isCompletedReviewRow (row))
|
rows.every (row => isCompletedReviewRow (row))
|
||||||
|| processableImportRows (rows).length > 0
|
|| processableImportRows (rows).length > 0
|
||||||
@@ -856,7 +862,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading (true)
|
setSubmitting (true)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const processingRows = processableImportRows (currentSession.rows)
|
const processingRows = processableImportRows (currentSession.rows)
|
||||||
@@ -887,7 +893,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
setLoading (false)
|
setSubmitting (false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -968,26 +974,29 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
|||||||
</MainArea>
|
</MainArea>
|
||||||
|
|
||||||
<PostImportFooter
|
<PostImportFooter
|
||||||
loading={busy}
|
metadataLoading={metadataLoading}
|
||||||
|
submitting={submitting}
|
||||||
canSubmit={canSubmit}
|
canSubmit={canSubmit}
|
||||||
creatableCount={counts.creatable}
|
creatableCount={counts.creatable}
|
||||||
manualSkippedCount={counts.manualSkipped}
|
manualSkippedCount={counts.manualSkipped}
|
||||||
existingSkippedCount={counts.existingSkipped}
|
existingSkippedCount={counts.existingSkipped}
|
||||||
pendingOrErrorCount={counts.pendingOrError}
|
pendingOrErrorCount={counts.pendingOrError}
|
||||||
onBack={() => navigate (-1)}
|
onBack={() => navigate ('/posts/new')}
|
||||||
onSubmit={() => submit ()}/>
|
onSubmit={() => submit ()}/>
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const PostImportFooter = (
|
const PostImportFooter = (
|
||||||
{ loading,
|
{ metadataLoading,
|
||||||
|
submitting,
|
||||||
canSubmit,
|
canSubmit,
|
||||||
creatableCount,
|
creatableCount,
|
||||||
manualSkippedCount,
|
manualSkippedCount,
|
||||||
existingSkippedCount,
|
existingSkippedCount,
|
||||||
pendingOrErrorCount,
|
pendingOrErrorCount,
|
||||||
onBack,
|
onBack,
|
||||||
onSubmit }: { loading: boolean
|
onSubmit }: { metadataLoading: boolean
|
||||||
|
submitting: boolean
|
||||||
canSubmit: boolean
|
canSubmit: boolean
|
||||||
creatableCount: number
|
creatableCount: number
|
||||||
manualSkippedCount: number
|
manualSkippedCount: number
|
||||||
@@ -1008,13 +1017,17 @@ const PostImportFooter = (
|
|||||||
<span>登録不可/未処理 {pendingOrErrorCount}件</span>
|
<span>登録不可/未処理 {pendingOrErrorCount}件</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2 sm:flex-row">
|
<div className="flex flex-col gap-2 sm:flex-row">
|
||||||
<Button type="button" variant="outline" onClick={onBack} disabled={loading}>
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={onBack}
|
||||||
|
disabled={submitting}>
|
||||||
URL リスト入力へ戻る
|
URL リスト入力へ戻る
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabled={loading || !(canSubmit)}>
|
disabled={metadataLoading || submitting || !(canSubmit)}>
|
||||||
{creatableCount > 1 ? '一括追加' : '追加'}
|
{creatableCount > 1 ? '一括追加' : '追加'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする