import { useEffect, useRef, useState } from 'react' import { Helmet } from 'react-helmet-async' import { useNavigate } from 'react-router-dom' import FieldError from '@/components/common/FieldError' import Form from '@/components/common/Form' import FormField from '@/components/common/FormField' import PageTitle from '@/components/common/PageTitle' import TextArea from '@/components/common/TextArea' import MainArea from '@/components/layout/MainArea' import { Button } from '@/components/ui/button' import { toast } from '@/components/ui/use-toast' import { SITE_TITLE } from '@/config' import { buildPostNewReviewPath, isPostNewReviewPathWithinLimit, } from '@/lib/postNewQueryState' import { countImportSourceLines, extractImportSourceUrls, validateImportSource, } from '@/lib/postImportSourceValidation' import { loadPostImportSourceDraft, savePostImportSourceDraft, } from '@/lib/postImportStorage' import { canEditContent } from '@/lib/users' import Forbidden from '@/pages/Forbidden' import type { FC } from 'react' import type { User } from '@/types' type Props = { user: User | null } const MAX_ROWS = 100 const SOURCE_ERROR_ID = 'post-import-source-error' const SOURCE_ISSUES_ID = 'post-import-source-issues' const PostImportSourcePage: FC = ({ user }) => { const editable = canEditContent (user) const navigate = useNavigate () const [source, setSource] = useState ('') const [loading, setLoading] = useState (false) const [sourceIssues, setSourceIssues] = useState> ([]) const [sourceError, setSourceError] = useState (null) const saveTimer = useRef (null) const editedRef = useRef (false) const lineCount = countImportSourceLines (source) const sourceUrls = extractImportSourceUrls (source) const withinPathLimit = isPostNewReviewPathWithinLimit (sourceUrls) const messages = sourceError != null ? [sourceError] : [] const sourceDescribedBy = [sourceError != null ? SOURCE_ERROR_ID : null, sourceIssues.length > 0 ? SOURCE_ISSUES_ID : null] .filter (value => value != null) .join (' ') useEffect (() => { const draft = loadPostImportSourceDraft (message => toast ({ title: '保存済み入力を復元できませんでした', description: message })) if (!(editedRef.current)) { setSource (current => current === '' ? draft.source : current) } }, []) useEffect (() => { if (saveTimer.current != null) window.clearTimeout (saveTimer.current) saveTimer.current = window.setTimeout (() => { savePostImportSourceDraft (source, message => toast ({ title: '入力内容を保存できませんでした', description: message })) }, 300) return () => { if (saveTimer.current != null) window.clearTimeout (saveTimer.current) } }, [source]) const preview = async () => { if (lineCount === 0) { setSourceIssues ([]) setSourceError ('URL を入力してください.') return } const issues = validateImportSource (source) if (issues.length > 0) { setSourceIssues (issues) setSourceError (null) return } if (!(withinPathLimit)) return setLoading (true) setSourceIssues ([]) setSourceError (null) try { navigate (buildPostNewReviewPath (sourceUrls)) } finally { setLoading (false) } } if (!(editable)) return return ( {`広場に投稿を追加 | ${ SITE_TITLE }`}
広場に投稿を追加 {() => (