Merge remote-tracking branch 'misskey-mattyatea/emoji-request' into develop
# Conflicts: # CHANGELOG.md # locales/index.d.ts # locales/ja-JP.yml # packages/backend/src/core/CustomEmojiService.ts # packages/backend/src/models/RepositoryModule.ts # packages/backend/src/server/api/EndpointsModule.ts # packages/backend/src/server/api/endpoints.ts # packages/backend/src/server/api/endpoints/admin/emoji/update.ts # packages/frontend/src/components/MkCustomEmojiEditLocal.vue # packages/frontend/src/components/MkCustomEmojiEditRemote.vue # packages/frontend/src/components/MkEmojiEditDialog.vue # packages/frontend/src/pages/about.emojis.vue # packages/frontend/src/pages/admin/roles.editor.vue # packages/frontend/src/pages/custom-emojis-manager.vue # packages/frontend/src/pages/emojis.emoji.vue
This commit is contained in:
commit
68b48bc16f
|
@ -37,7 +37,6 @@
|
||||||
- Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
|
- Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
|
||||||
- Enhance: ローカリゼーションの更新
|
- Enhance: ローカリゼーションの更新
|
||||||
- Enhance: 依存関係の更新
|
- Enhance: 依存関係の更新
|
||||||
- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
|
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
|
- Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
|
||||||
|
|
6
locales/index.d.ts
vendored
6
locales/index.d.ts
vendored
|
@ -279,6 +279,7 @@ export interface Locale {
|
||||||
"imageUrl": string;
|
"imageUrl": string;
|
||||||
"remove": string;
|
"remove": string;
|
||||||
"removed": string;
|
"removed": string;
|
||||||
|
"requestApprovalAreYouSure": string;
|
||||||
"removeAreYouSure": string;
|
"removeAreYouSure": string;
|
||||||
"deleteAreYouSure": string;
|
"deleteAreYouSure": string;
|
||||||
"undraftAreYouSure": string;
|
"undraftAreYouSure": string;
|
||||||
|
@ -869,9 +870,9 @@ export interface Locale {
|
||||||
"high": string;
|
"high": string;
|
||||||
"middle": string;
|
"middle": string;
|
||||||
"low": string;
|
"low": string;
|
||||||
|
"list": string;
|
||||||
"GamingSpeedChange": string;
|
"GamingSpeedChange": string;
|
||||||
"GamingSpeedChangeInfo": string;
|
"GamingSpeedChangeInfo": string;
|
||||||
"list": string;
|
|
||||||
"emailNotConfiguredWarning": string;
|
"emailNotConfiguredWarning": string;
|
||||||
"ratio": string;
|
"ratio": string;
|
||||||
"showVisibilityColor": string;
|
"showVisibilityColor": string;
|
||||||
|
@ -1077,6 +1078,9 @@ export interface Locale {
|
||||||
"license": string;
|
"license": string;
|
||||||
"draft": string;
|
"draft": string;
|
||||||
"undrafted": string;
|
"undrafted": string;
|
||||||
|
"requestPending": string;
|
||||||
|
"approval": string;
|
||||||
|
"requestingEmojis": string;
|
||||||
"unfavoriteConfirm": string;
|
"unfavoriteConfirm": string;
|
||||||
"myClips": string;
|
"myClips": string;
|
||||||
"drivecleaner": string;
|
"drivecleaner": string;
|
||||||
|
|
|
@ -276,6 +276,7 @@ announcements: "お知らせ"
|
||||||
imageUrl: "画像URL"
|
imageUrl: "画像URL"
|
||||||
remove: "削除"
|
remove: "削除"
|
||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
|
requestApprovalAreYouSure: "「{x}」のリクエストを承認しますか?"
|
||||||
removeAreYouSure: "「{x}」を削除しますか?"
|
removeAreYouSure: "「{x}」を削除しますか?"
|
||||||
deleteAreYouSure: "「{x}」を削除しますか?"
|
deleteAreYouSure: "「{x}」を削除しますか?"
|
||||||
undraftAreYouSure: "「{x}」をドラフト解除しますか?"
|
undraftAreYouSure: "「{x}」をドラフト解除しますか?"
|
||||||
|
@ -866,9 +867,9 @@ priority: "優先度"
|
||||||
high: "高"
|
high: "高"
|
||||||
middle: "中"
|
middle: "中"
|
||||||
low: "低"
|
low: "低"
|
||||||
|
list: "一覧"
|
||||||
GamingSpeedChange: "ゲーミングの光るスピードの調整"
|
GamingSpeedChange: "ゲーミングの光るスピードの調整"
|
||||||
GamingSpeedChangeInfo: "左にすれば早くなる、右にすれば遅くなる。それだけ。"
|
GamingSpeedChangeInfo: "左にすれば早くなる、右にすれば遅くなる。それだけ。"
|
||||||
list: "一覧"
|
|
||||||
emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
|
emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
|
||||||
ratio: "比率"
|
ratio: "比率"
|
||||||
showVisibilityColor: "ノートの公開範囲を色付けする"
|
showVisibilityColor: "ノートの公開範囲を色付けする"
|
||||||
|
@ -1072,6 +1073,9 @@ hiddenTags: "非表示ハッシュタグ"
|
||||||
hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。"
|
hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。"
|
||||||
notesSearchNotAvailable: "ノート検索は利用できません。"
|
notesSearchNotAvailable: "ノート検索は利用できません。"
|
||||||
license: "ライセンス"
|
license: "ライセンス"
|
||||||
|
requestPending: "申請中"
|
||||||
|
approval: "承認"
|
||||||
|
requestingEmojis: "リクエストされている絵文字"
|
||||||
draft: "ドラフト"
|
draft: "ドラフト"
|
||||||
undrafted: "ドラフト解除"
|
undrafted: "ドラフト解除"
|
||||||
unfavoriteConfirm: "お気に入り解除しますか?"
|
unfavoriteConfirm: "お気に入り解除しますか?"
|
||||||
|
|
13
packages/backend/migration/1698131657286-EmojiRequest.js
Normal file
13
packages/backend/migration/1698131657286-EmojiRequest.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export class EmojiRequest1698131657286 {
|
||||||
|
name = 'EmojiRequest1698131657286'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TABLE "emoji_request" ("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_3c74521e048dc744f0c7eb65f4a" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ea1d771e867e9843300f09d02c" ON "emoji_request" ("name") `);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_ea1d771e867e9843300f09d02c"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "emoji_request"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,6 +90,7 @@ import { ClipEntityService } from './entities/ClipEntityService.js';
|
||||||
import { DriveFileEntityService } from './entities/DriveFileEntityService.js';
|
import { DriveFileEntityService } from './entities/DriveFileEntityService.js';
|
||||||
import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js';
|
import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js';
|
||||||
import { EmojiEntityService } from './entities/EmojiEntityService.js';
|
import { EmojiEntityService } from './entities/EmojiEntityService.js';
|
||||||
|
import { EmojiRequestsEntityService } from './entities/EmojiRequestsEntityService.js';
|
||||||
import { FollowingEntityService } from './entities/FollowingEntityService.js';
|
import { FollowingEntityService } from './entities/FollowingEntityService.js';
|
||||||
import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js';
|
import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js';
|
||||||
import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js';
|
import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js';
|
||||||
|
@ -225,6 +226,7 @@ const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting
|
||||||
const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService };
|
const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService };
|
||||||
const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService };
|
const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService };
|
||||||
const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService };
|
const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService };
|
||||||
|
const $EmojiRequestsEntityService: Provider = { provide: 'EmojiRequestsEntityService', useExisting: EmojiRequestsEntityService };
|
||||||
const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService };
|
const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService };
|
||||||
const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService };
|
const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService };
|
||||||
const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService };
|
const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService };
|
||||||
|
@ -360,6 +362,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
DriveFileEntityService,
|
DriveFileEntityService,
|
||||||
DriveFolderEntityService,
|
DriveFolderEntityService,
|
||||||
EmojiEntityService,
|
EmojiEntityService,
|
||||||
|
EmojiRequestsEntityService,
|
||||||
FollowingEntityService,
|
FollowingEntityService,
|
||||||
FollowRequestEntityService,
|
FollowRequestEntityService,
|
||||||
GalleryLikeEntityService,
|
GalleryLikeEntityService,
|
||||||
|
@ -490,6 +493,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$DriveFileEntityService,
|
$DriveFileEntityService,
|
||||||
$DriveFolderEntityService,
|
$DriveFolderEntityService,
|
||||||
$EmojiEntityService,
|
$EmojiEntityService,
|
||||||
|
$EmojiRequestsEntityService,
|
||||||
$FollowingEntityService,
|
$FollowingEntityService,
|
||||||
$FollowRequestEntityService,
|
$FollowRequestEntityService,
|
||||||
$GalleryLikeEntityService,
|
$GalleryLikeEntityService,
|
||||||
|
@ -620,6 +624,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
DriveFileEntityService,
|
DriveFileEntityService,
|
||||||
DriveFolderEntityService,
|
DriveFolderEntityService,
|
||||||
EmojiEntityService,
|
EmojiEntityService,
|
||||||
|
EmojiRequestsEntityService,
|
||||||
FollowingEntityService,
|
FollowingEntityService,
|
||||||
FollowRequestEntityService,
|
FollowRequestEntityService,
|
||||||
GalleryLikeEntityService,
|
GalleryLikeEntityService,
|
||||||
|
@ -749,6 +754,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$DriveFileEntityService,
|
$DriveFileEntityService,
|
||||||
$DriveFolderEntityService,
|
$DriveFolderEntityService,
|
||||||
$EmojiEntityService,
|
$EmojiEntityService,
|
||||||
|
$EmojiRequestsEntityService,
|
||||||
$FollowingEntityService,
|
$FollowingEntityService,
|
||||||
$FollowRequestEntityService,
|
$FollowRequestEntityService,
|
||||||
$GalleryLikeEntityService,
|
$GalleryLikeEntityService,
|
||||||
|
|
|
@ -12,12 +12,14 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||||
import type { MiEmoji } from '@/models/Emoji.js';
|
import type { MiEmoji } from '@/models/Emoji.js';
|
||||||
import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js';
|
import type { EmojisRepository, EmojiRequestsRepository, MiRole, MiUser } from '@/models/_.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
|
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import type { Serialized } from '@/types.js';
|
import type { Serialized } from '@/types.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
import { MiEmojiRequest } from '@/models/EmojiRequest.js';
|
||||||
|
|
||||||
const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
|
const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -32,6 +34,9 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
|
||||||
|
@Inject(DI.emojiRequestsRepository)
|
||||||
|
private emojiRequestsRepository: EmojiRequestsRepository,
|
||||||
|
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private emojiEntityService: EmojiEntityService,
|
private emojiEntityService: EmojiEntityService,
|
||||||
|
@ -54,6 +59,41 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async request(data: {
|
||||||
|
driveFile: MiDriveFile;
|
||||||
|
name: string;
|
||||||
|
category: string | null;
|
||||||
|
aliases: string[];
|
||||||
|
license: string | null;
|
||||||
|
isSensitive: boolean;
|
||||||
|
localOnly: boolean;
|
||||||
|
}, me?: MiUser): Promise<MiEmojiRequest> {
|
||||||
|
const emoji = await this.emojiRequestsRepository.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.emojiRequestsRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
|
if (me) {
|
||||||
|
this.moderationLogService.log(me, 'addCustomEmoji', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
emoji: emoji,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async add(data: {
|
public async add(data: {
|
||||||
driveFile: MiDriveFile;
|
driveFile: MiDriveFile;
|
||||||
|
@ -64,7 +104,6 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
license: string | null;
|
license: string | null;
|
||||||
isSensitive: boolean;
|
isSensitive: boolean;
|
||||||
localOnly: boolean;
|
localOnly: boolean;
|
||||||
draft: boolean;
|
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
||||||
}, moderator?: MiUser): Promise<MiEmoji> {
|
}, moderator?: MiUser): Promise<MiEmoji> {
|
||||||
const emoji = await this.emojisRepository.insert({
|
const emoji = await this.emojisRepository.insert({
|
||||||
|
@ -81,7 +120,6 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
isSensitive: data.isSensitive,
|
isSensitive: data.isSensitive,
|
||||||
localOnly: data.localOnly,
|
localOnly: data.localOnly,
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||||
draft: data.draft,
|
|
||||||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
if (data.host == null) {
|
if (data.host == null) {
|
||||||
|
@ -111,7 +149,6 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
license?: string | null;
|
license?: string | null;
|
||||||
isSensitive?: boolean;
|
isSensitive?: boolean;
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
draft: boolean;
|
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
|
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
|
||||||
}, moderator?: MiUser): Promise<void> {
|
}, moderator?: MiUser): Promise<void> {
|
||||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||||
|
@ -126,7 +163,6 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
license: data.license,
|
license: data.license,
|
||||||
isSensitive: data.isSensitive,
|
isSensitive: data.isSensitive,
|
||||||
localOnly: data.localOnly,
|
localOnly: data.localOnly,
|
||||||
draft: data.draft,
|
|
||||||
originalUrl: data.driveFile != null ? data.driveFile.url : undefined,
|
originalUrl: data.driveFile != null ? data.driveFile.url : undefined,
|
||||||
publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? 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,
|
type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined,
|
||||||
|
@ -161,6 +197,36 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async updateRequest(id: MiEmojiRequest['id'], data: {
|
||||||
|
driveFile?: MiDriveFile;
|
||||||
|
name?: string;
|
||||||
|
category?: string | null;
|
||||||
|
aliases?: string[];
|
||||||
|
license?: string | null;
|
||||||
|
isSensitive?: boolean;
|
||||||
|
localOnly?: boolean;
|
||||||
|
}, moderator?: MiUser): Promise<void> {
|
||||||
|
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.emojiRequestsRepository.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
|
@bindThis
|
||||||
public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
|
public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
|
||||||
const emojis = await this.emojisRepository.findBy({
|
const emojis = await this.emojisRepository.findBy({
|
||||||
|
@ -232,36 +298,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
emojis: await this.emojiEntityService.packDetailedMany(ids),
|
emojis: await this.emojiEntityService.packDetailedMany(ids),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@bindThis
|
|
||||||
public async setLocalOnlyBulk(ids: MiEmoji['id'][], localOnly: boolean | false) {
|
|
||||||
await this.emojisRepository.update({
|
|
||||||
id: In(ids),
|
|
||||||
}, {
|
|
||||||
updatedAt: new Date(),
|
|
||||||
localOnly: localOnly,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.localEmojisCache.refresh();
|
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
|
||||||
emojis: await this.emojiEntityService.packDetailedMany(ids),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@bindThis
|
|
||||||
public async setisSensitiveBulk(ids: MiEmoji['id'][], isSensitive: boolean | false) {
|
|
||||||
await this.emojisRepository.update({
|
|
||||||
id: In(ids),
|
|
||||||
}, {
|
|
||||||
updatedAt: new Date(),
|
|
||||||
isSensitive: isSensitive,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.localEmojisCache.refresh();
|
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
|
||||||
emojis: await this.emojiEntityService.packDetailedMany(ids),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async setLicenseBulk(ids: MiEmoji['id'][], license: string | null) {
|
public async setLicenseBulk(ids: MiEmoji['id'][], license: string | null) {
|
||||||
await this.emojisRepository.update({
|
await this.emojisRepository.update({
|
||||||
|
@ -298,6 +335,13 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async deleteRequest(id: MiEmojiRequest['id']) {
|
||||||
|
const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id });
|
||||||
|
|
||||||
|
await this.emojiRequestsRepository.delete(emoji.id);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) {
|
public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) {
|
||||||
const emojis = await this.emojisRepository.findBy({
|
const emojis = await this.emojisRepository.findBy({
|
||||||
|
@ -419,11 +463,21 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
return this.emojisRepository.exist({ where: { name, host: IsNull() } });
|
return this.emojisRepository.exist({ where: { name, host: IsNull() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public checkRequestDuplicate(name: string): Promise<boolean> {
|
||||||
|
return this.emojiRequestsRepository.exist({ where: { name } });
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getEmojiById(id: string): Promise<MiEmoji | null> {
|
public getEmojiById(id: string): Promise<MiEmoji | null> {
|
||||||
return this.emojisRepository.findOneBy({ id });
|
return this.emojisRepository.findOneBy({ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public getEmojiRequestById(id: string): Promise<MiEmojiRequest | null> {
|
||||||
|
return this.emojiRequestsRepository.findOneBy({ id });
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.cache.dispose();
|
this.cache.dispose();
|
||||||
|
|
|
@ -72,6 +72,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
||||||
inviteLimitCycle: 60 * 24 * 7,
|
inviteLimitCycle: 60 * 24 * 7,
|
||||||
inviteExpirationTime: 0,
|
inviteExpirationTime: 0,
|
||||||
canManageCustomEmojis: false,
|
canManageCustomEmojis: false,
|
||||||
|
canRequestCustomEmojis: false,
|
||||||
canManageAvatarDecorations: false,
|
canManageAvatarDecorations: false,
|
||||||
canRequestCustomEmojis: false,
|
canRequestCustomEmojis: false,
|
||||||
canSearchNotes: false,
|
canSearchNotes: false,
|
||||||
|
@ -336,6 +337,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||||
inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)),
|
inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)),
|
||||||
inviteExpirationTime: calc('inviteExpirationTime', vs => Math.max(...vs)),
|
inviteExpirationTime: calc('inviteExpirationTime', vs => Math.max(...vs)),
|
||||||
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
|
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
|
||||||
|
canRequestCustomEmojis: calc('canRequestCustomEmojis', vs => vs.some(v => v === true)),
|
||||||
canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)),
|
canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)),
|
||||||
canRequestCustomEmojis: calc('canRequestCustomEmojis', vs => vs.some(v => v === true)),
|
canRequestCustomEmojis: calc('canRequestCustomEmojis', vs => vs.some(v => v === true)),
|
||||||
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
|
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
|
||||||
|
|
|
@ -133,7 +133,10 @@ export class DriveFileEntityService {
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
@bindThis
|
||||||
|
public async getFromUrl(url: string): Promise<MiDriveFile | null> {
|
||||||
|
return this.driveFilesRepository.findOneBy({ url: url });
|
||||||
|
}
|
||||||
@bindThis
|
@bindThis
|
||||||
public async calcDriveUsageOf(user: MiUser['id'] | { id: MiUser['id'] }): Promise<number> {
|
public async calcDriveUsageOf(user: MiUser['id'] | { id: MiUser['id'] }): Promise<number> {
|
||||||
const id = typeof user === 'object' ? user.id : user;
|
const id = typeof user === 'object' ? user.id : user;
|
||||||
|
|
|
@ -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 { EmojiRequestsRepository } from '@/models/_.js';
|
||||||
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { MiEmojiRequest } from '@/models/EmojiRequest.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EmojiRequestsEntityService {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.emojiRequestsRepository)
|
||||||
|
private emojiRequestsRepository: EmojiRequestsRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packSimple(
|
||||||
|
src: MiEmojiRequest['id'] | MiEmojiRequest,
|
||||||
|
): Promise<Packed<'EmojiRequestSimple'>> {
|
||||||
|
const emoji = typeof src === 'object' ? src : await this.emojiRequestsRepository.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: MiEmojiRequest['id'] | MiEmojiRequest,
|
||||||
|
): Promise<Packed<'EmojiRequestDetailed'>> {
|
||||||
|
const emoji = typeof src === 'object' ? src : await this.emojiRequestsRepository.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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ export const DI = {
|
||||||
followRequestsRepository: Symbol('followRequestsRepository'),
|
followRequestsRepository: Symbol('followRequestsRepository'),
|
||||||
instancesRepository: Symbol('instancesRepository'),
|
instancesRepository: Symbol('instancesRepository'),
|
||||||
emojisRepository: Symbol('emojisRepository'),
|
emojisRepository: Symbol('emojisRepository'),
|
||||||
|
emojiRequestsRepository: Symbol('emojiRequestsRepository'),
|
||||||
driveFilesRepository: Symbol('driveFilesRepository'),
|
driveFilesRepository: Symbol('driveFilesRepository'),
|
||||||
driveFoldersRepository: Symbol('driveFoldersRepository'),
|
driveFoldersRepository: Symbol('driveFoldersRepository'),
|
||||||
metasRepository: Symbol('metasRepository'),
|
metasRepository: Symbol('metasRepository'),
|
||||||
|
|
|
@ -33,7 +33,7 @@ import { packedClipSchema } from '@/models/json-schema/clip.js';
|
||||||
import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js';
|
import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js';
|
||||||
import { packedQueueCountSchema } from '@/models/json-schema/queue.js';
|
import { packedQueueCountSchema } from '@/models/json-schema/queue.js';
|
||||||
import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js';
|
import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js';
|
||||||
import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } 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 { packedFlashSchema } from '@/models/json-schema/flash.js';
|
||||||
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
||||||
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
||||||
|
@ -73,7 +73,9 @@ export const refs = {
|
||||||
FederationInstance: packedFederationInstanceSchema,
|
FederationInstance: packedFederationInstanceSchema,
|
||||||
GalleryPost: packedGalleryPostSchema,
|
GalleryPost: packedGalleryPostSchema,
|
||||||
EmojiSimple: packedEmojiSimpleSchema,
|
EmojiSimple: packedEmojiSimpleSchema,
|
||||||
|
EmojiRequestSimple: packedEmojiRequestSimpleSchema,
|
||||||
EmojiDetailed: packedEmojiDetailedSchema,
|
EmojiDetailed: packedEmojiDetailedSchema,
|
||||||
|
EmojiRequestDetailed: packedEmojiRequestDetailedSchema,
|
||||||
Flash: packedFlashSchema,
|
Flash: packedFlashSchema,
|
||||||
Signin: packedSigninSchema,
|
Signin: packedSigninSchema,
|
||||||
RoleLite: packedRoleLiteSchema,
|
RoleLite: packedRoleLiteSchema,
|
||||||
|
|
71
packages/backend/src/models/EmojiRequest.ts
Normal file
71
packages/backend/src/models/EmojiRequest.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 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_request')
|
||||||
|
@Index(['name'], { unique: true })
|
||||||
|
export class MiEmojiRequest {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public updatedAt: Date | null;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ import {
|
||||||
MiDriveFile,
|
MiDriveFile,
|
||||||
MiDriveFolder,
|
MiDriveFolder,
|
||||||
MiEmoji,
|
MiEmoji,
|
||||||
MiFlash,
|
MiEmojiRequest,MiFlash,
|
||||||
MiFlashLike,
|
MiFlashLike,
|
||||||
MiFollowRequest,
|
MiFollowRequest,
|
||||||
MiFollowing,
|
MiFollowing,
|
||||||
|
@ -244,6 +244,12 @@ const $emojisRepository: Provider = {
|
||||||
inject: [DI.db],
|
inject: [DI.db],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $emojiRequestsRepository: Provider = {
|
||||||
|
provide: DI.emojiRequestsRepository,
|
||||||
|
useFactory: (db: DataSource) => db.getRepository(MiEmojiRequest),
|
||||||
|
inject: [DI.db],
|
||||||
|
};
|
||||||
|
|
||||||
const $driveFilesRepository: Provider = {
|
const $driveFilesRepository: Provider = {
|
||||||
provide: DI.driveFilesRepository,
|
provide: DI.driveFilesRepository,
|
||||||
useFactory: (db: DataSource) => db.getRepository(MiDriveFile),
|
useFactory: (db: DataSource) => db.getRepository(MiDriveFile),
|
||||||
|
@ -504,6 +510,7 @@ const $userMemosRepository: Provider = {
|
||||||
$followRequestsRepository,
|
$followRequestsRepository,
|
||||||
$instancesRepository,
|
$instancesRepository,
|
||||||
$emojisRepository,
|
$emojisRepository,
|
||||||
|
$emojiRequestsRepository,
|
||||||
$driveFilesRepository,
|
$driveFilesRepository,
|
||||||
$driveFoldersRepository,
|
$driveFoldersRepository,
|
||||||
$metasRepository,
|
$metasRepository,
|
||||||
|
@ -572,6 +579,7 @@ const $userMemosRepository: Provider = {
|
||||||
$followRequestsRepository,
|
$followRequestsRepository,
|
||||||
$instancesRepository,
|
$instancesRepository,
|
||||||
$emojisRepository,
|
$emojisRepository,
|
||||||
|
$emojiRequestsRepository,
|
||||||
$driveFilesRepository,
|
$driveFilesRepository,
|
||||||
$driveFoldersRepository,
|
$driveFoldersRepository,
|
||||||
$metasRepository,
|
$metasRepository,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js';
|
||||||
import { MiDriveFile } from '@/models/DriveFile.js';
|
import { MiDriveFile } from '@/models/DriveFile.js';
|
||||||
import { MiDriveFolder } from '@/models/DriveFolder.js';
|
import { MiDriveFolder } from '@/models/DriveFolder.js';
|
||||||
import { MiEmoji } from '@/models/Emoji.js';
|
import { MiEmoji } from '@/models/Emoji.js';
|
||||||
|
import { MiEmojiRequest } from '@/models/EmojiRequest.js';
|
||||||
import { MiFollowing } from '@/models/Following.js';
|
import { MiFollowing } from '@/models/Following.js';
|
||||||
import { MiFollowRequest } from '@/models/FollowRequest.js';
|
import { MiFollowRequest } from '@/models/FollowRequest.js';
|
||||||
import { MiGalleryLike } from '@/models/GalleryLike.js';
|
import { MiGalleryLike } from '@/models/GalleryLike.js';
|
||||||
|
@ -90,6 +91,7 @@ export {
|
||||||
MiDriveFile,
|
MiDriveFile,
|
||||||
MiDriveFolder,
|
MiDriveFolder,
|
||||||
MiEmoji,
|
MiEmoji,
|
||||||
|
MiEmojiRequest,
|
||||||
MiFollowing,
|
MiFollowing,
|
||||||
MiFollowRequest,
|
MiFollowRequest,
|
||||||
MiGalleryLike,
|
MiGalleryLike,
|
||||||
|
@ -158,6 +160,7 @@ export type ClipFavoritesRepository = Repository<MiClipFavorite>;
|
||||||
export type DriveFilesRepository = Repository<MiDriveFile>;
|
export type DriveFilesRepository = Repository<MiDriveFile>;
|
||||||
export type DriveFoldersRepository = Repository<MiDriveFolder>;
|
export type DriveFoldersRepository = Repository<MiDriveFolder>;
|
||||||
export type EmojisRepository = Repository<MiEmoji>;
|
export type EmojisRepository = Repository<MiEmoji>;
|
||||||
|
export type EmojiRequestsRepository = Repository<MiEmojiRequest>;
|
||||||
export type FollowingsRepository = Repository<MiFollowing>;
|
export type FollowingsRepository = Repository<MiFollowing>;
|
||||||
export type FollowRequestsRepository = Repository<MiFollowRequest>;
|
export type FollowRequestsRepository = Repository<MiFollowRequest>;
|
||||||
export type GalleryLikesRepository = Repository<MiGalleryLike>;
|
export type GalleryLikesRepository = Repository<MiGalleryLike>;
|
||||||
|
|
|
@ -46,6 +46,36 @@ export const packedEmojiSimpleSchema = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
export const packedEmojiRequestSimpleSchema = {
|
||||||
|
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;
|
||||||
|
|
||||||
export const packedEmojiDetailedSchema = {
|
export const packedEmojiDetailedSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -108,3 +138,51 @@ export const packedEmojiDetailedSchema = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const packedEmojiRequestDetailedSchema = {
|
||||||
|
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;
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js';
|
||||||
import { MiDriveFile } from '@/models/DriveFile.js';
|
import { MiDriveFile } from '@/models/DriveFile.js';
|
||||||
import { MiDriveFolder } from '@/models/DriveFolder.js';
|
import { MiDriveFolder } from '@/models/DriveFolder.js';
|
||||||
import { MiEmoji } from '@/models/Emoji.js';
|
import { MiEmoji } from '@/models/Emoji.js';
|
||||||
|
import { MiEmojiRequest } from '@/models/EmojiRequest.js';
|
||||||
import { MiFollowing } from '@/models/Following.js';
|
import { MiFollowing } from '@/models/Following.js';
|
||||||
import { MiFollowRequest } from '@/models/FollowRequest.js';
|
import { MiFollowRequest } from '@/models/FollowRequest.js';
|
||||||
import { MiGalleryLike } from '@/models/GalleryLike.js';
|
import { MiGalleryLike } from '@/models/GalleryLike.js';
|
||||||
|
@ -164,6 +165,7 @@ export const entities = [
|
||||||
MiPoll,
|
MiPoll,
|
||||||
MiPollVote,
|
MiPollVote,
|
||||||
MiEmoji,
|
MiEmoji,
|
||||||
|
MiEmojiRequest,
|
||||||
MiHashtag,
|
MiHashtag,
|
||||||
MiSwSubscription,
|
MiSwSubscription,
|
||||||
MiAbuseUserReport,
|
MiAbuseUserReport,
|
||||||
|
|
|
@ -34,18 +34,20 @@ import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-al
|
||||||
import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js';
|
import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js';
|
||||||
import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js';
|
import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js';
|
||||||
import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.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_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_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_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_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_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_list from './endpoints/admin/emoji/list.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_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_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_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_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_update from './endpoints/admin/emoji/update.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_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_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
|
||||||
import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
|
import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
|
||||||
|
@ -255,6 +257,7 @@ import * as ep___invite_list from './endpoints/invite/list.js';
|
||||||
import * as ep___invite_limit from './endpoints/invite/limit.js';
|
import * as ep___invite_limit from './endpoints/invite/limit.js';
|
||||||
import * as ep___meta from './endpoints/meta.js';
|
import * as ep___meta from './endpoints/meta.js';
|
||||||
import * as ep___emojis from './endpoints/emojis.js';
|
import * as ep___emojis from './endpoints/emojis.js';
|
||||||
|
import * as ep___emojiRequests from './endpoints/emoji-requests.js';
|
||||||
import * as ep___emoji from './endpoints/emoji.js';
|
import * as ep___emoji from './endpoints/emoji.js';
|
||||||
import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js';
|
import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js';
|
||||||
import * as ep___mute_create from './endpoints/mute/create.js';
|
import * as ep___mute_create from './endpoints/mute/create.js';
|
||||||
|
@ -402,18 +405,20 @@ const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-ali
|
||||||
const $admin_emoji_setlocalOnlyBulk: Provider = { provide: 'ep:admin/emoji/set-localonly-bulk', useClass: ep___admin_emoji_setlocalOnlyBulk.default };
|
const $admin_emoji_setlocalOnlyBulk: Provider = { provide: 'ep:admin/emoji/set-localonly-bulk', useClass: ep___admin_emoji_setlocalOnlyBulk.default };
|
||||||
const $admin_emoji_setisSensitiveBulk: Provider = { provide: 'ep:admin/emoji/set-issensitive-bulk', useClass: ep___admin_emoji_setisSensitiveBulk.default };
|
const $admin_emoji_setisSensitiveBulk: Provider = { provide: 'ep:admin/emoji/set-issensitive-bulk', useClass: ep___admin_emoji_setisSensitiveBulk.default };
|
||||||
const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.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_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_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_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_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_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_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.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_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_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_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_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_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.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_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_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 };
|
const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default };
|
||||||
|
@ -624,6 +629,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 $invite_limit: Provider = { provide: 'ep:invite/limit', useClass: ep___invite_limit.default };
|
||||||
const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default };
|
const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default };
|
||||||
const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default };
|
const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default };
|
||||||
|
const $emoji_requests: Provider = { provide: 'ep:emoji-requests', useClass: ep___emojiRequests.default };
|
||||||
const $emoji: Provider = { provide: 'ep:emoji', useClass: ep___emoji.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 $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 };
|
const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default };
|
||||||
|
@ -774,18 +780,20 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$admin_emoji_setlocalOnlyBulk,
|
$admin_emoji_setlocalOnlyBulk,
|
||||||
$admin_emoji_setisSensitiveBulk,
|
$admin_emoji_setisSensitiveBulk,
|
||||||
$admin_emoji_add,
|
$admin_emoji_add,
|
||||||
$admin_emoji_addDraft,
|
$admin_emoji_addRequest,
|
||||||
$admin_emoji_copy,
|
$admin_emoji_copy,
|
||||||
$admin_emoji_deleteBulk,
|
$admin_emoji_deleteBulk,
|
||||||
$admin_emoji_delete,
|
$admin_emoji_delete,
|
||||||
$admin_emoji_importZip,
|
$admin_emoji_importZip,
|
||||||
$admin_emoji_listRemote,
|
$admin_emoji_listRemote,
|
||||||
$admin_emoji_list,
|
$admin_emoji_list,
|
||||||
|
$admin_emoji_listRequest,
|
||||||
$admin_emoji_removeAliasesBulk,
|
$admin_emoji_removeAliasesBulk,
|
||||||
$admin_emoji_setAliasesBulk,
|
$admin_emoji_setAliasesBulk,
|
||||||
$admin_emoji_setCategoryBulk,
|
$admin_emoji_setCategoryBulk,
|
||||||
$admin_emoji_setLicenseBulk,
|
$admin_emoji_setLicenseBulk,
|
||||||
$admin_emoji_update,
|
$admin_emoji_update,
|
||||||
|
$admin_emoji_updateRequest,
|
||||||
$admin_federation_deleteAllFiles,
|
$admin_federation_deleteAllFiles,
|
||||||
$admin_federation_refreshRemoteInstanceMetadata,
|
$admin_federation_refreshRemoteInstanceMetadata,
|
||||||
$admin_federation_removeAllFollowing,
|
$admin_federation_removeAllFollowing,
|
||||||
|
@ -996,6 +1004,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$invite_limit,
|
$invite_limit,
|
||||||
$meta,
|
$meta,
|
||||||
$emojis,
|
$emojis,
|
||||||
|
$emoji_requests,
|
||||||
$emoji,
|
$emoji,
|
||||||
$miauth_genToken,
|
$miauth_genToken,
|
||||||
$mute_create,
|
$mute_create,
|
||||||
|
@ -1138,13 +1147,14 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$admin_drive_showFile,
|
$admin_drive_showFile,
|
||||||
$admin_emoji_addAliasesBulk,
|
$admin_emoji_addAliasesBulk,
|
||||||
$admin_emoji_add,
|
$admin_emoji_add,
|
||||||
$admin_emoji_addDraft,
|
$admin_emoji_addRequest,
|
||||||
$admin_emoji_copy,
|
$admin_emoji_copy,
|
||||||
$admin_emoji_deleteBulk,
|
$admin_emoji_deleteBulk,
|
||||||
$admin_emoji_delete,
|
$admin_emoji_delete,
|
||||||
$admin_emoji_importZip,
|
$admin_emoji_importZip,
|
||||||
$admin_emoji_listRemote,
|
$admin_emoji_listRemote,
|
||||||
$admin_emoji_list,
|
$admin_emoji_list,
|
||||||
|
$admin_emoji_listRequest,
|
||||||
$admin_emoji_removeAliasesBulk,
|
$admin_emoji_removeAliasesBulk,
|
||||||
$admin_emoji_setAliasesBulk,
|
$admin_emoji_setAliasesBulk,
|
||||||
$admin_emoji_setCategoryBulk,
|
$admin_emoji_setCategoryBulk,
|
||||||
|
@ -1152,6 +1162,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$admin_emoji_setlocalOnlyBulk,
|
$admin_emoji_setlocalOnlyBulk,
|
||||||
$admin_emoji_setisSensitiveBulk,
|
$admin_emoji_setisSensitiveBulk,
|
||||||
$admin_emoji_update,
|
$admin_emoji_update,
|
||||||
|
$admin_emoji_updateRequest,
|
||||||
$admin_federation_deleteAllFiles,
|
$admin_federation_deleteAllFiles,
|
||||||
$i_userstats,
|
$i_userstats,
|
||||||
$admin_federation_refreshRemoteInstanceMetadata,
|
$admin_federation_refreshRemoteInstanceMetadata,
|
||||||
|
@ -1362,6 +1373,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$invite_limit,
|
$invite_limit,
|
||||||
$meta,
|
$meta,
|
||||||
$emojis,
|
$emojis,
|
||||||
|
$emoji_requests,
|
||||||
$emoji,
|
$emoji,
|
||||||
$miauth_genToken,
|
$miauth_genToken,
|
||||||
$mute_create,
|
$mute_create,
|
||||||
|
|
|
@ -33,18 +33,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_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_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_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_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_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_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_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_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_list from './endpoints/admin/emoji/list.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_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_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_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_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_update from './endpoints/admin/emoji/update.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_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_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
|
||||||
import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
|
import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
|
||||||
|
@ -255,6 +257,7 @@ import * as ep___invite_list from './endpoints/invite/list.js';
|
||||||
import * as ep___invite_limit from './endpoints/invite/limit.js';
|
import * as ep___invite_limit from './endpoints/invite/limit.js';
|
||||||
import * as ep___meta from './endpoints/meta.js';
|
import * as ep___meta from './endpoints/meta.js';
|
||||||
import * as ep___emojis from './endpoints/emojis.js';
|
import * as ep___emojis from './endpoints/emojis.js';
|
||||||
|
import * as ep___emojiRequests from './endpoints/emoji-requests.js';
|
||||||
import * as ep___emoji from './endpoints/emoji.js';
|
import * as ep___emoji from './endpoints/emoji.js';
|
||||||
import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js';
|
import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js';
|
||||||
import * as ep___mute_create from './endpoints/mute/create.js';
|
import * as ep___mute_create from './endpoints/mute/create.js';
|
||||||
|
@ -397,13 +400,14 @@ const eps = [
|
||||||
['admin/drive/show-file', ep___admin_drive_showFile],
|
['admin/drive/show-file', ep___admin_drive_showFile],
|
||||||
['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk],
|
['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk],
|
||||||
['admin/emoji/add', ep___admin_emoji_add],
|
['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/copy', ep___admin_emoji_copy],
|
||||||
['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk],
|
['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk],
|
||||||
['admin/emoji/delete', ep___admin_emoji_delete],
|
['admin/emoji/delete', ep___admin_emoji_delete],
|
||||||
['admin/emoji/import-zip', ep___admin_emoji_importZip],
|
['admin/emoji/import-zip', ep___admin_emoji_importZip],
|
||||||
['admin/emoji/list-remote', ep___admin_emoji_listRemote],
|
['admin/emoji/list-remote', ep___admin_emoji_listRemote],
|
||||||
['admin/emoji/list', ep___admin_emoji_list],
|
['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/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk],
|
||||||
['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk],
|
['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk],
|
||||||
['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk],
|
['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk],
|
||||||
|
@ -411,6 +415,7 @@ const eps = [
|
||||||
['admin/emoji/set-issensitive-bulk', ep___admin_emoji_setisSensitiveBulk],
|
['admin/emoji/set-issensitive-bulk', ep___admin_emoji_setisSensitiveBulk],
|
||||||
['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk],
|
['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk],
|
||||||
['admin/emoji/update', ep___admin_emoji_update],
|
['admin/emoji/update', ep___admin_emoji_update],
|
||||||
|
['admin/emoji/update-request', ep___admin_emoji_updateRequest],
|
||||||
['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles],
|
['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles],
|
||||||
['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata],
|
['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata],
|
||||||
['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing],
|
['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing],
|
||||||
|
@ -621,6 +626,7 @@ const eps = [
|
||||||
['invite/limit', ep___invite_limit],
|
['invite/limit', ep___invite_limit],
|
||||||
['meta', ep___meta],
|
['meta', ep___meta],
|
||||||
['emojis', ep___emojis],
|
['emojis', ep___emojis],
|
||||||
|
['emoji-requests', ep___emojiRequests],
|
||||||
['emoji', ep___emoji],
|
['emoji', ep___emoji],
|
||||||
['miauth/gen-token', ep___miauth_genToken],
|
['miauth/gen-token', ep___miauth_genToken],
|
||||||
['mute/create', ep___mute_create],
|
['mute/create', ep___mute_create],
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.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';
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
duplicateName: {
|
||||||
|
message: 'Duplicate name.',
|
||||||
|
code: 'DUPLICATE_NAME',
|
||||||
|
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} 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 },
|
||||||
|
isSensitive: { type: 'boolean', nullable: true },
|
||||||
|
localOnly: { type: 'boolean', nullable: true },
|
||||||
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['name', 'fileId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// TODO: ロジックをサービスに切り出す
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.driveFilesRepository)
|
||||||
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
|
|
||||||
|
private customEmojiService: CustomEmojiService,
|
||||||
|
|
||||||
|
private moderationLogService: ModerationLogService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name);
|
||||||
|
const isRequestDuplicate = await this.customEmojiService.checkRequestDuplicate(ps.name);
|
||||||
|
|
||||||
|
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.request({
|
||||||
|
driveFile,
|
||||||
|
name: ps.name,
|
||||||
|
category: ps.category ?? null,
|
||||||
|
aliases: ps.aliases ?? [],
|
||||||
|
license: ps.license ?? null,
|
||||||
|
isSensitive: ps.isSensitive ?? false,
|
||||||
|
localOnly: ps.localOnly ?? false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.moderationLogService.log(me, 'addCustomEmoji', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
emoji: emoji,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: emoji.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private customEmojiService: CustomEmojiService,
|
private customEmojiService: CustomEmojiService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
await this.customEmojiService.delete(ps.id, me);
|
const emoji = await this.customEmojiService.getEmojiById(ps.id);
|
||||||
|
const RequestEmoji = await this.customEmojiService.getEmojiRequestById(ps.id);
|
||||||
|
if (emoji != null) {
|
||||||
|
await this.customEmojiService.delete(ps.id, me);
|
||||||
|
}
|
||||||
|
if (RequestEmoji != null) {
|
||||||
|
await this.customEmojiService.deleteRequest(ps.id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { EmojiRequestsRepository } from '@/models/_.js';
|
||||||
|
import type { MiEmojiRequest } from '@/models/EmojiRequest.js';
|
||||||
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { EmojiRequestsEntityService } from '@/core/entities/EmojiRequestsEntityService.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<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.emojiRequestsRepository)
|
||||||
|
private emojiRequestsRepository: EmojiRequestsRepository,
|
||||||
|
|
||||||
|
private emojiRequestsEntityService: EmojiRequestsEntityService,
|
||||||
|
private queryService: QueryService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const q = this.queryService.makePaginationQuery(this.emojiRequestsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId);
|
||||||
|
|
||||||
|
let emojis: MiEmojiRequest[];
|
||||||
|
|
||||||
|
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.emojiRequestsEntityService.packDetailedMany(emojis);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* 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 } 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',
|
||||||
|
} },
|
||||||
|
Request: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
required: ['id', 'name', 'aliases'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.driveFilesRepository)
|
||||||
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
|
|
||||||
|
private customEmojiService: CustomEmojiService,
|
||||||
|
private driveFileEntityService: DriveFileEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
let driveFile;
|
||||||
|
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.getEmojiRequestById(ps.id);
|
||||||
|
if (emoji != null) {
|
||||||
|
if (ps.name !== emoji.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 (!isRequest) {
|
||||||
|
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.deleteRequest(ps.id);
|
||||||
|
} else {
|
||||||
|
await this.customEmojiService.updateRequest(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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||||
import type { DriveFilesRepository , EmojisRepository } from '@/models/_.js';
|
import type { DriveFilesRepository , EmojisRepository } from '@/models/_.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
@ -62,7 +63,7 @@ export const paramDef = {
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: {
|
roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
draft: { type: 'boolean' },
|
Request: { type: 'boolean' },
|
||||||
},
|
},
|
||||||
required: ['id', 'name', 'draft', 'aliases'],
|
required: ['id', 'name', 'draft', 'aliases'],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -73,10 +74,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
@Inject(DI.driveFilesRepository)
|
@Inject(DI.driveFilesRepository)
|
||||||
private driveFilesRepository: DriveFilesRepository,
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
private customEmojiService: CustomEmojiService,
|
private customEmojiService: CustomEmojiService,
|
||||||
|
private driveFileEntityService: DriveFileEntityService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
let driveFile;
|
let driveFile;
|
||||||
|
const isRequest = !!ps.Request;
|
||||||
if (ps.fileId) {
|
if (ps.fileId) {
|
||||||
driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
||||||
if (driveFile == null) throw new ApiError(meta.errors.noSuchFile);
|
if (driveFile == null) throw new ApiError(meta.errors.noSuchFile);
|
||||||
|
@ -91,7 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw new ApiError(meta.errors.noSuchEmoji);
|
throw new ApiError(meta.errors.noSuchEmoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.customEmojiService.update(ps.id, {
|
if (!isRequest) {await this.customEmojiService.update(ps.id, {
|
||||||
driveFile,
|
driveFile,
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
category: ps.category ?? null,
|
category: ps.category ?? null,
|
||||||
|
@ -101,7 +103,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
localOnly: ps.localOnly,
|
localOnly: ps.localOnly,
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||||
draft: ps.draft,
|
draft: ps.draft,
|
||||||
}, me);
|
}, me);} else {
|
||||||
|
const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl);
|
||||||
|
if (file === null) throw new ApiError(meta.errors.noSuchFile);
|
||||||
|
await this.customEmojiService.request({
|
||||||
|
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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
64
packages/backend/src/server/api/endpoints/emoji-requests.ts
Normal file
64
packages/backend/src/server/api/endpoints/emoji-requests.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { EmojiRequestsRepository } from '@/models/_.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { EmojiRequestsEntityService } from '@/core/entities/EmojiRequestsEntityService.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: 'EmojiRequestSimple',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.emojiRequestsRepository)
|
||||||
|
private emojiRequestsRepository: EmojiRequestsRepository,
|
||||||
|
|
||||||
|
private emojiRequestsEntityService: EmojiRequestsEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async () => {
|
||||||
|
const emojis = await this.emojiRequestsRepository.find({
|
||||||
|
order: {
|
||||||
|
category: 'ASC',
|
||||||
|
name: 'ASC',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
emojis: await this.emojiRequestsEntityService.packSimpleMany(emojis),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,7 @@ export const moderationLogTypes = [
|
||||||
'unsuspend',
|
'unsuspend',
|
||||||
'updateUserNote',
|
'updateUserNote',
|
||||||
'addCustomEmoji',
|
'addCustomEmoji',
|
||||||
|
'requestCustomEmoji',
|
||||||
'updateCustomEmoji',
|
'updateCustomEmoji',
|
||||||
'deleteCustomEmoji',
|
'deleteCustomEmoji',
|
||||||
'assignRole',
|
'assignRole',
|
||||||
|
@ -117,6 +118,10 @@ export type ModerationLogPayloads = {
|
||||||
emojiId: string;
|
emojiId: string;
|
||||||
emoji: any;
|
emoji: any;
|
||||||
};
|
};
|
||||||
|
requestCustomEmoji: {
|
||||||
|
emojiId: string;
|
||||||
|
emoji: any;
|
||||||
|
};
|
||||||
updateCustomEmoji: {
|
updateCustomEmoji: {
|
||||||
emojiId: string;
|
emojiId: string;
|
||||||
before: any;
|
before: any;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkInput v-model="query" :debounce="true" type="search">
|
<MkInput v-model="query" :debounce="true" type="search" autocapitalize="off">
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
<template #label>{{ i18n.ts.search }}</template>
|
<template #label>{{ i18n.ts.search }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -20,22 +20,22 @@
|
||||||
<MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100">
|
<MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100">
|
||||||
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
||||||
<template #default="{items}">
|
<template #default="{items}">
|
||||||
<div class="ldhfsamy">
|
<div :class="$style.root">
|
||||||
<div v-for="emoji in items" :key="emoji.id">
|
<div v-for="emoji in items" :key="emoji.id">
|
||||||
<button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
<button v-if="emoji.request" class="_panel _button" :class="[{ selected: selectedEmojis.includes(emoji.id) },$style.emoji,$style.emojirequest]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="name _monospace">{{ emoji.name + ' (draft)' }}</div>
|
|
||||||
<div class="info">{{ emoji.category }}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="name _monospace">{{ emoji.name }}</div>
|
<div class="name _monospace">{{ emoji.name }}</div>
|
||||||
<div class="info">{{ emoji.category }}</div>
|
<div class="info">{{ emoji.category }}</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button v-else class="_panel _button" :class="[{ selected: selectedEmojis.includes(emoji.id) },$style.emoji]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
||||||
|
<img :src="emoji.url" :class="$style.img" :alt="emoji.name"/>
|
||||||
|
<div :class="$style.body">
|
||||||
|
<div :class="$style.name" class="_monospace">{{ emoji.name }}</div>
|
||||||
|
<div :class="$style.info">{{ emoji.category }}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -64,6 +64,14 @@ const pagination = {
|
||||||
query: (query.value && query.value !== '') ? query.value : null,
|
query: (query.value && query.value !== '') ? query.value : null,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectAll = () => {
|
||||||
|
if (selectedEmojis.value.length > 0) {
|
||||||
|
selectedEmojis.value = [];
|
||||||
|
} else {
|
||||||
|
selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
const setisSensitiveBulk = async () => {
|
const setisSensitiveBulk = async () => {
|
||||||
const { canceled, result } = await os.switch1({
|
const { canceled, result } = await os.switch1({
|
||||||
title: 'isSensitive',
|
title: 'isSensitive',
|
||||||
|
@ -221,53 +229,48 @@ const delBulk = async () => {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.ldhfsamy {
|
.root {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||||||
grid-gap: var(--margin);
|
grid-gap: var(--margin);
|
||||||
|
}
|
||||||
|
.emoji {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border: solid 1px var(--panel);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
div > .emoji {
|
&:hover {
|
||||||
display: flex;
|
border-color: var(--inputBorderHover);
|
||||||
align-items: center;
|
}
|
||||||
padding: 11px;
|
|
||||||
text-align: left;
|
|
||||||
border: solid 1px var(--panel);
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
&.selected {
|
||||||
border-color: var(--inputBorderHover);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
&.selected {
|
.img {
|
||||||
border-color: var(--accent);
|
width: 42px;
|
||||||
}
|
height: 42px;
|
||||||
|
}
|
||||||
> .img {
|
.body {
|
||||||
width: 42px;
|
padding: 0 0 0 8px;
|
||||||
height: 42px;
|
white-space: nowrap;
|
||||||
}
|
overflow: hidden;
|
||||||
|
}
|
||||||
> .body {
|
.name {
|
||||||
padding: 0 0 0 8px;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
overflow: hidden;
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
> .name {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
opacity: 0.5;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-draft {
|
.info {
|
||||||
|
opacity: 0.5;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.emojirequest {
|
||||||
--c: rgb(255 196 0 / 15%);;
|
--c: rgb(255 196 0 / 15%);;
|
||||||
background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%);
|
background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%);
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<FormSplit>
|
<FormSplit>
|
||||||
<MkInput v-model="queryRemote" :debounce="true" type="search">
|
<MkInput v-model="queryRemote" :debounce="true" type="search" autocapitalize="off">
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
<template #label>{{ i18n.ts.search }}</template>
|
<template #label>{{ i18n.ts.search }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -8,15 +8,15 @@
|
||||||
<template #label>{{ i18n.ts.host }}</template>
|
<template #label>{{ i18n.ts.host }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
</FormSplit>
|
</FormSplit>
|
||||||
<MkPagination :pagination="remotePagination" :displayLimit="100">
|
<MkPagination :pagination="remotePagination">
|
||||||
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
||||||
<template #default="{items}">
|
<template #default="{items}">
|
||||||
<div class="ldhfsamy">
|
<div :class="$style.root">
|
||||||
<div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)">
|
<div v-for="emoji in items" :key="emoji.id" :class="$style.emoji" class="_panel _button" @click="remoteMenu(emoji, $event)">
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
<img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" :class="$style.img" :alt="emoji.name"/>
|
||||||
<div class="body">
|
<div :class="$style.body">
|
||||||
<div class="name _monospace">{{ emoji.name }}</div>
|
<div :class="$style.name" class="_monospace">{{ emoji.name }}</div>
|
||||||
<div class="info">{{ emoji.host }}</div>
|
<div :class="$style.info">{{ emoji.host }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,49 +62,45 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.empty {
|
|
||||||
margin: var(--margin);
|
.root {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
margin: var(--margin) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ldhfsamy {
|
.emoji {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
align-items: center;
|
||||||
grid-gap: 12px;
|
padding: 12px;
|
||||||
margin: var(--margin) 0;
|
text-align: left;
|
||||||
|
|
||||||
> .emoji {
|
&:hover {
|
||||||
display: flex;
|
color: var(--accent);
|
||||||
align-items: center;
|
}
|
||||||
padding: 12px;
|
}
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
&:hover {
|
.img {
|
||||||
color: var(--accent);
|
width: 32px;
|
||||||
}
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
> .img {
|
.body {
|
||||||
width: 32px;
|
padding: 0 0 0 8px;
|
||||||
height: 32px;
|
white-space: nowrap;
|
||||||
}
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
> .body {
|
.info {
|
||||||
padding: 0 0 0 8px;
|
opacity: 0.5;
|
||||||
white-space: nowrap;
|
font-size: 90%;
|
||||||
overflow: hidden;
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
> .name {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 90%;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
198
packages/frontend/src/components/MkCustomEmojiEditRequest.vue
Normal file
198
packages/frontend/src/components/MkCustomEmojiEditRequest.vue
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
<template>
|
||||||
|
<MkPagination ref="emojisRequestPaginationComponent" :pagination="paginationRequest">
|
||||||
|
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
||||||
|
<template #default="{items}">
|
||||||
|
<template v-for="emoji in items" :key="emoji.id">
|
||||||
|
<div :class="$style.emoji" class="_panel">
|
||||||
|
<div :class="$style.img">
|
||||||
|
<div :class="$style.imgLight"><img :src="emoji.url" :alt="emoji.name"/></div>
|
||||||
|
<div :class="$style.imgDark"><img :src="emoji.url" :alt="emoji.name"/></div>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.info">
|
||||||
|
<div :class="$style.name">{{ i18n.ts.name }}: {{ emoji.name }}</div>
|
||||||
|
<div :class="$style.category">{{ i18n.ts.category }}:{{ emoji.category }}</div>
|
||||||
|
<div :class="$style.aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div>
|
||||||
|
<div :class="$style.license">{{ i18n.ts.license }}:{{ emoji.license }}</div>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.editbutton">
|
||||||
|
<MkButton primary :class="$style.edit" @click="editRequest(emoji)">
|
||||||
|
{{ i18n.ts.edit }}
|
||||||
|
</MkButton>
|
||||||
|
<MkButton :class="$style.request" @click="unrequested(emoji)">
|
||||||
|
{{ i18n.ts.approval }}
|
||||||
|
</MkButton>
|
||||||
|
<MkButton danger :class="$style.delete" @click="deleteRequest(emoji)">
|
||||||
|
{{ i18n.ts.delete }}
|
||||||
|
</MkButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
|
||||||
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
||||||
|
const emojisRequestPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
|
const query = ref(null);
|
||||||
|
|
||||||
|
const paginationRequest = {
|
||||||
|
endpoint: 'admin/emoji/list-request' as const,
|
||||||
|
limit: 30,
|
||||||
|
params: computed(() => ({
|
||||||
|
query: (query.value && query.value !== '') ? query.value : null,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
function editRequest(emoji) {
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
||||||
|
emoji: emoji,
|
||||||
|
isRequest: true,
|
||||||
|
}, {
|
||||||
|
done: result => {
|
||||||
|
if (result.updated) {
|
||||||
|
emojisRequestPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
|
||||||
|
...oldEmoji,
|
||||||
|
...result.updated,
|
||||||
|
}));
|
||||||
|
emojisRequestPaginationComponent.value.reload();
|
||||||
|
} else if (result.deleted) {
|
||||||
|
emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
emojisRequestPaginationComponent.value.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, 'closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unrequested(emoji) {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.t('requestApprovalAreYouSure', { x: emoji.name }),
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
await os.api('admin/emoji/update-request', {
|
||||||
|
id: emoji.id,
|
||||||
|
fileId: emoji.fileId,
|
||||||
|
name: emoji.name,
|
||||||
|
category: emoji.category,
|
||||||
|
aliases: emoji.aliases,
|
||||||
|
license: emoji.license,
|
||||||
|
isSensitive: emoji.isSensitive,
|
||||||
|
localOnly: emoji.localOnly,
|
||||||
|
isRequest: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
emojisRequestPaginationComponent.value.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteRequest(emoji) {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.t('removeAreYouSure', { x: emoji.name }),
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
os.api('admin/emoji/delete', {
|
||||||
|
id: emoji.id,
|
||||||
|
}).then(() => {
|
||||||
|
emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
emojisRequestPaginationComponent.value.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.emoji {
|
||||||
|
align-items: center;
|
||||||
|
padding: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border: solid 1px var(--panel);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
.img {
|
||||||
|
display: grid;
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 1/ span 2;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
place-content: center;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
.imgLight {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
img {
|
||||||
|
max-height: 64px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.imgDark {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 2;
|
||||||
|
background-color: #000;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
img {
|
||||||
|
max-height: 64px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
display: grid;
|
||||||
|
grid-row: 2;
|
||||||
|
grid-template-rows: 30px 30px 30px;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
grid-row: 1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
grid-row: 2;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aliases {
|
||||||
|
grid-row: 3;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license {
|
||||||
|
grid-row: 4;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.editbutton {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 42px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.edit {
|
||||||
|
grid-row: 1;
|
||||||
|
width: 100%;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request {
|
||||||
|
grid-row: 2;
|
||||||
|
width: 100%;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
grid-row: 3;
|
||||||
|
width: 100%;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -7,12 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkModalWindow
|
<MkModalWindow
|
||||||
ref="dialog"
|
ref="dialog"
|
||||||
:width="400"
|
:width="400"
|
||||||
:withOkButton="false "
|
|
||||||
@close="dialog.close()"
|
@close="dialog.close()"
|
||||||
@closed="$emit('closed')"
|
@closed="$emit('closed')"
|
||||||
>
|
>
|
||||||
<template v-if="emoji" #header>:{{ emoji.name }}:</template>
|
<template v-if="emoji" #header>:{{ emoji.name }}:</template>
|
||||||
<template v-else-if="isRequest" #header>{{ i18n.ts.requestCustomEmojis }}</template>
|
<template v-else-if="isRequest && !emoji" #header>{{ i18n.ts.requestCustomEmojis }}</template>
|
||||||
<template v-else #header>New emoji</template>
|
<template v-else #header>New emoji</template>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -64,14 +63,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo>
|
<MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
<MkSwitch v-model="isSensitive">isSensitive</MkSwitch>
|
||||||
|
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
|
||||||
<MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch>
|
<MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch>
|
||||||
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
|
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
|
||||||
<MkSwitch v-model="isNotifyIsHome">
|
<MkSwitch v-model="isNotifyIsHome">
|
||||||
{{ i18n.ts.isNotifyIsHome }}
|
{{ i18n.ts.isNotifyIsHome }}
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSwitch v-if="!isRequest" v-model="draft" >
|
|
||||||
{{ i18n.ts.draft }}
|
|
||||||
</MkSwitch>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
@ -87,7 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, ref } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { DriveFile } from 'misskey-js/built/entities.js';
|
import { DriveFile } from 'misskey-js/built/entities.js';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
|
@ -99,144 +98,91 @@ import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { customEmojiCategories } from '@/custom-emojis.js';
|
import { customEmojiCategories } from '@/custom-emojis.js';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import { selectFile } from '@/scripts/select-file.js';
|
import { selectFile, selectFiles } from '@/scripts/select-file.js';
|
||||||
import MkRolePreview from '@/components/MkRolePreview.vue';
|
import MkRolePreview from '@/components/MkRolePreview.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
emoji?: any,
|
emoji?: any,
|
||||||
isRequest: boolean,
|
isRequest: boolean,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = ref(null);
|
let dialog = $ref(null);
|
||||||
const name = ref<string>(props.emoji ? props.emoji.name : '');
|
let name: string = $ref(props.emoji ? props.emoji.name : '');
|
||||||
const category = ref<string>(props.emoji ? props.emoji.category : '');
|
let category: string = $ref(props.emoji ? props.emoji.category : '');
|
||||||
const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : '');
|
let aliases: string = $ref(props.emoji ? props.emoji.aliases.join(' ') : '');
|
||||||
const license = ref<string>(props.emoji ? (props.emoji.license ?? '') : '');
|
let license: string = $ref(props.emoji ? (props.emoji.license ?? '') : '');
|
||||||
const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
|
let isSensitive = $ref(props.emoji ? props.emoji.isSensitive : false);
|
||||||
const localOnly = ref(props.emoji ? props.emoji.localOnly : false);
|
let localOnly = $ref(props.emoji ? props.emoji.localOnly : false);
|
||||||
const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
||||||
const rolesThatCanBeUsedThisEmojiAsReaction = ref([]);
|
let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]);
|
||||||
const file = ref<Misskey.entities.DriveFile>();
|
let file = $ref<Misskey.entities.DriveFile>();
|
||||||
let chooseFile = ref(null);
|
let chooseFile: DriveFile|null = $ref(null);
|
||||||
let draft = ref(props.emoji ? props.emoji.draft : false);
|
let isRequest = $ref(props.isRequest ?? false);
|
||||||
let isRequest = ref(props.isRequest);
|
watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => {
|
||||||
let isNotifyIsHome = ref(false);
|
rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
|
||||||
let url;
|
|
||||||
watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
|
|
||||||
rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
|
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null);
|
const imgUrl = computed(() => file ? file.url : props.emoji && !isRequest ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null);
|
||||||
const validation = computed(() => {
|
const validation = computed(() => {
|
||||||
return name.value.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null;
|
return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null;
|
||||||
});
|
});
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
|
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
|
||||||
(ev: 'closed'): void
|
(ev: 'closed'): void
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function ok() {
|
|
||||||
if (isRequest) {
|
|
||||||
if (chooseFile.value !== null && name.value.match(/^[a-zA-Z0-9_]+$/)) {
|
|
||||||
add();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function add() {
|
|
||||||
const ret = await os.api('admin/emoji/add-draft', {
|
|
||||||
name: name,
|
|
||||||
category: category.value,
|
|
||||||
aliases: aliases.value.split(' '),
|
|
||||||
license: license.value === '' ? null : license.value,
|
|
||||||
fileId: chooseFile.value.id,
|
|
||||||
isNotifyIsHome: isNotifyIsHome.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
emit('done', {
|
|
||||||
updated: {
|
|
||||||
id: ret.value.id,
|
|
||||||
name,
|
|
||||||
category,
|
|
||||||
aliases: aliases.value.split(' '),
|
|
||||||
license: license.value === '' ? null : license,
|
|
||||||
draft: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.value.close();
|
|
||||||
}
|
|
||||||
async function changeImage(ev) {
|
async function changeImage(ev) {
|
||||||
file.value = await selectFile(ev.currentTarget ?? ev.target, null);
|
file = await selectFile(ev.currentTarget ?? ev.target, null);
|
||||||
const candidate = file.value.name.replace(/\.(.+)$/, '');
|
const candidate = file.name.replace(/\.(.+)$/, '');
|
||||||
if (candidate.match(/^[a-z0-9_]+$/)) {
|
if (candidate.match(/^[a-z0-9_]+$/)) {
|
||||||
name.value = candidate;
|
name = candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addRole() {
|
async function addRole() {
|
||||||
const roles = await os.api('admin/roles/list');
|
const roles = await os.api('admin/roles/list');
|
||||||
const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id);
|
const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id);
|
||||||
|
|
||||||
const { canceled, result: role } = await os.select({
|
const { canceled, result: role } = await os.select({
|
||||||
items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })),
|
items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
rolesThatCanBeUsedThisEmojiAsReaction.value.push(role);
|
rolesThatCanBeUsedThisEmojiAsReaction.push(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeRole(role, ev) {
|
async function removeRole(role, ev) {
|
||||||
rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id);
|
rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id);
|
||||||
}
|
}
|
||||||
async function update() {
|
|
||||||
await os.apiWithDialog('admin/emoji/update', {
|
|
||||||
id: props.emoji.id,
|
|
||||||
name: name.value,
|
|
||||||
category: category.value === '' ? null : category.value,
|
|
||||||
aliases: aliases.value.split(' ').filter(x => x !== ''),
|
|
||||||
license: license.value === '' ? null : license.value,
|
|
||||||
fileId: chooseFile.value?.id,
|
|
||||||
draft: draft.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
emit('done', {
|
|
||||||
updated: {
|
|
||||||
id: props.emoji.id,
|
|
||||||
name: name.value,
|
|
||||||
category: category.value === '' ? null : category.value,
|
|
||||||
aliases: aliases.value.split(' ').filter(x => x !== ''),
|
|
||||||
license: license.value === '' ? null : license.value,
|
|
||||||
draft: draft.value,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.value.close();
|
|
||||||
}
|
|
||||||
async function done() {
|
async function done() {
|
||||||
const params = {
|
const params = {
|
||||||
name: name.value,
|
name,
|
||||||
category: category.value === '' ? null : category.value,
|
category: category === '' ? null : category,
|
||||||
aliases: aliases.value.split(' ').filter(x => x !== ''),
|
aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''),
|
||||||
license: license.value === '' ? null : license.value,
|
license: license === '' ? null : license,
|
||||||
isSensitive: isSensitive.value,
|
Request: isRequest,
|
||||||
draft: draft.value,
|
isSensitive,
|
||||||
localOnly: localOnly.value,
|
localOnly,
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id),
|
roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id),
|
||||||
isNotifyIsHome: isNotifyIsHome.value,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if (file.value) {
|
if (file) {
|
||||||
params.fileId = file.value.id;
|
params.fileId = file.id;
|
||||||
}
|
}
|
||||||
console.log(props.emoji);
|
|
||||||
if (props.emoji) {
|
if (props.emoji) {
|
||||||
await os.apiWithDialog('admin/emoji/update', {
|
if (isRequest) {
|
||||||
id: props.emoji.id,
|
await os.apiWithDialog('admin/emoji/update-request', {
|
||||||
...params,
|
id: props.emoji.id,
|
||||||
});
|
...params,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await os.apiWithDialog('admin/emoji/update', {
|
||||||
|
id: props.emoji.id,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
emit('done', {
|
emit('done', {
|
||||||
updated: {
|
updated: {
|
||||||
|
@ -245,31 +191,24 @@ async function done() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.value.close();
|
dialog.close();
|
||||||
} else {
|
} else {
|
||||||
const created = isRequest
|
const created = isRequest
|
||||||
? await os.apiWithDialog('admin/emoji/add-draft', params)
|
? await os.apiWithDialog('admin/emoji/add-request', params)
|
||||||
: await os.apiWithDialog('admin/emoji/add', params);
|
: await os.apiWithDialog('admin/emoji/add', params);
|
||||||
|
|
||||||
emit('done', {
|
emit('done', {
|
||||||
created: created,
|
created: created,
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.value.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseFileFrom(ev) {
|
|
||||||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
|
|
||||||
chooseFile.value = files_[0];
|
|
||||||
url = chooseFile.value.url;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function del() {
|
async function del() {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
text: i18n.t('removeAreYouSure', { x: name.value }),
|
text: i18n.t('removeAreYouSure', { x: name }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
|
@ -279,60 +218,60 @@ async function del() {
|
||||||
emit('done', {
|
emit('done', {
|
||||||
deleted: true,
|
deleted: true,
|
||||||
});
|
});
|
||||||
dialog.value.close();
|
dialog.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.imgs {
|
.imgs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imgContainer {
|
.imgContainer {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img {
|
.img {
|
||||||
display: block;
|
display: block;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
width: 64px;
|
width: 64px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roleItem {
|
.roleItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role {
|
.role {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roleUnassign {
|
.roleUnassign {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-top: solid 0.5px var(--divider);
|
border-top: solid 0.5px var(--divider);
|
||||||
-webkit-backdrop-filter: var(--blur, blur(15px));
|
-webkit-backdrop-filter: var(--blur, blur(15px));
|
||||||
backdrop-filter: var(--blur, blur(15px));
|
backdrop-filter: var(--blur, blur(15px));
|
||||||
}
|
}
|
||||||
|
|
||||||
.footerButtons {
|
.footerButtons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -212,7 +212,6 @@ async function init(): Promise<void> {
|
||||||
const item = res[i];
|
const item = res[i];
|
||||||
if (i === 3) item._shouldInsertAd_ = true;
|
if (i === 3) item._shouldInsertAd_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.length === 0 || props.pagination.noPaging) {
|
if (res.length === 0 || props.pagination.noPaging) {
|
||||||
concatItems(res);
|
concatItems(res);
|
||||||
more.value = false;
|
more.value = false;
|
||||||
|
@ -221,7 +220,6 @@ async function init(): Promise<void> {
|
||||||
concatItems(res);
|
concatItems(res);
|
||||||
more.value = true;
|
more.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset.value = res.length;
|
offset.value = res.length;
|
||||||
error.value = false;
|
error.value = false;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
|
|
|
@ -5,138 +5,112 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/>
|
|
||||||
</template>
|
|
||||||
<MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20">
|
<MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20">
|
||||||
<MkButton
|
<MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton>
|
||||||
v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link
|
<MkButton v-if="$i && (!$i.isModerator && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton>
|
||||||
to="/custom-emojis-manager"
|
|
||||||
>
|
|
||||||
{{ i18n.ts.manageCustomEmojis }}
|
|
||||||
</MkButton>
|
|
||||||
<MkButton
|
|
||||||
v-if="$i && (!$i.isModerator || !$i.policies.canManageCustomEmojis || $i.policies.canRequestCustomEmojis)"
|
|
||||||
primary style="margin-top: 8px;" @click="edit"
|
|
||||||
>
|
|
||||||
{{ i18n.ts.requestCustomEmojis }}
|
|
||||||
</MkButton>
|
|
||||||
|
|
||||||
<div class="query" style="margin-top: 10px;">
|
<div class="query" style="margin-top: 10px;">
|
||||||
<MkInput v-model="q" class="" :placeholder="i18n.ts.search" autocapitalize="off">
|
<MkInput v-model="q" class="" :placeholder="i18n.ts.search" autocapitalize="off">
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
||||||
<!-- たくさんあると邪魔
|
<!-- たくさんあると邪魔
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span>
|
<span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span>
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
|
||||||
<MkFoldableSection v-if="searchEmojis">
|
<MkFoldableSection v-if="searchEmojis">
|
||||||
<template #header>{{ i18n.ts.searchResult }}</template>
|
<template #header>{{ i18n.ts.searchResult }}</template>
|
||||||
<div :class="$style.emojis">
|
<div :class="$style.emojis">
|
||||||
<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/>
|
<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :request="emoji.request"/>
|
||||||
</div>
|
</div>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
|
|
||||||
<MkFoldableSection v-for="category in filteredCategories" v-once :key="category">
|
<MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category">
|
||||||
<template #header>{{ category || i18n.ts.other }}</template>
|
<template #header>{{ category || i18n.ts.other }}</template>
|
||||||
<div :class="$style.emojis">
|
<div :class="$style.emojis">
|
||||||
<XEmoji
|
<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/>
|
||||||
v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name"
|
|
||||||
:emoji="emoji" :draft="emoji.draft"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
<MkSpacer v-if="tab === 'request'" :contentMax="1000" :marginMin="20">
|
||||||
<MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20">
|
|
||||||
<div :class="$style.emojis">
|
<div :class="$style.emojis">
|
||||||
<XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/>
|
<XEmoji v-for="emoji in requestEmojis.emojis" :key="emoji.name" :emoji="emoji" :request="true"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, defineAsyncComponent, ref, computed } from 'vue';
|
import { watch, defineAsyncComponent, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import XEmoji from './emojis.emoji.vue';
|
import XEmoji from './emojis.emoji.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||||
import {customEmojis, customEmojiCategories, getCustomEmojiTags} from '@/custom-emojis.js';
|
import { customEmojis, customEmojiCategories } from '@/custom-emojis.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
|
||||||
let tab = ref('emojis');
|
let tab = $ref('emojis');
|
||||||
const headerActions = computed(() => []);
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
const customEmojiTags = getCustomEmojiTags();
|
const headerTabs = $computed(() => [{
|
||||||
const q = ref('');
|
key: 'emojis',
|
||||||
const searchEmojis = ref<Misskey.entities.EmojiSimple[]>(null);
|
title: i18n.ts.list,
|
||||||
const selectedTags = ref(new Set());
|
|
||||||
const headerTabs = computed(() => [{
|
|
||||||
key: 'emojis',
|
|
||||||
title: i18n.ts.list,
|
|
||||||
}, {
|
}, {
|
||||||
key: 'draft',
|
key: 'request',
|
||||||
title: i18n.ts.draftEmojis,
|
title: i18n.ts.requestingEmojis,
|
||||||
}]);
|
}]);
|
||||||
const filteredCategories = computed(() => {
|
|
||||||
return customEmojiCategories.value.filter((category: any) => {
|
|
||||||
return customEmojis.value.some((em: any) => em.category === category && !em.draft);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
definePageMetadata(ref({}));
|
definePageMetadata(ref({}));
|
||||||
const draftEmojis = customEmojis.value.filter(emoji => emoji.draft);
|
|
||||||
|
let q = $ref('');
|
||||||
|
let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null);
|
||||||
|
let selectedTags = $ref(new Set());
|
||||||
|
const requestEmojis = await os.apiGet('emoji-requests');
|
||||||
|
|
||||||
function search() {
|
function search() {
|
||||||
if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) {
|
if ((q === '' || q == null) && selectedTags.size === 0) {
|
||||||
searchEmojis.value = null;
|
searchEmojis = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedTags.value.size === 0) {
|
if (selectedTags.size === 0) {
|
||||||
const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g);
|
const queryarry = q.match(/\:([a-z0-9_]*)\:/g);
|
||||||
|
|
||||||
if (queryarry) {
|
if (queryarry) {
|
||||||
searchEmojis.value = customEmojis.value.filter(emoji =>
|
searchEmojis = customEmojis.value.filter(emoji =>
|
||||||
queryarry.includes(`:${emoji.name}:`),
|
queryarry.includes(`:${emoji.name}:`),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q.value));
|
searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
searchEmojis.value = customEmojis.value.filter(emoji => (emoji.name.includes(q.value) || emoji.aliases.includes(q.value)) && [...selectedTags.value].every(t => emoji.aliases.includes(t)));
|
searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleTag(tag) {
|
const edit = () => {
|
||||||
if (selectedTags.value.has(tag)) {
|
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
||||||
selectedTags.value.delete(tag);
|
isRequest: true,
|
||||||
} else {
|
}, {
|
||||||
selectedTags.value.add(tag);
|
done: result => {
|
||||||
}
|
window.location.reload();
|
||||||
}
|
},
|
||||||
|
}, 'closed');
|
||||||
|
};
|
||||||
|
|
||||||
watch(q, () => {
|
watch($$(q), () => {
|
||||||
search();
|
search();
|
||||||
});
|
});
|
||||||
const edit = () => {
|
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
watch($$(selectedTags), () => {
|
||||||
isRequest: true,
|
|
||||||
}, {
|
|
||||||
done: result => {
|
|
||||||
window.location.reload();
|
|
||||||
},
|
|
||||||
}, 'closed');
|
|
||||||
};
|
|
||||||
watch(selectedTags, () => {
|
|
||||||
search();
|
search();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
|
|
|
@ -89,9 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20">
|
<XEmojis v-else-if="tab === 'emojis'"/>
|
||||||
<XEmojis/>
|
|
||||||
</MkSpacer>
|
|
||||||
<MkSpacer v-else-if="tab === 'federation'" :contentMax="1000" :marginMin="20">
|
<MkSpacer v-else-if="tab === 'federation'" :contentMax="1000" :marginMin="20">
|
||||||
<XFederation/>
|
<XFederation/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
|
|
@ -319,46 +319,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])">
|
|
||||||
<template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template>
|
|
||||||
<template #suffix>
|
|
||||||
<span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
|
||||||
<span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span>
|
|
||||||
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span>
|
|
||||||
</template>
|
|
||||||
<div class="_gaps">
|
|
||||||
<MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly">
|
|
||||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
|
||||||
</MkRange>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])">
|
|
||||||
<template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template>
|
|
||||||
<template #suffix>
|
|
||||||
<span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
|
||||||
<span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span>
|
|
||||||
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span>
|
|
||||||
</template>
|
|
||||||
<div class="_gaps">
|
|
||||||
<MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly">
|
|
||||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
|
||||||
</MkRange>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])">
|
||||||
<template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template>
|
<template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
|
|
|
@ -8,17 +8,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :contentMax="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="ogwlenmc">
|
<div class="ogwlenmc">
|
||||||
<div v-if="tab === 'local'" class="local">
|
<div v-if="tab === 'local'" class="local">
|
||||||
<MkCustomEmojiEditLocal/>
|
<MkCustomEmojiEditLocal/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tab === 'request'" class="request">
|
<div v-if="tab === 'request'" class="request">
|
||||||
<MkCustomEmojiEditDraft/>
|
<MkCustomEmojiEditRequest/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'remote'" class="remote">
|
<div v-else-if="tab === 'remote'" class="remote">
|
||||||
<MkCustomEmojiEditRemote/>
|
<MkCustomEmojiEditRemote/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, ref } from 'vue';
|
import { computed, defineAsyncComponent, ref } from 'vue';
|
||||||
import MkCustomEmojiEditDraft from '@/components/MkCustomEmojiEditDraft.vue';
|
import MkCustomEmojiEditRequest from '@/components/MkCustomEmojiEditRequest.vue';
|
||||||
import MkCustomEmojiEditLocal from '@/components/MkCustomEmojiEditLocal.vue';
|
import MkCustomEmojiEditLocal from '@/components/MkCustomEmojiEditLocal.vue';
|
||||||
import MkCustomEmojiEditRemote from '@/components/MkCustomEmojiEditRemote.vue';
|
import MkCustomEmojiEditRemote from '@/components/MkCustomEmojiEditRemote.vue';
|
||||||
import { selectFile } from '@/scripts/select-file';
|
import { selectFile } from '@/scripts/select-file';
|
||||||
|
@ -34,15 +34,17 @@ import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
|
||||||
const tab = ref('draft');
|
const tab = ref('request');
|
||||||
|
|
||||||
const add = async (ev: MouseEvent) => {
|
const add = async (ev: MouseEvent) => {
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
|
||||||
}, {
|
}, {
|
||||||
done: result => {
|
done: result => {
|
||||||
if (result.created) {
|
//TODO: emitにして追加を反映
|
||||||
emojisPaginationComponent.value.prepend(result.created);
|
// if (result.created) {
|
||||||
}
|
// emojisPaginationComponent.value.prepend(result.created);
|
||||||
|
// emojisPaginationComponent.value.reload();
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
};
|
};
|
||||||
|
@ -89,7 +91,7 @@ const menu = (ev: MouseEvent) => {
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerActions = computed(() => [{
|
const headerActions = $computed(() => [{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: 'ti ti-plus',
|
icon: 'ti ti-plus',
|
||||||
text: i18n.ts.addEmoji,
|
text: i18n.ts.addEmoji,
|
||||||
|
@ -99,9 +101,9 @@ const headerActions = computed(() => [{
|
||||||
handler: menu,
|
handler: menu,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
const headerTabs = computed(() => [{
|
const headerTabs = $computed(() => [{
|
||||||
key: 'draft',
|
key: 'request',
|
||||||
title: i18n.ts.draftEmojis,
|
title: i18n.ts.requestingEmojis,
|
||||||
}, {
|
}, {
|
||||||
key: 'local',
|
key: 'local',
|
||||||
title: i18n.ts.local,
|
title: i18n.ts.local,
|
||||||
|
|
|
@ -4,36 +4,35 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button v-if="emoji.draft" class="zuvgdzyu _button emoji-draft" @click="menu">
|
<button v-if="request" class="_button emoji-request" :class="$style.root" @click="menu">
|
||||||
<img style="max-height: 64px;object-fit: contain;" :src="emoji.url" class="img" loading="lazy"/>
|
<img :src="emoji.url" :class="$style.img" loading="lazy"/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="name _monospace">{{ emoji.name + ' (draft)' }}</div>
|
<div class="name _monospace">{{ emoji.name + ' (request)' }}</div>
|
||||||
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button v-else class="_button" :class="$style.root" @click="menu">
|
<button v-else class="_button" :class="$style.root" @click="menu">
|
||||||
<img :src="emoji.url" :class="$style.img" loading="lazy"/>
|
<img :src="emoji.url" :class="$style.img" loading="lazy"/>
|
||||||
<div :class="$style.body">
|
<div :class="$style.body">
|
||||||
<div :class="$style.name">{{ emoji.name }}</div>
|
<div class="name _monospace">{{ emoji.name }}</div>
|
||||||
<div :class="$style.info">{{ emoji.aliases.join(' ') }}</div>
|
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
emoji: {
|
emoji: {
|
||||||
name: string;
|
name: string;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
category: string;
|
category: string;
|
||||||
url: string;
|
url: string;
|
||||||
draft: boolean;
|
};
|
||||||
};
|
request?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function menu(ev) {
|
function menu(ev) {
|
||||||
|
@ -51,10 +50,10 @@ function menu(ev) {
|
||||||
text: i18n.ts.info,
|
text: i18n.ts.info,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
action: () => {
|
action: () => {
|
||||||
os.apiGet('emoji', { name: props.emoji.name }).then(res => {
|
os.apiGet('emoji-requests', { name: props.emoji.name }).then(res => {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
text: `Name: ${res.name}\nAliases: ${res.aliases.join(' ')}\nCategory: ${res.category}\nisSensitive: ${res.isSensitive}\nlocalOnly: ${res.localOnly}\nLicense: ${res.license}\nURL: ${res.url}`,
|
text: `License: ${res.license}`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -64,47 +63,45 @@ function menu(ev) {
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.img {
|
.img {
|
||||||
width: 42px;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
padding: 0 0 0 8px;
|
padding: 0 0 0 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-draft {
|
.emoji-request {
|
||||||
--c: rgb(255 196 0 / 15%);;
|
--c: rgb(255 196 0 / 15%);;
|
||||||
background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%);
|
background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%);
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
max-width: 64px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue