170 行
6.5 KiB
TypeScript
170 行
6.5 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
import { useEffect, useMemo } from 'react'
|
|
import { useLocation, useParams } from 'react-router-dom'
|
|
|
|
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
|
|
import { useUITranslation } from '@/lib/useUITranslation'
|
|
import PostList from '@/components/PostList'
|
|
import PrefetchLink from '@/components/PrefetchLink'
|
|
import TagLink from '@/components/TagLink'
|
|
import WikiBody from '@/components/WikiBody'
|
|
import TabGroup, { Tab } from '@/components/common/TabGroup'
|
|
import MainArea from '@/components/layout/MainArea'
|
|
import PageHead from '@/components/common/PageHead'
|
|
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
|
import { getHelpTitleIndex } from '@/lib/help'
|
|
import { localeLanguage } from '@/lib/locales'
|
|
import { fetchPosts } from '@/lib/posts'
|
|
import { helpKeys, postsKeys, wikiKeys } from '@/lib/queryKeys'
|
|
import { fetchWikiPageByReference, fetchWikiTag } from '@/lib/wiki'
|
|
import NotFound from '@/pages/NotFound'
|
|
|
|
import type { FC } from 'react'
|
|
|
|
import type { Tag, WikiTagReference } from '@/types'
|
|
|
|
|
|
const WikiDetailPage: FC = () => {
|
|
const { message, label } = useUITranslation ('shared')
|
|
const locale = useLocale ()
|
|
const params = useParams ()
|
|
const title = params.title ?? ''
|
|
const numericReference = /^\d+$/.test (title)
|
|
|
|
const location = useLocation ()
|
|
const navigate = useLocaleNavigate ()
|
|
|
|
const defaultTag = useMemo (() => ({ name: title, category: 'general' } as Tag), [title])
|
|
|
|
const query = new URLSearchParams (location.search)
|
|
const version = query.get ('version') || undefined
|
|
|
|
const { data: wikiPage, isLoading: loading, isSuccess, isFetching, error } = useQuery ({
|
|
enabled: Boolean (title),
|
|
queryKey: wikiKeys.show (title, { version }, locale),
|
|
queryFn: () => fetchWikiPageByReference (title, { version }, locale) })
|
|
|
|
const missingWiki = isSuccess && !(isFetching) && wikiPage == null
|
|
const { data: titleIndex, isPending: lookupPending,
|
|
isFetching: lookupFetching, error: lookupError } = useQuery ({
|
|
enabled: missingWiki && !numericReference,
|
|
queryKey: helpKeys.titles ('ja'),
|
|
queryFn: () => getHelpTitleIndex ('ja'),
|
|
staleTime: Infinity })
|
|
const lookupTitle = title.trim ().normalize ('NFKC').toLowerCase ().replace (/^ヘルプ:/, '')
|
|
const identifiers = useMemo (() => (
|
|
(titleIndex != null && Object.hasOwn (titleIndex, lookupTitle))
|
|
? titleIndex[lookupTitle] : []), [titleIndex, lookupTitle])
|
|
|
|
useEffect (() => {
|
|
if (missingWiki && !numericReference
|
|
&& !(lookupFetching) && lookupError == null && identifiers.length === 1)
|
|
navigate (`/help/${ encodeURIComponent (identifiers[0]) }`, { replace: true })
|
|
}, [missingWiki, numericReference, lookupFetching, lookupError, identifiers, navigate])
|
|
|
|
const effectiveTitle = wikiPage?.title ?? title
|
|
const deprecated = wikiPage?.deprecatedAt != null
|
|
const tagReference: WikiTagReference = (wikiPage != null || !numericReference)
|
|
? { name: effectiveTitle } : { wikiId: title }
|
|
|
|
const { data: tag, isPending: tagPending,
|
|
isFetching: tagFetching, error: tagError } = useQuery ({
|
|
enabled: Boolean (title) && (missingWiki || wikiPage != null),
|
|
queryKey: wikiKeys.tag (tagReference, locale),
|
|
queryFn: () => fetchWikiTag (tagReference, locale) })
|
|
const displayTitle = wikiPage?.title ?? tag?.name ?? title
|
|
|
|
const keys = {
|
|
tags: displayTitle, match: 'all', page: 1, limit: 8, url: '', title: '',
|
|
originalCreatedFrom: '', originalCreatedTo: '',
|
|
createdFrom: '', createdTo: '', updatedFrom: '', updatedTo: '',
|
|
order: 'original_created_at:desc' } as const
|
|
const { data } = useQuery ({
|
|
enabled: Boolean (effectiveTitle) && !(version)
|
|
&& (!numericReference || wikiPage != null || tag != null),
|
|
queryKey: postsKeys.index (keys, locale),
|
|
queryFn: () => fetchPosts (keys, locale) })
|
|
const posts = data?.posts || []
|
|
|
|
useEffect (() => {
|
|
if (!(wikiPage))
|
|
return
|
|
|
|
WikiIdBus.set (wikiPage.id)
|
|
|
|
if (!numericReference && wikiPage.title !== title)
|
|
{
|
|
navigate ({ pathname: `/wiki/${ encodeURIComponent (wikiPage.title) }`,
|
|
search: location.search, hash: location.hash }, { replace: true })
|
|
}
|
|
|
|
return () => WikiIdBus.set (null)
|
|
}, [wikiPage, title, numericReference, navigate, location.search, location.hash])
|
|
|
|
const displayError = error ?? (missingWiki ? lookupError ?? tagError : null)
|
|
if (displayError != null)
|
|
return <MainArea><p role="alert">{displayError.message}</p></MainArea>
|
|
|
|
const waiting = loading || (isFetching && wikiPage == null)
|
|
|| (missingWiki && ((!numericReference
|
|
&& (lookupPending || lookupFetching || identifiers.length === 1))
|
|
|| tagPending || (tagFetching && tag == null)))
|
|
|
|
if (missingWiki && !(waiting) && (tag == null || version != null))
|
|
return <NotFound/>
|
|
|
|
return (
|
|
<MainArea>
|
|
<PageHead title={{
|
|
text: `${displayTitle}${deprecated
|
|
? message ('labels.deprecatedSuffix').text : ''} Wiki`,
|
|
language: deprecated ? message ('labels.deprecatedSuffix').language : locale }}>
|
|
{!(wikiPage?.body) && <meta name="robots" content="noindex"/>}
|
|
</PageHead>
|
|
|
|
{(wikiPage && version) && (
|
|
<div className="text-sm flex gap-3 items-center justify-center
|
|
border border-gray-700 rounded px-2 py-1 mb-4">
|
|
{wikiPage.pred ? (
|
|
<PrefetchLink to={`/wiki/${ encodeURIComponent (title) }?version=${ wikiPage.pred }`}>
|
|
{label ('actions.old')}
|
|
</PrefetchLink>) : label ('actions.oldest')}
|
|
|
|
<span>{wikiPage.updatedAt}</span>
|
|
|
|
{wikiPage.succ ? (
|
|
<PrefetchLink to={`/wiki/${ encodeURIComponent (title) }?version=${ wikiPage.succ }`}>
|
|
{label ('actions.new')}
|
|
</PrefetchLink>) : label ('actions.latest')}
|
|
</div>)}
|
|
|
|
<article className="prose dark:prose-invert mx-auto p-4">
|
|
<h1 className="prose-a:no-underline">
|
|
<TagLink tag={tag ?? { ...defaultTag,
|
|
name: displayTitle,
|
|
deprecatedAt: wikiPage?.deprecatedAt ?? null }}
|
|
withWiki={false}
|
|
withCount={false}
|
|
{...(version && { to: `/wiki/${ encodeURIComponent (title) }` })}/>
|
|
{deprecated && label ('labels.deprecatedSuffix')}
|
|
</h1>
|
|
{waiting
|
|
? label ('common:loading')
|
|
: <WikiBody title={displayTitle} body={wikiPage?.body}
|
|
language={wikiPage?.languageCode ?? (wikiPage?.resourceType === 'tag_description'
|
|
? localeLanguage (locale) : 'und')}/>}
|
|
|
|
{(!(version) && posts.length > 0) && (
|
|
<div className="not-prose">
|
|
<TabGroup>
|
|
<Tab name={message ('navigation:posts').text}>
|
|
<PostList posts={posts}/>
|
|
</Tab>
|
|
</TabGroup>
|
|
</div>)}
|
|
</article>
|
|
</MainArea>)
|
|
}
|
|
|
|
export default WikiDetailPage
|