|
|
@@ -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>) |
|
|
|
} |