このコミットが含まれているのは:
2026-07-04 21:47:16 +09:00
コミット 518c5fa0f2
16個のファイルの変更840行の追加598行の削除
+90 -11
ファイルの表示
@@ -13,11 +13,18 @@ import PageTitle from '@/components/common/PageTitle'
import Pagination from '@/components/common/Pagination'
import TagInput from '@/components/common/TagInput'
import MainArea from '@/components/layout/MainArea'
import { useUserSettings } from '@/components/users/UserSettingsProvider'
import { SITE_TITLE } from '@/config'
import { fetchPosts } from '@/lib/posts'
import { postsKeys } from '@/lib/queryKeys'
import { toFetchPostsOrder } from '@/lib/settings'
import {
DEFAULT_POST_LIST_LIMIT,
DEFAULT_POST_LIST_ORDER,
getClientListLimit,
getClientListOrder,
LIST_LIMIT_OPTIONS,
POST_LIST_ORDER_OPTIONS,
setClientListSettings,
} from '@/lib/settings'
import { dateString, inputClass, originalCreatedAtString } from '@/lib/utils'
import type { FC, FormEvent } from 'react'
@@ -33,19 +40,44 @@ const setIf = (qs: URLSearchParams, k: string, v: string | null) => {
qs.set (k, t)
}
const postOrderLabel: Record<FetchPostsOrder, string> = {
'title:asc': 'タイトル昇順',
'title:desc': 'タイトル降順',
'url:asc': 'URL 昇順',
'url:desc': 'URL 降順',
'original_created_at:asc': 'オリジナル投稿日時 昇順',
'original_created_at:desc': 'オリジナル投稿日時 降順',
'created_at:asc': '投稿日 昇順',
'created_at:desc': '投稿日 降順',
'updated_at:asc': '更新日 昇順',
'updated_at:desc': '更新日 降順',
}
const parseLimit = (value: string | null): 20 | 50 | 100 | null => {
const n = Number (value)
return n === 20 || n === 50 || n === 100 ? n : null
}
const parseOrder = (value: string | null): FetchPostsOrder | null =>
POST_LIST_ORDER_OPTIONS.some (option => option === value)
? value as FetchPostsOrder
: null
const PostSearchPage: FC = () => {
const location = useLocation ()
const { settings } = useUserSettings ()
const navigate = useNavigate ()
const query = useMemo (() => new URLSearchParams (location.search),
[location.search])
const defaultOrder = toFetchPostsOrder (settings.postListOrder)
const page = Number (query.get ('page') ?? 1)
const limit = Number (query.get ('limit') ?? settings.postListLimit)
const limit = (
parseLimit (query.get ('limit'))
?? getClientListLimit ('postSearch')
?? DEFAULT_POST_LIST_LIMIT
)
const qURL = query.get ('url') ?? ''
const qTitle = query.get ('title') ?? ''
@@ -57,7 +89,11 @@ const PostSearchPage: FC = () => {
const qCreatedTo = query.get ('created_to') ?? ''
const qUpdatedFrom = query.get ('updated_from') ?? ''
const qUpdatedTo = query.get ('updated_to') ?? ''
const order = (query.get ('order') || defaultOrder) as FetchPostsOrder
const order = (
parseOrder (query.get ('order'))
?? getClientListOrder<FetchPostsOrder> ('postSearch')
?? DEFAULT_POST_LIST_ORDER
)
const [createdFrom, setCreatedFrom] = useState<string | null> (null)
const [createdTo, setCreatedTo] = useState<string | null> (null)
@@ -87,6 +123,10 @@ const PostSearchPage: FC = () => {
const results = data?.posts ?? []
const totalPages = data ? Math.ceil (data.count / limit) : 0
useEffect (() => {
setClientListSettings ('postSearch', { limit, order })
}, [limit, order])
useEffect (() => {
setURL (qURL ?? '')
setTitle (qTitle ?? '')
@@ -117,6 +157,16 @@ const PostSearchPage: FC = () => {
qs.set ('match', matchType)
qs.set ('page', '1')
qs.set ('order', order)
qs.set ('limit', String (limit))
navigate (`${ location.pathname }?${ qs.toString () }`)
}
const updateListQuery = (next: { limit?: 20 | 50 | 100
order?: FetchPostsOrder }) => {
const qs = new URLSearchParams (location.search)
qs.set ('limit', String (next.limit ?? limit))
qs.set ('order', next.order ?? order)
qs.set ('page', '1')
navigate (`${ location.pathname }?${ qs.toString () }`)
}
@@ -141,6 +191,38 @@ const PostSearchPage: FC = () => {
<PageTitle></PageTitle>
<form onSubmit={handleSearch} className="space-y-2">
<div className="grid gap-3 rounded-xl border border-border bg-background/80 p-3 sm:grid-cols-2">
<FormField label="表示件数">
{() => (
<select
className={inputClass (false)}
value={limit}
onChange={ev => updateListQuery ({
limit: Number (ev.target.value) as 20 | 50 | 100,
})}>
{LIST_LIMIT_OPTIONS.map (value => (
<option key={value} value={value}>
{value}
</option>))}
</select>)}
</FormField>
<FormField label="並び順">
{() => (
<select
className={inputClass (false)}
value={order}
onChange={ev => updateListQuery ({
order: ev.target.value as FetchPostsOrder,
})}>
{POST_LIST_ORDER_OPTIONS.map (value => (
<option key={value} value={value}>
{postOrderLabel[value]}
</option>))}
</select>)}
</FormField>
</div>
{/* タイトル */}
<FormField label="タイトル">
{({ invalid }) => (
@@ -165,6 +247,7 @@ const PostSearchPage: FC = () => {
<FormField label="タグ">
{() => (
<TagInput
includeNico={true}
value={tagsStr}
setValue={setTagsStr}/>)}
</FormField>
@@ -302,11 +385,7 @@ const PostSearchPage: FC = () => {
{results.map (row => (
<tr
key={row.id}
className={
settings.viewedPostDisplay === 'dim' && row.viewed
? 'even:bg-gray-100 opacity-40 saturate-50 dark:even:bg-gray-700'
: 'even:bg-gray-100 dark:even:bg-gray-700'
}>
className="even:bg-gray-100 dark:even:bg-gray-700">
<td className="p-2">
<PrefetchLink to={`/posts/${ row.id }`} title={row.title || undefined}>
<motion.div