From 08311ece4145b3d7e32e90b17412752f71818003 Mon Sep 17 00:00:00 2001 From: tar_bin Date: Wed, 17 May 2023 00:44:19 +0900 Subject: [PATCH 01/32] =?UTF-8?q?Feat:=E7=B5=B5=E6=96=87=E5=AD=97=E7=94=B3?= =?UTF-8?q?=E8=AB=8B=E6=A9=9F=E8=83=BD=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 + locales/ja-JP.yml | 3 + .../1684236161625-addEmojiDraftFlag.js | 11 +++ .../backend/src/core/CustomEmojiService.ts | 50 ++++++++---- packages/backend/src/core/RoleService.ts | 3 + .../src/core/entities/EmojiEntityService.ts | 2 + packages/backend/src/models/Emoji.ts | 6 ++ .../backend/src/models/json-schema/emoji.ts | 8 ++ .../ImportCustomEmojisProcessorService.ts | 1 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/admin/emoji/add-draft.ts | 80 +++++++++++++++++++ .../server/api/endpoints/admin/emoji/add.ts | 4 +- .../api/endpoints/admin/emoji/update.ts | 5 +- .../src/components/MkAutocomplete.vue | 4 + .../frontend/src/components/MkEmojiPicker.vue | 4 +- packages/frontend/src/const.ts | 1 + packages/frontend/src/pages/about.emojis.vue | 26 +++++- .../frontend/src/pages/admin/roles.editor.vue | 20 +++++ packages/frontend/src/pages/admin/roles.vue | 8 ++ .../src/pages/custom-emojis-manager.vue | 35 +++++--- .../frontend/src/pages/emoji-edit-dialog.vue | 73 +++++++++++++++++ packages/frontend/src/pages/emojis.emoji.vue | 16 +++- packages/misskey-js/src/entities.ts | 1 + 24 files changed, 339 insertions(+), 31 deletions(-) create mode 100644 packages/backend/migration/1684236161625-addEmojiDraftFlag.js create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index 2494c1709b..504df64250 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -978,6 +978,7 @@ export interface Locale { "unassign": string; "color": string; "manageCustomEmojis": string; + "requestCustomEmojis": string; "youCannotCreateAnymore": string; "cannotPerformTemporary": string; "cannotPerformTemporaryDescription": string; @@ -1020,6 +1021,7 @@ export interface Locale { "sensitiveWordsDescription2": string; "notesSearchNotAvailable": string; "license": string; + "draft": string; "unfavoriteConfirm": string; "myClips": string; "drivecleaner": string; @@ -1556,6 +1558,7 @@ export interface Locale { "inviteLimitCycle": string; "inviteExpirationTime": string; "canManageCustomEmojis": string; + "canRequestCustomEmojis": string; "driveCapacity": string; "alwaysMarkNsfw": string; "pinMax": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9adc4381a7..db2420c40d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -975,6 +975,7 @@ assign: "アサイン" unassign: "アサインを解除" color: "色" manageCustomEmojis: "カスタム絵文字の管理" +requestCustomEmojis: "カスタム絵文字のリクエスト" youCannotCreateAnymore: "これ以上作成することはできません。" cannotPerformTemporary: "一時的に利用できません" cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。" @@ -1017,6 +1018,7 @@ sensitiveWordsDescription: "設定したワードが含まれるノートの公 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" +draft: "ドラフト" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" drivecleaner: "ドライブクリーナー" @@ -1477,6 +1479,7 @@ _role: inviteLimitCycle: "招待コードの発行間隔" inviteExpirationTime: "招待コードの有効期限" canManageCustomEmojis: "カスタム絵文字の管理" + canRequestCustomEmojis: "カスタム絵文字のリクエスト" driveCapacity: "ドライブ容量" alwaysMarkNsfw: "ファイルにNSFWを常に付与" pinMax: "ノートのピン留めの最大数" diff --git a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js new file mode 100644 index 0000000000..b0a13ea498 --- /dev/null +++ b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js @@ -0,0 +1,11 @@ +export class AddEmojiDraftFlag1684236161625 { + name = 'AddEmojiDraftFlag1684236161625' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" ADD "draft" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "draft"`); + } +} diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 505c8e4269..d3152f2685 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -13,10 +13,10 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -34,6 +34,9 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + private utilityService: UtilityService, private idService: IdService, private emojiEntityService: EmojiEntityService, @@ -66,6 +69,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; }, moderator?: MiUser): Promise { const emoji = await this.emojisRepository.insert({ @@ -82,6 +86,7 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: data.draft, }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); if (data.host == null) { @@ -109,27 +114,44 @@ export class CustomEmojiService implements OnApplicationShutdown { category?: string | null; aliases?: string[]; license?: string | null; + fileId?: string | null; isSensitive?: boolean; localOnly?: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; }, moderator?: MiUser): Promise { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); + const driveFile = data.fileId !== null ? await this.driveFilesRepository.findOneBy({ id: data.fileId }) : null; const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); - await this.emojisRepository.update(emoji.id, { - updatedAt: new Date(), - name: data.name, - category: data.category, - aliases: data.aliases, - license: data.license, - isSensitive: data.isSensitive, - localOnly: data.localOnly, - originalUrl: data.driveFile != null ? data.driveFile.url : undefined, - publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, - type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, - roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, - }); + if (driveFile !== null) { + await this.emojisRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + draft: data.draft, + }); + } else { + await this.emojisRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + draft: data.draft, + roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, + }); + } this.localEmojisCache.refresh(); diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index d18fb240f7..7f451b6510 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -32,6 +32,7 @@ export type RolePolicies = { inviteLimitCycle: number; inviteExpirationTime: number; canManageCustomEmojis: boolean; + canRequestCustomEmojis: boolean; canSearchNotes: boolean; canUseTranslator: boolean; canHideAds: boolean; @@ -57,6 +58,7 @@ export const DEFAULT_POLICIES: RolePolicies = { inviteLimitCycle: 60 * 24 * 7, inviteExpirationTime: 0, canManageCustomEmojis: false, + canRequestCustomEmojis: false, canSearchNotes: false, canUseTranslator: true, canHideAds: false, @@ -300,6 +302,7 @@ export class RoleService implements OnApplicationShutdown { inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), inviteExpirationTime: calc('inviteExpirationTime', vs => Math.max(...vs)), canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)), + canRequestCustomEmojis: calc('canRequestCustomEmojis', vs => vs.some(v => v === true)), canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)), canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 5b97cfad5e..d50d7356ae 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -33,6 +33,7 @@ export class EmojiEntityService { url: emoji.publicUrl || emoji.originalUrl, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, + draft: emoji.draft, }; } @@ -61,6 +62,7 @@ export class EmojiEntityService { isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: emoji.draft, }; } diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index 563ac1d9d3..bd8d54ffc3 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -81,4 +81,10 @@ export class MiEmoji { array: true, length: 128, default: '{}', }) public roleIdsThatCanBeUsedThisEmojiAsReaction: string[]; + + @Column('boolean', { + default: false, + nullable: false, + }) + public draft: boolean; } diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 99a58f8773..90054cbc50 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -40,6 +40,10 @@ export const packedEmojiSimpleSchema = { format: 'id', }, }, + draft: { + type: 'boolean', + optional: false, nullable: true, + }, }, } as const; @@ -81,6 +85,10 @@ export const packedEmojiDetailedSchema = { type: 'string', optional: false, nullable: true, }, + draft: { + type: 'boolean', + optional: false, nullable: true, + }, isSensitive: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index a52af54a39..088596bba6 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -103,6 +103,7 @@ export class ImportCustomEmojisProcessorService { isSensitive: emojiInfo.isSensitive, localOnly: emojiInfo.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: [], + draft: false, }); } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index f834561456..35d44d3579 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -25,6 +25,7 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; @@ -375,6 +376,7 @@ const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; +const $admin_emoji_addDraft: Provider = { provide: 'ep:admin/emoji/add-draft', useClass: ep___admin_emoji_addDraft.default }; const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; @@ -729,6 +731,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, + $admin_emoji_addDraft, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, @@ -1077,6 +1080,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, + $admin_emoji_addDraft, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d12a035afa..202f449efc 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -25,6 +25,7 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; @@ -373,6 +374,7 @@ const eps = [ ['admin/drive/show-file', ep___admin_drive_showFile], ['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk], ['admin/emoji/add', ep___admin_emoji_add], + ['admin/emoji/add-draft', ep___admin_emoji_addDraft], ['admin/emoji/copy', ep___admin_emoji_copy], ['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk], ['admin/emoji/delete', ep___admin_emoji_delete], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts new file mode 100644 index 0000000000..ac5082091e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -0,0 +1,80 @@ +import { Inject, Injectable } from '@nestjs/common'; +import rndstr from 'rndstr'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canRequestCustomEmojis', + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, + category: { + type: 'string', + nullable: true, + description: 'Use `null` to reset the category.', + }, + aliases: { type: 'array', items: { + type: 'string', + } }, + license: { type: 'string', nullable: true }, + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['name', 'fileId'], +} as const; + +// TODO: ロジックをサービスに切り出す + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + private customEmojiService: CustomEmojiService, + + private moderationLogService: ModerationLogService, + ) { + super(meta, paramDef, async (ps, me) => { + const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + + const emoji = await this.customEmojiService.add({ + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + host: null, + draft: true, + }); + + this.moderationLogService.insertModerationLog(me, 'addEmoji', { + emojiId: emoji.id, + }); + + return { + id: emoji.id, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index faab8ee608..5e1b0e345c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -51,7 +51,7 @@ export const paramDef = { type: 'string', } }, }, - required: ['name', 'fileId'], + required: ['name','fileId', 'draft'], } as const; // TODO: ロジックをサービスに切り出す @@ -68,6 +68,7 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.duplicateName); @@ -81,6 +82,7 @@ export default class extends Endpoint { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, + draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 04226d8953..bebfdae8f2 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -55,8 +55,9 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, + draft: { type: 'boolean' }, }, - required: ['id', 'name', 'aliases'], + required: ['id', 'name', 'aliases', 'draft'], } as const; @Injectable() @@ -93,6 +94,8 @@ export default class extends Endpoint { // eslint- isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, + fileId: ps.fileId ?? null, + draft: ps.draft, }, me); }); } diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 7c4f910559..f8b655e772 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -96,6 +96,10 @@ const emojiDb = computed(() => { const customEmojiDB: EmojiDef[] = []; for (const x of customEmojis.value) { + if (x.draft) { + continue; + } + customEmojiDB.push({ name: x.name, emoji: `:${x.name}:`, diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 7eff637482..25c4d028e6 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="category in customEmojiCategories" :key="`custom:${category}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(emoji => !emoji.draft).filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" @chosen="chosen" > {{ category || i18n.ts.other }} @@ -157,7 +157,7 @@ watch(q, () => { const searchCustom = () => { const max = 100; - const emojis = customEmojis.value; + const emojis = customEmojis.value.filter(emoji => !emoji.draft); const matches = new Set(); const exactMatch = emojis.find(emoji => emoji.name === newQ); diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 3998df9efe..89bd092aa2 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -66,6 +66,7 @@ export const ROLE_POLICIES = [ 'inviteLimitCycle', 'inviteExpirationTime', 'canManageCustomEmojis', + 'canRequestCustomEmojis', 'canSearchNotes', 'canUseTranslator', 'canHideAds', diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index c8f56f1674..6fb691bc5e 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only + + diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue new file mode 100644 index 0000000000..7112a38430 --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue new file mode 100644 index 0000000000..26c8dd66ac --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue similarity index 79% rename from packages/frontend/src/pages/emoji-edit-dialog.vue rename to packages/frontend/src/components/MkEmojiEditDialog.vue index f830f09473..83c7fa4d22 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -7,11 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only +
@@ -34,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.selectFile }} + @@ -45,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -64,15 +66,17 @@ SPDX-License-Identifier: AGPL-3.0-only isSensitive {{ i18n.ts.localOnly }} - {{ i18n.ts.delete }} + + {{ i18n.ts.draft }} +
- {{ props.emoji ? i18n.ts.update : i18n.ts.create }} - - {{ i18n.ts.draft }} - - {{ i18n.ts.delete }} +
+ {{ i18n.ts.delete }} + {{ props.emoji ? i18n.ts.update : i18n.ts.create }} + {{ props.emoji ? i18n.ts.update : i18n.ts.create }} +
@@ -81,6 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only From 6f964f56cc700c44775c065165332d18c3666ff6 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Thu, 19 Oct 2023 22:51:25 +0900 Subject: [PATCH 03/32] =?UTF-8?q?fix:=20unused=E3=81=AA=E3=82=84=E3=81=A4?= =?UTF-8?q?=E6=B6=88=E3=81=97=E3=81=9F=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea --- .../api/endpoints/admin/emoji/add-draft.ts | 3 +-- .../src/components/MkEmojiEditDialog.vue | 19 ------------------- packages/frontend/src/pages/about.emojis.vue | 2 +- packages/frontend/src/pages/emojis.emoji.vue | 1 - 4 files changed, 2 insertions(+), 23 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 57de1e0ed8..5212d7285b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 83c7fa4d22..65690042a5 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -116,7 +115,6 @@ let file = $ref(); let chooseFile: DriveFile|null = $ref(null); let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); -let url; watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); @@ -131,16 +129,6 @@ const emit = defineEmits<{ (ev: 'closed'): void }>(); -function ok() { - if (isRequest) { - if (chooseFile !== null && name.match(/^[a-zA-Z0-9_]+$/)) { - add(); - } - } else { - update(); - } -} - async function add() { const ret = await os.api('admin/emoji/add-draft', { name: name, @@ -253,13 +241,6 @@ async function done() { } } -function chooseFileFrom(ev) { - selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { - chooseFile = files_[0]; - url = chooseFile.url; - }); -} - async function del() { const { canceled } = await os.confirm({ type: 'warning', diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 61843c393a..f51cc1b640 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only From fb2482eb9806d4ddd964ba41a98165de48a4a626 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Fri, 20 Oct 2023 17:04:13 +0900 Subject: [PATCH 09/32] Fix Signed-off-by: mattyatea --- locales/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index d6d7f5752a..e662093c9b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1028,7 +1028,7 @@ export interface Locale { "license": string; "draft": string; "undrafted": string; - "requestEmojisEmojis": string; + "requestEmojis": string; "emojiNameValidation": string; "unfavoriteConfirm": string; "myClips": string; From a4fef11e3c37cc1f293b7faaa6da1dddd4cf4d91 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Fri, 20 Oct 2023 17:07:26 +0900 Subject: [PATCH 10/32] Fix Signed-off-by: mattyatea --- locales/ja-JP.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 448aa7429c..723e8b2b3e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1025,7 +1025,7 @@ notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" draft: "下書き" undrafted: "下書き解除" -requestEmojisEmojis: "申請されてる絵文字" +requestEmojis: "リクエストされている絵文字" emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" From 97590f2567e85b740c2dface424cd898e3a09a5d Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 15:55:18 +0900 Subject: [PATCH 11/32] Update locales Signed-off-by: mattyatea --- locales/ja-JP.yml | 1 + .../backend/migration/1697813507149-EmojiDraft.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 packages/backend/migration/1697813507149-EmojiDraft.js diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 723e8b2b3e..77485e0de1 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -258,6 +258,7 @@ announcements: "お知らせ" imageUrl: "画像URL" remove: "削除" removed: "削除しました" +undraftAreYouSure: "「{x}」をドラフト解除しますか?" removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" resetAreYouSure: "リセットしますか?" diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js new file mode 100644 index 0000000000..8aadc76f80 --- /dev/null +++ b/packages/backend/migration/1697813507149-EmojiDraft.js @@ -0,0 +1,15 @@ +export class EmojiDraft1697813507149 { + name = 'EmojiDraft1697813507149' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_j7skve3cwn4pfb2qg9mx1la8iy" ON "emoji_draft" ("name") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e99cd746a9632624dae39cfb35" ON "emoji_draft" ("name") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_e99cd746a9632624dae39cfb35"`); + await queryRunner.query(`DROP INDEX "public"."IDX_j7skve3cwn4pfb2qg9mx1la8iy"`); + await queryRunner.query(`DROP TABLE "emoji_draft"`); + } +} From fe938bf8e68a20035861ed549650314e7c30a420 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 16:29:09 +0900 Subject: [PATCH 12/32] =?UTF-8?q?Feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E7=94=B3=E8=AB=8B=E4=B8=AD=E3=81=AE=E3=82=84=E3=81=A4=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=83=BC=E3=83=96=E3=83=AB=E3=82=92=E5=88=86=E3=81=91?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea --- locales/index.d.ts | 1 + .../1684236161625-addEmojiDraftFlag.js | 11 -- packages/backend/src/core/CoreModule.ts | 6 + .../backend/src/core/CustomEmojiService.ts | 127 +++++++++++++----- .../core/entities/DriveFileEntityService.ts | 6 + .../core/entities/EmojiDraftsEntityService.ts | 70 ++++++++++ .../src/core/entities/EmojiEntityService.ts | 2 - packages/backend/src/di-symbols.ts | 1 + packages/backend/src/misc/json-schema.ts | 4 +- packages/backend/src/models/Emoji.ts | 6 - packages/backend/src/models/EmojiDraft.ts | 72 ++++++++++ .../backend/src/models/RepositoryModule.ts | 10 +- packages/backend/src/models/_.ts | 3 + .../backend/src/models/json-schema/emoji.ts | 82 ++++++++++- packages/backend/src/postgres.ts | 2 + .../backend/src/server/api/EndpointsModule.ts | 8 ++ packages/backend/src/server/api/endpoints.ts | 4 + .../api/endpoints/admin/emoji/add-draft.ts | 14 +- .../server/api/endpoints/admin/emoji/add.ts | 3 +- .../api/endpoints/admin/emoji/delete.ts | 9 +- .../api/endpoints/admin/emoji/draft-update.ts | 120 +++++++++++++++++ .../api/endpoints/admin/emoji/update.ts | 45 ++++--- .../src/server/api/endpoints/emoji-drafts.ts | 65 +++++++++ packages/backend/src/types.ts | 5 + .../src/components/MkCustomEmojiEditDraft.vue | 11 +- .../src/components/MkEmojiEditDialog.vue | 78 +++-------- .../frontend/src/components/MkPagination.vue | 11 +- packages/frontend/src/pages/about.emojis.vue | 7 +- packages/frontend/src/pages/emojis.emoji.vue | 72 +++++----- 29 files changed, 666 insertions(+), 189 deletions(-) delete mode 100644 packages/backend/migration/1684236161625-addEmojiDraftFlag.js create mode 100644 packages/backend/src/core/entities/EmojiDraftsEntityService.ts create mode 100644 packages/backend/src/models/EmojiDraft.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts create mode 100644 packages/backend/src/server/api/endpoints/emoji-drafts.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index e662093c9b..12f61f1aac 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -261,6 +261,7 @@ export interface Locale { "imageUrl": string; "remove": string; "removed": string; + "undraftAreYouSure": string; "removeAreYouSure": string; "deleteAreYouSure": string; "resetAreYouSure": string; diff --git a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js deleted file mode 100644 index b0a13ea498..0000000000 --- a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js +++ /dev/null @@ -1,11 +0,0 @@ -export class AddEmojiDraftFlag1684236161625 { - name = 'AddEmojiDraftFlag1684236161625' - - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" ADD "draft" boolean NOT NULL DEFAULT false`); - } - - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "draft"`); - } -} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index e7e66646fc..44894ffa25 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -86,6 +86,7 @@ import { ClipEntityService } from './entities/ClipEntityService.js'; import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js'; import { EmojiEntityService } from './entities/EmojiEntityService.js'; +import { EmojiDraftsEntityService } from './entities/EmojiDraftsEntityService.js'; import { FollowingEntityService } from './entities/FollowingEntityService.js'; import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js'; import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js'; @@ -217,6 +218,7 @@ const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService }; const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService }; const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService }; +const $EmojiDraftsEntityService: Provider = { provide: 'EmojiDraftsEntityService', useExisting: EmojiDraftsEntityService }; const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService }; const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService }; const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService }; @@ -348,6 +350,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, + EmojiDraftsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -474,6 +477,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, + $EmojiDraftsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, @@ -600,6 +604,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, + EmojiDraftsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -725,6 +730,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, + $EmojiDraftsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index cdc7cae852..1aa6377be3 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -12,12 +12,13 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; -import type { EmojisRepository, MiRole, MiUser, DriveFilesRepository } from '@/models/_.js'; +import type { EmojisRepository, EmojiDraftsRepository, MiRole, MiUser } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @@ -33,8 +34,8 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, private utilityService: UtilityService, private idService: IdService, @@ -58,6 +59,40 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } + @bindThis + public async draft(data: { + driveFile: MiDriveFile; + name: string; + category: string | null; + aliases: string[]; + license: string | null; + isSensitive: boolean; + localOnly: boolean; + }, me?: MiUser): Promise { + const emoji = await this.emojiDraftsRepository.insert({ + id: this.idService.gen(), + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + originalUrl: data.driveFile.url, + publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url, + type: data.driveFile.webpublicType ?? data.driveFile.type, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + fileId: data.driveFile.id, + }).then(x => this.emojiDraftsRepository.findOneByOrFail(x.identifiers[0])); + + if (me) { + this.moderationLogService.log(me, 'addCustomEmoji', { + emojiId: emoji.id, + emoji: emoji, + }); + } + + return emoji; + } @bindThis public async add(data: { driveFile: MiDriveFile; @@ -68,7 +103,6 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; - draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; }, moderator?: MiUser): Promise { const emoji = await this.emojisRepository.insert({ @@ -85,7 +119,6 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction, - draft: data.draft, }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); if (data.host == null) { @@ -113,44 +146,27 @@ export class CustomEmojiService implements OnApplicationShutdown { category?: string | null; aliases?: string[]; license?: string | null; - fileId?: string | null; isSensitive?: boolean; localOnly?: boolean; - draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; }, moderator?: MiUser): Promise { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); - const driveFile = data.fileId !== null ? await this.driveFilesRepository.findOneBy({ id: data.fileId }) : null; const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); - if (driveFile !== null) { - await this.emojisRepository.update(emoji.id, { - updatedAt: new Date(), - name: data.name, - category: data.category, - aliases: data.aliases, - license: data.license, - isSensitive: data.isSensitive, - localOnly: data.localOnly, - originalUrl: data.driveFile != null ? data.driveFile.url : undefined, - publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, - type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, - draft: data.draft, - }); - } else { - await this.emojisRepository.update(emoji.id, { - updatedAt: new Date(), - name: data.name, - category: data.category, - aliases: data.aliases, - license: data.license, - isSensitive: data.isSensitive, - localOnly: data.localOnly, - draft: data.draft, - roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, - }); - } + await this.emojisRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, + }); this.localEmojisCache.refresh(); @@ -179,7 +195,35 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } } + @bindThis + public async draftUpdate(id: MiEmoji['id'], data: { + driveFile?: MiDriveFile; + name?: string; + category?: string | null; + aliases?: string[]; + license?: string | null; + isSensitive?: boolean; + localOnly?: boolean; + }, moderator?: MiUser): Promise { + const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); + const sameNameEmoji = await this.emojiDraftsRepository.findOneBy({ name: data.name }); + if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); + await this.emojiDraftsRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + }); + + this.localEmojisCache.refresh(); + } @bindThis public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) { const emojis = await this.emojisRepository.findBy({ @@ -287,7 +331,12 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } } + @bindThis + public async draftDelete(id: MiEmojiDraft['id']) { + const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); + await this.emojiDraftsRepository.delete(emoji.id); + } @bindThis public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) { const emojis = await this.emojisRepository.findBy({ @@ -408,12 +457,20 @@ export class CustomEmojiService implements OnApplicationShutdown { public checkDuplicate(name: string): Promise { return this.emojisRepository.exist({ where: { name, host: IsNull() } }); } + @bindThis + public checkDraftDuplicate(name: string): Promise { + return this.emojiDraftsRepository.exist({ where: { name } }); + } @bindThis public getEmojiById(id: string): Promise { return this.emojisRepository.findOneBy({ id }); } + @bindThis + public getEmojiDraftById(id: string): Promise { + return this.emojiDraftsRepository.findOneBy({ id }); + } @bindThis public dispose(): void { this.cache.dispose(); diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 14be000367..d5406373b7 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -133,7 +133,13 @@ export class DriveFileEntityService { } return url; } + @bindThis + public async getFromUrl(url: string): Promise { + const file = await this.driveFilesRepository.findOneBy({ url: url }); + if (file === null ) return null; + return file; + } @bindThis public async calcDriveUsageOf(user: MiUser['id'] | { id: MiUser['id'] }): Promise { const id = typeof user === 'object' ? user.id : user; diff --git a/packages/backend/src/core/entities/EmojiDraftsEntityService.ts b/packages/backend/src/core/entities/EmojiDraftsEntityService.ts new file mode 100644 index 0000000000..de1a621553 --- /dev/null +++ b/packages/backend/src/core/entities/EmojiDraftsEntityService.ts @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { Packed } from '@/misc/json-schema.js'; +import { bindThis } from '@/decorators.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; + +@Injectable() +export class EmojiDraftsEntityService { + constructor( + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + ) { + } + + @bindThis + public async packSimple( + src: MiEmojiDraft['id'] | MiEmojiDraft, + ): Promise> { + const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + + return { + aliases: emoji.aliases, + name: emoji.name, + category: emoji.category, + // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) + url: emoji.publicUrl, + isSensitive: emoji.isSensitive ? true : undefined, + }; + } + + @bindThis + public packSimpleMany( + emojis: any[], + ) { + return Promise.all(emojis.map(x => this.packSimple(x))); + } + + @bindThis + public async packDetailed( + src: MiEmojiDraft['id'] | MiEmojiDraft, + ): Promise> { + const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + + return { + id: emoji.id, + aliases: emoji.aliases, + name: emoji.name, + category: emoji.category, + url: emoji.publicUrl, + license: emoji.license, + isSensitive: emoji.isSensitive, + localOnly: emoji.localOnly, + fileId: emoji.fileId, + }; + } + + @bindThis + public packDetailedMany( + emojis: any[], + ) { + return Promise.all(emojis.map(x => this.packDetailed(x))); + } +} + diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index d50d7356ae..5b97cfad5e 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -33,7 +33,6 @@ export class EmojiEntityService { url: emoji.publicUrl || emoji.originalUrl, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, - draft: emoji.draft, }; } @@ -62,7 +61,6 @@ export class EmojiEntityService { isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, - draft: emoji.draft, }; } diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index edcdd21d60..a374370edc 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -39,6 +39,7 @@ export const DI = { followRequestsRepository: Symbol('followRequestsRepository'), instancesRepository: Symbol('instancesRepository'), emojisRepository: Symbol('emojisRepository'), + emojiDraftsRepository: Symbol('emojiDraftsRepository'), driveFilesRepository: Symbol('driveFilesRepository'), driveFoldersRepository: Symbol('driveFoldersRepository'), metasRepository: Symbol('metasRepository'), diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index 80c1041c62..d203d5fa82 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -33,7 +33,7 @@ import { packedClipSchema } from '@/models/json-schema/clip.js'; import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js'; import { packedQueueCountSchema } from '@/models/json-schema/queue.js'; import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js'; -import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js'; +import { packedEmojiDetailedSchema, packedEmojiDraftSimpleSchema, packedEmojiSimpleSchema, packedEmojiDraftDetailedSchema } from '@/models/json-schema/emoji.js'; import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; @@ -69,7 +69,9 @@ export const refs = { FederationInstance: packedFederationInstanceSchema, GalleryPost: packedGalleryPostSchema, EmojiSimple: packedEmojiSimpleSchema, + EmojiDraftSimple: packedEmojiDraftSimpleSchema, EmojiDetailed: packedEmojiDetailedSchema, + EmojiDraftDetailed: packedEmojiDraftDetailedSchema, Flash: packedFlashSchema, }; diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index bd8d54ffc3..563ac1d9d3 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -81,10 +81,4 @@ export class MiEmoji { array: true, length: 128, default: '{}', }) public roleIdsThatCanBeUsedThisEmojiAsReaction: string[]; - - @Column('boolean', { - default: false, - nullable: false, - }) - public draft: boolean; } diff --git a/packages/backend/src/models/EmojiDraft.ts b/packages/backend/src/models/EmojiDraft.ts new file mode 100644 index 0000000000..6d469fd200 --- /dev/null +++ b/packages/backend/src/models/EmojiDraft.ts @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from './util/id.js'; + +@Entity('emoji_draft') +@Index(['name'], { unique: true }) +export class MiEmojiDraft { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + nullable: true, + }) + public updatedAt: Date | null; + + @Index() + @Column('varchar', { + length: 128, + }) + public name: string; + + @Column('varchar', { + length: 128, nullable: true, + }) + public category: string | null; + + @Column('varchar', { + length: 512, + }) + public originalUrl: string; + + @Column('varchar', { + length: 512, + default: '', + }) + public publicUrl: string; + + // publicUrlの方のtypeが入る + @Column('varchar', { + length: 64, nullable: true, + }) + public type: string | null; + + @Column('varchar', { + array: true, length: 128, default: '{}', + }) + public aliases: string[]; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public license: string | null; + + @Column('varchar', { + length: 1024, nullable: false, + }) + public fileId: string; + + @Column('boolean', { + default: false, + }) + public localOnly: boolean; + + @Column('boolean', { + default: false, + }) + public isSensitive: boolean; +} diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 9efd6841b1..30f11cbc4e 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,7 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiEmojiDraft, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -165,6 +165,12 @@ const $emojisRepository: Provider = { inject: [DI.db], }; +const $emojiDraftsRepository: Provider = { + provide: DI.emojiDraftsRepository, + useFactory: (db: DataSource) => db.getRepository(MiEmojiDraft), + inject: [DI.db], +}; + const $driveFilesRepository: Provider = { provide: DI.driveFilesRepository, useFactory: (db: DataSource) => db.getRepository(MiDriveFile), @@ -423,6 +429,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, + $emojiDraftsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, @@ -489,6 +496,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, + $emojiDraftsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index f974f95ed8..98392c0af4 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -67,6 +67,7 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; import type { Repository } from 'typeorm'; export { @@ -87,6 +88,7 @@ export { MiDriveFile, MiDriveFolder, MiEmoji, + MiEmojiDraft, MiFollowing, MiFollowRequest, MiGalleryLike, @@ -153,6 +155,7 @@ export type ClipFavoritesRepository = Repository; export type DriveFilesRepository = Repository; export type DriveFoldersRepository = Repository; export type EmojisRepository = Repository; +export type EmojiDraftsRepository = Repository; export type FollowingsRepository = Repository; export type FollowRequestsRepository = Repository; export type GalleryLikesRepository = Repository; diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 90054cbc50..70da8dc67f 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -40,10 +40,36 @@ export const packedEmojiSimpleSchema = { format: 'id', }, }, - draft: { - type: 'boolean', + }, +} as const; +export const packedEmojiDraftSimpleSchema = { + type: 'object', + properties: { + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', optional: false, nullable: true, }, + url: { + type: 'string', + optional: false, nullable: false, + }, + isSensitive: { + type: 'boolean', + optional: true, nullable: false, + }, }, } as const; @@ -85,10 +111,6 @@ export const packedEmojiDetailedSchema = { type: 'string', optional: false, nullable: true, }, - draft: { - type: 'boolean', - optional: false, nullable: true, - }, isSensitive: { type: 'boolean', optional: false, nullable: false, @@ -108,3 +130,51 @@ export const packedEmojiDetailedSchema = { }, }, } as const; + +export const packedEmojiDraftDetailedSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + url: { + type: 'string', + optional: false, nullable: false, + }, + license: { + type: 'string', + optional: false, nullable: true, + }, + isSensitive: { + type: 'boolean', + optional: false, nullable: false, + }, + localOnly: { + type: 'boolean', + optional: false, nullable: false, + }, + fileId: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index d4c6ad82ce..16c3387ad2 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -28,6 +28,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -160,6 +161,7 @@ export const entities = [ MiPoll, MiPollVote, MiEmoji, + MiEmojiDraft, MiHashtag, MiSwSubscription, MiAbuseUserReport, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 35d44d3579..f52791fe62 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -37,6 +37,7 @@ import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-al import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -243,6 +244,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; +import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -388,6 +390,7 @@ const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-ali const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default }; const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; +const $admin_emoji_draftUpdate: Provider = { provide: 'ep:admin/emoji/draft-update', useClass: ep___admin_emoji_draftUpdate.default }; const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; @@ -594,6 +597,7 @@ const $invite_list: Provider = { provide: 'ep:invite/list', useClass: ep___invit const $invite_limit: Provider = { provide: 'ep:invite/limit', useClass: ep___invite_limit.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; +const $emoji_drafts: Provider = { provide: 'ep:emoji-drafts', useClass: ep___emojiDrafts.default }; const $emoji: Provider = { provide: 'ep:emoji', useClass: ep___emoji.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; @@ -743,6 +747,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, + $admin_emoji_draftUpdate, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -949,6 +954,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, + $emoji_drafts, $emoji, $miauth_genToken, $mute_create, @@ -1092,6 +1098,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, + $admin_emoji_draftUpdate, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -1298,6 +1305,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, + $emoji_drafts, $emoji, $miauth_genToken, $mute_create, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 202f449efc..748a73b17e 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -37,6 +37,7 @@ import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-al import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -243,6 +244,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; +import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -386,6 +388,7 @@ const eps = [ ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], + ['admin/emoji/draft-update', ep___admin_emoji_draftUpdate], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], @@ -592,6 +595,7 @@ const eps = [ ['invite/limit', ep___invite_limit], ['meta', ep___meta], ['emojis', ep___emojis], + ['emoji-drafts', ep___emojiDrafts], ['emoji', ep___emoji], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 5212d7285b..af85b2c10a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -18,6 +18,11 @@ export const meta = { code: 'NO_SUCH_FILE', id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', }, + duplicateName: { + message: 'Duplicate name.', + code: 'DUPLICATE_NAME', + id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975', + }, }, } as const; @@ -55,11 +60,15 @@ export default class extends Endpoint { private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { + const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); + const isDraftDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + + if (isDuplicate || isDraftDuplicate) throw new ApiError(meta.errors.duplicateName); const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const emoji = await this.customEmojiService.add({ + const emoji = await this.customEmojiService.draft({ driveFile, name: ps.name, category: ps.category ?? null, @@ -67,9 +76,6 @@ export default class extends Endpoint { license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, - host: null, - draft: true, - roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); await this.moderationLogService.log(me, 'addCustomEmoji', { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 251abbc8d0..292cc89f54 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -51,7 +51,7 @@ export const paramDef = { type: 'string', } }, }, - required: ['name', 'fileId', 'draft'], + required: ['name', 'fileId'], } as const; // TODO: ロジックをサービスに切り出す @@ -82,7 +82,6 @@ export default class extends Endpoint { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, - draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 58aa0b9950..b3ec350272 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -36,7 +36,14 @@ export default class extends Endpoint { // eslint- private customEmojiService: CustomEmojiService, ) { super(meta, paramDef, async (ps, me) => { - await this.customEmojiService.delete(ps.id, me); + const emoji = await this.customEmojiService.getEmojiById(ps.id); + const draftEmoji = await this.customEmojiService.getEmojiDraftById(ps.id); + if (emoji != null) { + await this.customEmojiService.delete(ps.id, me); + } + if (draftEmoji != null) { + await this.customEmojiService.draftDelete(ps.id); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts new file mode 100644 index 0000000000..3e1aa4131a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; +import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', + + errors: { + noSuchEmoji: { + message: 'No such emoji.', + code: 'NO_SUCH_EMOJI', + id: '684dec9d-a8c2-4364-9aa8-456c49cb1dc8', + }, + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: '14fb9fd9-0731-4e2f-aeb9-f09e4740333d', + }, + sameNameEmojiExists: { + message: 'Emoji that have same name already exists.', + code: 'SAME_NAME_EMOJI_EXISTS', + id: '7180fe9d-1ee3-bff9-647d-fe9896d2ffb8', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, + fileId: { type: 'string', format: 'misskey:id' }, + category: { + type: 'string', + nullable: true, + description: 'Use `null` to reset the category.', + }, + aliases: { type: 'array', items: { + type: 'string', + } }, + license: { type: 'string', nullable: true }, + isSensitive: { type: 'boolean' }, + localOnly: { type: 'boolean' }, + roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { + type: 'string', + } }, + draft: { type: 'boolean' }, + }, + required: ['id', 'name', 'aliases'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + + private customEmojiService: CustomEmojiService, + private driveFileEntityService: DriveFileEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + let driveFile; + const isDraft = !!ps.draft; + if (ps.fileId) { + driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + } + + const emoji = await this.customEmojiService.getEmojiDraftById(ps.id); + if (emoji != null) { + if (ps.name !== emoji.name) { + const isDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); + } + } else { + throw new ApiError(meta.errors.noSuchEmoji); + } + if (!isDraft) { + const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); + if (file === null) throw new ApiError(meta.errors.noSuchFile); + await this.customEmojiService.add({ + driveFile: file, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + host: null, + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + roleIdsThatCanBeUsedThisEmojiAsReaction: [], + }, me); + await this.customEmojiService.draftDelete(ps.id); + } else { + await this.customEmojiService.draftUpdate(ps.id, { + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + }, me); + } + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index bebfdae8f2..f996560ce1 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -5,8 +5,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository } from '@/models/_.js'; +import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -57,7 +58,7 @@ export const paramDef = { } }, draft: { type: 'boolean' }, }, - required: ['id', 'name', 'aliases', 'draft'], + required: ['id', 'name', 'aliases'], } as const; @Injectable() @@ -67,10 +68,11 @@ export default class extends Endpoint { // eslint- private driveFilesRepository: DriveFilesRepository, private customEmojiService: CustomEmojiService, + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { let driveFile; - + const isDraft = !!ps.draft; if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); @@ -85,18 +87,31 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchEmoji); } - await this.customEmojiService.update(ps.id, { - driveFile, - name: ps.name, - category: ps.category ?? null, - aliases: ps.aliases, - license: ps.license ?? null, - isSensitive: ps.isSensitive, - localOnly: ps.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, - fileId: ps.fileId ?? null, - draft: ps.draft, - }, me); + if (!isDraft) { + await this.customEmojiService.update(ps.id, { + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases, + license: ps.license ?? null, + isSensitive: ps.isSensitive, + localOnly: ps.localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, + }, me); + } else { + const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); + if (file === null) throw new ApiError(meta.errors.noSuchFile); + await this.customEmojiService.draft({ + driveFile: file, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + }, me); + await this.customEmojiService.delete(ps.id); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/emoji-drafts.ts b/packages/backend/src/server/api/endpoints/emoji-drafts.ts new file mode 100644 index 0000000000..5b23dac093 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emoji-drafts.ts @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { EmojiDraftsRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['meta'], + + requireCredential: false, + allowGet: true, + cacheSec: 3600, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + emojis: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'EmojiDraftDetailed', + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + + private emojiDraftsEntityService: EmojiDraftsEntityService, + ) { + super(meta, paramDef, async () => { + const emojis = await this.emojiDraftsRepository.find({ + order: { + category: 'ASC', + name: 'ASC', + }, + }); + + return { + emojis: await this.emojiDraftsEntityService.packDetailedMany(emojis), + }; + }); + } +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 316073c992..f7ebabca2d 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -33,6 +33,7 @@ export const moderationLogTypes = [ 'unsuspend', 'updateUserNote', 'addCustomEmoji', + 'requestCustomEmoji', 'updateCustomEmoji', 'deleteCustomEmoji', 'assignRole', @@ -88,6 +89,10 @@ export type ModerationLogPayloads = { emojiId: string; emoji: any; }; + requestCustomEmoji: { + emojiId: string; + emoji: any; + }; updateCustomEmoji: { emojiId: string; before: any; diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue index c609898b66..d5446eb18e 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue @@ -45,18 +45,19 @@ const emojisDraftPaginationComponent = shallowRef ({ query: (query.value && query.value !== '') ? query.value : null, - draft: true, })), }; const editDraft = (emoji) => { + emoji.isDraft = true; os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { emoji: emoji, isRequest: false, + isDraftEdit: true, }, { done: result => { if (result.updated) { @@ -80,16 +81,16 @@ async function undrafted(emoji) { }); if (canceled) return; - await os.api('admin/emoji/update', { + await os.api('admin/emoji/draft-update', { id: emoji.id, + fileId: emoji.fileId, name: emoji.name, category: emoji.category, aliases: emoji.aliases, license: emoji.license, - draft: false, isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + isDraft: false, }); emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 65690042a5..cc0c17fd10 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only isSensitive {{ i18n.ts.localOnly }} - + {{ i18n.ts.draft }} @@ -100,6 +100,7 @@ import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ emoji?: any, isRequest: boolean, + isDraftEdit?: boolean, }>(); let dialog = $ref(null); @@ -109,18 +110,18 @@ let aliases: string = $ref(props.emoji ? props.emoji.aliases.join(' ') : ''); let license: string = $ref(props.emoji ? (props.emoji.license ?? '') : ''); let isSensitive = $ref(props.emoji ? props.emoji.isSensitive : false); let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); -let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); +let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref(); let chooseFile: DriveFile|null = $ref(null); -let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); - +let isDraftEdit = $ref(props.isDraftEdit ?? false); +let isDraft = $ref(!!(isDraftEdit || props.isRequest)); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); -const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); +const imgUrl = computed(() => file ? file.url : props.emoji && !isDraftEdit ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); const validation = computed(() => { return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; }); @@ -129,28 +130,6 @@ const emit = defineEmits<{ (ev: 'closed'): void }>(); -async function add() { - const ret = await os.api('admin/emoji/add-draft', { - name: name, - category: category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - fileId: chooseFile.id, - }); - - emit('done', { - updated: { - id: ret.id, - name, - category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - draft: true, - }, - }); - - dialog.close(); -} async function changeImage(ev) { file = await selectFile(ev.currentTarget ?? ev.target, null); const candidate = file.name.replace(/\.(.+)$/, ''); @@ -174,37 +153,13 @@ async function addRole() { async function removeRole(role, ev) { rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); } -async function update() { - await os.apiWithDialog('admin/emoji/update', { - id: props.emoji.id, - name, - category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - fileId: chooseFile?.id, - draft: draft, - }); - - emit('done', { - updated: { - id: props.emoji.id, - name, - category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - draft: draft, - }, - }); - - dialog.close(); -} async function done() { const params = { name, category: category === '' ? null : category, aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''), license: license === '' ? null : license, - draft: draft, + draft: isDraft, isSensitive, localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), @@ -213,12 +168,19 @@ async function done() { if (file) { params.fileId = file.id; } - console.log(props.emoji); + if (props.emoji) { - await os.apiWithDialog('admin/emoji/update', { - id: props.emoji.id, - ...params, - }); + if (isDraftEdit) { + await os.apiWithDialog('admin/emoji/draft-update', { + id: props.emoji.id, + ...params, + }); + } else { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, + ...params, + }); + } emit('done', { updated: { diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 5a87273386..2a0b0fa025 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -201,11 +201,13 @@ async function init(): Promise { ...params, limit: props.pagination.limit ?? 10, }).then(res => { + if (props.pagination.endpoint === 'emoji-drafts') { + res = res.emojis; + } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 3) item._shouldInsertAd_ = true; } - if (res.length === 0 || props.pagination.noPaging) { concatItems(res); more.value = false; @@ -214,7 +216,6 @@ async function init(): Promise { concatItems(res); more.value = true; } - offset.value = res.length; error.value = false; fetching.value = false; @@ -241,6 +242,9 @@ const fetchMore = async (): Promise => { untilId: Array.from(items.value.keys()).at(-1), }), }).then(res => { + if (props.pagination.endpoint === 'emoji-drafts') { + res = res.emojis; + } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 10) item._shouldInsertAd_ = true; @@ -305,6 +309,9 @@ const fetchMoreAhead = async (): Promise => { sinceId: Array.from(items.value.keys()).at(-1), }), }).then(res => { + if (props.pagination.endpoint === 'emoji-drafts') { + res = res.emojis; + } if (res.length === 0) { items.value = concatMapWithArray(items.value, res); more.value = false; diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index ced2881786..ecbefbd086 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -32,13 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
- +
@@ -73,8 +73,7 @@ definePageMetadata(ref({})); let q = $ref(''); let searchEmojis = $ref(null); let selectedTags = $ref(new Set()); -const draftEmojis = customEmojis.value.filter(emoji => emoji.draft); - +const draftEmojis = await os.apiGet('emoji-drafts'); function search() { if ((q === '' || q == null) && selectedTags.size === 0) { searchEmojis = null; diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index bd2cb33f55..d112cfcb7c 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only --> @@ -26,13 +26,13 @@ import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - emoji: { - name: string; - aliases: string[]; - category: string; - url: string; - draft: boolean; - }; + emoji: { + name: string; + aliases: string[]; + category: string; + url: string; + }; + draft?: boolean; }>(); function menu(ev) { @@ -50,7 +50,7 @@ function menu(ev) { text: i18n.ts.info, icon: 'ti ti-info-circle', action: () => { - os.apiGet('emoji', { name: props.emoji.name }).then(res => { + os.apiGet('emoji-drafts', { name: props.emoji.name }).then(res => { os.alert({ type: 'info', text: `License: ${res.license}`, @@ -63,45 +63,45 @@ function menu(ev) { From 761eaeafbb770029d921096f79bd5596b6b2de3e Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 16:40:20 +0900 Subject: [PATCH 13/32] type fix Signed-off-by: mattyatea --- .../src/queue/processors/ImportCustomEmojisProcessorService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 088596bba6..a52af54a39 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -103,7 +103,6 @@ export class ImportCustomEmojisProcessorService { isSensitive: emojiInfo.isSensitive, localOnly: emojiInfo.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: [], - draft: false, }); } From 52e3fefa2023a6677eed6d62bee821de3532b24e Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 18:43:14 +0900 Subject: [PATCH 14/32] ? Signed-off-by: mattyatea --- packages/backend/migration/1697813507149-EmojiDraft.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js index 8aadc76f80..423a76c568 100644 --- a/packages/backend/migration/1697813507149-EmojiDraft.js +++ b/packages/backend/migration/1697813507149-EmojiDraft.js @@ -4,11 +4,11 @@ export class EmojiDraft1697813507149 { async up(queryRunner) { await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); await queryRunner.query(`CREATE INDEX "IDX_j7skve3cwn4pfb2qg9mx1la8iy" ON "emoji_draft" ("name") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e99cd746a9632624dae39cfb35" ON "emoji_draft" ("name") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_aw4zqjtyvxcsh797kbpzxmca6x" ON "emoji_draft" ("name") `); } async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_e99cd746a9632624dae39cfb35"`); + await queryRunner.query(`DROP INDEX "public"."IDX_aw4zqjtyvxcsh797kbpzxmca6x"`); await queryRunner.query(`DROP INDEX "public"."IDX_j7skve3cwn4pfb2qg9mx1la8iy"`); await queryRunner.query(`DROP TABLE "emoji_draft"`); } From b8dd1c5806228a13770c49d6b6e8ff132b44d21e Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 19:01:12 +0900 Subject: [PATCH 15/32] Update migration file Signed-off-by: mattyatea --- packages/backend/migration/1697813507149-EmojiDraft.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js index 423a76c568..10e03ce949 100644 --- a/packages/backend/migration/1697813507149-EmojiDraft.js +++ b/packages/backend/migration/1697813507149-EmojiDraft.js @@ -3,13 +3,11 @@ export class EmojiDraft1697813507149 { async up(queryRunner) { await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_j7skve3cwn4pfb2qg9mx1la8iy" ON "emoji_draft" ("name") `); await queryRunner.query(`CREATE UNIQUE INDEX "IDX_aw4zqjtyvxcsh797kbpzxmca6x" ON "emoji_draft" ("name") `); } async down(queryRunner) { await queryRunner.query(`DROP INDEX "public"."IDX_aw4zqjtyvxcsh797kbpzxmca6x"`); - await queryRunner.query(`DROP INDEX "public"."IDX_j7skve3cwn4pfb2qg9mx1la8iy"`); await queryRunner.query(`DROP TABLE "emoji_draft"`); } } From 00c4624dc8810a124213b87546a021ffb5e17d6f Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 19:54:03 +0900 Subject: [PATCH 16/32] Update EmojiDraft.ts Signed-off-by: mattyatea --- packages/backend/src/models/EmojiDraft.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/models/EmojiDraft.ts b/packages/backend/src/models/EmojiDraft.ts index 6d469fd200..1d4ee95319 100644 --- a/packages/backend/src/models/EmojiDraft.ts +++ b/packages/backend/src/models/EmojiDraft.ts @@ -17,7 +17,6 @@ export class MiEmojiDraft { }) public updatedAt: Date | null; - @Index() @Column('varchar', { length: 128, }) From 66e8548403175c8370e4bf30a845f757db93be7f Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 19:57:05 +0900 Subject: [PATCH 17/32] Update misskey-js.api.md Signed-off-by: mattyatea --- packages/misskey-js/etc/misskey-js.api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 7e513f0780..24d4e2eae2 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -3022,6 +3022,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/entities.ts:113:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts +// src/entities.ts:610:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From c15ac0580069e2682f62fb5ade81cdc919b1873c Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sat, 21 Oct 2023 23:31:37 +0900 Subject: [PATCH 18/32] Update CHANGELOG.md Signed-off-by: mattyatea --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae566fd1d7..854c5f70ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ## 2023.x.x (unreleased) ### General -- +- Feat: 絵文字のリクエスト機能が追加されました ## Client - Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました @@ -30,7 +30,6 @@ ### General - Feat: アンテナでローカルの投稿のみ収集できるようになりました - Feat: サーバーサイレンス機能が追加されました -- Feat: 絵文字のリクエスト機能が追加されました - Enhance: 新規にフォローした人の返信をデフォルトでTLに追加できるオプションを追加 - Enhance: HTL/LTL/STLを2023.10.0アップデート以前まで遡れるように - Enhance: フォロー/フォロー解除したときに過去分のHTLにも含まれる投稿が反映されるように From f382520593989b8ff857d465b1c68864a9e32a6e Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sun, 22 Oct 2023 21:17:20 +0900 Subject: [PATCH 19/32] Update misskey-js.api.md Signed-off-by: mattyatea --- packages/misskey-js/etc/misskey-js.api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0a6806ae60..29cdd5eead 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -284,6 +284,7 @@ type CustomEmoji = { url: string; category: string; aliases: string[]; + draft: boolean; }; // @public (undocumented) @@ -3024,7 +3025,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:115:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:611:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:612:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From 53d250dddf02a450191f076fc71ef35e866232a1 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Tue, 24 Oct 2023 15:37:43 +0900 Subject: [PATCH 20/32] =?UTF-8?q?draft-update=20=E3=81=8B=E3=82=89=20updat?= =?UTF-8?q?e-draft=20=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit admin/emoji/list-draft を追加 Update MkPagination.vue Signed-off-by: mattyatea --- .../backend/src/server/api/EndpointsModule.ts | 12 +- packages/backend/src/server/api/endpoints.ts | 6 +- .../api/endpoints/admin/emoji/list-draft.ts | 108 ++++++++++++++++++ .../{draft-update.ts => update-draft.ts} | 5 +- .../src/server/api/endpoints/emoji-drafts.ts | 4 +- .../src/components/MkCustomEmojiEditDraft.vue | 4 +- .../src/components/MkEmojiEditDialog.vue | 2 +- .../frontend/src/components/MkPagination.vue | 9 -- 8 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts rename packages/backend/src/server/api/endpoints/admin/emoji/{draft-update.ts => update-draft.ts} (95%) diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 92e6da4ba5..6db60b456f 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -36,12 +36,13 @@ import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; +import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -396,12 +397,13 @@ const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useCla const $admin_emoji_importZip: Provider = { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }; const $admin_emoji_listRemote: Provider = { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }; const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }; +const $admin_emoji_listDraft: Provider = { provide: 'ep:admin/emoji/list-draft', useClass: ep___admin_emoji_listDraft.default }; const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }; const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }; const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default }; const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; -const $admin_emoji_draftUpdate: Provider = { provide: 'ep:admin/emoji/draft-update', useClass: ep___admin_emoji_draftUpdate.default }; +const $admin_emoji_updateDraft: Provider = { provide: 'ep:admin/emoji/update-draft', useClass: ep___admin_emoji_updateDraft.default }; const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; @@ -760,12 +762,13 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, + $admin_emoji_listDraft, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_draftUpdate, + $admin_emoji_updateDraft, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -1118,12 +1121,13 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, + $admin_emoji_listDraft, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_draftUpdate, + $admin_emoji_updateDraft, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 26b91b1f41..37f60cf210 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -36,12 +36,13 @@ import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; +import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -393,13 +394,14 @@ const eps = [ ['admin/emoji/delete', ep___admin_emoji_delete], ['admin/emoji/import-zip', ep___admin_emoji_importZip], ['admin/emoji/list-remote', ep___admin_emoji_listRemote], + ['admin/emoji/list-draft', ep___admin_emoji_listDraft], ['admin/emoji/list', ep___admin_emoji_list], ['admin/emoji/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk], ['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk], ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], - ['admin/emoji/draft-update', ep___admin_emoji_draftUpdate], + ['admin/emoji/update-draft', ep___admin_emoji_updateDraft], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts new file mode 100644 index 0000000000..95dea00426 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { QueryService } from '@/core/QueryService.js'; +import { DI } from '@/di-symbols.js'; +import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +//import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + url: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + query: { type: 'string', nullable: true, default: null }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + + private emojiDraftsEntityService: EmojiDraftsEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const q = this.queryService.makePaginationQuery(this.emojiDraftsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + + let emojis: MiEmojiDraft[]; + + if (ps.query) { + //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); + //const emojis = await q.limit(ps.limit).getMany(); + + emojis = await q.getMany(); + const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g); + + if (queryarry) { + emojis = emojis.filter(emoji => + queryarry.includes(`:${emoji.name}:`), + ); + } else { + emojis = emojis.filter(emoji => + emoji.name.includes(ps.query!) || + emoji.aliases.some(a => a.includes(ps.query!)) || + emoji.category?.includes(ps.query!)); + } + emojis.splice(ps.limit + 1); + } else { + emojis = await q.limit(ps.limit).getMany(); + } + + return this.emojiDraftsEntityService.packDetailedMany(emojis); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts similarity index 95% rename from packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts index 3e1aa4131a..79eee1b019 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -67,9 +67,6 @@ export default class extends Endpoint { // eslint- @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, - private customEmojiService: CustomEmojiService, private driveFileEntityService: DriveFileEntityService, ) { diff --git a/packages/backend/src/server/api/endpoints/emoji-drafts.ts b/packages/backend/src/server/api/endpoints/emoji-drafts.ts index 5b23dac093..13afee2a08 100644 --- a/packages/backend/src/server/api/endpoints/emoji-drafts.ts +++ b/packages/backend/src/server/api/endpoints/emoji-drafts.ts @@ -27,7 +27,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'EmojiDraftDetailed', + ref: 'EmojiDraftSimple', }, }, }, @@ -58,7 +58,7 @@ export default class extends Endpoint { // eslint- }); return { - emojis: await this.emojiDraftsEntityService.packDetailedMany(emojis), + emojis: await this.emojiDraftsEntityService.packSimpleMany(emojis), }; }); } diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue index d5446eb18e..6a97ae2019 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue @@ -45,7 +45,7 @@ const emojisDraftPaginationComponent = shallowRef ({ query: (query.value && query.value !== '') ? query.value : null, @@ -81,7 +81,7 @@ async function undrafted(emoji) { }); if (canceled) return; - await os.api('admin/emoji/draft-update', { + await os.api('admin/emoji/update-draft', { id: emoji.id, fileId: emoji.fileId, name: emoji.name, diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index cc0c17fd10..150d0b63c1 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -171,7 +171,7 @@ async function done() { if (props.emoji) { if (isDraftEdit) { - await os.apiWithDialog('admin/emoji/draft-update', { + await os.apiWithDialog('admin/emoji/update-draft', { id: props.emoji.id, ...params, }); diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 2a0b0fa025..ae309da82e 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -201,9 +201,6 @@ async function init(): Promise { ...params, limit: props.pagination.limit ?? 10, }).then(res => { - if (props.pagination.endpoint === 'emoji-drafts') { - res = res.emojis; - } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 3) item._shouldInsertAd_ = true; @@ -242,9 +239,6 @@ const fetchMore = async (): Promise => { untilId: Array.from(items.value.keys()).at(-1), }), }).then(res => { - if (props.pagination.endpoint === 'emoji-drafts') { - res = res.emojis; - } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 10) item._shouldInsertAd_ = true; @@ -309,9 +303,6 @@ const fetchMoreAhead = async (): Promise => { sinceId: Array.from(items.value.keys()).at(-1), }), }).then(res => { - if (props.pagination.endpoint === 'emoji-drafts') { - res = res.emojis; - } if (res.length === 0) { items.value = concatMapWithArray(items.value, res); more.value = false; From 8fd5eb7010ec91f3a189b967ccbe35a8ce373463 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Tue, 24 Oct 2023 16:55:22 +0900 Subject: [PATCH 21/32] =?UTF-8?q?draft=E3=81=8B=E3=82=89request=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea --- locales/index.d.ts | 6 +-- locales/ja-JP.yml | 6 +-- .../migration/1697813507149-EmojiDraft.js | 13 ----- packages/backend/src/core/CoreModule.ts | 12 ++--- .../backend/src/core/CustomEmojiService.ts | 38 +++++++------- ...rvice.ts => EmojiRequestsEntityService.ts} | 22 ++++---- packages/backend/src/di-symbols.ts | 2 +- packages/backend/src/misc/json-schema.ts | 6 +-- .../models/{EmojiDraft.ts => EmojiRequest.ts} | 4 +- .../backend/src/models/RepositoryModule.ts | 12 ++--- packages/backend/src/models/_.ts | 6 +-- .../backend/src/models/json-schema/emoji.ts | 4 +- packages/backend/src/postgres.ts | 4 +- .../backend/src/server/api/EndpointsModule.ts | 32 ++++++------ packages/backend/src/server/api/endpoints.ts | 16 +++--- .../emoji/{add-draft.ts => add-request.ts} | 6 +-- .../api/endpoints/admin/emoji/delete.ts | 6 +-- .../emoji/{list-draft.ts => list-request.ts} | 18 +++---- .../{update-draft.ts => update-request.ts} | 14 ++--- .../api/endpoints/admin/emoji/update.ts | 10 ++-- .../{emoji-drafts.ts => emoji-requests.ts} | 17 +++--- .../src/components/MkCustomEmojiEditLocal.vue | 8 +-- ...Draft.vue => MkCustomEmojiEditRequest.vue} | 52 +++++++++---------- .../src/components/MkEmojiEditDialog.vue | 30 +++++------ .../frontend/src/components/MkEmojiPicker.vue | 2 +- packages/frontend/src/pages/about.emojis.vue | 12 ++--- .../src/pages/custom-emojis-manager.vue | 10 ++-- packages/frontend/src/pages/emojis.emoji.vue | 10 ++-- packages/misskey-js/etc/misskey-js.api.md | 2 +- packages/misskey-js/src/entities.ts | 2 +- 30 files changed, 184 insertions(+), 198 deletions(-) delete mode 100644 packages/backend/migration/1697813507149-EmojiDraft.js rename packages/backend/src/core/entities/{EmojiDraftsEntityService.ts => EmojiRequestsEntityService.ts} (71%) rename packages/backend/src/models/{EmojiDraft.ts => EmojiRequest.ts} (95%) rename packages/backend/src/server/api/endpoints/admin/emoji/{add-draft.ts => add-request.ts} (90%) rename packages/backend/src/server/api/endpoints/admin/emoji/{list-draft.ts => list-request.ts} (79%) rename packages/backend/src/server/api/endpoints/admin/emoji/{update-draft.ts => update-request.ts} (89%) rename packages/backend/src/server/api/endpoints/{emoji-drafts.ts => emoji-requests.ts} (66%) rename packages/frontend/src/components/{MkCustomEmojiEditDraft.vue => MkCustomEmojiEditRequest.vue} (73%) diff --git a/locales/index.d.ts b/locales/index.d.ts index 81352c786c..3090f0cd04 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -261,7 +261,7 @@ export interface Locale { "imageUrl": string; "remove": string; "removed": string; - "undraftAreYouSure": string; + "requestApprovalAreYouSure": string; "removeAreYouSure": string; "deleteAreYouSure": string; "resetAreYouSure": string; @@ -1027,8 +1027,8 @@ export interface Locale { "sensitiveWordsDescription2": string; "notesSearchNotAvailable": string; "license": string; - "draft": string; - "undrafted": string; + "requestPending": string; + "requestApproval": string; "requestEmojis": string; "emojiNameValidation": string; "unfavoriteConfirm": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f6a9050331..92c209c681 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -258,7 +258,7 @@ announcements: "お知らせ" imageUrl: "画像URL" remove: "削除" removed: "削除しました" -undraftAreYouSure: "「{x}」をドラフト解除しますか?" +requestApprovalAreYouSure: "「{x}」のリクエストを承認しますか?" removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" resetAreYouSure: "リセットしますか?" @@ -1024,8 +1024,8 @@ sensitiveWordsDescription: "設定したワードが含まれるノートの公 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" -draft: "下書き" -undrafted: "下書き解除" +requestPending: "申請中" +requestApproval: "リクエストを承認" requestEmojis: "リクエストされている絵文字" emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js deleted file mode 100644 index 10e03ce949..0000000000 --- a/packages/backend/migration/1697813507149-EmojiDraft.js +++ /dev/null @@ -1,13 +0,0 @@ -export class EmojiDraft1697813507149 { - name = 'EmojiDraft1697813507149' - - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_aw4zqjtyvxcsh797kbpzxmca6x" ON "emoji_draft" ("name") `); - } - - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_aw4zqjtyvxcsh797kbpzxmca6x"`); - await queryRunner.query(`DROP TABLE "emoji_draft"`); - } -} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 295197acd2..10fceb87e7 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -87,7 +87,7 @@ import { ClipEntityService } from './entities/ClipEntityService.js'; import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js'; import { EmojiEntityService } from './entities/EmojiEntityService.js'; -import { EmojiDraftsEntityService } from './entities/EmojiDraftsEntityService.js'; +import { EmojiRequestsEntityService } from './entities/EmojiRequestsEntityService.js'; import { FollowingEntityService } from './entities/FollowingEntityService.js'; import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js'; import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js'; @@ -220,7 +220,7 @@ const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService }; const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService }; const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService }; -const $EmojiDraftsEntityService: Provider = { provide: 'EmojiDraftsEntityService', useExisting: EmojiDraftsEntityService }; +const $EmojiRequestsEntityService: Provider = { provide: 'EmojiRequestsEntityService', useExisting: EmojiRequestsEntityService }; const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService }; const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService }; const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService }; @@ -353,7 +353,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, - EmojiDraftsEntityService, + EmojiRequestsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -481,7 +481,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, - $EmojiDraftsEntityService, + $EmojiRequestsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, @@ -609,7 +609,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, - EmojiDraftsEntityService, + EmojiRequestsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -736,7 +736,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, - $EmojiDraftsEntityService, + $EmojiRequestsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 1aa6377be3..1346d3d839 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -12,13 +12,13 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; -import type { EmojisRepository, EmojiDraftsRepository, MiRole, MiUser } from '@/models/_.js'; +import type { EmojisRepository, EmojiRequestsRepository, MiRole, MiUser } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @@ -34,8 +34,8 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, private utilityService: UtilityService, private idService: IdService, @@ -60,7 +60,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async draft(data: { + public async Request(data: { driveFile: MiDriveFile; name: string; category: string | null; @@ -68,8 +68,8 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; - }, me?: MiUser): Promise { - const emoji = await this.emojiDraftsRepository.insert({ + }, me?: MiUser): Promise { + const emoji = await this.emojiRequestsRepository.insert({ id: this.idService.gen(), updatedAt: new Date(), name: data.name, @@ -82,7 +82,7 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, fileId: data.driveFile.id, - }).then(x => this.emojiDraftsRepository.findOneByOrFail(x.identifiers[0])); + }).then(x => this.emojiRequestsRepository.findOneByOrFail(x.identifiers[0])); if (me) { this.moderationLogService.log(me, 'addCustomEmoji', { @@ -196,7 +196,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async draftUpdate(id: MiEmoji['id'], data: { + public async RequestUpdate(id: MiEmoji['id'], data: { driveFile?: MiDriveFile; name?: string; category?: string | null; @@ -205,11 +205,11 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive?: boolean; localOnly?: boolean; }, moderator?: MiUser): Promise { - const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); - const sameNameEmoji = await this.emojiDraftsRepository.findOneBy({ name: data.name }); + const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id }); + const sameNameEmoji = await this.emojiRequestsRepository.findOneBy({ name: data.name }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); - await this.emojiDraftsRepository.update(emoji.id, { + await this.emojiRequestsRepository.update(emoji.id, { updatedAt: new Date(), name: data.name, category: data.category, @@ -332,10 +332,10 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async draftDelete(id: MiEmojiDraft['id']) { - const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); + public async RequestDelete(id: MiEmojiRequest['id']) { + const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id }); - await this.emojiDraftsRepository.delete(emoji.id); + await this.emojiRequestsRepository.delete(emoji.id); } @bindThis public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) { @@ -458,8 +458,8 @@ export class CustomEmojiService implements OnApplicationShutdown { return this.emojisRepository.exist({ where: { name, host: IsNull() } }); } @bindThis - public checkDraftDuplicate(name: string): Promise { - return this.emojiDraftsRepository.exist({ where: { name } }); + public checkRequestDuplicate(name: string): Promise { + return this.emojiRequestsRepository.exist({ where: { name } }); } @bindThis @@ -468,8 +468,8 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public getEmojiDraftById(id: string): Promise { - return this.emojiDraftsRepository.findOneBy({ id }); + public getEmojiRequestById(id: string): Promise { + return this.emojiRequestsRepository.findOneBy({ id }); } @bindThis public dispose(): void { diff --git a/packages/backend/src/core/entities/EmojiDraftsEntityService.ts b/packages/backend/src/core/entities/EmojiRequestsEntityService.ts similarity index 71% rename from packages/backend/src/core/entities/EmojiDraftsEntityService.ts rename to packages/backend/src/core/entities/EmojiRequestsEntityService.ts index de1a621553..46308e654c 100644 --- a/packages/backend/src/core/entities/EmojiDraftsEntityService.ts +++ b/packages/backend/src/core/entities/EmojiRequestsEntityService.ts @@ -5,24 +5,24 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { EmojiRequestsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; @Injectable() -export class EmojiDraftsEntityService { +export class EmojiRequestsEntityService { constructor( - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, ) { } @bindThis public async packSimple( - src: MiEmojiDraft['id'] | MiEmojiDraft, - ): Promise> { - const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + src: MiEmojiRequest['id'] | MiEmojiRequest, + ): Promise> { + const emoji = typeof src === 'object' ? src : await this.emojiRequestsRepository.findOneByOrFail({ id: src }); return { aliases: emoji.aliases, @@ -43,9 +43,9 @@ export class EmojiDraftsEntityService { @bindThis public async packDetailed( - src: MiEmojiDraft['id'] | MiEmojiDraft, - ): Promise> { - const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + src: MiEmojiRequest['id'] | MiEmojiRequest, + ): Promise> { + const emoji = typeof src === 'object' ? src : await this.emojiRequestsRepository.findOneByOrFail({ id: src }); return { id: emoji.id, diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 7da4e75ae7..8c56300750 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -40,7 +40,7 @@ export const DI = { followRequestsRepository: Symbol('followRequestsRepository'), instancesRepository: Symbol('instancesRepository'), emojisRepository: Symbol('emojisRepository'), - emojiDraftsRepository: Symbol('emojiDraftsRepository'), + emojiRequestsRepository: Symbol('emojiRequestsRepository'), driveFilesRepository: Symbol('driveFilesRepository'), driveFoldersRepository: Symbol('driveFoldersRepository'), metasRepository: Symbol('metasRepository'), diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index d203d5fa82..d99a3edac4 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -33,7 +33,7 @@ import { packedClipSchema } from '@/models/json-schema/clip.js'; import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js'; import { packedQueueCountSchema } from '@/models/json-schema/queue.js'; import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js'; -import { packedEmojiDetailedSchema, packedEmojiDraftSimpleSchema, packedEmojiSimpleSchema, packedEmojiDraftDetailedSchema } from '@/models/json-schema/emoji.js'; +import { packedEmojiDetailedSchema, packedEmojiRequestSimpleSchema, packedEmojiSimpleSchema, packedEmojiRequestDetailedSchema } from '@/models/json-schema/emoji.js'; import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; @@ -69,9 +69,9 @@ export const refs = { FederationInstance: packedFederationInstanceSchema, GalleryPost: packedGalleryPostSchema, EmojiSimple: packedEmojiSimpleSchema, - EmojiDraftSimple: packedEmojiDraftSimpleSchema, + EmojiRequestSimple: packedEmojiRequestSimpleSchema, EmojiDetailed: packedEmojiDetailedSchema, - EmojiDraftDetailed: packedEmojiDraftDetailedSchema, + EmojiRequestDetailed: packedEmojiRequestDetailedSchema, Flash: packedFlashSchema, }; diff --git a/packages/backend/src/models/EmojiDraft.ts b/packages/backend/src/models/EmojiRequest.ts similarity index 95% rename from packages/backend/src/models/EmojiDraft.ts rename to packages/backend/src/models/EmojiRequest.ts index 1d4ee95319..53a5563f7e 100644 --- a/packages/backend/src/models/EmojiDraft.ts +++ b/packages/backend/src/models/EmojiRequest.ts @@ -6,9 +6,9 @@ import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; import { id } from './util/id.js'; -@Entity('emoji_draft') +@Entity('emoji_request') @Index(['name'], { unique: true }) -export class MiEmojiDraft { +export class MiEmojiRequest { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index b8202c0f81..bc5c61c547 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,7 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiEmojiDraft, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiEmojiRequest, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -171,9 +171,9 @@ const $emojisRepository: Provider = { inject: [DI.db], }; -const $emojiDraftsRepository: Provider = { - provide: DI.emojiDraftsRepository, - useFactory: (db: DataSource) => db.getRepository(MiEmojiDraft), +const $emojiRequestsRepository: Provider = { + provide: DI.emojiRequestsRepository, + useFactory: (db: DataSource) => db.getRepository(MiEmojiRequest), inject: [DI.db], }; @@ -436,7 +436,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, - $emojiDraftsRepository, + $emojiRequestsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, @@ -504,7 +504,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, - $emojiDraftsRepository, + $emojiRequestsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index f72c7e1065..14b94e30fc 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -21,6 +21,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -68,7 +69,6 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; import type { Repository } from 'typeorm'; export { @@ -90,7 +90,7 @@ export { MiDriveFile, MiDriveFolder, MiEmoji, - MiEmojiDraft, + MiEmojiRequest, MiFollowing, MiFollowRequest, MiGalleryLike, @@ -158,7 +158,7 @@ export type ClipFavoritesRepository = Repository; export type DriveFilesRepository = Repository; export type DriveFoldersRepository = Repository; export type EmojisRepository = Repository; -export type EmojiDraftsRepository = Repository; +export type EmojiRequestsRepository = Repository; export type FollowingsRepository = Repository; export type FollowRequestsRepository = Repository; export type GalleryLikesRepository = Repository; diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 70da8dc67f..56b888c62a 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -42,7 +42,7 @@ export const packedEmojiSimpleSchema = { }, }, } as const; -export const packedEmojiDraftSimpleSchema = { +export const packedEmojiRequestSimpleSchema = { type: 'object', properties: { aliases: { @@ -131,7 +131,7 @@ export const packedEmojiDetailedSchema = { }, } as const; -export const packedEmojiDraftDetailedSchema = { +export const packedEmojiRequestDetailedSchema = { type: 'object', properties: { id: { diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index d62ad62ba6..a22b39f766 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -29,7 +29,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -163,7 +163,7 @@ export const entities = [ MiPoll, MiPollVote, MiEmoji, - MiEmojiDraft, + MiEmojiRequest, MiHashtag, MiSwSubscription, MiAbuseUserReport, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 6db60b456f..6a86447c42 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -29,20 +29,20 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; -import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; +import * as ep___admin_emoji_addRequest from './endpoints/admin/emoji/add-request.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; -import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; +import * as ep___admin_emoji_listRequest from './endpoints/admin/emoji/list-request.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; +import * as ep___admin_emoji_updateRequest from './endpoints/admin/emoji/update-request.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -251,7 +251,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; -import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; +import * as ep___emojiRequests from './endpoints/emoji-requests.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -390,20 +390,20 @@ const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; -const $admin_emoji_addDraft: Provider = { provide: 'ep:admin/emoji/add-draft', useClass: ep___admin_emoji_addDraft.default }; +const $admin_emoji_addRequest: Provider = { provide: 'ep:admin/emoji/add-request', useClass: ep___admin_emoji_addRequest.default }; const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; const $admin_emoji_importZip: Provider = { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }; const $admin_emoji_listRemote: Provider = { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }; const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }; -const $admin_emoji_listDraft: Provider = { provide: 'ep:admin/emoji/list-draft', useClass: ep___admin_emoji_listDraft.default }; +const $admin_emoji_listRequest: Provider = { provide: 'ep:admin/emoji/list-request', useClass: ep___admin_emoji_listRequest.default }; const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }; const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }; const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default }; const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; -const $admin_emoji_updateDraft: Provider = { provide: 'ep:admin/emoji/update-draft', useClass: ep___admin_emoji_updateDraft.default }; +const $admin_emoji_updateRequest: Provider = { provide: 'ep:admin/emoji/update-request', useClass: ep___admin_emoji_updateRequest.default }; const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; @@ -612,7 +612,7 @@ const $invite_list: Provider = { provide: 'ep:invite/list', useClass: ep___invit const $invite_limit: Provider = { provide: 'ep:invite/limit', useClass: ep___invite_limit.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; -const $emoji_drafts: Provider = { provide: 'ep:emoji-drafts', useClass: ep___emojiDrafts.default }; +const $emoji_requests: Provider = { provide: 'ep:emoji-requests', useClass: ep___emojiRequests.default }; const $emoji: Provider = { provide: 'ep:emoji', useClass: ep___emoji.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; @@ -755,20 +755,20 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, - $admin_emoji_addDraft, + $admin_emoji_addRequest, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, - $admin_emoji_listDraft, + $admin_emoji_listRequest, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_updateDraft, + $admin_emoji_updateRequest, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -977,7 +977,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, - $emoji_drafts, + $emoji_requests, $emoji, $miauth_genToken, $mute_create, @@ -1114,20 +1114,20 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, - $admin_emoji_addDraft, + $admin_emoji_addRequest, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, - $admin_emoji_listDraft, + $admin_emoji_listRequest, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_updateDraft, + $admin_emoji_updateRequest, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -1336,7 +1336,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, - $emoji_drafts, + $emoji_requests, $emoji, $miauth_genToken, $mute_create, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 37f60cf210..74df9a959a 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -29,20 +29,20 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; -import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; +import * as ep___admin_emoji_addRequest from './endpoints/admin/emoji/add-request.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; -import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; +import * as ep___admin_emoji_listRequest from './endpoints/admin/emoji/list-request.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; +import * as ep___admin_emoji_updateRequest from './endpoints/admin/emoji/update-request.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -251,7 +251,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; -import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; +import * as ep___emojiRequests from './endpoints/emoji-requests.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -388,20 +388,20 @@ const eps = [ ['admin/drive/show-file', ep___admin_drive_showFile], ['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk], ['admin/emoji/add', ep___admin_emoji_add], - ['admin/emoji/add-draft', ep___admin_emoji_addDraft], + ['admin/emoji/add-request', ep___admin_emoji_addRequest], ['admin/emoji/copy', ep___admin_emoji_copy], ['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk], ['admin/emoji/delete', ep___admin_emoji_delete], ['admin/emoji/import-zip', ep___admin_emoji_importZip], ['admin/emoji/list-remote', ep___admin_emoji_listRemote], - ['admin/emoji/list-draft', ep___admin_emoji_listDraft], ['admin/emoji/list', ep___admin_emoji_list], + ['admin/emoji/list-request', ep___admin_emoji_listRequest], ['admin/emoji/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk], ['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk], ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], - ['admin/emoji/update-draft', ep___admin_emoji_updateDraft], + ['admin/emoji/update-request', ep___admin_emoji_updateRequest], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], @@ -610,7 +610,7 @@ const eps = [ ['invite/limit', ep___invite_limit], ['meta', ep___meta], ['emojis', ep___emojis], - ['emoji-drafts', ep___emojiDrafts], + ['emoji-requests', ep___emojiRequests], ['emoji', ep___emoji], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts similarity index 90% rename from packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts index af85b2c10a..50ffb89b06 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts @@ -61,14 +61,14 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); - const isDraftDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + const isRequestDuplicate = await this.customEmojiService.checkRequestDuplicate(ps.name); - if (isDuplicate || isDraftDuplicate) throw new ApiError(meta.errors.duplicateName); + if (isDuplicate || isRequestDuplicate) throw new ApiError(meta.errors.duplicateName); const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const emoji = await this.customEmojiService.draft({ + const emoji = await this.customEmojiService.Request({ driveFile, name: ps.name, category: ps.category ?? null, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index b3ec350272..9711db5a11 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -37,12 +37,12 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const emoji = await this.customEmojiService.getEmojiById(ps.id); - const draftEmoji = await this.customEmojiService.getEmojiDraftById(ps.id); + const RequestEmoji = await this.customEmojiService.getEmojiRequestById(ps.id); if (emoji != null) { await this.customEmojiService.delete(ps.id, me); } - if (draftEmoji != null) { - await this.customEmojiService.draftDelete(ps.id); + if (RequestEmoji != null) { + await this.customEmojiService.RequestDelete(ps.id); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-request.ts similarity index 79% rename from packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/list-request.ts index 95dea00426..f5faca972f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-request.ts @@ -5,11 +5,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { EmojiDraftsRepository } from '@/models/_.js'; -import type { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import type { EmojiRequestsRepository } from '@/models/_.js'; +import type { MiEmojiRequest } from '@/models/EmojiRequest.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; -import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +import { EmojiRequestsEntityService } from '@/core/entities/EmojiRequestsEntityService.js'; //import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; export const meta = { @@ -69,16 +69,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, - private emojiDraftsEntityService: EmojiDraftsEntityService, + private emojiRequestsEntityService: EmojiRequestsEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = this.queryService.makePaginationQuery(this.emojiDraftsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + const q = this.queryService.makePaginationQuery(this.emojiRequestsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); - let emojis: MiEmojiDraft[]; + let emojis: MiEmojiRequest[]; if (ps.query) { //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); @@ -102,7 +102,7 @@ export default class extends Endpoint { // eslint- emojis = await q.limit(ps.limit).getMany(); } - return this.emojiDraftsEntityService.packDetailedMany(emojis); + return this.emojiRequestsEntityService.packDetailedMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts similarity index 89% rename from packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts index 79eee1b019..98e50389cc 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts @@ -56,7 +56,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, - draft: { type: 'boolean' }, + Request: { type: 'boolean' }, }, required: ['id', 'name', 'aliases'], } as const; @@ -72,22 +72,22 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { let driveFile; - const isDraft = !!ps.draft; + const isRequest = !!ps.Request; if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } - const emoji = await this.customEmojiService.getEmojiDraftById(ps.id); + const emoji = await this.customEmojiService.getEmojiRequestById(ps.id); if (emoji != null) { if (ps.name !== emoji.name) { - const isDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + const isDuplicate = await this.customEmojiService.checkRequestDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); } } else { throw new ApiError(meta.errors.noSuchEmoji); } - if (!isDraft) { + if (!isRequest) { const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); if (file === null) throw new ApiError(meta.errors.noSuchFile); await this.customEmojiService.add({ @@ -101,9 +101,9 @@ export default class extends Endpoint { // eslint- localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: [], }, me); - await this.customEmojiService.draftDelete(ps.id); + await this.customEmojiService.RequestDelete(ps.id); } else { - await this.customEmojiService.draftUpdate(ps.id, { + await this.customEmojiService.RequestUpdate(ps.id, { name: ps.name, category: ps.category ?? null, aliases: ps.aliases ?? [], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index f996560ce1..7f7dc774d5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -56,7 +56,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, - draft: { type: 'boolean' }, + Request: { type: 'boolean' }, }, required: ['id', 'name', 'aliases'], } as const; @@ -72,7 +72,7 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { let driveFile; - const isDraft = !!ps.draft; + const isRequest = !!ps.Request; if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); @@ -87,7 +87,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchEmoji); } - if (!isDraft) { + if (!isRequest) { await this.customEmojiService.update(ps.id, { driveFile, name: ps.name, @@ -101,7 +101,7 @@ export default class extends Endpoint { // eslint- } else { const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); if (file === null) throw new ApiError(meta.errors.noSuchFile); - await this.customEmojiService.draft({ + await this.customEmojiService.Request({ driveFile: file, name: ps.name, category: ps.category ?? null, diff --git a/packages/backend/src/server/api/endpoints/emoji-drafts.ts b/packages/backend/src/server/api/endpoints/emoji-requests.ts similarity index 66% rename from packages/backend/src/server/api/endpoints/emoji-drafts.ts rename to packages/backend/src/server/api/endpoints/emoji-requests.ts index 13afee2a08..63ca88e93e 100644 --- a/packages/backend/src/server/api/endpoints/emoji-drafts.ts +++ b/packages/backend/src/server/api/endpoints/emoji-requests.ts @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { EmojiRequestsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +import { EmojiRequestsEntityService } from '@/core/entities/EmojiRequestsEntityService.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -27,7 +26,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'EmojiDraftSimple', + ref: 'EmojiRequestSimple', }, }, }, @@ -44,13 +43,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, - private emojiDraftsEntityService: EmojiDraftsEntityService, + private emojiRequestsEntityService: EmojiRequestsEntityService, ) { super(meta, paramDef, async () => { - const emojis = await this.emojiDraftsRepository.find({ + const emojis = await this.emojiRequestsRepository.find({ order: { category: 'ASC', name: 'ASC', @@ -58,7 +57,7 @@ export default class extends Endpoint { // eslint- }); return { - emojis: await this.emojiDraftsEntityService.packSimpleMany(emojis), + emojis: await this.emojiRequestsEntityService.packSimpleMany(emojis), }; }); } diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 7112a38430..7060f2080b 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -20,10 +20,10 @@