みてるぞ 3 weeks ago
parent
commit
068d0aed14
4 changed files with 94 additions and 32 deletions
  1. +6
    -6
      backend/app/controllers/wiki_pages_controller.rb
  2. +68
    -8
      frontend/src/components/TopNav.tsx
  3. +17
    -16
      frontend/src/pages/posts/PostListPage.tsx
  4. +3
    -2
      frontend/src/pages/wiki/WikiSearchPage.tsx

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

@@ -66,14 +66,14 @@ class WikiPagesController < ApplicationController
end

def search
q = WikiPage.all
title = params[:title]&.strip

if params[:title].present?
title = params[:title].to_s.strip
q = q.where('title LIKE ?', "%#{ WikiPage.sanitize_sql_like(title) }%")
end
q = WikiPage.all
q = q.where('title LIKE ?', "%#{ WikiPage.sanitize_sql_like(title) }%") if title.present?

render json: q.limit(20)
render json: q.limit(20).map { |page|
page.sha = nil
page }
end

def changes


+ 68
- 8
frontend/src/components/TopNav.tsx View File

@@ -12,7 +12,7 @@ type Props = { user: User

const enum Menu { None,
Post,
Deerjikist,
User,
Tag,
Wiki }

@@ -28,6 +28,8 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
const [activeIndex, setActiveIndex] = useState (-1)
const [suggestions, setSuggestions] = useState<WikiPage[]> ([])
const [suggestionsVsbl, setSuggestionsVsbl] = useState (false)
const [tagSearch, setTagSearch] = useState ('')
const [userSearch, setUserSearch] = useState ('')

const MyLink = ({ to, title, menu, base }: { to: string
title: string
@@ -40,16 +42,43 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
{title}
</Link>)

const whenWikiSearchChanged = e => {
setWikiSearch (e.target.value)
const whenTagSearchChanged = ev => {
// TODO: 実装

const q: string = e.target.value.split (' ').at (-1)
setTagSearch (ev.target.value)

const q: string = ev.target.value.split (' ').at (-1)
if (!(q))
{
setSuggestions ([])
return
}
}

const whenWikiSearchChanged = ev => {
// TODO: 実装

setWikiSearch (ev.target.value)

const q: string = ev.target.value.split (' ').at (-1)
if (!(q))
{
setSuggestions ([])
return
}
}

const whenUserSearchChanged = ev => {
// TODO: 実装

setUserSearch (ev.target.value)

const q: string = ev.target.value.split (' ').at (-1)
if (!(q))
{
setSuggestions ([])
return
}
// void (axios.get(`${ API_BASE_URL }/`))
}

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
@@ -70,8 +99,8 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
useEffect (() => {
if (location.pathname.startsWith ('/posts'))
setSelectedMenu (Menu.Post)
else if (location.pathname.startsWith ('/deerjikists'))
setSelectedMenu (Menu.Deerjikist)
else if (location.pathname.startsWith ('/users'))
setSelectedMenu (Menu.User)
else if (location.pathname.startsWith ('/tags'))
setSelectedMenu (Menu.Tag)
else if (location.pathname.startsWith ('/wiki'))
@@ -86,9 +115,9 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
<div className="flex items-center gap-2 h-full">
<Link to="/posts" className="mx-4 text-xl font-bold text-orange-500">ぼざクリ タグ広場</Link>
<MyLink to="/posts" title="広場" />
<MyLink to="/deerjikists" title="ニジラー" />
<MyLink to="/tags" title="タグ" />
<MyLink to="/wiki/ヘルプ:ホーム" base="/wiki" title="Wiki" />
<MyLink to="/users" title="ニジラー" />
</div>
<div className="ml-auto pr-4">
<Button onClick={() => setSettingsVsbl (true)}>{user?.name || '名もなきニジラー'}</Button>
@@ -112,6 +141,23 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
<Link to="/posts/new" className={subClass}>投稿追加</Link>
<Link to="/wiki/ヘルプ:広場" className={subClass}>ヘルプ</Link>
</div>)
case Menu.Tag:
return (
<div className={className}>
<input type="text"
className={inputBox}
placeholder="タグ検索"
value={tagSearch}
onChange={whenTagSearchChanged}
onFocus={() => setSuggestionsVsbl (true)}
onBlur={() => setSuggestionsVsbl (false)}
onKeyDown={handleKeyDown} />
<Link to="/tags" className={subClass}>タグ</Link>
<Link to="/tags/aliases" className={subClass}>別名タグ</Link>
<Link to="/tags/implications" className={subClass}>上位タグ</Link>
<Link to="/wiki/ヘルプ:タグのつけ方" className={subClass}>タグのつけ方</Link>
<Link to="/wiki/ヘルプ:タグ" className={subClass}>ヘルプ</Link>
</div>)
case Menu.Wiki:
return (
<div className={className}>
@@ -135,6 +181,20 @@ const TopNav: React.FC = ({ user, setUser }: Props) => {
<Link to={`/wiki/${ wikiId || location.pathname.split ('/')[2] }/edit`} className={subClass}>編輯</Link>
</>}
</div>)
case Menu.User:
return (
<div className={className}>
<input type="text"
className={inputBox}
placeholder="ニジラー検索"
value={userSearch}
onChange={whenUserSearchChanged}
onFocus={() => setSuggestionsVsbl (true)}
onBlur={() => setSuggestionsVsbl (false)}
onKeyDown={handleKeyDown} />
<Link to="/users" className={subClass}>一覧</Link>
{user && <Link to={`/users/${ user.id }`} className={subClass}>お前</Link>}
</div>)
}
}) ()}
</>)


+ 17
- 16
frontend/src/pages/posts/PostListPage.tsx View File

@@ -14,7 +14,7 @@ import type { Post, Tag, WikiPage } from '@/types'


export default () => {
const [posts, setPosts] = useState<Post[]> ([])
const [posts, setPosts] = useState<Post[] | null> (null)
const [wikiPage, setWikiPage] = useState<WikiPage | null> (null)

const location = useLocation ()
@@ -30,7 +30,7 @@ export default () => {
.then (res => setPosts (toCamel (res.data, { deep: true })))
.catch (err => {
console.error ('Failed to fetch posts:', err)
setPosts ([])
setPosts (null)
}))

setWikiPage (null)
@@ -53,25 +53,26 @@ export default () => {
: `${ SITE_TITLE } 〜 ぼざクリも、ぼざろ外も、外伝もあるんだよ`}
</title>
</Helmet>
<TagSidebar posts={posts} />
<TagSidebar posts={posts || []} />
<MainArea>
<TabGroup key={wikiPage}>
<Tab name="広場">
{posts.length
? (
<div className="flex flex-wrap gap-4 p-4">
{posts.map (post => (
<Link to={`/posts/${ post.id }`}
key={post.id}
className="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg">
<img src={post.thumbnail ?? post.thumbnailBase}
className="object-none w-full h-full" />
</Link>))}
</div>)
: '広場には何もありませんよ.'}
{posts == null ? 'Loading...' : (
posts.length
? (
<div className="flex flex-wrap gap-4 p-4">
{posts.map (post => (
<Link to={`/posts/${ post.id }`}
key={post.id}
className="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg">
<img src={post.thumbnail ?? post.thumbnailBase}
className="object-none w-full h-full" />
</Link>))}
</div>)
: '広場には何もありませんよ.')}
</Tab>
{(wikiPage && wikiPage.body) && (
<Tab name="Wiki" init={!(posts.length)}>
<Tab name="Wiki" init={!(posts?.length)}>
<WikiBody body={wikiPage.body} />
<div className="my-2">
<Link to={`/wiki/${ wikiPage.title }`}>Wiki を見る</Link>


+ 3
- 2
frontend/src/pages/wiki/WikiSearchPage.tsx View File

@@ -1,4 +1,5 @@
import axios from 'axios'
import toCamel from 'camelcase-keys'
import React, { useEffect, useState } from 'react'
import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'
@@ -17,7 +18,7 @@ export default () => {

const search = () => {
void (axios.get (`${ API_BASE_URL }/wiki/search`, { params: { title } })
.then (res => setResults (res.data)))
.then (res => setResults (toCamel (res.data, { deep: true }))))
}

const handleSearch = (e: React.FormEvent) => {
@@ -82,7 +83,7 @@ export default () => {
</Link>
</td>
<td className="p-2 text-gray-100 text-sm">
{page.updated_at}
{page.updatedAt}
</td>
</tr>))}
</tbody>


Loading…
Cancel
Save