90 行
3.2 KiB
TypeScript
90 行
3.2 KiB
TypeScript
import { createContext, useContext } from 'react'
|
|
|
|
import { translateUIMessage } from '@/lib/i18n'
|
|
import { apiGet } from '@/lib/api'
|
|
|
|
import type { LocaleCode } from '@/lib/locales'
|
|
import type { CountField, CountsResource } from '@/lib/counts'
|
|
|
|
export type CountsAxis = { field: string; grain?: string }
|
|
export type CountsCondition = {
|
|
field: string; operator: string; value?: string; end?: string; blank?: boolean }
|
|
export type CountsFieldType = 'string' | 'integer' | 'datetime' | 'enum' | 'json' | 'boolean'
|
|
|
|
export const countsResources: CountsResource[] = [
|
|
'posts', 'tags', 'nico_tags', 'tag_descriptions', 'tag_groups', 'materials']
|
|
export type CountsFieldMetadata = {
|
|
key: string; type: CountsFieldType; nullable: boolean; operators: string[] }
|
|
export type CountsDefinition = {
|
|
fields: CountsFieldMetadata[]
|
|
axes: { latest: string[]; history: string[] }
|
|
grains: string[] }
|
|
|
|
export const CountsDefinitionContext = createContext<CountsDefinition | null> (null)
|
|
|
|
|
|
export const useCountsDefinition = (): CountsDefinition => {
|
|
const definition = useContext (CountsDefinitionContext)
|
|
if (definition == null) throw new Error ('Missing Counts definition')
|
|
return definition
|
|
}
|
|
|
|
|
|
export const fetchCountsDefinition = (resource: CountsResource): Promise<CountsDefinition> =>
|
|
apiGet (`/counts/definitions/${encodeURIComponent (resource)}`)
|
|
|
|
|
|
export const countsFilterFields = (
|
|
definition: CountsDefinition,
|
|
): Record<string, CountsFieldType> => Object.fromEntries (
|
|
definition.fields.map (field => [field.key, field.type]))
|
|
|
|
|
|
export const countsFieldLabel = (key: string): string => {
|
|
const labels: Record<string, string> = {
|
|
resource_id: 'columns.resourceId', version_no: 'history:version',
|
|
user: 'history:actor', created_at: 'columns.changedAt', name: 'history:name',
|
|
title: 'columns.postTitle', url: 'columns.postURL', thumbnail_base: 'columns.thumbnailURL',
|
|
tags: 'columns.tags', tags_json: 'columns.tags', names: 'columns.names',
|
|
aliases: 'tags:aliases', category: 'common:categoryLabel',
|
|
language_code: 'columns.language', source_language_code: 'attributes.sourceLanguage',
|
|
changed_field: 'changedField' }
|
|
return labels[key] ?? `attributes.${key}`
|
|
}
|
|
|
|
|
|
export const countsEventTypes = ['create', 'update', 'discard', 'restore'] as const
|
|
|
|
|
|
export const countsAxisLabel = (
|
|
axis: CountsAxis,
|
|
definition: CountsDefinition,
|
|
locale: LocaleCode,
|
|
): { text: string; language: string } => {
|
|
const field = translateUIMessage (countsFieldLabel (axis.field), 'counts', locale)
|
|
if (axis.grain == null || definition.fields.find (item => item.key === axis.field)
|
|
?.type !== 'datetime') return field
|
|
return translateUIMessage ('axisWithGrain', 'counts', locale, {
|
|
field: field.text,
|
|
grain: translateUIMessage (`grains.${axis.grain}`, 'counts', locale).text })
|
|
}
|
|
|
|
|
|
export const countsAxisFields = (
|
|
definition: CountsDefinition,
|
|
history: boolean,
|
|
): CountField[] => definition.axes[history ? 'history' : 'latest']
|
|
.map (key => ({ key, label: countsFieldLabel (key) }))
|
|
|
|
|
|
export const readCountsJSON = <T,> (params: URLSearchParams, key: string, fallback: T): T => {
|
|
try
|
|
{
|
|
return JSON.parse (params.get (key) ?? JSON.stringify (fallback)) as T
|
|
}
|
|
catch
|
|
{
|
|
return fallback
|
|
}
|
|
}
|