From fe8739b29059a2bf43c2c1e13d96b1fb5f0a40db Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 15 Jun 2025 14:24:41 +0900 Subject: [PATCH] #19 --- .../app/controllers/wiki_pages_controller.rb | 11 +-- backend/app/models/wiki_page.rb | 2 +- frontend/src/App.tsx | 3 +- frontend/src/components/TopNav.tsx | 8 +- frontend/src/lib/eventBus/EventBus.ts | 35 ++++++++ frontend/src/lib/eventBus/WikiIdBus.ts | 3 + frontend/src/pages/WikiDetailPage.tsx | 8 +- frontend/src/pages/WikiEditPage.tsx | 85 +++++++++++++++++++ 8 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 frontend/src/lib/eventBus/EventBus.ts create mode 100644 frontend/src/lib/eventBus/WikiIdBus.ts create mode 100644 frontend/src/pages/WikiEditPage.tsx diff --git a/backend/app/controllers/wiki_pages_controller.rb b/backend/app/controllers/wiki_pages_controller.rb index c098995..4a25deb 100644 --- a/backend/app/controllers/wiki_pages_controller.rb +++ b/backend/app/controllers/wiki_pages_controller.rb @@ -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 diff --git a/backend/app/models/wiki_page.rb b/backend/app/models/wiki_page.rb index dce407d..0d71ffd 100644 --- a/backend/app/models/wiki_page.rb +++ b/backend/app/models/wiki_page.rb @@ -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 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f978178..7ca85ae 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -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 () => { } /> } /> } /> - {/* } /> */} + } /> diff --git a/frontend/src/components/TopNav.tsx b/frontend/src/components/TopNav.tsx index df0304b..a8ddca7 100644 --- a/frontend/src/components/TopNav.tsx +++ b/frontend/src/components/TopNav.tsx @@ -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.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} ) + useEffect (() => WikiIdBus.subscribe (setWikiId), []) + useEffect (() => { if (location.pathname.startsWith ('/posts')) setSelectedMenu (Menu.Post) @@ -90,8 +94,8 @@ const TopNav: React.FC = ({ user, setUser }: Props) => { <> 投稿 - 履歴 - 編輯 + 履歴 + 編輯 } ) } diff --git a/frontend/src/lib/eventBus/EventBus.ts b/frontend/src/lib/eventBus/EventBus.ts new file mode 100644 index 0000000..ee08a08 --- /dev/null +++ b/frontend/src/lib/eventBus/EventBus.ts @@ -0,0 +1,35 @@ +export class EventBus +{ + 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) + } + } +} diff --git a/frontend/src/lib/eventBus/WikiIdBus.ts b/frontend/src/lib/eventBus/WikiIdBus.ts new file mode 100644 index 0000000..e492987 --- /dev/null +++ b/frontend/src/lib/eventBus/WikiIdBus.ts @@ -0,0 +1,3 @@ +import { EventBus } from './EventBus' + +export const WikiIdBus = new EventBus (null) diff --git a/frontend/src/pages/WikiDetailPage.tsx b/frontend/src/pages/WikiDetailPage.tsx index 42a9c25..a07e595 100644 --- a/frontend/src/pages/WikiDetailPage.tsx +++ b/frontend/src/pages/WikiDetailPage.tsx @@ -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 () => {
(href?.startsWith ('/') + ({ href, children }) => (['/', '.'].some (e => href?.startsWith (e)) ? {children} : {children})) }}> {markdown || `このページは存在しません。[新規作成してください](/wiki/new?title=${ name })。`} diff --git a/frontend/src/pages/WikiEditPage.tsx b/frontend/src/pages/WikiEditPage.tsx new file mode 100644 index 0000000..926d235 --- /dev/null +++ b/frontend/src/pages/WikiEditPage.tsx @@ -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 ( + +
+

Wiki ページを編輯

+ + {/* タイトル */} + {/* TODO: タグ補完 */} +
+ + setTitle (e.target.value)} + className="w-full border p-2 rounded" /> +
+ + {/* 本文 */} +
+ + mdParser.render (text)} + onChange={({ text }) => setBody (text)} /> +
+ + {/* 送信 */} + +
+
) +}