import { useQuery, useQueryClient } from '@tanstack/react-query' import { motion } from 'framer-motion' import { useEffect } from 'react' import { Helmet } from 'react-helmet-async' import { useLocation } from 'react-router-dom' import TagLink from '@/components/TagLink' import PrefetchLink from '@/components/PrefetchLink' import PageTitle from '@/components/common/PageTitle' import Pagination from '@/components/common/Pagination' import MainArea from '@/components/layout/MainArea' import { toast } from '@/components/ui/use-toast' import { SITE_TITLE } from '@/config' import { apiPut } from '@/lib/api' import { fetchPostChanges } from '@/lib/posts' import { postsKeys, tagsKeys } from '@/lib/queryKeys' import { fetchTag } from '@/lib/tags' import { cn, dateString, originalCreatedAtString } from '@/lib/utils' import type { FC } from 'react' const renderDiff = (diff: { current: string | null; prev: string | null }) => ( <> {(diff.prev && diff.prev !== diff.current) && ( <> {diff.prev} {diff.current &&
} )} {diff.current} ) export default (() => { const location = useLocation () const query = new URLSearchParams (location.search) const id = query.get ('id') const tagId = query.get ('tag') const page = Number (query.get ('page') ?? 1) const limit = Number (query.get ('limit') ?? 20) // 投稿列の結合で使用 let rowsCnt: number const { data: tag } = tagId ? useQuery ({ queryKey: tagsKeys.show (tagId), queryFn: () => fetchTag (tagId) }) : { data: null } const { data, isLoading: loading } = useQuery ({ queryKey: postsKeys.changes ({ ...(id && { post: id }), ...(tagId && { tag: tagId }), page, limit }), queryFn: () => fetchPostChanges ({ ...(id && { post: id }), ...(tagId && { tag: tagId }), page, limit }) }) const changes = data?.versions ?? [] const totalPages = data ? Math.ceil (data.count / limit) : 0 const qc = useQueryClient () useEffect (() => { document.querySelector ('table')?.scrollIntoView ({ behavior: 'smooth' }) }, [location.search]) const layoutIds: string[] = [] return ( {`耕作履歴 | ${ SITE_TITLE }`} 耕作履歴 {id && <>: 投稿 {#{id}}} {tag && <>()} {loading ? 'Loading...' : ( <>
{/* 投稿 */} {/* 版 */} {/* タイトル */} {/* URL */} {/* タグ */} {/* オリジナルの投稿日時 */} {/* 更新日時 */} {/* (差戻ボタン) */} {changes.map ((change, i) => { const withPost = i === 0 || change.postId !== changes[i - 1].postId if (withPost) { rowsCnt = 1 for (let j = i + 1; (j < changes.length && change.postId === changes[j].postId); ++j) ++rowsCnt } let layoutId: string | undefined = `page-${ change.postId }` if (layoutIds.includes (layoutId)) layoutId = undefined else layoutIds.push (layoutId) return ( {withPost && ( )} ) })}
投稿 タイトル URL タグ オリジナルの投稿日時 更新日時
{change.title.current {change.postId}.{change.versionNo} {renderDiff (change.title)} {renderDiff (change.url)} {change.tags.map ((tag, i) => ( tag.type === 'added' ? ( {tag.name} ) : ( tag.type === 'removed' ? ( {tag.name} ) : ( {tag.name} ))))} {change.versionNo === 1 ? originalCreatedAtString (change.originalCreatedFrom.current, change.originalCreatedBefore.current) : renderDiff ({ current: originalCreatedAtString ( change.originalCreatedFrom.current, change.originalCreatedBefore.current), prev: originalCreatedAtString ( change.originalCreatedFrom.prev, change.originalCreatedBefore.prev) })} {change.createdByUser ? ( {change.createdByUser.name || `名もなきニジラー(#${ change.createdByUser.id })`} ) : 'bot 操作'}
{dateString (change.createdAt)}
{ e.preventDefault () if (!(confirm ( `『${ change.title.current || change.url.current }』を版 ${ change.versionNo } に差戻します.\nよろしいですか?`))) return await apiPut ( `/posts/${ change.postId }`, { title: change.title.current, tags: change.tags .filter (t => t.type !== 'removed') .map (t => t.name) .filter (t => t.slice (0, 5) !== 'nico:') .join (' '), original_created_from: change.originalCreatedFrom.current, original_created_before: change.originalCreatedBefore.current }) qc.invalidateQueries ({ queryKey: postsKeys.root }) qc.invalidateQueries ({ queryKey: tagsKeys.root }) toast ({ description: '更新しました.' }) }}> 復元
)}
) }) satisfies FC