このコミットが含まれているのは:
@@ -16,7 +16,9 @@ const hasWarnings = (row: PostImportRow): boolean =>
|
||||
export const displayPostImportStatus = (
|
||||
row: PostImportRow,
|
||||
): PostImportDisplayStatus | null =>
|
||||
(row.skipReason != null || row.importStatus === 'skipped')
|
||||
row.status === 'pending'
|
||||
? null
|
||||
: (row.skipReason != null || row.importStatus === 'skipped')
|
||||
? 'skipped'
|
||||
: (row.importStatus === 'created')
|
||||
? 'created'
|
||||
|
||||
@@ -73,8 +73,30 @@ export const validatePostImportSessionId = (value: unknown): string | null => {
|
||||
}
|
||||
|
||||
|
||||
export const generatePostImportSessionId = (): string =>
|
||||
crypto.randomUUID ()
|
||||
const generateUuidV4FromRandomValues = (): string => {
|
||||
const bytes = new Uint8Array (16)
|
||||
crypto.getRandomValues (bytes)
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
||||
|
||||
const hex = Array.from (
|
||||
bytes,
|
||||
byte => byte.toString (16).padStart (2, '0'))
|
||||
return [
|
||||
hex.slice (0, 4).join (''),
|
||||
hex.slice (4, 6).join (''),
|
||||
hex.slice (6, 8).join (''),
|
||||
hex.slice (8, 10).join (''),
|
||||
hex.slice (10, 16).join ('')].join ('-')
|
||||
}
|
||||
|
||||
|
||||
export const generatePostImportSessionId = (): string => {
|
||||
if (typeof crypto.randomUUID === 'function')
|
||||
return crypto.randomUUID ()
|
||||
|
||||
return generateUuidV4FromRandomValues ()
|
||||
}
|
||||
|
||||
|
||||
const sessionKey = (sessionId: string): string => `${ SESSION_PREFIX }${ sessionId }`
|
||||
@@ -156,7 +178,10 @@ const ensureStringListRecord = (value: unknown): Record<string, string[]> | null
|
||||
const isValidStatus = (
|
||||
value: unknown,
|
||||
): value is PostImportRow['status'] =>
|
||||
value === 'ready' || value === 'warning' || value === 'error'
|
||||
value === 'pending'
|
||||
|| value === 'ready'
|
||||
|| value === 'warning'
|
||||
|| value === 'error'
|
||||
|
||||
|
||||
const isValidImportStatus = (
|
||||
|
||||
@@ -34,7 +34,7 @@ export type PostImportRow = {
|
||||
importErrors?: Record<string, string[]>
|
||||
provenance: Record<string, PostImportOrigin>
|
||||
tagSources?: Record<PostImportOrigin, string>
|
||||
status: 'ready' | 'warning' | 'error'
|
||||
status: 'pending' | 'ready' | 'warning' | 'error'
|
||||
skipReason?: PostImportSkipReason
|
||||
existingPostId?: number
|
||||
existingPost?: PostImportExistingPost
|
||||
|
||||
@@ -344,7 +344,7 @@ const buildRow = (state: FullRowState): PostImportRow => {
|
||||
validationErrors: { },
|
||||
provenance,
|
||||
tagSources,
|
||||
status: 'ready',
|
||||
status: 'pending',
|
||||
skipReason: state.skip ?? undefined,
|
||||
existingPostId: state.existing_post_id ?? undefined,
|
||||
resetSnapshot: {
|
||||
|
||||
@@ -75,7 +75,7 @@ const buildInitialRows = (source: string): PostImportRow[] =>
|
||||
tagSources: {
|
||||
automatic: '',
|
||||
manual: '' },
|
||||
status: 'ready' as const,
|
||||
status: 'pending' as const,
|
||||
resetSnapshot: {
|
||||
url: row.url,
|
||||
attributes: {
|
||||
@@ -122,6 +122,9 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
|
||||
.filter (value => value != null)
|
||||
.join (' ')
|
||||
|
||||
const showStorageError = (message: string) =>
|
||||
toast ({ description: message })
|
||||
|
||||
useEffect (() => {
|
||||
cleanupExpiredPostImportSessions (message =>
|
||||
toast ({ title: '保存済みデータを整理できませんでした', description: message }))
|
||||
@@ -176,28 +179,65 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
|
||||
{
|
||||
const nextRows = initialisePreviewRows (buildInitialRows (source))
|
||||
const serialised = await serialisePostNewState (nextRows)
|
||||
if (serialised == null)
|
||||
return
|
||||
|
||||
const sessionId = generatePostImportSessionId ()
|
||||
const sessionId = (() => {
|
||||
try
|
||||
{
|
||||
return generatePostImportSessionId ()
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null
|
||||
}
|
||||
}) ()
|
||||
const session = {
|
||||
source,
|
||||
rows: nextRows,
|
||||
repairMode: resultRepairMode (nextRows) }
|
||||
const saved = savePostImportSession (sessionId, session)
|
||||
const sessionLoaded = loadPostImportSession (sessionId)
|
||||
if (!(serialised.complete)
|
||||
&& (!(saved))
|
||||
&& !(serialisedPostImportRowsEqual (sessionLoaded?.rows ?? [], nextRows)))
|
||||
return
|
||||
if (saved && !(serialisedPostImportRowsEqual (sessionLoaded?.rows ?? [], nextRows)))
|
||||
return
|
||||
const saved =
|
||||
sessionId == null
|
||||
? false
|
||||
: savePostImportSession (sessionId, session, showStorageError)
|
||||
const loadedSession =
|
||||
sessionId == null
|
||||
? null
|
||||
: loadPostImportSession (sessionId, showStorageError)
|
||||
const sessionRoundTripSucceeded =
|
||||
saved
|
||||
&& serialisedPostImportRowsEqual (
|
||||
loadedSession?.rows ?? [],
|
||||
nextRows)
|
||||
const canNavigate =
|
||||
serialised?.complete === true
|
||||
|| sessionRoundTripSucceeded
|
||||
if (!(canNavigate))
|
||||
{
|
||||
showStorageError ('ブラウザへ保存できませんでした.')
|
||||
return
|
||||
}
|
||||
|
||||
navigate (appendPostNewSessionId (serialised.path, sessionId))
|
||||
const nextPath = (() => {
|
||||
if (serialised != null)
|
||||
{
|
||||
return sessionId == null
|
||||
? serialised.path
|
||||
: appendPostNewSessionId (serialised.path, sessionId)
|
||||
}
|
||||
if (sessionRoundTripSucceeded && sessionId != null)
|
||||
return `/posts/new?session_id=${ sessionId }`
|
||||
return null
|
||||
}) ()
|
||||
if (nextPath == null)
|
||||
{
|
||||
showStorageError ('ブラウザへ保存できませんでした.')
|
||||
return
|
||||
}
|
||||
|
||||
navigate (nextPath)
|
||||
}
|
||||
catch
|
||||
catch (error)
|
||||
{
|
||||
return
|
||||
console.error (error)
|
||||
showStorageError ('ブラウザへ保存できませんでした.')
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -249,13 +289,13 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
|
||||
</li>))}
|
||||
</ul>
|
||||
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="relative z-10 flex items-center justify-between gap-3">
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-300">
|
||||
入力行数 {lineCount} / {MAX_ROWS}
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
className="shrink-0"
|
||||
className="pointer-events-auto shrink-0"
|
||||
onClick={preview}
|
||||
disabled={loading}>
|
||||
次へ
|
||||
|
||||
新しい課題から参照
ユーザをブロックする