このコミットが含まれているのは:
+61
-34
@@ -1,9 +1,11 @@
|
||||
import { apiGet, apiPost } from '@/lib/api'
|
||||
import { countsAttributes, countsFieldLabel } from '@/lib/countsDefinitions'
|
||||
import { apiDownload, apiGet } from '@/lib/api'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { TagNameSnapshot } from '@/types'
|
||||
|
||||
export type CountsResource = 'posts' | 'tags'
|
||||
export type CountsResource = 'posts' | 'tags' | 'nico_tags'
|
||||
| 'tag_descriptions' | 'tag_groups' | 'materials'
|
||||
export type CountsName = TagNameSnapshot & {
|
||||
languageCode: string; languageName?: string | null; scriptName?: string | null }
|
||||
export type CountsTag = {
|
||||
@@ -17,6 +19,8 @@ export type CountsTag = {
|
||||
sections: { beginMs: number; endMs: number | null }[]
|
||||
names: CountsName[] }
|
||||
export type CountsVersion = {
|
||||
[key: string]: unknown
|
||||
eventType?: string
|
||||
id: number
|
||||
resourceId: number
|
||||
versionNo: number
|
||||
@@ -51,6 +55,12 @@ export type CountsStateTotals = {
|
||||
deprecatedTags: number | null
|
||||
externalAssignments: number | null }
|
||||
export type CountsResult = {
|
||||
executionAt?: string
|
||||
columnValues?: (string | number | null)[][]
|
||||
cells?: { values: (string | number | null)[]; count: number }[]
|
||||
axisLabels?: Record<string, Record<string, string | null>>
|
||||
rows?: { field: string; grain?: string }[]
|
||||
cols?: { field: string; grain?: string }[]
|
||||
count: number
|
||||
page: number
|
||||
limit: number
|
||||
@@ -69,7 +79,8 @@ export type CountSort = { key: string; direction: 'asc' | 'desc' }
|
||||
export type CountField = { key: string; label: string; children?: CountField[] }
|
||||
export type CountRecord = Record<string, unknown>
|
||||
|
||||
export const countColumns = {
|
||||
export const countColumns: Record<string, string> = {
|
||||
event_type: 'counts:eventType',
|
||||
thumbnail: 'counts:columns.thumbnailDisplay', resource_id: 'counts:columns.resourceId',
|
||||
version_no: 'history:version', is_latest: 'counts:columns.latest',
|
||||
title: 'counts:columns.postTitle', url: 'counts:columns.postURL',
|
||||
@@ -82,7 +93,7 @@ export const countColumns = {
|
||||
category: 'common:categoryLabel', aliases: 'tags:aliases',
|
||||
deprecated: 'counts:columns.deprecated', user: 'history:actor',
|
||||
created_at: 'counts:columns.changedAt' }
|
||||
export type CountColumn = keyof typeof countColumns
|
||||
export type CountColumn = string
|
||||
|
||||
const aliasFields: CountField[] = [
|
||||
{ key: 'name', label: 'history:name' },
|
||||
@@ -93,7 +104,24 @@ const nameFields: CountField[] = [
|
||||
{ key: 'name', label: 'history:name' },
|
||||
{ key: 'aliases', label: 'tags:aliases', children: aliasFields },
|
||||
{ key: 'autoGenerated', label: 'history:nameChanges.automatic' }]
|
||||
const placementFields = (depth: number): CountField[] => [
|
||||
{ key: 'sectionName', label: 'counts:attributes.section_name' },
|
||||
{ key: 'description', label: 'counts:attributes.description' },
|
||||
{ key: 'tagId', label: 'counts:attributes.tag_id' },
|
||||
{ key: 'tagGroupId', label: 'counts:resources.tag_groups' },
|
||||
...(depth > 0 ? [{ key: 'children', label: 'counts:attributes.children',
|
||||
children: placementFields (depth - 1) }] : [])]
|
||||
|
||||
|
||||
const nested: Partial<Record<CountColumn, CountField[]>> = {
|
||||
placements: placementFields (4),
|
||||
export_paths_json: [
|
||||
{ key: 'profile', label: 'counts:attributes.profile' },
|
||||
{ key: 'path', label: 'counts:attributes.path' }],
|
||||
members: [
|
||||
{ key: 'tagId', label: 'counts:columns.resourceId' },
|
||||
{ key: 'tagGroupId', label: 'counts:resources.tag_groups' },
|
||||
{ key: 'versionNo', label: 'history:version' }],
|
||||
tags: [
|
||||
{ key: 'id', label: 'counts:columns.resourceId' },
|
||||
{ key: 'versionNo', label: 'history:version' },
|
||||
@@ -108,19 +136,22 @@ const nested: Partial<Record<CountColumn, CountField[]>> = {
|
||||
|
||||
|
||||
export const columnsForCounts = (resource: CountsResource): CountColumn[] => [
|
||||
...(resource === 'posts' ? ['thumbnail'] as const : []),
|
||||
...(resource === 'posts' ? ['thumbnail'] : []),
|
||||
'resource_id', 'version_no', 'is_latest',
|
||||
...(resource === 'posts'
|
||||
? ['title', 'url', 'thumbnail_base', 'tags', 'parents', 'video_ms',
|
||||
'original_created_at', 'original_created_from', 'original_created_before'] as const
|
||||
: ['name', 'names', 'category', 'aliases', 'parents', 'deprecated'] as const),
|
||||
'user', 'created_at']
|
||||
? ['title', 'url', 'thumbnail_base', 'tags', 'parents', 'video_ms',
|
||||
'original_created_at', 'original_created_from', 'original_created_before']
|
||||
: (resource === 'tags'
|
||||
? ['name', 'names', 'category', 'aliases', 'parents', 'deprecated']
|
||||
: Object.keys (countsAttributes[resource]))),
|
||||
'event_type', 'user', 'created_at']
|
||||
|
||||
|
||||
export const fieldsForCounts = (resource: CountsResource): CountField[] =>
|
||||
columnsForCounts (resource).map (key => ({
|
||||
key,
|
||||
label: (key === 'parents' && resource === 'tags') ? 'tags:parents' : countColumns[key],
|
||||
label: (key === 'parents' && resource === 'tags') ? 'tags:parents'
|
||||
: (countColumns[key] ?? countsFieldLabel (key)),
|
||||
children: nested[key] }))
|
||||
|
||||
|
||||
@@ -185,7 +216,7 @@ export const countChildPaths = (fields: CountField[], prefix = ''): string[] =>
|
||||
export const selectedCountChildren = (params: URLSearchParams): Set<string> =>
|
||||
new Set (params.has ('children') ? params.get ('children')!.split (',') :
|
||||
Object.entries (nested).flatMap (([key, fields]) =>
|
||||
countChildPaths (fields, key)))
|
||||
countChildPaths (fields ?? [], key)))
|
||||
|
||||
|
||||
export const readCountSort = (value: string | null): CountSort[] =>
|
||||
@@ -234,24 +265,22 @@ export const normaliseCountsParams = (
|
||||
source: URLSearchParams,
|
||||
): URLSearchParams => {
|
||||
const params = new URLSearchParams (source)
|
||||
params.delete ('event_type')
|
||||
if (!params.has ('definition'))
|
||||
params.set ('definition', params.get ('basis') === 'history' ? 'history' : 'latest')
|
||||
const legacy = params.get ('mode')
|
||||
if (['state', 'trend', 'snapshot'].includes (legacy ?? ''))
|
||||
{
|
||||
params.set ('mode', legacy === 'snapshot' ? 'details' : 'summary')
|
||||
if (legacy !== 'snapshot') params.set ('axis', resource === 'posts' ? 'tags' : 'month')
|
||||
if (legacy === 'trend')
|
||||
{
|
||||
if (params.get ('before')) params.set ('at', params.get ('before')!)
|
||||
params.delete ('from')
|
||||
params.delete ('before')
|
||||
}
|
||||
}
|
||||
if (!params.has ('mode')) params.set ('mode', 'summary')
|
||||
const axes = (resource === 'posts' && params.get ('basis') !== 'history')
|
||||
? ['tags', 'month', 'user'] : ['month', 'user']
|
||||
if (!axes.includes (params.get ('axis') ?? '')) params.set ('axis', axes[0])
|
||||
params.delete ('grain')
|
||||
if (!params.has ('rows') && params.has ('axis'))
|
||||
{
|
||||
const field = params.get ('axis') === 'month' ? 'created_at' : params.get ('axis')!
|
||||
params.set ('rows', JSON.stringify ([{
|
||||
field, ...(field === 'created_at' ? { grain: 'month' } : { }) }]))
|
||||
}
|
||||
if (params.has ('before') && !params.has ('through'))
|
||||
params.set ('through', params.get ('before')!)
|
||||
for (const key of ['basis', 'axis', 'latest', 'grain', 'before', 'summary_columns'])
|
||||
params.delete (key)
|
||||
if (params.has ('columns'))
|
||||
{
|
||||
const selected = new Set (params.get ('columns')!.split (','))
|
||||
@@ -263,14 +292,13 @@ export const normaliseCountsParams = (
|
||||
|
||||
|
||||
export const isCountsState = (params: URLSearchParams): boolean =>
|
||||
params.get ('basis') !== 'history'
|
||||
params.get ('definition') !== 'history'
|
||||
|
||||
|
||||
export const countsRequestParams = (params: URLSearchParams): URLSearchParams => {
|
||||
const result = new URLSearchParams (params)
|
||||
const hidden = isCountsState (params) ? ['latest'] : ['kind']
|
||||
for (const key of ['columns', 'summary_columns', 'children', 'child_sort',
|
||||
'nested_json', 'order', 'basis', 'grain', ...hidden])
|
||||
'nested_json', 'order', 'basis', 'axis', 'latest', 'grain'])
|
||||
result.delete (key)
|
||||
return result
|
||||
}
|
||||
@@ -280,10 +308,9 @@ export const fetchCounts = (
|
||||
resource: CountsResource,
|
||||
params: URLSearchParams,
|
||||
locale: LocaleCode,
|
||||
): Promise<CountsResult> => apiGet (
|
||||
isCountsState (params) ? '/counts/state' : `/counts/${resource}`, {
|
||||
locale, params: { ...Object.fromEntries (countsRequestParams (params)),
|
||||
...(isCountsState (params) ? { resource } : { }) } })
|
||||
): Promise<CountsResult> => apiGet ('/counts/query', {
|
||||
locale, params: { ...Object.fromEntries (countsRequestParams (params)), resource } })
|
||||
|
||||
|
||||
|
||||
export const exportCounts = (
|
||||
@@ -294,10 +321,10 @@ export const exportCounts = (
|
||||
columns: string[],
|
||||
headers: string[],
|
||||
labels: Record<string, string>,
|
||||
): Promise<Blob> => apiPost ('/counts/export', {
|
||||
): Promise<void> => apiDownload ('/counts/export', {
|
||||
...Object.fromEntries (params), resource, format,
|
||||
columns: columns.join (','), headers: JSON.stringify (headers),
|
||||
labels: JSON.stringify (labels) }, { locale, responseType: 'blob' })
|
||||
labels: JSON.stringify (labels) }, locale)
|
||||
|
||||
|
||||
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする