| @@ -1,14 +1,69 @@ | |||
| import axios from 'axios' | |||
| import toCamel from 'camelcase-keys' | |||
| import { useEffect, useState } from 'react' | |||
| import ReactMarkdown from 'react-markdown' | |||
| import { Link } from 'react-router-dom' | |||
| import { API_BASE_URL } from '@/config' | |||
| import type { WikiPage } from '@/types' | |||
| type Props = { title: string | |||
| body?: string } | |||
| export default ({ title, body }: Props) => ( | |||
| <ReactMarkdown components={{ a: ( | |||
| ({ href, children }) => (['/', '.'].some (e => href?.startsWith (e)) | |||
| ? <Link to={href!}>{children}</Link> | |||
| : <a href={href} target="_blank" rel="noopener noreferrer">{children}</a>)) }}> | |||
| {body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`} | |||
| </ReactMarkdown>) | |||
| export default ({ title, body }: Props) => { | |||
| const [pageNames, setPageNames] = useState<string[]> ([]) | |||
| const [realBody, setRealBody] = useState<string> ('') | |||
| useEffect (() => { | |||
| const matchIndices = (target: string, keyword: string) => { | |||
| const indices: number[] = [] | |||
| let pos = 0 | |||
| while (true) | |||
| { | |||
| const idx = target.indexOf (keyword, pos) | |||
| if (idx < 0) | |||
| break | |||
| indices.push (idx) | |||
| pos = idx + keyword.length | |||
| } | |||
| return indices | |||
| } | |||
| void (async () => { | |||
| try | |||
| { | |||
| const res = await axios.get (`${ API_BASE_URL }/wiki`) | |||
| const data = toCamel (res.data as any, { deep: true }) as WikiPage[] | |||
| setPageNames (data.map (page => page.title).sort ((a, b) => b.length - a.length)) | |||
| } | |||
| catch | |||
| { | |||
| setPageNames ([]) | |||
| } | |||
| }) () | |||
| const linkIndices: [string, [number, number]][] = [] | |||
| pageNames.forEach (name => { | |||
| matchIndices (body ?? '', name).map (idx => [idx, idx + name.length]).forEach (([start, end]) => { | |||
| if (linkIndices.every (([, [st, ed]]) => (start < st || ed <= start) && (end < st || ed <= end))) | |||
| linkIndices.push ([name, [start, end]]) | |||
| }) | |||
| }) | |||
| linkIndices.sort (([, [a]], [, [b]]) => b - a) | |||
| linkIndices.forEach (([name, [start, end]]) => { | |||
| setRealBody (prev => `${ prev.slice (0, start) }[${ name }](/wiki/${ encodeURIComponent (name) })${ prev.slice (end) }`) | |||
| }) | |||
| }, []) | |||
| return ( | |||
| <ReactMarkdown components={{ a: ( | |||
| ({ href, children }) => (['/', '.'].some (e => href?.startsWith (e)) | |||
| ? <Link to={href!}>{children}</Link> | |||
| : <a href={href} target="_blank" rel="noopener noreferrer">{children}</a>)) }}> | |||
| {realBody || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`} | |||
| </ReactMarkdown>) | |||
| } | |||