Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance: カテゴリごとの絵文字エクスポート #79

Draft
wants to merge 10 commits into
base: hanami
Choose a base branch
from
4 changes: 4 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5096,6 +5096,10 @@ export interface Locale extends ILocale {
* 新規登録したユーザーに表示されるチュートリアルをスキップできないようにします。チュートリアルを完了しなかったりチュートリアルページを回避したりした場合でも、強制的にリダイレクトされます。
*/
"prohibitSkippingInitialTutorialDescription": string;
/**
* エクスポートしたいカテゴリを選択
*/
"selectCategoryYouWantToExport": string;
"_delivery": {
/**
* 配信状態
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,7 @@ createdAntennas: "作成したアンテナ"
clipNoteLimitExceeded: "これ以上このクリップにノートを追加できません。"
prohibitSkippingInitialTutorial: "チュートリアルをスキップできないようにする"
prohibitSkippingInitialTutorialDescription: "新規登録したユーザーに表示されるチュートリアルをスキップできないようにします。チュートリアルを完了しなかったりチュートリアルページを回避したりした場合でも、強制的にリダイレクトされます。"
selectCategoryYouWantToExport: "エクスポートしたいカテゴリを選択"

_delivery:
status: "配信状態"
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/core/QueueService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ export class QueueService {
}

@bindThis
public createExportCustomEmojisJob(user: ThinUser) {
public createExportCustomEmojisJob(user: ThinUser, opts: { categories?: (string | null)[]; } = {}) {
return this.dbQueue.add('exportCustomEmojis', {
user: { id: user.id },
categories: opts.categories,
}, {
removeOnComplete: true,
removeOnFail: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class ExportCustomEmojisProcessorService {

await writeMeta(`{"metaVersion":2,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","emojis":[`);

const customEmojis = await this.emojisRepository.find({
let customEmojis = await this.emojisRepository.find({
where: {
host: IsNull(),
},
Expand All @@ -84,6 +84,20 @@ export class ExportCustomEmojisProcessorService {
},
});

// job.data.categoriesに指定がある場合はそのカテゴリのみをエクスポート
const categories = job.data.categories as (string | null)[] | null;
this.logger.info(`Exporting categories: ${categories}`);
if (categories != null) {
customEmojis = customEmojis.filter(emoji => {
if (emoji.category == null || emoji.category === 'null') {
// カテゴリがnull、つまりカテゴリなしのもの
return categories.includes(null) || categories.includes('null');
} else {
return categories.includes(emoji.category);
}
});
}

for (const emoji of customEmojis) {
if (!/^[a-zA-Z0-9_]+$/.test(emoji.name)) {
this.logger.error(`invalid emoji name: ${emoji.name}`);
Expand Down
7 changes: 6 additions & 1 deletion packages/backend/src/queue/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type DbJobData<T extends keyof DbJobMap> = DbJobMap[T];

export type DbJobMap = {
deleteDriveFiles: DbJobDataWithUser;
exportCustomEmojis: DbJobDataWithUser;
exportCustomEmojis: DbExportCustomEmojisJobData;
exportAntennas: DBExportAntennasData;
exportNotes: DbJobDataWithUser;
exportFavorites: DbJobDataWithUser;
Expand Down Expand Up @@ -71,6 +71,11 @@ export type DbJobDataWithUser = {
user: ThinUser;
}

export type DbExportCustomEmojisJobData = {
user: ThinUser;
categories?: string[]
}

export type DbExportFollowingData = {
user: ThinUser;
excludeMuting: boolean;
Expand Down
14 changes: 12 additions & 2 deletions packages/backend/src/server/api/endpoints/export-custom-emojis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ export const meta = {

export const paramDef = {
type: 'object',
properties: {},
properties: {
categories: {
type: 'array',
items: {
type: 'string',
nullable: true,
},
description: 'If null is provided instead of array, all emojis will be exported. If null is provided as an element of the array, emojis without category will be exported.',
nullable: true,
},
},
required: [],
} as const;

Expand All @@ -29,7 +39,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queueService: QueueService,
) {
super(meta, paramDef, async (ps, me) => {
this.queueService.createExportCustomEmojisJob(me);
this.queueService.createExportCustomEmojisJob(me, { categories: ps.categories ?? [] });
});
}
}
36 changes: 36 additions & 0 deletions packages/frontend/src/pages/custom-emojis-manager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ import { selectFile } from '@/scripts/select-file.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import type { EnumItem } from '@/scripts/form.js';
import { customEmojiCategories } from '@/custom-emojis.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';

const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
Expand Down Expand Up @@ -180,7 +182,41 @@ const menu = (ev: MouseEvent) => {
icon: 'ti ti-download',
text: i18n.ts.export,
action: async () => {
const emojiCategoryItems = customEmojiCategories.value.map(category => {
if (category == null) {
return { value: '_NULL_', label: i18n.ts.other };
} else {
return { value: category, label: category };
}
}) satisfies EnumItem[];

// TODO: 複数選択可にする
const { canceled, result } = await os.form(i18n.ts.selectCategoryYouWantToExport, {
category: {
type: 'enum',
label: i18n.ts.category,
enum: [
{ value: '_ALL_', label: i18n.ts.all },
...emojiCategoryItems,
],
default: '_ALL_',
},
});

if (canceled) return;

let categories: (string | null)[] | null = null;

if (result.category === '_ALL_') {
categories = null;
} else if (result.category === '_NULL_') {
categories = [null];
} else {
categories = [result.category];
}

misskeyApi('export-custom-emojis', {
categories,
})
.then(() => {
os.alert({
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/scripts/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as Misskey from 'misskey-js';

type EnumItem = string | {
export type EnumItem = string | {
label: string;
value: string;
};
Expand Down
4 changes: 4 additions & 0 deletions packages/misskey-js/etc/misskey-js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@ declare namespace entities {
EndpointRequest,
EndpointResponse,
EndpointsResponse,
ExportCustomEmojisRequest,
FederationFollowersRequest,
FederationFollowersResponse,
FederationFollowingRequest,
Expand Down Expand Up @@ -1828,6 +1829,9 @@ export { entities }
// @public (undocumented)
type Error_2 = components['schemas']['Error'];

// @public (undocumented)
type ExportCustomEmojisRequest = operations['export-custom-emojis']['requestBody']['content']['application/json'];

// @public (undocumented)
type FederationFollowersRequest = operations['federation___followers']['requestBody']['content']['application/json'];

Expand Down
3 changes: 2 additions & 1 deletion packages/misskey-js/src/autogen/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ import type {
EndpointRequest,
EndpointResponse,
EndpointsResponse,
ExportCustomEmojisRequest,
FederationFollowersRequest,
FederationFollowersResponse,
FederationFollowingRequest,
Expand Down Expand Up @@ -741,7 +742,7 @@ export type Endpoints = {
'email-address/available': { req: EmailAddressAvailableRequest; res: EmailAddressAvailableResponse };
'endpoint': { req: EndpointRequest; res: EndpointResponse };
'endpoints': { req: EmptyRequest; res: EndpointsResponse };
'export-custom-emojis': { req: EmptyRequest; res: EmptyResponse };
'export-custom-emojis': { req: ExportCustomEmojisRequest; res: EmptyResponse };
'federation/followers': { req: FederationFollowersRequest; res: FederationFollowersResponse };
'federation/following': { req: FederationFollowingRequest; res: FederationFollowingResponse };
'federation/instances': { req: FederationInstancesRequest; res: FederationInstancesResponse };
Expand Down
1 change: 1 addition & 0 deletions packages/misskey-js/src/autogen/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export type EmailAddressAvailableResponse = operations['email-address___availabl
export type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
export type EndpointResponse = operations['endpoint']['responses']['200']['content']['application/json'];
export type EndpointsResponse = operations['endpoints']['responses']['200']['content']['application/json'];
export type ExportCustomEmojisRequest = operations['export-custom-emojis']['requestBody']['content']['application/json'];
export type FederationFollowersRequest = operations['federation___followers']['requestBody']['content']['application/json'];
export type FederationFollowersResponse = operations['federation___followers']['responses']['200']['content']['application/json'];
export type FederationFollowingRequest = operations['federation___following']['requestBody']['content']['application/json'];
Expand Down
8 changes: 8 additions & 0 deletions packages/misskey-js/src/autogen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14905,6 +14905,14 @@ export type operations = {
* **Credential required**: *Yes*
*/
'export-custom-emojis': {
requestBody: {
content: {
'application/json': {
/** @description If null is provided instead of array, all emojis will be exported. If null is provided as an element of the array, emojis without category will be exported. */
categories?: ((string | null)[]) | null;
};
};
};
responses: {
/** @description OK (without any results) */
204: {
Expand Down
Loading