diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 9386365113..23c17649c7 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -27,47 +27,34 @@ export const fetchEmojisHostTypes = [ ] as const; export type FetchEmojisHostTypes = typeof fetchEmojisHostTypes[number]; export const fetchEmojisSortKeys = [ - 'id', - 'updatedAt', - 'name', - 'host', - 'uri', - 'publicUrl', - 'type', - 'aliases', - 'category', - 'license', - 'isSensitive', - 'localOnly', - 'roleIdsThatCanBeUsedThisEmojiAsReaction', + '+id', + '-id', + '+updatedAt', + '-updatedAt', + '+name', + '-name', + '+host', + '-host', + '+uri', + '-uri', + '+publicUrl', + '-publicUrl', + '+type', + '-type', + '+aliases', + '-aliases', + '+category', + '-category', + '+license', + '-license', + '+isSensitive', + '-isSensitive', + '+localOnly', + '-localOnly', + '+roleIdsThatCanBeUsedThisEmojiAsReaction', + '-roleIdsThatCanBeUsedThisEmojiAsReaction', ] as const; export type FetchEmojisSortKeys = typeof fetchEmojisSortKeys[number]; -export type FetchEmojisParams = { - query?: { - updatedAtFrom?: string; - updatedAtTo?: string; - name?: string; - host?: string; - uri?: string; - publicUrl?: string; - type?: string; - aliases?: string; - category?: string; - license?: string; - isSensitive?: boolean; - localOnly?: boolean; - hostType?: FetchEmojisHostTypes; - roleIds?: string[]; - }, - sinceId?: string; - untilId?: string; - limit?: number; - page?: number; - sort?: { - key: FetchEmojisSortKeys; - direction: 'ASC' | 'DESC'; - }[] -} @Injectable() export class CustomEmojiService implements OnApplicationShutdown { @@ -449,7 +436,33 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async fetchEmojis(params?: FetchEmojisParams) { + public async fetchEmojis( + params?: { + query?: { + updatedAtFrom?: string; + updatedAtTo?: string; + name?: string; + host?: string; + uri?: string; + publicUrl?: string; + type?: string; + aliases?: string; + category?: string; + license?: string; + isSensitive?: boolean; + localOnly?: boolean; + hostType?: FetchEmojisHostTypes; + roleIds?: string[]; + }, + sinceId?: string; + untilId?: string; + }, + opts?: { + limit?: number; + page?: number; + sortKeys?: FetchEmojisSortKeys[] + }, + ) { function multipleWordsToQuery( query: string, builder: SelectQueryBuilder, @@ -565,17 +578,19 @@ export class CustomEmojiService implements OnApplicationShutdown { builder.andWhere('emoji.id < :untilId', { untilId: params.untilId }); } - if (params?.sort && params.sort.length > 0) { - for (const sort of params.sort) { - builder.addOrderBy(`emoji.${sort.key}`, sort.direction); + if (opts?.sortKeys && opts.sortKeys.length > 0) { + for (const sortKey of opts.sortKeys) { + const direction = sortKey.startsWith('-') ? 'DESC' : 'ASC'; + const key = sortKey.replace(/^[+-]/, ''); + builder.addOrderBy(`emoji.${key}`, direction); } } else { builder.addOrderBy('emoji.id', 'DESC'); } - const limit = params?.limit ?? 10; - if (params?.page) { - builder.skip((params.page - 1) * limit); + const limit = opts?.limit ?? 10; + if (opts?.page) { + builder.skip((opts.page - 1) * limit); } builder.take(limit); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/v2/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/v2/list.ts index a65f9bca3a..9426318e34 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/v2/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/v2/list.ts @@ -6,7 +6,7 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; -import { CustomEmojiService, FetchEmojisParams } from '@/core/CustomEmojiService.js'; +import { CustomEmojiService, fetchEmojisHostTypes, fetchEmojisSortKeys } from '@/core/CustomEmojiService.js'; export const meta = { tags: ['admin'], @@ -52,7 +52,11 @@ export const paramDef = { license: { type: 'string' }, isSensitive: { type: 'boolean' }, localOnly: { type: 'boolean' }, - hostType: { type: 'string', enum: ['local', 'remote', 'all'], default: 'all' }, + hostType: { + type: 'string', + enum: fetchEmojisHostTypes, + default: 'all', + }, roleIds: { type: 'array', items: { type: 'string', format: 'misskey:id' }, @@ -63,37 +67,12 @@ export const paramDef = { untilId: { type: 'string', format: 'misskey:id' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, page: { type: 'integer' }, - sort: { + sortKeys: { type: 'array', + default: ['-id'], items: { - type: 'object', - properties: { - key: { - type: 'string', - enum: [ - 'id', - 'updatedAt', - 'name', - 'host', - 'uri', - 'publicUrl', - 'type', - 'aliases', - 'category', - 'license', - 'isSensitive', - 'localOnly', - 'roleIdsThatCanBeUsedThisEmojiAsReaction', - ], - default: 'id', - }, - direction: { - type: 'string', - enum: ['ASC', 'DESC'], - default: 'DESC', - }, - }, - required: ['key', 'direction'], + type: 'string', + enum: fetchEmojisSortKeys, }, }, }, @@ -107,37 +86,34 @@ export default class extends Endpoint { // eslint- private emojiEntityService: EmojiEntityService, ) { super(meta, paramDef, async (ps, me) => { - const params: FetchEmojisParams = {}; - - if (ps.query) { - params.query = { - updatedAtFrom: ps.query.updatedAtFrom, - updatedAtTo: ps.query.updatedAtTo, - name: ps.query.name, - host: ps.query.host, - uri: ps.query.uri, - publicUrl: ps.query.publicUrl, - type: ps.query.type, - aliases: ps.query.aliases, - category: ps.query.category, - license: ps.query.license, - isSensitive: ps.query.isSensitive, - localOnly: ps.query.localOnly, - hostType: ps.query.hostType, - roleIds: ps.query.roleIds, - }; - } - - params.sinceId = ps.sinceId; - params.untilId = ps.untilId; - params.limit = ps.limit; - params.page = ps.page; - params.sort = ps.sort?.map(it => ({ - key: it.key, - direction: it.direction, - })); - - const result = await this.customEmojiService.fetchEmojis(params); + const q = ps.query; + const result = await this.customEmojiService.fetchEmojis( + { + query: { + updatedAtFrom: q?.updatedAtFrom, + updatedAtTo: q?.updatedAtTo, + name: q?.name, + host: q?.host, + uri: q?.uri, + publicUrl: q?.publicUrl, + type: q?.type, + aliases: q?.aliases, + category: q?.category, + license: q?.license, + isSensitive: q?.isSensitive, + localOnly: q?.localOnly, + hostType: q?.hostType, + roleIds: q?.roleIds, + }, + sinceId: ps.sinceId, + untilId: ps.untilId, + }, + { + limit: ps.limit, + page: ps.page, + sortKeys: ps.sortKeys, + }, + ); return { emojis: await this.emojiEntityService.packDetailedAdminMany(result.emojis), diff --git a/packages/frontend/src/components/MkSortOrderEditor.define.ts b/packages/frontend/src/components/MkSortOrderEditor.define.ts new file mode 100644 index 0000000000..f023b5d72b --- /dev/null +++ b/packages/frontend/src/components/MkSortOrderEditor.define.ts @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export type SortOrderDirection = '+' | '-' + +export type SortOrder = { + key: T; + direction: SortOrderDirection; +} diff --git a/packages/frontend/src/components/MkSortOrderEditor.vue b/packages/frontend/src/components/MkSortOrderEditor.vue index ff038d3709..92b85ff88f 100644 --- a/packages/frontend/src/components/MkSortOrderEditor.vue +++ b/packages/frontend/src/components/MkSortOrderEditor.vue @@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -22,58 +22,57 @@ SPDX-License-Identifier: AGPL-3.0-only
-