Reviewed-on: #433 Co-authored-by: miteruzo <miteruzo@naver.com>
このコミットはプルリクエスト #433 でマージされました。
このコミットが含まれているのは:
@@ -1,10 +1,11 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { motion } from 'framer-motion'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
|
||||
import PrefetchLink from '@/components/PrefetchLink'
|
||||
import LocalisedText from '@/components/LocalisedText'
|
||||
import SortHeader from '@/components/SortHeader'
|
||||
import TagLink from '@/components/TagLink'
|
||||
import DateTimeField from '@/components/common/DateTimeField'
|
||||
@@ -13,7 +14,7 @@ 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 { SITE_TITLE } from '@/config'
|
||||
import PageHead from '@/components/common/PageHead'
|
||||
import { clientAnimationTransition,
|
||||
clientScrollBehaviour } from '@/lib/clientAnimation'
|
||||
import { fetchPosts } from '@/lib/posts'
|
||||
@@ -28,7 +29,9 @@ import {
|
||||
} from '@/lib/settings'
|
||||
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
||||
import { useKeyboardShortcuts } from '@/lib/useKeyboardShortcuts'
|
||||
import { dateString, inputClass, originalCreatedAtString } from '@/lib/utils'
|
||||
import { localisedDateString, inputClass, originalCreatedAtString } from '@/lib/utils'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import { useHydrated } from '@/lib/useHydrated'
|
||||
|
||||
import type { FC, FormEvent } from 'react'
|
||||
|
||||
@@ -70,6 +73,9 @@ const parseOrder = (value: string | null): FetchPostsOrder | null => {
|
||||
|
||||
|
||||
const PostSearchPage: FC = () => {
|
||||
const locale = useLocale ()
|
||||
const hydrated = useHydrated ()
|
||||
const { message, label } = useUITranslation ('posts')
|
||||
const behaviourSettings = useClientBehaviourSettings ()
|
||||
const animationMode = behaviourSettings.animation ?? 'normal'
|
||||
const thumbnailTransition = clientAnimationTransition (
|
||||
@@ -79,7 +85,7 @@ const PostSearchPage: FC = () => {
|
||||
)
|
||||
const location = useLocation ()
|
||||
|
||||
const navigate = useNavigate ()
|
||||
const navigate = useLocaleNavigate ()
|
||||
|
||||
const query = useMemo (() => new URLSearchParams (location.search),
|
||||
[location.search])
|
||||
@@ -87,7 +93,7 @@ const PostSearchPage: FC = () => {
|
||||
const page = Number (query.get ('page') ?? 1)
|
||||
const limit = (
|
||||
parseLimit (query.get ('limit'))
|
||||
?? getClientListLimit ('postSearch')
|
||||
?? (hydrated ? getClientListLimit ('postSearch') : null)
|
||||
?? DEFAULT_POST_LIST_LIMIT
|
||||
)
|
||||
|
||||
@@ -103,7 +109,7 @@ const PostSearchPage: FC = () => {
|
||||
const qUpdatedTo = query.get ('updated_to') ?? ''
|
||||
const order = (
|
||||
parseOrder (query.get ('order'))
|
||||
?? getClientListOrder<FetchPostsOrder> ('postSearch')
|
||||
?? (hydrated ? getClientListOrder<FetchPostsOrder> ('postSearch') : null)
|
||||
?? DEFAULT_POST_LIST_ORDER
|
||||
)
|
||||
|
||||
@@ -120,6 +126,7 @@ const PostSearchPage: FC = () => {
|
||||
|
||||
const keys: FetchPostsParams = {
|
||||
tags: qTags, match: qMatch, page, limit,
|
||||
...(query.get ('tag_refs') && { tagReferences: query.get ('tag_refs')! }),
|
||||
url: qURL,
|
||||
title: qTitle,
|
||||
originalCreatedFrom: qOriginalCreatedFrom,
|
||||
@@ -130,14 +137,16 @@ const PostSearchPage: FC = () => {
|
||||
updatedTo: qUpdatedTo,
|
||||
order }
|
||||
const { data, isLoading: loading } = useQuery ({
|
||||
queryKey: postsKeys.index (keys),
|
||||
queryFn: () => fetchPosts (keys) })
|
||||
queryKey: postsKeys.index (keys, locale),
|
||||
queryFn: () => fetchPosts (keys, locale) })
|
||||
const results = data?.posts ?? []
|
||||
const totalPages = data ? Math.ceil (data.count / limit) : 0
|
||||
|
||||
useEffect (() => {
|
||||
if (!hydrated)
|
||||
return
|
||||
setClientListSettings ('postSearch', { limit, order })
|
||||
}, [limit, order])
|
||||
}, [hydrated, limit, order])
|
||||
|
||||
useEffect (() => {
|
||||
setURL (qURL ?? '')
|
||||
@@ -220,15 +229,13 @@ const PostSearchPage: FC = () => {
|
||||
|
||||
return (
|
||||
<MainArea>
|
||||
<Helmet>
|
||||
<title>広場検索 | {SITE_TITLE}</title>
|
||||
</Helmet>
|
||||
<PageHead title={message ('search.title')}/>
|
||||
|
||||
<div className="max-w-xl">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<PageTitle>広場検索</PageTitle>
|
||||
<PageTitle>{label ('search.title')}</PageTitle>
|
||||
<label className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>件数</span>
|
||||
<span>{label ('common:countLabel')}</span>
|
||||
<select
|
||||
className="rounded border border-border bg-background px-2 py-1 text-sm
|
||||
text-foreground"
|
||||
@@ -246,7 +253,7 @@ const PostSearchPage: FC = () => {
|
||||
|
||||
<form onSubmit={handleSearch} className="space-y-2">
|
||||
{/* タイトル */}
|
||||
<FormField label="タイトル">
|
||||
<FormField label={label ('shared:labels.title')}>
|
||||
{({ invalid }) => (
|
||||
<input
|
||||
type="text"
|
||||
@@ -257,7 +264,7 @@ const PostSearchPage: FC = () => {
|
||||
</FormField>
|
||||
|
||||
{/* URL */}
|
||||
<FormField label="URL">
|
||||
<FormField label={label ('shared:url')}>
|
||||
{({ invalid }) => (
|
||||
<input
|
||||
type="text"
|
||||
@@ -267,7 +274,7 @@ const PostSearchPage: FC = () => {
|
||||
</FormField>
|
||||
|
||||
{/* タグ */}
|
||||
<FormField label="タグ">
|
||||
<FormField label={label ('navigation:tags')}>
|
||||
{() => (
|
||||
<TagInput
|
||||
includeNico={true}
|
||||
@@ -276,14 +283,14 @@ const PostSearchPage: FC = () => {
|
||||
</FormField>
|
||||
<div>
|
||||
<fieldset className="w-full my-2">
|
||||
<label>検索区分:</label>
|
||||
<label>{label ('search.matchType')}</label>
|
||||
<label className="mx-2">
|
||||
<input
|
||||
type="radio"
|
||||
name="match-type"
|
||||
checked={matchType === 'all'}
|
||||
onChange={() => setMatchType ('all')}/>
|
||||
AND
|
||||
{label ('search.all')}
|
||||
</label>
|
||||
<label className="mx-2">
|
||||
<input
|
||||
@@ -291,13 +298,13 @@ const PostSearchPage: FC = () => {
|
||||
name="match-type"
|
||||
checked={matchType === 'any'}
|
||||
onChange={() => setMatchType ('any')}/>
|
||||
OR
|
||||
{label ('search.any')}
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
{/* オリジナルの投稿日時 */}
|
||||
<FormField label="オリジナルの投稿日時">
|
||||
<FormField label={label ('history:originalPostedAt')}>
|
||||
{() => (
|
||||
<>
|
||||
<DateTimeField
|
||||
@@ -311,7 +318,7 @@ const PostSearchPage: FC = () => {
|
||||
</FormField>
|
||||
|
||||
{/* 投稿日時 */}
|
||||
<FormField label="投稿日時">
|
||||
<FormField label={label ('search.createdAt')}>
|
||||
{() => (
|
||||
<>
|
||||
<DateTimeField
|
||||
@@ -325,7 +332,7 @@ const PostSearchPage: FC = () => {
|
||||
</FormField>
|
||||
|
||||
{/* 更新日時 */}
|
||||
<FormField label="更新日時">
|
||||
<FormField label={label ('tags:updatedAt')}>
|
||||
{() => (
|
||||
<>
|
||||
<DateTimeField
|
||||
@@ -343,13 +350,13 @@ const PostSearchPage: FC = () => {
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded">
|
||||
検索
|
||||
{label ('common:search')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{loading ? 'Loading...' : (results.length > 0 ? (
|
||||
{loading ? label ('common:loading') : (results.length > 0 ? (
|
||||
<div className="mt-4">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[1200px] table-fixed border-collapse">
|
||||
@@ -365,40 +372,40 @@ const PostSearchPage: FC = () => {
|
||||
|
||||
<thead className="border-b-2 border-black dark:border-white">
|
||||
<tr>
|
||||
<th className="p-2 text-left whitespace-nowrap">投稿</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">{label ('history:post')}</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">
|
||||
<SortHeader<FetchPostsOrderField>
|
||||
by="title"
|
||||
label="タイトル"
|
||||
label={label ('shared:labels.title')}
|
||||
currentOrder={order}
|
||||
defaultDirection={defaultDirection}/>
|
||||
</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">
|
||||
<SortHeader<FetchPostsOrderField>
|
||||
by="url"
|
||||
label="URL"
|
||||
label={label ('shared:url')}
|
||||
currentOrder={order}
|
||||
defaultDirection={defaultDirection}/>
|
||||
</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">タグ</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">{label ('navigation:tags')}</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">
|
||||
<SortHeader<FetchPostsOrderField>
|
||||
by="original_created_at"
|
||||
label="オリジナルの投稿日時"
|
||||
label={label ('history:originalPostedAt')}
|
||||
currentOrder={order}
|
||||
defaultDirection={defaultDirection}/>
|
||||
</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">
|
||||
<SortHeader<FetchPostsOrderField>
|
||||
by="created_at"
|
||||
label="投稿日時"
|
||||
label={label ('search.createdAt')}
|
||||
currentOrder={order}
|
||||
defaultDirection={defaultDirection}/>
|
||||
</th>
|
||||
<th className="p-2 text-left whitespace-nowrap">
|
||||
<SortHeader<FetchPostsOrderField>
|
||||
by="updated_at"
|
||||
label="更新日時"
|
||||
label={label ('tags:updatedAt')}
|
||||
currentOrder={order}
|
||||
defaultDirection={defaultDirection}/>
|
||||
</th>
|
||||
@@ -443,18 +450,28 @@ const PostSearchPage: FC = () => {
|
||||
</span>))}
|
||||
</td>
|
||||
<td className="p-2">
|
||||
{originalCreatedAtString (row.originalCreatedFrom,
|
||||
row.originalCreatedBefore)}
|
||||
<LocalisedText language={locale}>
|
||||
{originalCreatedAtString (row.originalCreatedFrom,
|
||||
row.originalCreatedBefore, locale)}
|
||||
</LocalisedText>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<LocalisedText language={locale}>
|
||||
{localisedDateString (row.createdAt, locale)}
|
||||
</LocalisedText>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<LocalisedText language={locale}>
|
||||
{localisedDateString (row.updatedAt, locale)}
|
||||
</LocalisedText>
|
||||
</td>
|
||||
<td className="p-2">{dateString (row.createdAt)}</td>
|
||||
<td className="p-2">{dateString (row.updatedAt)}</td>
|
||||
</tr>))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<Pagination page={page} totalPages={totalPages}/>
|
||||
</div>) : '結果ないよ(笑)')}
|
||||
</div>) : label ('tags:empty'))}
|
||||
</MainArea>)
|
||||
}
|
||||
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする