タグ一覧ページの作成(#61) (#298)

#61

#61

Merge remote-tracking branch 'origin/main' into feature/061

#61

#61

#61

#61

#61

#61

#61

#61

#61

#61

日づけ不詳の表示修正

Co-authored-by: miteruzo <miteruzo@naver.com>
Reviewed-on: #298
This commit was merged in pull request #298.
This commit is contained in:
2026-03-21 19:58:02 +09:00
parent 8cf7107445
commit ee93ff8ea0
26 changed files with 1135 additions and 283 deletions
+6 -3
View File
@@ -5,7 +5,7 @@ import type { FetchPostsParams, Post, PostTagChange } from '@/types'
export const fetchPosts = async (
{ url, title, tags, match, createdFrom, createdTo, updatedFrom, updatedTo,
originalCreatedFrom, originalCreatedTo, page, limit, order }: FetchPostsParams
originalCreatedFrom, originalCreatedTo, page, limit, order }: FetchPostsParams,
): Promise<{
posts: Post[]
count: number }> =>
@@ -29,14 +29,17 @@ export const fetchPost = async (id: string): Promise<Post> => await apiGet (`/po
export const fetchPostChanges = async (
{ id, page, limit }: {
{ id, tag, page, limit }: {
id?: string
tag?: string
page: number
limit: number },
): Promise<{
changes: PostTagChange[]
count: number }> =>
await apiGet ('/posts/changes', { params: { ...(id && { id }), page, limit } })
await apiGet ('/posts/changes', { params: { ...(id && { id }),
...(tag && { tag }),
page, limit } })
export const toggleViewedFlg = async (id: string, viewed: boolean): Promise<void> => {
+44 -5
View File
@@ -3,12 +3,12 @@ import { match } from 'path-to-regexp'
import { fetchPost, fetchPosts, fetchPostChanges } from '@/lib/posts'
import { postsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys'
import { fetchTagByName } from '@/lib/tags'
import { fetchTagByName, fetchTag, fetchTags } from '@/lib/tags'
import { fetchWikiPage,
fetchWikiPageByTitle,
fetchWikiPages } from '@/lib/wiki'
import type { FetchPostsOrder } from '@/types'
import type { Category, FetchPostsOrder, FetchTagsOrder } from '@/types'
type Prefetcher = (qc: QueryClient, url: URL) => Promise<void>
@@ -122,12 +122,50 @@ const prefetchPostShow: Prefetcher = async (qc, url) => {
const prefetchPostChanges: Prefetcher = async (qc, url) => {
const id = url.searchParams.get ('id')
const tag = url.searchParams.get ('tag')
const page = Number (url.searchParams.get ('page') || 1)
const limit = Number (url.searchParams.get ('limit') || 20)
if (tag)
{
await qc.prefetchQuery ({
queryKey: tagsKeys.show (tag),
queryFn: () => fetchTag (tag) })
}
await qc.prefetchQuery ({
queryKey: postsKeys.changes ({ ...(id && { id }), page, limit }),
queryFn: () => fetchPostChanges ({ ...(id && { id }), page, limit }) })
queryKey: postsKeys.changes ({ ...(id && { id }),
...(tag && { tag }),
page, limit }),
queryFn: () => fetchPostChanges ({ ...(id && { id }),
...(tag && { tag }),
page, limit }) })
}
const prefetchTagsIndex: Prefetcher = async (qc, url) => {
const postRaw = url.searchParams.get ('post')
const post = postRaw ? Number (postRaw) : null
const name = url.searchParams.get ('name') ?? ''
const category = (url.searchParams.get ('category') || null) as Category | null
const postCountGTE = Number (url.searchParams.get ('post_count_gte') || 1)
const postCountLTERaw = url.searchParams.get ('post_count_lte')
const postCountLTE = postCountLTERaw ? Number (postCountLTERaw) : null
const createdFrom = url.searchParams.get ('created_from') ?? ''
const createdTo = url.searchParams.get ('created_to') ?? ''
const updatedFrom = url.searchParams.get ('updated_from') ?? ''
const updatedTo = url.searchParams.get ('updated_to') ?? ''
const page = Number (url.searchParams.get ('page') || 1)
const limit = Number (url.searchParams.get ('limit') || 20)
const order = (url.searchParams.get ('order') ?? 'post_count:desc') as FetchTagsOrder
const keys = {
post, name, category, postCountGTE, postCountLTE, createdFrom, createdTo,
updatedFrom, updatedTo, page, limit, order }
await qc.prefetchQuery ({
queryKey: tagsKeys.index (keys),
queryFn: () => fetchTags (keys) })
}
@@ -141,7 +179,8 @@ export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[]
{ test: u => u.pathname === '/wiki', run: prefetchWikiPagesIndex },
{ test: u => (!(['/wiki/new', '/wiki/changes'].includes (u.pathname))
&& Boolean (mWiki (u.pathname))),
run: prefetchWikiPageShow }]
run: prefetchWikiPageShow },
{ test: u => u.pathname === '/tags', run: prefetchTagsIndex }]
export const prefetchForURL = async (qc: QueryClient, urlLike: string): Promise<void> => {
+5 -4
View File
@@ -1,16 +1,17 @@
import type { FetchPostsParams } from '@/types'
import type { FetchPostsParams, FetchTagsParams } from '@/types'
export const postsKeys = {
root: ['posts'] as const,
index: (p: FetchPostsParams) => ['posts', 'index', p] as const,
show: (id: string) => ['posts', id] as const,
related: (id: string) => ['related', id] as const,
changes: (p: { id?: string; page: number; limit: number }) =>
changes: (p: { id?: string; tag?: string; page: number; limit: number }) =>
['posts', 'changes', p] as const }
export const tagsKeys = {
root: ['tags'] as const,
show: (name: string) => ['tags', name] as const }
root: ['tags'] as const,
index: (p: FetchTagsParams) => ['tags', 'index', p] as const,
show: (name: string) => ['tags', name] as const }
export const wikiKeys = {
root: ['wiki'] as const,
+33 -1
View File
@@ -1,6 +1,38 @@
import { apiGet } from '@/lib/api'
import type { Tag } from '@/types'
import type { FetchTagsParams, Tag } from '@/types'
export const fetchTags = async (
{ post, name, category, postCountGTE, postCountLTE, createdFrom, createdTo,
updatedFrom, updatedTo, page, limit, order }: FetchTagsParams,
): Promise<{ tags: Tag[]
count: number }> =>
await apiGet ('/tags', { params: {
...(post != null && { post }),
...(name && { name }),
...(category && { category }),
...(postCountGTE != null && { post_count_gte: postCountGTE }),
...(postCountLTE != null && { post_count_lte: postCountLTE }),
...(createdFrom && { created_from: createdFrom }),
...(createdTo && { created_to: createdTo }),
...(updatedFrom && { updated_from: updatedFrom }),
...(updatedTo && { updated_to: updatedTo }),
...(page && { page }),
...(limit && { limit }),
...(order && { order }) } })
export const fetchTag = async (id: string): Promise<Tag | null> => {
try
{
return await apiGet (`/tags/${ id }`)
}
catch
{
return null
}
}
export const fetchTagByName = async (name: string): Promise<Tag | null> => {
+56 -9
View File
@@ -10,17 +10,64 @@ export const toDate = (d: string | Date): Date => typeof d === 'string' ? new Da
export const cn = (...inputs: ClassValue[]) => twMerge (clsx (...inputs))
export const dateString = (d: string | Date): string =>
toDate (d).toLocaleString ('ja-JP-u-ca-japanese')
export const dateString = (
d: string | Date,
unknown: 'month' | 'day' | 'hour' | 'minute' | 'second' | null = null,
): string =>
toDate (d).toLocaleString (
'ja-JP-u-ca-japanese',
{ era: 'long',
year: 'numeric',
month: (unknown === 'month' ? undefined : 'long'),
day: unknown != null && ['month', 'day'].includes (unknown) ? undefined : 'numeric',
weekday: unknown != null && ['month', 'day'].includes (unknown) ? undefined : 'short',
hour: unknown == null || ['second', 'minute'].includes (unknown) ? 'numeric' : undefined,
minute: unknown == null || unknown === 'second' ? 'numeric' : undefined,
second: unknown == null ? 'numeric' : undefined })
// TODO: 表示形式きしょすぎるので何とかする
export const originalCreatedAtString = (
f: string | Date | null,
b: string | Date | null,
): string =>
([f ? `${ dateString (f) } 以降` : '',
b ? `${ dateString (b) } より前` : '']
.filter (Boolean)
.join (' '))
|| '不明'
): string => {
const from = f ? toDate (f) : null
const before = b ? toDate (b) : null
if (from && before)
{
const diff = before.getTime () - from.getTime ()
if (diff <= 60_000 /* 1 分 */)
return dateString (from, 'second')
if (from.getMinutes () === 0 && before.getMinutes () === 0)
{
if (Math.abs (diff - 3_600_000 /* 1 時間 */) < 60_000)
return dateString (from, 'minute') + ' (分不詳)'
if (from.getHours () === 0 && before.getHours () === 0)
{
if (Math.abs (diff - 86_400_000 /* 1 日 */) < 60_000)
return dateString (from, 'hour') + ' (時刻不詳)'
if (from.getDate () === 1 && before.getDate () === 1)
{
if (2_332_800_000 /* 27 日 */ < diff && diff < 2_764_800_000 /* 32 日 */)
return dateString (from, 'day') + ' (日不詳)'
if (from.getMonth () === 0 && before.getMonth () === 0
&& (31_449_600_000 /* 364 日 */ <= diff
&& diff < 31_708_800_000 /* 367 日 */))
return dateString (from, 'month') + ' (月日不詳)'
}
}
}
}
const rtn = ([from ? `${ dateString (from, 'second') }` : '',
'〜',
before ? `${ dateString (new Date (before.getTime () - 60_000), 'second') }` : '']
.filter (Boolean)
.join (' '))
return rtn === '〜' ? '年月日不詳' : rtn
}