#45 完了
This commit is contained in:
@@ -1,25 +1,10 @@
|
||||
class WikiPagesController < ApplicationController
|
||||
def show
|
||||
wiki_page = WikiPage.find(params[:id])
|
||||
return head :not_found unless wiki_page
|
||||
|
||||
render json: wiki_page.as_json.merge(body: wiki_page.body)
|
||||
render_wiki_page_or_404 WikiPage.find(params[:id])
|
||||
end
|
||||
|
||||
def show_by_title
|
||||
title = params[:title]
|
||||
version = params[:version].presence
|
||||
|
||||
wiki_page = WikiPage.find_by(title:)
|
||||
return head :not_found unless wiki_page
|
||||
|
||||
wiki_page.sha = version
|
||||
|
||||
body = wiki_page.body
|
||||
sha = wiki_page.sha
|
||||
pred = wiki_page.pred
|
||||
succ = wiki_page.succ
|
||||
render json: wiki_page.as_json.merge(body:, sha:, pred:, succ:)
|
||||
render_wiki_page_or_404 WikiPage.find_by(title: params[:title])
|
||||
end
|
||||
|
||||
def diff
|
||||
@@ -117,4 +102,16 @@ class WikiPagesController < ApplicationController
|
||||
def wiki
|
||||
@wiki ||= Gollum::Wiki.new(WIKI_PATH)
|
||||
end
|
||||
|
||||
def render_wiki_page_or_404 wiki_page
|
||||
return head :not_found unless wiki_page
|
||||
|
||||
wiki_page.sha = params[:version].presence
|
||||
|
||||
body = wiki_page.body
|
||||
sha = wiki_page.sha
|
||||
pred = wiki_page.pred
|
||||
succ = wiki_page.succ
|
||||
render json: wiki_page.as_json.merge(body:, sha:, pred:, succ:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,6 +20,7 @@ class WikiPage < ApplicationRecord
|
||||
idx = vers.find_index { |ver| ver.id == @sha }
|
||||
@pred = vers[idx + 1]&.id
|
||||
@succ = idx.positive? ? vers[idx - 1].id : nil
|
||||
@updated_at = vers[idx].authored_date
|
||||
@sha
|
||||
end
|
||||
|
||||
@@ -35,6 +36,10 @@ class WikiPage < ApplicationRecord
|
||||
@succ
|
||||
end
|
||||
|
||||
def updated_at
|
||||
@updated_at
|
||||
end
|
||||
|
||||
def body
|
||||
sha = nil unless @page
|
||||
@page&.raw_data&.force_encoding('UTF-8')
|
||||
|
||||
@@ -10,6 +10,7 @@ import PostDetailPage from '@/pages/PostDetailPage'
|
||||
import WikiPage from '@/pages/WikiPage'
|
||||
import WikiNewPage from '@/pages/WikiNewPage'
|
||||
import WikiEditPage from '@/pages/WikiEditPage'
|
||||
import WikiDiffPage from '@/pages/WikiDiffPage'
|
||||
import WikiDetailPage from '@/pages/WikiDetailPage'
|
||||
import WikiHistoryPage from '@/pages/WikiHistoryPage'
|
||||
import { API_BASE_URL } from '@/config'
|
||||
@@ -64,9 +65,10 @@ export default () => {
|
||||
<Route path="/posts/:id" element={<PostDetailPage user={user} />} />
|
||||
<Route path="/tags/:tag" element={<TagPage />} />
|
||||
<Route path="/wiki" element={<WikiPage />} />
|
||||
<Route path="/wiki/:name" element={<WikiDetailPage />} />
|
||||
<Route path="/wiki/:title" element={<WikiDetailPage />} />
|
||||
<Route path="/wiki/new" element={<WikiNewPage />} />
|
||||
<Route path="/wiki/:id/edit" element={<WikiEditPage />} />
|
||||
<Route path="/wiki/:id/diff" element={<WikiDiffPage />} />
|
||||
<Route path="/wiki/changes" element={<WikiHistoryPage />} />
|
||||
</Routes>
|
||||
</div>
|
||||
|
||||
@@ -1,45 +1,68 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, useParams, useNavigate } from 'react-router-dom'
|
||||
import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import axios from 'axios'
|
||||
import { API_BASE_URL } from '@/config'
|
||||
import MainArea from '@/components/layout/MainArea'
|
||||
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
||||
|
||||
import type { WikiPage } from '@/types'
|
||||
|
||||
|
||||
export default () => {
|
||||
const { name } = useParams ()
|
||||
const { title } = useParams ()
|
||||
|
||||
const location = useLocation ()
|
||||
const navigate = useNavigate ()
|
||||
|
||||
const [markdown, setMarkdown] = useState<string | null> (null)
|
||||
const [wikiPage, setWikiPage] = useState<WikiPage | null | undefined> (undefined)
|
||||
|
||||
const query = new URLSearchParams (location.search)
|
||||
const version = query.get ('version')
|
||||
|
||||
useEffect (() => {
|
||||
if (/^\d+$/.test (name))
|
||||
if (/^\d+$/.test (title))
|
||||
{
|
||||
void (axios.get (`${ API_BASE_URL }/wiki/${ name }`)
|
||||
void (axios.get (`${ API_BASE_URL }/wiki/${ title }`)
|
||||
.then (res => navigate (`/wiki/${ res.data.title }`, { replace: true })))
|
||||
return
|
||||
}
|
||||
|
||||
void (axios.get (`${ API_BASE_URL }/wiki/title/${ encodeURIComponent (name) }`)
|
||||
void (axios.get (`${ API_BASE_URL }/wiki/title/${ encodeURIComponent (title) }`, version && { params: { version } })
|
||||
.then (res => {
|
||||
setMarkdown (res.data.body)
|
||||
setWikiPage (res.data)
|
||||
WikiIdBus.set (res.data.id)
|
||||
})
|
||||
.catch (() => setMarkdown ('')))
|
||||
}, [name])
|
||||
.catch (() => setWikiPage (null)))
|
||||
}, [title, location.search])
|
||||
|
||||
return (
|
||||
<MainArea>
|
||||
{(wikiPage && version) && (
|
||||
<div className="text-sm flex gap-3 items-center justify-center border border-gray-700 rounded px-2 py-1 mb-4">
|
||||
{wikiPage.pred ? (
|
||||
<Link to={`/wiki/${ title }?version=${ wikiPage.pred }`}
|
||||
className="text-blue-400 hover:underline">
|
||||
< 前
|
||||
</Link>) : <>< 前</>}
|
||||
|
||||
<span>{wikiPage.updated_at}</span>
|
||||
|
||||
{wikiPage.succ ? (
|
||||
<Link to={`/wiki/${ title }?version=${ wikiPage.succ }`}
|
||||
className="text-blue-400 hover:underline">
|
||||
後 >
|
||||
</Link>) : <>後 ></>}
|
||||
</div>)}
|
||||
<h1>{title}</h1>
|
||||
<div className="prose mx-auto p-4">
|
||||
{markdown == null ? 'Loading...' : (
|
||||
{wikiPage === undefined ? 'Loading...' : (
|
||||
<>
|
||||
<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>)) }}>
|
||||
{markdown || `このページは存在しません。[新規作成してください](/wiki/new?title=${ name })。`}
|
||||
{wikiPage?.body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ title })。`}
|
||||
</ReactMarkdown>
|
||||
</>)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, useLocation, useParams } from 'react-router-dom'
|
||||
import axios from 'axios'
|
||||
import MainArea from '@/components/layout/MainArea'
|
||||
import { API_BASE_URL } from '@/config'
|
||||
|
||||
import type { WikiPageDiff } from '@/types'
|
||||
|
||||
|
||||
export default () => {
|
||||
const { id } = useParams ()
|
||||
|
||||
const location = useLocation ()
|
||||
|
||||
const [diff, setDiff] = useState<WikiPageDiff | null> (null)
|
||||
|
||||
const query = new URLSearchParams (location.search)
|
||||
const from = query.get ('from')
|
||||
const to = query.get ('to')
|
||||
|
||||
useEffect (() => {
|
||||
void (axios.get (`${ API_BASE_URL }/wiki/${ id }/diff`, { params: { from, to } })
|
||||
.then (res => setDiff (res.data)))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<MainArea>
|
||||
<h1>{diff?.title}</h1>
|
||||
<div className="prose mx-auto p-4">
|
||||
{diff ? (
|
||||
diff.diff.map (d => (
|
||||
<span className={d.type === 'added' ? 'bg-green-800' : d.type === 'removed' ? 'bg-red-800' : ''}>
|
||||
{d.content == '\n' ? <br /> : d.content}
|
||||
</span>))) : 'Loading...'}
|
||||
</div>
|
||||
</MainArea>)
|
||||
}
|
||||
@@ -33,10 +33,12 @@ export default () => {
|
||||
<tbody>
|
||||
{changes.map (change => (
|
||||
<tr key={change.sha}>
|
||||
<td>{change.change_type === 'update' && (
|
||||
<Link>
|
||||
<td>
|
||||
{change.change_type === 'update' && (
|
||||
<Link to={`/wiki/${ change.wiki_page.id }/diff?from=${ change.pred }&to=${ change.sha }`}>
|
||||
差分
|
||||
</Link>)}</td>
|
||||
</Link>)}
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<Link to={`/wiki/${ encodeURIComponent (change.wiki_page.title) }?version=${ change.sha }`}
|
||||
className="text-blue-400 hover:underline">
|
||||
|
||||
@@ -25,13 +25,29 @@ export type User = {
|
||||
export type WikiPage = {
|
||||
id: number
|
||||
title: string
|
||||
sha: string
|
||||
pred?: string
|
||||
succ?: string
|
||||
updated_at?: string }
|
||||
|
||||
export type WikiPageChange = {
|
||||
sha: string
|
||||
pred?: string
|
||||
succ?: string
|
||||
wiki_page: WikiPage
|
||||
user: User
|
||||
change_type: string
|
||||
timestamp: string }
|
||||
|
||||
export type WikiPageDiff = {
|
||||
wiki_page_id: number
|
||||
title: string
|
||||
older_sha: string
|
||||
newer_sha: string
|
||||
diff: WikiPageDiffDiff[] }
|
||||
|
||||
export type WikiPageDiffDiff = {
|
||||
type: 'context' | 'added' | 'removed'
|
||||
content: string }
|
||||
|
||||
export type UserRole = typeof USER_ROLES[number]
|
||||
|
||||
Reference in New Issue
Block a user