このコミットが含まれているのは:
+11
-4
@@ -5,7 +5,10 @@ import { API_BASE_URL } from '@/config'
|
||||
|
||||
import type { AxiosError, AxiosRequestConfig } from 'axios'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
|
||||
type Opt = {
|
||||
locale?: LocaleCode
|
||||
params?: AxiosRequestConfig['params']
|
||||
headers?: Record<string, string>
|
||||
signal?: AbortSignal
|
||||
@@ -14,10 +17,14 @@ type Opt = {
|
||||
const client = axios.create ({ baseURL: API_BASE_URL })
|
||||
|
||||
|
||||
const withUserCode = (opt?: Opt): Opt => ({
|
||||
...opt,
|
||||
headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '',
|
||||
...(opt?.headers ?? { }) } })
|
||||
const withUserCode = (opt?: Opt): AxiosRequestConfig => {
|
||||
const { locale, ...config } = opt ?? { }
|
||||
return {
|
||||
...config,
|
||||
...(locale != null && { params: { locale, ...config.params } }),
|
||||
headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '',
|
||||
...(config.headers ?? { }) } }
|
||||
}
|
||||
|
||||
|
||||
const apiP = async <T> (
|
||||
|
||||
+31
-15
@@ -1,5 +1,6 @@
|
||||
import { apiGet, apiPost } from '@/lib/api'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { Post } from '@/types'
|
||||
|
||||
export type GekanatorAnswerValue =
|
||||
@@ -411,24 +412,30 @@ export const storeGekanatorQuestion = (
|
||||
exampleAnswers: question.exampleAnswers })
|
||||
|
||||
|
||||
export const fetchGekanatorPosts = async (): Promise<Post[]> => {
|
||||
const data = await apiGet<{ posts: Post[] }> ('/gekanator/posts')
|
||||
export const fetchGekanatorPosts = async (
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Post[]> => {
|
||||
const data = await apiGet<{ posts: Post[] }> ('/gekanator/posts', { locale })
|
||||
return data.posts
|
||||
}
|
||||
|
||||
|
||||
export const fetchGekanatorQuestions = async (): Promise<StoredGekanatorQuestion[]> => {
|
||||
const data = await apiGet<{ questions: StoredGekanatorQuestion[] }> ('/gekanator/questions')
|
||||
export const fetchGekanatorQuestions = async (
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<StoredGekanatorQuestion[]> => {
|
||||
const data = await apiGet<{ questions: StoredGekanatorQuestion[] }> ('/gekanator/questions', { locale })
|
||||
return data.questions
|
||||
}
|
||||
|
||||
|
||||
export const fetchGekanatorExtraQuestions = async (
|
||||
gameId: number,
|
||||
nonce?: string): Promise<GekanatorExtraQuestion[]> => {
|
||||
nonce?: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<GekanatorExtraQuestion[]> => {
|
||||
const data = await apiGet<{ questions: GekanatorExtraQuestion[] }> (
|
||||
`/gekanator/games/${ gameId }/extra_questions`,
|
||||
{ params: nonce ? { nonce } : undefined })
|
||||
{ locale, params: nonce ? { nonce } : undefined })
|
||||
return data.questions
|
||||
}
|
||||
|
||||
@@ -606,7 +613,8 @@ export const buildGekanatorQuestions = (
|
||||
}
|
||||
|
||||
|
||||
export const saveGekanatorGame = async ({
|
||||
export const saveGekanatorGame = async (
|
||||
{
|
||||
guessedPostId,
|
||||
correctPostId,
|
||||
answers,
|
||||
@@ -614,7 +622,9 @@ export const saveGekanatorGame = async ({
|
||||
guessedPostId: number
|
||||
correctPostId: number
|
||||
answers: GekanatorAnswerLog[]
|
||||
}): Promise<{ id: number; learnedExampleCount: number }> =>
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{ id: number; learnedExampleCount: number }> =>
|
||||
await apiPost ('/gekanator/games', {
|
||||
guessed_post_id: guessedPostId,
|
||||
correct_post_id: correctPostId,
|
||||
@@ -627,10 +637,11 @@ export const saveGekanatorGame = async ({
|
||||
effective_question: answer.effectiveQuestion,
|
||||
learning_question: answer.learningQuestion,
|
||||
answer: answer.answer,
|
||||
original_answer: answer.originalAnswer })) })
|
||||
original_answer: answer.originalAnswer })) }, { locale })
|
||||
|
||||
|
||||
export const saveGekanatorQuestionSuggestion = async ({
|
||||
export const saveGekanatorQuestionSuggestion = async (
|
||||
{
|
||||
gekanatorGameId,
|
||||
existingQuestionId,
|
||||
questionText,
|
||||
@@ -640,22 +651,27 @@ export const saveGekanatorQuestionSuggestion = async ({
|
||||
existingQuestionId?: number
|
||||
questionText?: string
|
||||
answer: GekanatorAnswerValue
|
||||
}): Promise<{ id: number; count: number }> =>
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{ id: number; count: number }> =>
|
||||
await apiPost ('/gekanator/question_suggestions', {
|
||||
gekanator_game_id: gekanatorGameId,
|
||||
existing_question_id: existingQuestionId,
|
||||
question_text: questionText,
|
||||
answer })
|
||||
answer }, { locale })
|
||||
|
||||
|
||||
export const saveGekanatorExtraQuestionAnswers = async ({
|
||||
export const saveGekanatorExtraQuestionAnswers = async (
|
||||
{
|
||||
gameId,
|
||||
answers,
|
||||
}: {
|
||||
gameId: number
|
||||
answers: { questionId: number; answer: GekanatorAnswerValue }[]
|
||||
}) =>
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
await apiPost (`/gekanator/games/${ gameId }/extra_question_answers`, {
|
||||
answers: answers.map (item => ({
|
||||
question_id: item.questionId,
|
||||
answer: item.answer })) })
|
||||
answer: item.answer })) }, { locale })
|
||||
|
||||
+16
-3
@@ -13,12 +13,14 @@ export type HelpEditionText = {
|
||||
|
||||
export type HelpSearchEntry = HelpEditionText & {
|
||||
identifier: string
|
||||
language?: string
|
||||
edition: string }
|
||||
|
||||
export type HelpEditionContent = HelpEditionText & { content: ComponentType }
|
||||
|
||||
export type HelpEditionSource = HelpEditionText & { source: string }
|
||||
export type HelpChange = Pick<HelpSearchEntry, 'identifier' | 'edition' | 'title'> & {
|
||||
language?: string
|
||||
pred: string | null
|
||||
change: 'new' | 'update'
|
||||
date: string }
|
||||
@@ -82,6 +84,15 @@ export const getHelpArticle = async (identifier: string): Promise<ArticleMeta |
|
||||
}
|
||||
|
||||
|
||||
export const resolveHelpLanguage = (
|
||||
article: ArticleMeta | null | undefined,
|
||||
requestedLanguage: string,
|
||||
): string =>
|
||||
article?.languages[requestedLanguage] != null
|
||||
? requestedLanguage
|
||||
: article?.canonicalLanguage ?? 'ja'
|
||||
|
||||
|
||||
export const getHelpEditions = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
@@ -144,7 +155,8 @@ export const getHelpSearchEntries = async (
|
||||
language: string,
|
||||
): Promise<HelpSearchEntry[]> => {
|
||||
const { default: search } = await import ('virtual:help/search')
|
||||
return search[language] ?? []
|
||||
const contentLanguage = search[language] != null ? language : 'ja'
|
||||
return (search[contentLanguage] ?? []).map (entry => ({ ...entry, language: contentLanguage }))
|
||||
}
|
||||
|
||||
|
||||
@@ -163,13 +175,14 @@ export const getHelpEditionContent = async (
|
||||
|
||||
export const getHelpChanges = async (language: string): Promise<HelpChange[]> => {
|
||||
const { default: changes } = await import ('virtual:help/changes')
|
||||
return changes[language] ?? []
|
||||
const contentLanguage = changes[language] != null ? language : 'ja'
|
||||
return (changes[contentLanguage] ?? []).map (entry => ({ ...entry, language: contentLanguage }))
|
||||
}
|
||||
|
||||
|
||||
export const getHelpTitleIndex = async (language: string): Promise<HelpTitleIndex> => {
|
||||
const { default: titles } = await import ('virtual:help/titles')
|
||||
return titles[language] ?? { }
|
||||
return titles[language] ?? titles.ja ?? { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { unlocalisedPath } from '@/lib/locales'
|
||||
|
||||
export type ShortcutScope =
|
||||
| 'global'
|
||||
| 'posts'
|
||||
@@ -308,7 +310,8 @@ export const getConflictingShortcutActionIds = (
|
||||
)
|
||||
}
|
||||
|
||||
export const resolveShortcutScope = (pathname: string): ShortcutScope => {
|
||||
export const resolveShortcutScope = (fullPathname: string): ShortcutScope => {
|
||||
const pathname = unlocalisedPath (fullPathname)
|
||||
if (pathname.startsWith ('/posts/search'))
|
||||
return 'postSearch'
|
||||
if (pathname.startsWith ('/posts'))
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { isLocaleCode,
|
||||
legacyLocalePath,
|
||||
localeFromPath,
|
||||
localiseTo,
|
||||
switchLocalePath,
|
||||
unlocalisedPath } from '@/lib/locales'
|
||||
|
||||
|
||||
describe ('locale URL generation', () => {
|
||||
it.each (['ja', 'ain-Kana']) ('recognises the supported locale %s', locale => {
|
||||
expect (isLocaleCode (locale)).toBe (true)
|
||||
expect (localeFromPath (`/${ locale }/posts`)).toBe (locale)
|
||||
})
|
||||
|
||||
it.each (['en', 'ain', 'JA', '', undefined]) ('rejects %s', locale => {
|
||||
expect (isLocaleCode (locale)).toBe (false)
|
||||
})
|
||||
|
||||
it.each ([
|
||||
'/posts', '/posts/new', '/posts/search', '/posts/42', '/posts/changes',
|
||||
'/tags', '/tags/42', '/tags/42/deerjikists', '/tags/nico', '/nico/tags', '/tags/changes',
|
||||
'/materials', '/materials/new', '/materials/42', '/materials/changes',
|
||||
'/materials/suppressions', '/theatres/42', '/wiki', '/wiki/new', '/wiki/title',
|
||||
'/wiki/42/edit', '/wiki/42/diff', '/wiki/changes', '/help', '/help/changes',
|
||||
'/help/article', '/help/article/diff', '/users/settings', '/settings',
|
||||
'/tos', '/gekanator', '/more']) ('localises and redirects %s', pathname => {
|
||||
const location = { pathname, search: '?page=2&tag=a%23b', hash: '#section' }
|
||||
expect (localiseTo (pathname, 'ain-Kana')).toBe (`/ain-Kana${ pathname }`)
|
||||
expect (legacyLocalePath (location)).toBe (`/ja${ pathname }${ location.search }#section`)
|
||||
expect (unlocalisedPath (`/ain-Kana${ pathname }`)).toBe (pathname)
|
||||
})
|
||||
|
||||
it ('preserves the resource, query and fragment on locale changes', () => {
|
||||
expect (switchLocalePath ({ pathname: '/ja/tags/42', search: '?x=1&x=2', hash: '#history' },
|
||||
'ain-Kana')).toBe ('/ain-Kana/tags/42?x=1&x=2#history')
|
||||
})
|
||||
|
||||
it ('preserves explicit locales and relative, fragment and external targets', () => {
|
||||
for (const target of ['/ja/posts', '/ain-Kana/posts', '?page=2', '#body',
|
||||
'../changes', 'https://example.com/posts', '//example.com/posts'])
|
||||
expect (localiseTo (target, 'ain-Kana')).toBe (target)
|
||||
})
|
||||
|
||||
it ('preserves structured targets', () => {
|
||||
expect (localiseTo ({ pathname: '/tags/42', search: '?q=one', hash: '#two' }, 'ain-Kana'))
|
||||
.toEqual ({ pathname: '/ain-Kana/tags/42', search: '?q=one', hash: '#two' })
|
||||
})
|
||||
|
||||
it.each (['/en/posts', '/ain/posts', '/unknown', '/api/tags', '/assets/image.png']) (
|
||||
'does not redirect invalid or non-page paths: %s', pathname => {
|
||||
expect (legacyLocalePath ({ pathname, search: '', hash: '' })).toBeNull ()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { To } from 'react-router-dom'
|
||||
|
||||
export const SUPPORTED_LOCALES = ['ja', 'ain-Kana'] as const
|
||||
|
||||
export type LocaleCode = typeof SUPPORTED_LOCALES[number]
|
||||
|
||||
export const LOCALE_LABELS: Record<LocaleCode, string> = {
|
||||
ja: '日本語', 'ain-Kana': 'ヤウンモシルン アイヌ イタㇰ' }
|
||||
|
||||
const LEGACY_ROOTS = [
|
||||
'posts', 'tags', 'nico', 'theatres', 'materials', 'wiki', 'help',
|
||||
'users', 'settings', 'tos', 'gekanator', 'more']
|
||||
|
||||
|
||||
export const isLocaleCode = (value: unknown): value is LocaleCode =>
|
||||
SUPPORTED_LOCALES.some (locale => locale === value)
|
||||
|
||||
|
||||
export const localeLanguage = (locale: LocaleCode): 'ja' | 'ain' =>
|
||||
locale === 'ja' ? 'ja' : 'ain'
|
||||
|
||||
|
||||
export const localeFromPath = (pathname: string): LocaleCode | null => {
|
||||
const segment = pathname.split (/[/?#]/)[1]
|
||||
return isLocaleCode (segment) ? segment : null
|
||||
}
|
||||
|
||||
|
||||
export const unlocalisedPath = (pathname: string): string => {
|
||||
const locale = localeFromPath (pathname)
|
||||
return locale == null ? pathname : pathname.slice (locale.length + 1) || '/'
|
||||
}
|
||||
|
||||
|
||||
export const localiseTo = (to: To, locale: LocaleCode): To => {
|
||||
const pathname = typeof to === 'string' ? to : to.pathname
|
||||
if (pathname == null
|
||||
|| !(pathname.startsWith ('/'))
|
||||
|| pathname.startsWith ('//')
|
||||
|| localeFromPath (pathname) != null)
|
||||
return to
|
||||
|
||||
const localised = `/${ locale }${ pathname }`
|
||||
return typeof to === 'string' ? localised : { ...to, pathname: localised }
|
||||
}
|
||||
|
||||
|
||||
export const switchLocalePath = (
|
||||
location: { pathname: string; search: string; hash: string },
|
||||
locale: LocaleCode,
|
||||
): string =>
|
||||
`/${ locale }${ unlocalisedPath (location.pathname) }${ location.search }${ location.hash }`
|
||||
|
||||
|
||||
export const legacyLocalePath = (
|
||||
location: { pathname: string; search: string; hash: string },
|
||||
): string | null => {
|
||||
const root = location.pathname.split ('/')[1]
|
||||
return (location.pathname === '/' || LEGACY_ROOTS.includes (root))
|
||||
? `/ja${ location.pathname }${ location.search }${ location.hash }`
|
||||
: null
|
||||
}
|
||||
+38
-17
@@ -1,5 +1,6 @@
|
||||
import { apiGet, isApiError, apiPost, apiPut } from '@/lib/api'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { Material,
|
||||
MaterialIndexResponse,
|
||||
MaterialVersion,
|
||||
@@ -36,8 +37,10 @@ export const fetchMaterials = async (
|
||||
updatedFrom, updatedTo, sort, direction, page,
|
||||
tagId, includeDescendants, groupBy,
|
||||
limit }: FetchMaterialsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
|
||||
): Promise<MaterialIndexResponse> =>
|
||||
await apiGet ('/materials', { params: {
|
||||
await apiGet ('/materials', { locale, params: {
|
||||
...(q && { q }),
|
||||
tag_state: tagState,
|
||||
media_kind: mediaKind,
|
||||
@@ -54,10 +57,13 @@ export const fetchMaterials = async (
|
||||
limit} })
|
||||
|
||||
|
||||
export const fetchMaterial = async (id: string): Promise<Material | null> => {
|
||||
export const fetchMaterial = async (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Material | null> => {
|
||||
try
|
||||
{
|
||||
return await apiGet (`/materials/${ id }`)
|
||||
return await apiGet (`/materials/${ id }`, { locale })
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
@@ -75,8 +81,10 @@ export const fetchMaterialChanges = async (
|
||||
eventType?: string
|
||||
page: number
|
||||
limit: number
|
||||
}): Promise<MaterialChangesResponse> =>
|
||||
await apiGet ('/materials/versions', { params: {
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<MaterialChangesResponse> =>
|
||||
await apiGet ('/materials/versions', { locale, params: {
|
||||
...(materialId && { material_id: materialId }),
|
||||
...(tag && { tag }),
|
||||
...(eventType && { event_type: eventType }),
|
||||
@@ -85,19 +93,23 @@ export const fetchMaterialChanges = async (
|
||||
|
||||
|
||||
export const fetchMaterialTagTree = async (
|
||||
{ parentId, materialFilter }: FetchMaterialTreeParams): Promise<MaterialSidebarTag[]> =>
|
||||
await apiGet ('/tags/with-depth', { params: {
|
||||
{ parentId, materialFilter }: FetchMaterialTreeParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<MaterialSidebarTag[]> =>
|
||||
await apiGet ('/tags/with-depth', { locale, params: {
|
||||
...(parentId != null && { parent: String (parentId) }),
|
||||
material_filter: materialFilter} })
|
||||
|
||||
|
||||
export const fetchMaterialTagByName = async (
|
||||
name: string,
|
||||
materialFilter: MaterialFilter): Promise<MaterialTagTree | null> => {
|
||||
materialFilter: MaterialFilter,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<MaterialTagTree | null> => {
|
||||
try
|
||||
{
|
||||
return await apiGet (`/tags/name/${ encodeURIComponent (name) }/materials`,
|
||||
{ params: { material_filter: materialFilter } })
|
||||
{ locale, params: { material_filter: materialFilter } })
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
@@ -108,19 +120,26 @@ export const fetchMaterialTagByName = async (
|
||||
}
|
||||
|
||||
|
||||
export const createMaterial = async (formData: FormData): Promise<Material> =>
|
||||
await apiPost ('/materials', formData)
|
||||
export const createMaterial = async (
|
||||
formData: FormData,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Material> =>
|
||||
await apiPost ('/materials', formData, { locale })
|
||||
|
||||
|
||||
export const updateMaterial = async (
|
||||
id: string,
|
||||
formData: FormData): Promise<Material> =>
|
||||
await apiPut (`/materials/${ id }`, formData)
|
||||
formData: FormData,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Material> =>
|
||||
await apiPut (`/materials/${ id }`, formData, { locale })
|
||||
|
||||
|
||||
export const fetchMaterialSyncSuppressions =
|
||||
async (): Promise<MaterialSyncSuppressionResponse> =>
|
||||
await apiGet ('/materials/suppressions')
|
||||
async (
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<MaterialSyncSuppressionResponse> =>
|
||||
await apiGet ('/materials/suppressions', { locale })
|
||||
|
||||
|
||||
export const createMaterialSyncSuppression = async (
|
||||
@@ -130,10 +149,12 @@ export const createMaterialSyncSuppression = async (
|
||||
drivePath?: string
|
||||
driveFileId?: string
|
||||
reason: string
|
||||
}): Promise<MaterialSyncSuppression> =>
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<MaterialSyncSuppression> =>
|
||||
await apiPost ('/materials/suppressions', {
|
||||
source_kind: payload.sourceKind,
|
||||
source_uri: payload.sourceUri,
|
||||
drive_path: payload.drivePath,
|
||||
drive_file_id: payload.driveFileId,
|
||||
reason: payload.reason})
|
||||
reason: payload.reason}, { locale })
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
|
||||
const japanese = {
|
||||
square: '広場',
|
||||
list: '一覧',
|
||||
search: '検索',
|
||||
add: '追加',
|
||||
history: '履歴',
|
||||
allHistory: '全体履歴',
|
||||
help: 'ヘルプ',
|
||||
tags: 'タグ',
|
||||
master: 'マスタ',
|
||||
nicoLinks: 'ニコニコ連携',
|
||||
materials: '素材',
|
||||
suppressions: '抑止',
|
||||
wiki: 'Wiki',
|
||||
create: '新規',
|
||||
edit: '編輯',
|
||||
entertainment: 'おたのしみ',
|
||||
theatre: '上映会 (β)',
|
||||
gekanator: 'グカネータ (β)',
|
||||
users: 'ユーザ',
|
||||
currentUser: 'お前',
|
||||
settings: '設定',
|
||||
legal: '法規',
|
||||
terms: '利用規約' }
|
||||
|
||||
export type UIMessageKey = keyof typeof japanese
|
||||
|
||||
const ainu: Partial<Record<UIMessageKey, string>> = { }
|
||||
|
||||
|
||||
export const uiMessage = (locale: LocaleCode, key: UIMessageKey): string =>
|
||||
(locale === 'ain-Kana' ? ainu[key] : null) ?? japanese[key]
|
||||
|
||||
|
||||
export const uiMessageLanguage = (locale: LocaleCode, key: UIMessageKey): 'ja' | 'ain' =>
|
||||
(locale === 'ain-Kana' && ainu[key] != null) ? 'ain' : 'ja'
|
||||
+17
-7
@@ -1,14 +1,17 @@
|
||||
import { apiDelete, apiGet, apiPost, apiPut } from '@/lib/api'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { FetchPostsParams, Post, PostVersion } from '@/types'
|
||||
|
||||
|
||||
export const fetchPosts = async (
|
||||
{ url, title, tags, match, createdFrom, createdTo, updatedFrom, updatedTo,
|
||||
originalCreatedFrom, originalCreatedTo, page, limit, order }: FetchPostsParams): Promise<{
|
||||
originalCreatedFrom, originalCreatedTo, page, limit, order }: FetchPostsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{
|
||||
posts: Post[]
|
||||
count: number }> =>
|
||||
await apiGet ('/posts', { params: {
|
||||
await apiGet ('/posts', { locale, params: {
|
||||
...(url && { url }),
|
||||
...(title && { title }),
|
||||
...(tags && { tags }),
|
||||
@@ -24,7 +27,10 @@ export const fetchPosts = async (
|
||||
...(order && { order }) } })
|
||||
|
||||
|
||||
export const fetchPost = async (id: string): Promise<Post> => await apiGet (`/posts/${ id }`)
|
||||
export const fetchPost = async (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Post> => await apiGet (`/posts/${ id }`, { locale })
|
||||
|
||||
|
||||
export const fetchPostChanges = async (
|
||||
@@ -33,10 +39,12 @@ export const fetchPostChanges = async (
|
||||
tag?: string
|
||||
externalTag?: string
|
||||
page: number
|
||||
limit: number }): Promise<{
|
||||
limit: number },
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{
|
||||
versions: PostVersion[]
|
||||
count: number }> =>
|
||||
await apiGet ('/posts/versions', { params: { ...(post && { post }),
|
||||
await apiGet ('/posts/versions', { locale, params: { ...(post && { post }),
|
||||
...(tag && { tag }),
|
||||
...(externalTag && {
|
||||
external_tag: externalTag }),
|
||||
@@ -54,7 +62,9 @@ export const updatePost = async (
|
||||
{ baseVersionNo, force, merge }: {
|
||||
baseVersionNo?: number
|
||||
force?: boolean
|
||||
merge?: boolean }) =>
|
||||
merge?: boolean },
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
await apiPut<Post> (
|
||||
`/posts/${ post.id }`,
|
||||
{ title: post.title,
|
||||
@@ -63,7 +73,7 @@ export const updatePost = async (
|
||||
parent_post_ids: post.parentPostIds,
|
||||
original_created_from: post.originalCreatedFrom,
|
||||
original_created_before: post.originalCreatedBefore },
|
||||
{ params: { ...(baseVersionNo && { base_version_no: String (baseVersionNo) }),
|
||||
{ locale, params: { ...(baseVersionNo && { base_version_no: String (baseVersionNo) }),
|
||||
force: force ? '1' : '0',
|
||||
merge: merge ? '1' : '0' } })
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import { getHelpArticle,
|
||||
getHelpChanges,
|
||||
getHelpDiff,
|
||||
getHelpEditionContent,
|
||||
getHelpSearchEntries } from '@/lib/help'
|
||||
getHelpSearchEntries,
|
||||
resolveHelpLanguage } from '@/lib/help'
|
||||
import { localeFromPath, localeLanguage, unlocalisedPath } from '@/lib/locales'
|
||||
import { fetchPost, fetchPosts, fetchPostChanges } from '@/lib/posts'
|
||||
import { helpKeys, postsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys'
|
||||
import { fetchNicoTags, fetchTagByName, fetchTag, fetchTagChanges, fetchTags } from '@/lib/tags'
|
||||
@@ -13,9 +15,10 @@ import { fetchWikiPage,
|
||||
fetchWikiPageByTitle,
|
||||
fetchWikiPages } from '@/lib/wiki'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { Category, FetchPostsOrder, FetchTagsOrder } from '@/types'
|
||||
|
||||
type Prefetcher = (qc: QueryClient, url: URL) => Promise<void>
|
||||
type Prefetcher = (qc: QueryClient, url: URL, locale: LocaleCode) => Promise<void>
|
||||
|
||||
const mPost = match<{ id: string }> ('/posts/:id')
|
||||
const mWiki = match<{ title: string }> ('/wiki/:title')
|
||||
@@ -28,25 +31,25 @@ const boolFromQuery = (value: string | null): boolean =>
|
||||
['1', 'true', 'on', 'yes', ''].includes ((value ?? '').toLowerCase ())
|
||||
|
||||
|
||||
const prefetchHelpIndex: Prefetcher = async qc => {
|
||||
const prefetchHelpIndex: Prefetcher = async (qc, url, locale) => {
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: helpKeys.index ('ja'),
|
||||
queryFn: () => getHelpSearchEntries ('ja'),
|
||||
queryKey: helpKeys.index (localeLanguage (locale)),
|
||||
queryFn: () => getHelpSearchEntries (localeLanguage (locale)),
|
||||
staleTime: Infinity })
|
||||
}
|
||||
|
||||
|
||||
const prefetchHelpShow: Prefetcher = async (qc, url) => {
|
||||
const prefetchHelpShow: Prefetcher = async (qc, url, locale) => {
|
||||
const matched = mHelp (url.pathname)
|
||||
if (!(matched))
|
||||
return
|
||||
|
||||
const { identifier } = matched.params
|
||||
const language = 'ja'
|
||||
const article = await qc.fetchQuery ({
|
||||
queryKey: helpKeys.article (identifier),
|
||||
queryFn: () => getHelpArticle (identifier),
|
||||
staleTime: Infinity })
|
||||
const language = resolveHelpLanguage (article, localeLanguage (locale))
|
||||
const edition = url.searchParams.get ('edition') ?? article?.languages[language]?.currentEdition
|
||||
if (edition == null)
|
||||
return
|
||||
@@ -58,15 +61,15 @@ const prefetchHelpShow: Prefetcher = async (qc, url) => {
|
||||
}
|
||||
|
||||
|
||||
const prefetchHelpChanges: Prefetcher = async qc => {
|
||||
const prefetchHelpChanges: Prefetcher = async (qc, url, locale) => {
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: helpKeys.changes ('ja'),
|
||||
queryFn: () => getHelpChanges ('ja'),
|
||||
queryKey: helpKeys.changes (localeLanguage (locale)),
|
||||
queryFn: () => getHelpChanges (localeLanguage (locale)),
|
||||
staleTime: Infinity })
|
||||
}
|
||||
|
||||
|
||||
const prefetchHelpDiff: Prefetcher = async (qc, url) => {
|
||||
const prefetchHelpDiff: Prefetcher = async (qc, url, locale) => {
|
||||
const matched = mHelpDiff (url.pathname)
|
||||
const fromValues = url.searchParams.getAll ('from')
|
||||
const toValues = url.searchParams.getAll ('to')
|
||||
@@ -76,23 +79,28 @@ const prefetchHelpDiff: Prefetcher = async (qc, url) => {
|
||||
return
|
||||
|
||||
const { identifier } = matched.params
|
||||
const article = await qc.fetchQuery ({
|
||||
queryKey: helpKeys.article (identifier),
|
||||
queryFn: () => getHelpArticle (identifier),
|
||||
staleTime: Infinity })
|
||||
const language = resolveHelpLanguage (article, localeLanguage (locale))
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: helpKeys.diff (identifier, 'ja', from, to),
|
||||
queryFn: () => getHelpDiff (identifier, 'ja', from, to),
|
||||
queryKey: helpKeys.diff (identifier, language, from, to),
|
||||
queryFn: () => getHelpDiff (identifier, language, from, to),
|
||||
staleTime: Infinity })
|
||||
}
|
||||
|
||||
|
||||
const prefetchWikiPagesIndex: Prefetcher = async (qc, url) => {
|
||||
const prefetchWikiPagesIndex: Prefetcher = async (qc, url, locale) => {
|
||||
const title = url.searchParams.get ('title') ?? ''
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: wikiKeys.index ({ title }),
|
||||
queryFn: () => fetchWikiPages ({ title }) })
|
||||
queryKey: wikiKeys.index ({ title }, locale),
|
||||
queryFn: () => fetchWikiPages ({ title }, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchWikiPageShow: Prefetcher = async (qc, url) => {
|
||||
const prefetchWikiPageShow: Prefetcher = async (qc, url, locale) => {
|
||||
const m = mWiki (url.pathname)
|
||||
if (!(m))
|
||||
return
|
||||
@@ -101,29 +109,29 @@ const prefetchWikiPageShow: Prefetcher = async (qc, url) => {
|
||||
const version = url.searchParams.get ('version') || undefined
|
||||
|
||||
const wikiPage = await qc.fetchQuery ({
|
||||
queryKey: wikiKeys.show (title, { version }),
|
||||
queryFn: () => fetchWikiPageByTitle (title, { version }) })
|
||||
queryKey: wikiKeys.show (title, { version }, locale),
|
||||
queryFn: () => fetchWikiPageByTitle (title, { version }, locale) })
|
||||
|
||||
if (wikiPage)
|
||||
{
|
||||
const effectiveId = String (wikiPage.id ?? '')
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: wikiKeys.show (effectiveId, { }),
|
||||
queryFn: () => fetchWikiPage (effectiveId, { } ) })
|
||||
queryKey: wikiKeys.show (effectiveId, { }, locale),
|
||||
queryFn: () => fetchWikiPage (effectiveId, { }, locale ) })
|
||||
|
||||
if (wikiPage.body)
|
||||
{
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: wikiKeys.index ({ }),
|
||||
queryFn: () => fetchWikiPages ({ }) })
|
||||
queryKey: wikiKeys.index ({ }, locale),
|
||||
queryFn: () => fetchWikiPages ({ }, locale) })
|
||||
}
|
||||
}
|
||||
|
||||
const effectiveTitle = wikiPage?.title ?? title
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: tagsKeys.show (effectiveTitle),
|
||||
queryFn: () => fetchTagByName (effectiveTitle) })
|
||||
queryKey: tagsKeys.show (effectiveTitle, locale),
|
||||
queryFn: () => fetchTagByName (effectiveTitle, locale) })
|
||||
|
||||
if (version)
|
||||
return
|
||||
@@ -143,12 +151,12 @@ const prefetchWikiPageShow: Prefetcher = async (qc, url) => {
|
||||
updatedTo: '',
|
||||
order: 'original_created_at:desc' } as const
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: postsKeys.index (p),
|
||||
queryFn: () => fetchPosts (p) })
|
||||
queryKey: postsKeys.index (p, locale),
|
||||
queryFn: () => fetchPosts (p, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchPostsIndex: Prefetcher = async (qc, url) => {
|
||||
const prefetchPostsIndex: Prefetcher = async (qc, url, locale) => {
|
||||
const tags = url.searchParams.get ('tags') ?? ''
|
||||
const qURL = url.searchParams.get ('url') ?? ''
|
||||
const title = url.searchParams.get ('title') ?? ''
|
||||
@@ -169,12 +177,12 @@ const prefetchPostsIndex: Prefetcher = async (qc, url) => {
|
||||
updatedFrom, updatedTo, order }
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: postsKeys.index (keys),
|
||||
queryFn: () => fetchPosts (keys) })
|
||||
queryKey: postsKeys.index (keys, locale),
|
||||
queryFn: () => fetchPosts (keys, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchPostShow: Prefetcher = async (qc, url) => {
|
||||
const prefetchPostShow: Prefetcher = async (qc, url, locale) => {
|
||||
const m = mPost (url.pathname)
|
||||
if (!(m))
|
||||
return
|
||||
@@ -182,12 +190,12 @@ const prefetchPostShow: Prefetcher = async (qc, url) => {
|
||||
const { id } = m.params
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: postsKeys.show (id),
|
||||
queryFn: () => fetchPost (id) })
|
||||
queryKey: postsKeys.show (id, locale),
|
||||
queryFn: () => fetchPost (id, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchPostChanges: Prefetcher = async (qc, url) => {
|
||||
const prefetchPostChanges: Prefetcher = async (qc, url, locale) => {
|
||||
const id = url.searchParams.get ('id')
|
||||
const tag = url.searchParams.get ('tag')
|
||||
const externalTag = url.searchParams.get ('external_tag')
|
||||
@@ -197,23 +205,23 @@ const prefetchPostChanges: Prefetcher = async (qc, url) => {
|
||||
if (tag)
|
||||
{
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: tagsKeys.show (tag),
|
||||
queryFn: () => fetchTag (tag) })
|
||||
queryKey: tagsKeys.show (tag, locale),
|
||||
queryFn: () => fetchTag (tag, locale) })
|
||||
}
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: postsKeys.changes ({ ...(id && { id }),
|
||||
...(tag && { tag }),
|
||||
...(externalTag && { externalTag }),
|
||||
page, limit }),
|
||||
page, limit }, locale),
|
||||
queryFn: () => fetchPostChanges ({ ...(id && { id }),
|
||||
...(tag && { tag }),
|
||||
...(externalTag && { externalTag }),
|
||||
page, limit }) })
|
||||
page, limit }, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchTagsIndex: Prefetcher = async (qc, url) => {
|
||||
const prefetchTagsIndex: Prefetcher = async (qc, url, locale) => {
|
||||
const postRaw = url.searchParams.get ('post')
|
||||
const post = postRaw ? Number (postRaw) : null
|
||||
const name = url.searchParams.get ('name') ?? ''
|
||||
@@ -237,12 +245,12 @@ const prefetchTagsIndex: Prefetcher = async (qc, url) => {
|
||||
updatedFrom, updatedTo, deprecated, page, limit, order }
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: tagsKeys.index (keys),
|
||||
queryFn: () => fetchTags (keys) })
|
||||
queryKey: tagsKeys.index (keys, locale),
|
||||
queryFn: () => fetchTags (keys, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchNicoTagsIndex: Prefetcher = async (qc, url) => {
|
||||
const prefetchNicoTagsIndex: Prefetcher = async (qc, url, locale) => {
|
||||
const keys = {
|
||||
name: url.searchParams.get ('name') ?? '',
|
||||
linkedTag: url.searchParams.get ('linked_tag') ?? '',
|
||||
@@ -255,12 +263,12 @@ const prefetchNicoTagsIndex: Prefetcher = async (qc, url) => {
|
||||
| 'updated_at:asc' | 'updated_at:desc' }
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: tagsKeys.nicoIndex (keys),
|
||||
queryFn: () => fetchNicoTags (keys) })
|
||||
queryKey: tagsKeys.nicoIndex (keys, locale),
|
||||
queryFn: () => fetchNicoTags (keys, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchTagShow: Prefetcher = async (qc, url) => {
|
||||
const prefetchTagShow: Prefetcher = async (qc, url, locale) => {
|
||||
const m = mTag (url.pathname)
|
||||
if (!(m))
|
||||
return
|
||||
@@ -268,19 +276,19 @@ const prefetchTagShow: Prefetcher = async (qc, url) => {
|
||||
const { id } = m.params
|
||||
|
||||
await qc.prefetchQuery ({
|
||||
queryKey: tagsKeys.show (id),
|
||||
queryFn: () => fetchTag (id) })
|
||||
queryKey: tagsKeys.show (id, locale),
|
||||
queryFn: () => fetchTag (id, locale) })
|
||||
}
|
||||
|
||||
|
||||
const prefetchTagChanges: Prefetcher = async (qc, url) => {
|
||||
const prefetchTagChanges: Prefetcher = async (qc, url, locale) => {
|
||||
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: tagsKeys.changes ({ ...(id && { id }), page, limit }),
|
||||
queryFn: () => fetchTagChanges ({ ...(id && { id }), page, limit }) })
|
||||
queryKey: tagsKeys.changes ({ ...(id && { id }), page, limit }, locale),
|
||||
queryFn: () => fetchTagChanges ({ ...(id && { id }), page, limit }, locale) })
|
||||
}
|
||||
|
||||
|
||||
@@ -313,9 +321,14 @@ export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[]
|
||||
|
||||
export const prefetchForURL = async (qc: QueryClient, urlLike: string): Promise<void> => {
|
||||
const u = new URL (urlLike, location.origin)
|
||||
if (u.origin !== location.origin)
|
||||
return
|
||||
|
||||
const locale = localeFromPath (u.pathname) ?? 'ja'
|
||||
u.pathname = unlocalisedPath (u.pathname)
|
||||
const r = routePrefetchers.find (x => x.test (u))
|
||||
if (!(r))
|
||||
return
|
||||
|
||||
await r.run (qc, u)
|
||||
await r.run (qc, u, locale)
|
||||
}
|
||||
|
||||
+97
-30
@@ -1,3 +1,4 @@
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type {
|
||||
FetchNicoTagsParams,
|
||||
FetchMaterialsParams,
|
||||
@@ -8,59 +9,125 @@ import type {
|
||||
|
||||
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: { post?: string
|
||||
index: (
|
||||
p: FetchPostsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['posts', locale, 'index', p] as const,
|
||||
show: (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['posts', locale, id] as const,
|
||||
related: (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['related', locale, id] as const,
|
||||
changes: (
|
||||
p: { post?: string
|
||||
tag?: string
|
||||
externalTag?: string
|
||||
page: number
|
||||
limit: number }) =>
|
||||
['posts', 'changes', p] as const }
|
||||
limit: number },
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
['posts', locale, 'changes', p] as const }
|
||||
|
||||
export const gekanatorKeys = {
|
||||
root: ['gekanator'] as const,
|
||||
posts: () => ['gekanator', 'posts'] as const,
|
||||
questions: () => ['gekanator', 'questions'] as const,
|
||||
extraQuestions: (gameId: number, nonce: string) =>
|
||||
['gekanator', 'games', gameId, 'extra-questions', nonce] as const }
|
||||
posts: (
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['gekanator', locale, 'posts'] as const,
|
||||
questions: (
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['gekanator', locale, 'questions'] as const,
|
||||
extraQuestions: (
|
||||
gameId: number,
|
||||
nonce: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
['gekanator', locale, 'games', gameId, 'extra-questions', nonce] as const }
|
||||
|
||||
export const tagsKeys = {
|
||||
root: ['tags'] as const,
|
||||
index: (p: FetchTagsParams) => ['tags', 'index', p] as const,
|
||||
index: (
|
||||
p: FetchTagsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['tags', locale, '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,
|
||||
deerjikists: (id: string) => ['tags', 'deerjikists', id] as const }
|
||||
nicoIndex: (
|
||||
p: FetchNicoTagsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['tags', locale, 'nico', 'index', p] as const,
|
||||
externalShow: (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['tags', locale, 'nico', id] as const,
|
||||
show: (
|
||||
name: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['tags', locale, name] as const,
|
||||
changes: (
|
||||
p: { id?: string; page: number; limit: number },
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
['tags', locale, 'changes', p] as const,
|
||||
deerjikists: (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['tags', locale, 'deerjikists', id] as const }
|
||||
|
||||
export const materialsKeys = {
|
||||
root: ['materials'] as const,
|
||||
index: (p: FetchMaterialsParams) => ['materials', 'index', p] as const,
|
||||
changes: (p: {
|
||||
index: (
|
||||
p: FetchMaterialsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['materials', locale, 'index', p] as const,
|
||||
changes: (
|
||||
p: {
|
||||
materialId?: string
|
||||
tag?: string
|
||||
eventType?: string
|
||||
page: number
|
||||
limit: number
|
||||
}) => ['materials', 'changes', p] as const,
|
||||
byTagName: (name: string, materialFilter: MaterialFilter) =>
|
||||
['materials', 'tag', name, materialFilter] as const,
|
||||
show: (id: string) => ['materials', id] as const,
|
||||
suppressions: () => ['materials', 'suppressions'] as const,
|
||||
tree: (p: {
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['materials', locale, 'changes', p] as const,
|
||||
byTagName: (
|
||||
name: string,
|
||||
materialFilter: MaterialFilter,
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
['materials', locale, 'tag', name, materialFilter] as const,
|
||||
show: (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['materials', locale, id] as const,
|
||||
suppressions: (
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['materials', locale, 'suppressions'] as const,
|
||||
tree: (
|
||||
p: {
|
||||
parentId?: number | null
|
||||
materialFilter: MaterialFilter
|
||||
}) => ['materials', 'tree', p] as const,
|
||||
unclassified: (p: { page?: number; limit?: number } = { }) =>
|
||||
['materials', 'unclassified', p] as const}
|
||||
},
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['materials', locale, 'tree', p] as const,
|
||||
unclassified: (
|
||||
p: { page?: number; limit?: number } = { },
|
||||
locale: LocaleCode = 'ja',
|
||||
) =>
|
||||
['materials', locale, 'unclassified', p] as const}
|
||||
|
||||
export const wikiKeys = {
|
||||
root: ['wiki'] as const,
|
||||
index: (p: { title?: string }) => ['wiki', 'index', p] as const,
|
||||
show: (title: string, p: { version?: string }) => ['wiki', title, p] as const }
|
||||
index: (
|
||||
p: { title?: string },
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['wiki', locale, 'index', p] as const,
|
||||
show: (
|
||||
title: string,
|
||||
p: { version?: string },
|
||||
locale: LocaleCode = 'ja',
|
||||
) => ['wiki', locale, title, p] as const }
|
||||
|
||||
export const helpKeys = {
|
||||
root: ['help'] as const,
|
||||
|
||||
+32
-14
@@ -2,6 +2,7 @@ import { isAxiosError } from 'axios'
|
||||
|
||||
import { apiGet } from '@/lib/api'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { Deerjikist,
|
||||
FetchNicoTagsParams,
|
||||
FetchTagsParams,
|
||||
@@ -13,9 +14,11 @@ import type { Deerjikist,
|
||||
export const fetchTags = async (
|
||||
{ post, name, category, postCountGTE, postCountLTE, createdFrom, createdTo,
|
||||
updatedFrom, updatedTo, deprecated,
|
||||
page, limit, order }: FetchTagsParams): Promise<{ tags: Tag[]
|
||||
page, limit, order }: FetchTagsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{ tags: Tag[]
|
||||
count: number }> =>
|
||||
await apiGet ('/tags', { params: {
|
||||
await apiGet ('/tags', { locale, params: {
|
||||
...(post != null && { post }),
|
||||
...(name && { name }),
|
||||
...(category && { category }),
|
||||
@@ -32,9 +35,11 @@ export const fetchTags = async (
|
||||
|
||||
|
||||
export const fetchNicoTags = async (
|
||||
{ name, linkedTag, linkStatus, page, limit, order }: FetchNicoTagsParams): Promise<{ tags: NicoTag[]
|
||||
{ name, linkedTag, linkStatus, page, limit, order }: FetchNicoTagsParams,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{ tags: NicoTag[]
|
||||
count: number }> =>
|
||||
await apiGet ('/tags/nico', { params: {
|
||||
await apiGet ('/tags/nico', { locale, params: {
|
||||
page,
|
||||
limit,
|
||||
name,
|
||||
@@ -43,10 +48,13 @@ export const fetchNicoTags = async (
|
||||
order } })
|
||||
|
||||
|
||||
export const fetchTag = async (id: string): Promise<Tag | null> => {
|
||||
export const fetchTag = async (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Tag | null> => {
|
||||
try
|
||||
{
|
||||
return await apiGet (`/tags/${ id }`)
|
||||
return await apiGet (`/tags/${ id }`, { locale })
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -55,10 +63,13 @@ export const fetchTag = async (id: string): Promise<Tag | null> => {
|
||||
}
|
||||
|
||||
|
||||
export const fetchExternalTag = async (id: string): Promise<Tag | null> => {
|
||||
export const fetchExternalTag = async (
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Tag | null> => {
|
||||
try
|
||||
{
|
||||
return await apiGet (`/tags/nico/${ id }`)
|
||||
return await apiGet (`/tags/nico/${ id }`, { locale })
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -67,10 +78,13 @@ export const fetchExternalTag = async (id: string): Promise<Tag | null> => {
|
||||
}
|
||||
|
||||
|
||||
export const fetchTagByName = async (name: string): Promise<Tag | null> => {
|
||||
export const fetchTagByName = async (
|
||||
name: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<Tag | null> => {
|
||||
try
|
||||
{
|
||||
return await apiGet (`/tags/name/${ encodeURIComponent (name) }`)
|
||||
return await apiGet (`/tags/name/${ encodeURIComponent (name) }`, { locale })
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
@@ -84,12 +98,16 @@ export const fetchTagChanges = async (
|
||||
{ id, page, limit }: {
|
||||
id?: string
|
||||
page: number
|
||||
limit: number }): Promise<{
|
||||
limit: number },
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{
|
||||
versions: TagVersion[]
|
||||
count: number }> =>
|
||||
await apiGet ('/tags/versions', { params: { ...(id && { id }), page, limit } })
|
||||
await apiGet ('/tags/versions', { locale, params: { ...(id && { id }), page, limit } })
|
||||
|
||||
|
||||
export const fetchDeerjikistsByTag = async (
|
||||
id: string): Promise<{ tag: Tag; deerjikists: Deerjikist[] }> =>
|
||||
await apiGet (`/tags/${ id }/deerjikists`)
|
||||
id: string,
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<{ tag: Tag; deerjikists: Deerjikist[] }> =>
|
||||
await apiGet (`/tags/${ id }/deerjikists`, { locale })
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useLocaleNavigate } from '@/lib/useLocale'
|
||||
import ShortcutHelpDialog from '@/components/users/ShortcutHelpDialog'
|
||||
import {
|
||||
DEFAULT_KEY_BINDINGS,
|
||||
@@ -95,7 +96,7 @@ const focusSearchTarget = (): void => {
|
||||
|
||||
export const KeyboardShortcutsProvider = ({ children }: PropsWithChildren) => {
|
||||
const location = useLocation ()
|
||||
const navigate = useNavigate ()
|
||||
const navigate = useLocaleNavigate ()
|
||||
|
||||
const [keyboardSettings, setKeyboardSettingsState] =
|
||||
useState<ClientKeyboardSettings> (() => getClientKeyboardSettings ())
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
|
||||
import { isLocaleCode, localeFromPath, localiseTo } from '@/lib/locales'
|
||||
|
||||
import type { NavigateFunction, NavigateOptions, To } from 'react-router-dom'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
|
||||
|
||||
export const useLocale = (): LocaleCode => {
|
||||
const { locale } = useParams ()
|
||||
const location = useLocation ()
|
||||
return isLocaleCode (locale) ? locale : localeFromPath (location.pathname) ?? 'ja'
|
||||
}
|
||||
|
||||
|
||||
export const useLocaleNavigate = (): NavigateFunction => {
|
||||
const navigate = useNavigate ()
|
||||
const locale = useLocale ()
|
||||
return useCallback ((to: To | number, options?: NavigateOptions) => {
|
||||
if (typeof to === 'number')
|
||||
navigate (to)
|
||||
else
|
||||
navigate (localiseTo (to, locale), options)
|
||||
}, [locale, navigate])
|
||||
}
|
||||
+13
-6
@@ -2,26 +2,33 @@ import { isAxiosError } from 'axios'
|
||||
|
||||
import { apiGet } from '@/lib/api'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { WikiPage } from '@/types'
|
||||
|
||||
|
||||
export const fetchWikiPages = async (
|
||||
{ title }: { title?: string }): Promise<WikiPage[]> =>
|
||||
await apiGet ('/wiki', { params: { title } })
|
||||
{ title }: { title?: string },
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<WikiPage[]> =>
|
||||
await apiGet ('/wiki', { locale, params: { title } })
|
||||
|
||||
|
||||
export const fetchWikiPage = async (
|
||||
id: string,
|
||||
{ version }: { version?: string }): Promise<WikiPage> =>
|
||||
await apiGet (`/wiki/${ id }`, { params: version ? { version } : { } })
|
||||
{ version }: { version?: string },
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<WikiPage> =>
|
||||
await apiGet (`/wiki/${ id }`, { locale, params: version ? { version } : { } })
|
||||
|
||||
|
||||
export const fetchWikiPageByTitle = async (
|
||||
title: string,
|
||||
{ version }: { version?: string }): Promise<WikiPage | null> => {
|
||||
{ version }: { version?: string },
|
||||
locale: LocaleCode = 'ja',
|
||||
): Promise<WikiPage | null> => {
|
||||
try
|
||||
{
|
||||
return await apiGet (`/wiki/title/${ encodeURIComponent (title) }`, { params: { version } })
|
||||
return await apiGet (`/wiki/title/${ encodeURIComponent (title) }`, { locale, params: { version } })
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする