タグ一覧ページの作成(#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:
@@ -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> => {
|
||||
|
||||
@@ -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> => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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> => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user