From 914dc43889aa89cbeea2d0f8bc5d0dbc33aad2ea Mon Sep 17 00:00:00 2001 From: miteruzo Date: Wed, 4 Feb 2026 00:51:40 +0900 Subject: [PATCH] #140 --- frontend/src/components/TopNav.tsx | 2 +- frontend/src/lib/posts.ts | 17 ++- frontend/src/lib/prefetchers.ts | 33 ++++- frontend/src/lib/queryKeys.ts | 9 +- frontend/src/lib/wiki.ts | 14 +- frontend/src/pages/posts/PostHistoryPage.tsx | 128 +++++++++---------- frontend/src/pages/wiki/WikiDetailPage.tsx | 2 +- 7 files changed, 120 insertions(+), 85 deletions(-) diff --git a/frontend/src/components/TopNav.tsx b/frontend/src/components/TopNav.tsx index 8256759..2133395 100644 --- a/frontend/src/components/TopNav.tsx +++ b/frontend/src/components/TopNav.tsx @@ -120,7 +120,7 @@ export default (({ user }: Props) => { const fetchPostCount = async () => { try { - const wikiPage = await fetchWikiPage (String (wikiId ?? '')) + const wikiPage = await fetchWikiPage (String (wikiId ?? ''), { }) const tag = await fetchTagByName (wikiPage.title) setPostCount (tag.postCount) diff --git a/frontend/src/lib/posts.ts b/frontend/src/lib/posts.ts index 31010aa..99c95f3 100644 --- a/frontend/src/lib/posts.ts +++ b/frontend/src/lib/posts.ts @@ -1,6 +1,6 @@ import { apiDelete, apiGet, apiPost } from '@/lib/api' -import type { Post } from '@/types' +import type { Post, PostTagChange } from '@/types' export const fetchPosts = async ( @@ -13,8 +13,8 @@ export const fetchPosts = async ( ): Promise<{ posts: Post[] count: number - nextCursor: string }> => await apiGet ('/posts', { - params: { + nextCursor: string }> => + await apiGet ('/posts', { params: { tags, match, ...(page && { page }), @@ -25,6 +25,17 @@ export const fetchPosts = async ( export const fetchPost = async (id: string): Promise => await apiGet (`/posts/${ id }`) +export const fetchPostChanges = async ( + { id, page, limit }: { + id?: string + page: number + limit: number }, +): Promise<{ + changes: PostTagChange[] + count: number }> => + await apiGet ('/posts/changes', { params: { ...(id && { id }), page, limit } }) + + export const toggleViewedFlg = async (id: string, viewed: boolean): Promise => { await (viewed ? apiPost : apiDelete) (`/posts/${ id }/viewed`) } diff --git a/frontend/src/lib/prefetchers.ts b/frontend/src/lib/prefetchers.ts index 8e0c30b..d2ca3ec 100644 --- a/frontend/src/lib/prefetchers.ts +++ b/frontend/src/lib/prefetchers.ts @@ -1,14 +1,23 @@ import { QueryClient } from '@tanstack/react-query' import { match } from 'path-to-regexp' -import { fetchPost, fetchPosts } from '@/lib/posts' -import { postsKeys } from '@/lib/queryKeys' +import { fetchPost, fetchPosts, fetchPostChanges } from '@/lib/posts' +import { postsKeys, wikiKeys } from '@/lib/queryKeys' +import { fetchWikiPages } from '@/lib/wiki' type Prefetcher = (qc: QueryClient, url: URL) => Promise const mPost = match<{ id: string }> ('/posts/:id') +const prefetchWikiPagesIndex: Prefetcher = async (qc, url) => { + const title = url.searchParams.get ('title') ?? '' + await qc.prefetchQuery ({ + queryKey: wikiKeys.index ({ title }), + queryFn: () => fetchWikiPages ({ title }) }) +} + + const prefetchPostsIndex: Prefetcher = async (qc, url) => { const tags = url.searchParams.get ('tags') ?? '' const m = url.searchParams.get ('match') === 'any' ? 'any' : 'all' @@ -32,11 +41,23 @@ const prefetchPostShow: Prefetcher = async (qc, url) => { } -export const routePrefetchers: { - test: (u: URL) => boolean - run: Prefetcher }[] = [ +const prefetchPostChanges: Prefetcher = async (qc, url) => { + const id = url.searchParams.get ('id') + const page = Number (url.searchParams.get ('page') || 1) + const limit = Number (url.searchParams.get ('limit') || 20) + await qc.prefetchQuery ({ + queryKey: postsKeys.changes ({ ...(id && { id }), page, limit }), + queryFn: () => fetchPostChanges ({ ...(id && { id }), page, limit }) }) +} + + +export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[] = [ { test: u => u.pathname === '/' || u.pathname === '/posts', run: prefetchPostsIndex }, - { test: u => Boolean (mPost (u.pathname)), run: prefetchPostShow }] + { test: u => (['/posts/new', '/posts/changes'].includes(u.pathname) + && Boolean (mPost (u.pathname))), + run: prefetchPostShow }, + { test: u => u.pathname === '/posts/changes', run: prefetchPostChanges }, + { test: u => u.pathname === '/wiki', run: prefetchWikiPagesIndex }] export const prefetchForURL = async (qc: QueryClient, urlLike: string): Promise => { diff --git a/frontend/src/lib/queryKeys.ts b/frontend/src/lib/queryKeys.ts index 037f338..d1b1f90 100644 --- a/frontend/src/lib/queryKeys.ts +++ b/frontend/src/lib/queryKeys.ts @@ -3,8 +3,11 @@ export const postsKeys = { index: (p: { tags: string; match: 'any' | 'all'; page: number; limit: number }) => ['posts', 'index', p] as const, show: (id: string) => ['posts', id] as const, - related: (id: string) => ['related', id] as const } + related: (id: string) => ['related', id] as const, + changes: (p: { id?: string; page: number; limit: number }) => + ['posts', 'changes', p] as const } export const wikiKeys = { - root: ['wiki'] as const, - show: (title: string, p: { version: string }) => ['wiki', title, p] as const } + root: ['wiki'] as const, + index: (p: { title: string }) => ['wiki', 'index', p] as const, + show: (title: string, p: { version: string }) => ['wiki', title, p] as const } diff --git a/frontend/src/lib/wiki.ts b/frontend/src/lib/wiki.ts index c2b9d30..ce07111 100644 --- a/frontend/src/lib/wiki.ts +++ b/frontend/src/lib/wiki.ts @@ -3,12 +3,20 @@ import { apiGet } from '@/lib/api' import type { WikiPage } from '@/types' -export const fetchWikiPage = async (id: string): Promise => - await apiGet (`/wiki/${ id }`) +export const fetchWikiPages = async ({ title }: { title: string }) => + await apiGet ('/wiki', { params: { title } }) + + +export const fetchWikiPage = async ( + id: string, + { version }: { version?: string }, +): Promise => + await apiGet (`/wiki/${ id }`, { params: version ? { version } : { } }) export const fetchWikiPageByTitle = async ( title: string, { version }: { version?: string }, ): Promise => - await apiGet (`/wiki/title/${ title }`, { params: version ? { version } : { } }) + await apiGet (`/wiki/title/${ title }`, + { params: version ? { version } : { } }) diff --git a/frontend/src/pages/posts/PostHistoryPage.tsx b/frontend/src/pages/posts/PostHistoryPage.tsx index 9e5da57..0b09fe7 100644 --- a/frontend/src/pages/posts/PostHistoryPage.tsx +++ b/frontend/src/pages/posts/PostHistoryPage.tsx @@ -1,6 +1,4 @@ -import axios from 'axios' -import toCamel from 'camelcase-keys' -import { useEffect, useState } from 'react' +import { useQuery } from '@tanstack/react-query' import { Helmet } from 'react-helmet-async' import { useLocation } from 'react-router-dom' @@ -9,17 +7,14 @@ import PrefetchLink from '@/components/PrefetchLink' import PageTitle from '@/components/common/PageTitle' import Pagination from '@/components/common/Pagination' import MainArea from '@/components/layout/MainArea' -import { API_BASE_URL, SITE_TITLE } from '@/config' +import { SITE_TITLE } from '@/config' +import { fetchPostChanges } from '@/lib/posts' +import { postsKeys } from '@/lib/queryKeys' import type { FC } from 'react' -import type { PostTagChange } from '@/types' - export default (() => { - const [changes, setChanges] = useState ([]) - const [totalPages, setTotalPages] = useState (0) - const location = useLocation () const query = new URLSearchParams (location.search) const id = query.get ('id') @@ -29,17 +24,11 @@ export default (() => { // 投稿列の結合で使用 let rowsCnt: number - useEffect (() => { - void (async () => { - const res = await axios.get (`${ API_BASE_URL }/posts/changes`, - { params: { ...(id && { id }), page, limit } }) - const data = toCamel (res.data as any, { deep: true }) as { - changes: PostTagChange[] - count: number } - setChanges (data.changes) - setTotalPages (Math.ceil (data.count / limit)) - }) () - }, [id, page, limit]) + const { data, isLoading: loading } = useQuery ({ + queryKey: postsKeys.changes ({ ...(id && { id }), page, limit }), + queryFn: () => fetchPostChanges ({ ...(id && { id }), page, limit }) }) + const changes = data?.changes ?? [] + const totalPages = data ? Math.ceil (data.count / limit) : 0 return ( @@ -52,54 +41,57 @@ export default (() => { {id && <>: 投稿 {#{id}}} - - - - - - - - - - {changes.map ((change, i) => { - let withPost = i === 0 || change.post.id !== changes[i - 1].post.id - if (withPost) - { - rowsCnt = 1 - for (let j = i + 1; - (j < changes.length - && change.post.id === changes[j].post.id); - ++j) - ++rowsCnt - } - return ( - - {withPost && ( - )} - - - ) - })} - -
投稿変更日時
- - {change.post.title - - - - {`を${ change.changeType === 'add' ? '追加' : '削除' }`} - - {change.user ? ( - - {change.user.name} - ) : 'bot 操作'} -
- {change.timestamp} -
+ {loading ? 'Loading...' : ( + <> + + + + + + + + + + {changes.map ((change, i) => { + let withPost = i === 0 || change.post.id !== changes[i - 1].post.id + if (withPost) + { + rowsCnt = 1 + for (let j = i + 1; + (j < changes.length + && change.post.id === changes[j].post.id); + ++j) + ++rowsCnt + } + return ( + + {withPost && ( + )} + + + ) + })} + +
投稿変更日時
+ + {change.post.title + + + + {`を${ change.changeType === 'add' ? '記載' : '消除' }`} + + {change.user ? ( + + {change.user.name} + ) : 'bot 操作'} +
+ {change.timestamp} +
- + + )}
) }) satisfies FC diff --git a/frontend/src/pages/wiki/WikiDetailPage.tsx b/frontend/src/pages/wiki/WikiDetailPage.tsx index c87ff52..fb17f51 100644 --- a/frontend/src/pages/wiki/WikiDetailPage.tsx +++ b/frontend/src/pages/wiki/WikiDetailPage.tsx @@ -41,7 +41,7 @@ export default () => { setWikiPage (undefined) try { - const data = await fetchWikiPage (title) + const data = await fetchWikiPage (title, { }) navigate (`/wiki/${ encodeURIComponent(data.title) }`, { replace: true }) } catch