From b04f3e698dc5679334b1605634027c6047936a72 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Thu, 24 Sep 2026 08:46:22 +0900 Subject: [PATCH] #419 --- backend/spec/requests/tags_spec.rb | 8 +++--- frontend/src/components/common/PageTitle.tsx | 6 ++--- frontend/src/lib/queryKeys.ts | 1 + frontend/src/lib/tags.ts | 12 +++++++++ .../src/pages/posts/PostHistoryPage.test.tsx | 25 ++++++++++++++++--- frontend/src/pages/posts/PostHistoryPage.tsx | 19 +++++++++++--- 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/backend/spec/requests/tags_spec.rb b/backend/spec/requests/tags_spec.rb index b7232da..3c1088c 100644 --- a/backend/spec/requests/tags_spec.rb +++ b/backend/spec/requests/tags_spec.rb @@ -396,7 +396,9 @@ RSpec.describe 'Tags API', type: :request do end it 'prefers Tag when Tag and ExternalTag have the same id' do - internal = create(:tag, name: 'internal_collision') + internal = create( + :tag, + tag_name: create(:tag_name, name: 'internal_collision')) create( :external_tag, id: internal.id, @@ -407,8 +409,8 @@ RSpec.describe 'Tags API', type: :request do expect(response).to have_http_status(:ok) expect(json).to include( 'id' => internal.id, - 'name' => 'internal_collision') - expect(json.fetch('category')).not_to eq('nico') + 'name' => 'internal_collision', + 'category' => internal.category) end end diff --git a/frontend/src/components/common/PageTitle.tsx b/frontend/src/components/common/PageTitle.tsx index 6ae991e..8295b41 100644 --- a/frontend/src/components/common/PageTitle.tsx +++ b/frontend/src/components/common/PageTitle.tsx @@ -1,10 +1,8 @@ -import React from 'react' - import { cn } from '@/lib/utils' -import type { FC } from 'react' +import type { ComponentProps, FC } from 'react' -type Props = { children: React.ReactNode; className?: string } +type Props = ComponentProps<'h1'> const PageTitle: FC = ({ children, className, ...rest }) => ( diff --git a/frontend/src/lib/queryKeys.ts b/frontend/src/lib/queryKeys.ts index 49c231f..a1d03d3 100644 --- a/frontend/src/lib/queryKeys.ts +++ b/frontend/src/lib/queryKeys.ts @@ -30,6 +30,7 @@ export const tagsKeys = { index: (p: FetchTagsParams) => ['tags', 'index', p] as const, nicoRoot: ['tags', 'nico'] as const, nicoIndex: (p: FetchNicoTagsParams) => ['tags', 'nico', 'index', p] as const, + externalShow: (id: string) => ['tags', 'nico', id] as const, show: (name: string) => ['tags', name] as const, changes: (p: { id?: string; page: number; limit: number }) => ['tags', 'changes', p] as const, diff --git a/frontend/src/lib/tags.ts b/frontend/src/lib/tags.ts index 4c017ab..d27173b 100644 --- a/frontend/src/lib/tags.ts +++ b/frontend/src/lib/tags.ts @@ -53,6 +53,18 @@ export const fetchTag = async (id: string): Promise => { } +export const fetchExternalTag = async (id: string): Promise => { + try + { + return await apiGet (`/tags/nico/${ id }`) + } + catch + { + return null + } +} + + export const fetchTagByName = async (name: string): Promise => { try { diff --git a/frontend/src/pages/posts/PostHistoryPage.test.tsx b/frontend/src/pages/posts/PostHistoryPage.test.tsx index 44b2ee5..b65a0b9 100644 --- a/frontend/src/pages/posts/PostHistoryPage.test.tsx +++ b/frontend/src/pages/posts/PostHistoryPage.test.tsx @@ -1,7 +1,8 @@ -import { waitFor } from '@testing-library/react' +import { screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import PostHistoryPage from '@/pages/posts/PostHistoryPage' +import { buildTag } from '@/test/factories' import { renderWithProviders } from '@/test/render' const postsApi = vi.hoisted (() => ({ @@ -10,7 +11,8 @@ const postsApi = vi.hoisted (() => ({ })) const tagsApi = vi.hoisted (() => ({ - fetchTag: vi.fn (), + fetchTag: vi.fn (), + fetchExternalTag: vi.fn (), })) vi.mock ('@/lib/posts', () => postsApi) @@ -19,13 +21,22 @@ vi.mock ('@/lib/tags', () => tagsApi) describe ('PostHistoryPage', () => { beforeEach (() => { vi.clearAllMocks () + postsApi.fetchPostChanges.mockResolvedValue ({ versions: [], count: 0, }) }) - it ('filters by external_tag without resolving the id as an internal tag', async () => { + it ('shows the external tag name when filtering by external_tag', async () => { + const external = buildTag ({ + id: 7, + name: 'nico:external_history', + category: 'nico', + }) + + tagsApi.fetchExternalTag.mockResolvedValue (external) + renderWithProviders ( , { route: '/posts/changes?external_tag=7' }, @@ -39,6 +50,14 @@ describe ('PostHistoryPage', () => { }) }) + expect (tagsApi.fetchExternalTag).toHaveBeenCalledWith ('7') expect (tagsApi.fetchTag).not.toHaveBeenCalled () + + expect ( + await screen.findByRole ('heading', { + level: 1, + name: '耕作履歴(nico:external_history)', + }), + ).toBeInTheDocument () }) }) diff --git a/frontend/src/pages/posts/PostHistoryPage.tsx b/frontend/src/pages/posts/PostHistoryPage.tsx index 70b517d..446dc38 100644 --- a/frontend/src/pages/posts/PostHistoryPage.tsx +++ b/frontend/src/pages/posts/PostHistoryPage.tsx @@ -16,7 +16,7 @@ import { clientAnimationTransition, clientScrollBehaviour } from '@/lib/clientAnimation' import { fetchPostChanges, updatePost } from '@/lib/posts' import { postsKeys, tagsKeys } from '@/lib/queryKeys' -import { fetchTag } from '@/lib/tags' +import { fetchExternalTag, fetchTag } from '@/lib/tags' import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings' import { cn, dateString, originalCreatedAtString } from '@/lib/utils' @@ -64,6 +64,12 @@ const PostHistoryPage: FC = () => { queryKey: tagsKeys.show (tagQueryId), queryFn: () => fetchTag (tagQueryId) }) + const externalTagQueryId = externalTagId ?? '' + const { data: externalTag } = useQuery ({ + enabled: Boolean (externalTagId), + queryKey: tagsKeys.externalShow (externalTagQueryId), + queryFn: () => fetchExternalTag (externalTagQueryId) }) + const { data, isLoading: loading } = useQuery ({ queryKey: postsKeys.changes ({ ...(id && { post: id }), ...(tagId && { tag: tagId }), @@ -76,6 +82,9 @@ const PostHistoryPage: FC = () => { page, limit }) }) const changes = data?.versions ?? [] const totalPages = data ? Math.ceil (data.count / limit) : 0 + const displayedTag = externalTag ?? tag + const pageTitleLabel = `耕作履歴${ id ? `: 投稿 #${ id }` : '' }${ + displayedTag ? `(${ displayedTag.name })` : '' }` const qc = useQueryClient () @@ -138,10 +147,14 @@ const PostHistoryPage: FC = () => { {`耕作履歴 | ${ SITE_TITLE }`} - + 耕作履歴 {id && <>: 投稿 {#{id}}} - {tag && <>()} + {displayedTag && ( + <>())} {loading ? 'Loading...' : (