This commit is contained in:
@@ -66,14 +66,14 @@ class WikiPagesController < ApplicationController
|
||||
end
|
||||
|
||||
def search
|
||||
title = params[:title]&.strip
|
||||
|
||||
q = WikiPage.all
|
||||
q = q.where('title LIKE ?', "%#{ WikiPage.sanitize_sql_like(title) }%") if title.present?
|
||||
|
||||
if params[:title].present?
|
||||
title = params[:title].to_s.strip
|
||||
q = q.where('title LIKE ?', "%#{ WikiPage.sanitize_sql_like(title) }%")
|
||||
end
|
||||
|
||||
render json: q.limit(20)
|
||||
render json: q.limit(20).map { |page|
|
||||
page.sha = nil
|
||||
page }
|
||||
end
|
||||
|
||||
def changes
|
||||
|
||||
@@ -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>)
|
||||
}
|
||||
}) ()}
|
||||
</>)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user