import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { motion } from 'framer-motion' import { useEffect, useRef } from 'react' import { useParams } from 'react-router-dom' import { useLocale } from '@/lib/useLocale' import PostEditForm from '@/components/PostEditForm' import PostEmbed from '@/components/PostEmbed' import PostList from '@/components/PostList' import TagDetailSidebar from '@/components/TagDetailSidebar' import TabGroup, { Tab } from '@/components/common/TabGroup' import MainArea from '@/components/layout/MainArea' import { Button } from '@/components/ui/button' import { toast } from '@/components/ui/use-toast' import PageHead from '@/components/common/PageHead' import { isApiError } from '@/lib/api' import { fetchPost, toggleViewedFlg } from '@/lib/posts' import { postsKeys, tagsKeys } from '@/lib/queryKeys' import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings' import { canEditContent } from '@/lib/users' import { cn } from '@/lib/utils' import { useUITranslation } from '@/lib/useUITranslation' import NotFound from '@/pages/NotFound' import ServiceUnavailable from '@/pages/ServiceUnavailable' import type { FC } from 'react' import type { NiconicoViewerHandle, Post, User } from '@/types' type Props = { user: User | null } const PostDetailPage: FC = ({ user }) => { const locale = useLocale () const { message, label } = useUITranslation ('posts') const editable = canEditContent (user) const behaviourSettings = useClientBehaviourSettings () const thumbnailMode = behaviourSettings.thumbnailMode ?? 'normal' const animationMode = behaviourSettings.animation ?? 'normal' const { id } = useParams () const heroLayoutId = animationMode === 'off' ? undefined : `page-${ id }` const heroTransition = animationMode === 'off' ? { duration: 0 } : animationMode === 'reduced' ? { duration: .08, ease: 'linear' as const } : { duration: .2, ease: 'easeOut' as const } const postId = String (id ?? '') const postKey = postsKeys.show (postId, locale) const { data: post, isError: errorFlg, error } = useQuery ({ enabled: Boolean (id), queryKey: postKey, queryFn: () => fetchPost (postId, locale) }) const qc = useQueryClient () const embedRef = useRef (null) const status = (errorFlg && isApiError (error)) ? error.response?.status ?? 200 : 200 const changeViewedFlg = useMutation ({ mutationFn: async (next: boolean) => { await toggleViewedFlg (postId, next) return next }, onMutate: async (next: boolean) => { await qc.cancelQueries ({ queryKey: postKey }) const prev = qc.getQueryData (postKey) qc.setQueryData (postKey, (cur: Post | undefined) => cur ? { ...cur, viewed: next } : cur) return { prev } }, onError: (...[, , ctx]) => { if (ctx?.prev) qc.setQueryData (postKey, ctx.prev) toast ({ title: label ('shared:messages.failed'), description: label ('detail.networkFailed') }) }, onSuccess: () => { qc.invalidateQueries ({ queryKey: postsKeys.root })} }) useEffect (() => { scroll (0, 0) }, [id]) switch (status) { case 404: return case 503: return } const viewedClass = (post?.viewed ? 'bg-blue-600 hover:bg-blue-700' : 'bg-gray-500 hover:bg-gray-600') return (
{(post?.thumbnail || post?.thumbnailBase) && ( )} {post && } {post ? ( <> {(post.childPosts ?? []).length > 0 && (

{label ('detail.children', { count: post.childPosts!.length })}

({ ...p, parentPosts: [{ } as Post] }))]}/>
)} {(post.parentPosts ?? []).map (pp => { const siblings = post.siblingPosts?.[String (pp.id) as `${ number }`] if (!(siblings)) return return (

{siblings.length > 1 ? label ('detail.parentAndSiblings', { count: siblings.length - 1 }) : label ('detail.parent')}

({ ...p, parentPosts: [{ } as Post] }))]}/>
) })} {thumbnailMode !== 'off' && (post.thumbnail || post.thumbnailBase) && ( {post.title )} embedRef.current?.play ()}/> {post.related.length > 0 ? : label ('detail.emptyRelated')} {editable && ( { qc.setQueryData (postsKeys.show (postId, locale), (prev: Post | undefined) => newPost ?? prev) qc.invalidateQueries ({ queryKey: postsKeys.root }) qc.invalidateQueries ({ queryKey: tagsKeys.root }) }}/> )} ) : label ('common:loading')}
{post && }
) } export default PostDetailPage