みてるぞ 1 month ago
parent
commit
fe8739b290
8 changed files with 144 additions and 11 deletions
  1. +6
    -5
      backend/app/controllers/wiki_pages_controller.rb
  2. +1
    -1
      backend/app/models/wiki_page.rb
  3. +2
    -1
      frontend/src/App.tsx
  4. +6
    -2
      frontend/src/components/TopNav.tsx
  5. +35
    -0
      frontend/src/lib/eventBus/EventBus.ts
  6. +3
    -0
      frontend/src/lib/eventBus/WikiIdBus.ts
  7. +6
    -2
      frontend/src/pages/WikiDetailPage.tsx
  8. +85
    -0
      frontend/src/pages/WikiEditPage.tsx

+ 6
- 5
backend/app/controllers/wiki_pages_controller.rb View File

@@ -1,15 +1,16 @@
class WikiPagesController < ApplicationController
def show
wiki_page = WikiPage.find(params[:id])
render json: wiki_page.as_json
return head :not_found unless wiki_page

render json: wiki_page.as_json.merge(body: wiki_page.body)
end

def show_by_title
wiki_page = WikiPage.find_by(title: params[:title])
body = wiki_page&.body
return head :not_found unless body
return head :not_found unless wiki_page

render plain: body
render json: wiki_page.as_json.merge(body: wiki_page.body)
end

def create
@@ -29,7 +30,7 @@ class WikiPagesController < ApplicationController
return head :unauthorized unless current_user

wiki_page = WikiPage.find(params[:id])
return head :not_found unless wiki_pages
return head :not_found unless wiki_page

wiki_page.updated_user = current_user
wiki_page.set_body params[:body], user: current_user


+ 1
- 1
backend/app/models/wiki_page.rb View File

@@ -21,7 +21,7 @@ class WikiPage < ApplicationRecord
email: 'dummy@example.com' }

if page
page.update(content, commit: commit_info)
wiki.update_page(page, id.to_s, :markdown, content, commit_info)
else
wiki.write_page(id.to_s, :markdown, content, commit_info)
end


+ 2
- 1
frontend/src/App.tsx View File

@@ -8,6 +8,7 @@ import PostPage from '@/pages/PostPage'
import PostNewPage from '@/pages/PostNewPage'
import PostDetailPage from '@/pages/PostDetailPage'
import WikiNewPage from '@/pages/WikiNewPage'
import WikiEditPage from '@/pages/WikiEditPage'
import WikiDetailPage from '@/pages/WikiDetailPage'
import { API_BASE_URL } from '@/config'
import axios from 'axios'
@@ -62,7 +63,7 @@ export default () => {
<Route path="/tags/:tag" element={<TagPage />} />
<Route path="/wiki/:name" element={<WikiDetailPage />} />
<Route path="/wiki/new" element={<WikiNewPage />} />
{/* <Route path="/wiki/:id/edit" element={<WikiEditPage />} /> */}
<Route path="/wiki/:id/edit" element={<WikiEditPage />} />
</Routes>
</div>
</div>


+ 6
- 2
frontend/src/components/TopNav.tsx View File

@@ -3,6 +3,7 @@ import { Link, useLocation, useParams } from 'react-router-dom'
import SettingsDialogue from './SettingsDialogue'
import { Button } from './ui/button'
import clsx from 'clsx'
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'

import type { User } from '@/types'

@@ -21,6 +22,7 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {

const [settingsVisible, setSettingsVisible] = useState (false)
const [selectedMenu, setSelectedMenu] = useState<Menu> (Menu.None)
const [wikiId, setWikiId] = useState (WikiIdBus.get ())

const MyLink = ({ to, title, menu, base }: { to: string
title: string
@@ -33,6 +35,8 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
{title}
</Link>)

useEffect (() => WikiIdBus.subscribe (setWikiId), [])

useEffect (() => {
if (location.pathname.startsWith ('/posts'))
setSelectedMenu (Menu.Post)
@@ -90,8 +94,8 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
<>
<Separator />
<Link to={`/posts?tags=${ location.pathname.split ('/')[2] }`} className={subClass}>投稿</Link>
<Link to={`/wiki/${ location.pathname.split ('/')[2] }/history`} className={subClass}>履歴</Link>
<Link to={`/wiki/${ location.pathname.split ('/')[2] }/edit`} className={subClass}>編輯</Link>
<Link to={`/wiki/${ wikiId || location.pathname.split ('/')[2] }/history`} className={subClass}>履歴</Link>
<Link to={`/wiki/${ wikiId || location.pathname.split ('/')[2] }/edit`} className={subClass}>編輯</Link>
</>}
</div>)
}


+ 35
- 0
frontend/src/lib/eventBus/EventBus.ts View File

@@ -0,0 +1,35 @@
export class EventBus<T>
{
private value: T
private subscribers: ((val: T) => void)[] = []

constructor (
initialValue: T)
{
this.value = initialValue
}

get ()
: T
{
return this.value
}

set (
val: T)
: void
{
this.value = val
this.subscribers.forEach (f => f (val))
}

subscribe (
func: (val: T) => void)
: () => void
{
this.subscribers.push (func)
return () => {
this.subscribers = this.subscribers.filter (sub => sub !== func)
}
}
}

+ 3
- 0
frontend/src/lib/eventBus/WikiIdBus.ts View File

@@ -0,0 +1,3 @@
import { EventBus } from './EventBus'

export const WikiIdBus = new EventBus<number | null> (null)

+ 6
- 2
frontend/src/pages/WikiDetailPage.tsx View File

@@ -4,6 +4,7 @@ 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'


export default () => {
@@ -22,7 +23,10 @@ export default () => {
}

void (axios.get (`${ API_BASE_URL }/wiki/title/${ encodeURIComponent (name) }`)
.then (res => setMarkdown (res.data))
.then (res => {
setMarkdown (res.data.body)
WikiIdBus.set (res.data.id)
})
.catch (() => setMarkdown (null)))
}, [name])

@@ -30,7 +34,7 @@ export default () => {
<MainArea>
<div className="prose mx-auto p-4">
<ReactMarkdown components={{ a: (
({ href, children }) => (href?.startsWith ('/')
({ 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 })。`}


+ 85
- 0
frontend/src/pages/WikiEditPage.tsx View File

@@ -0,0 +1,85 @@
import React, { useEffect, useState, useRef } from 'react'
import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'
import axios from 'axios'
import { API_BASE_URL, SITE_TITLE } from '@/config'
import NicoViewer from '@/components/NicoViewer'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import { cn } from '@/lib/utils'
import MarkdownIt from 'markdown-it'
import MdEditor from 'react-markdown-editor-lite'
import 'react-markdown-editor-lite/lib/index.css'
import MainArea from '@/components/layout/MainArea'

import type { Tag } from '@/types'

const mdParser = new MarkdownIt


export default () => {
const { id } = useParams ()

const location = useLocation ()
const navigate = useNavigate ()

const [title, setTitle] = useState ('')
const [body, setBody] = useState ('')

const handleSubmit = () => {
const formData = new FormData ()
formData.append ('title', title)
formData.append ('body', body)

void (axios.put (`${ API_BASE_URL }/wiki/${ id }`, formData, { headers: {
'Content-Type': 'multipart/form-data',
'X-Transfer-Code': localStorage.getItem ('user_code') || '' } })
.then (res => {
toast ({ title: '投稿成功!' })
navigate (`/wiki/${ title }`)
})
.catch (e => toast ({ title: '投稿失敗',
description: '入力を確認してください。' })))
}

useEffect (() => {
void (axios.get (`${ API_BASE_URL }/wiki/${ id }`)
.then (res => {
setTitle (res.data.title)
setBody (res.data.body)
}))

document.title = `Wiki ページを編輯 | ${ SITE_TITLE }`
}, [id])

return (
<MainArea>
<div className="max-w-xl mx-auto p-4 space-y-4">
<h1 className="text-2xl font-bold mb-2">Wiki ページを編輯</h1>

{/* タイトル */}
{/* TODO: タグ補完 */}
<div>
<label className="block font-semibold mb-1">タイトル</label>
<input type="text"
value={title}
onChange={e => setTitle (e.target.value)}
className="w-full border p-2 rounded" />
</div>

{/* 本文 */}
<div>
<label className="block font-semibold mb-1">本文</label>
<MdEditor value={body}
style={{ height: '500px' }}
renderHTML={text => mdParser.render (text)}
onChange={({ text }) => setBody (text)} />
</div>

{/* 送信 */}
<button onClick={handleSubmit}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400">
追加
</button>
</div>
</MainArea>)
}

Loading…
Cancel
Save