diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f49f9c5a3c..94c00bad39 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1052,6 +1052,7 @@ failedToPreviewUrl: "プレビューできません" update: "更新" rolesThatCanBeUsedThisEmojiAsReaction: "リアクションとして使えるロール" rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ロールの指定が一つもない場合、誰でもリアクションとして使えます。" +rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "ロールは公開ロールである必要があります。" _initialAccountSetting: accountCreated: "アカウントの作成が完了しました!" diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 0c7bd9ed9a..4a18cd1b3b 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -26,7 +26,8 @@ export class EmojiEntityService { category: emoji.category, // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) url: emoji.publicUrl || emoji.originalUrl, - isSensitive: emoji.isSensitive, + isSensitive: emoji.isSensitive ? true : undefined, + roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, }; } diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index c59b5d1ef4..63f56e77cb 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -24,7 +24,16 @@ export const packedEmojiSimpleSchema = { }, isSensitive: { type: 'boolean', - optional: false, nullable: false, + optional: true, nullable: false, + }, + roleIdsThatCanBeUsedThisEmojiAsReaction: { + type: 'array', + optional: true, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, }, }, } as const; diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 9b104391d7..4770f616ac 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -3,11 +3,11 @@ import * as misskey from 'misskey-js'; import { showSuspendedDialog } from './scripts/show-suspended-dialog'; import { i18n } from './i18n'; import { miLocalStorage } from './local-storage'; +import { MenuButton } from './types/menu'; import { del, get, set } from '@/scripts/idb-proxy'; import { apiUrl } from '@/config'; import { waiting, api, popup, popupMenu, success, alert } from '@/os'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; -import { MenuButton } from './types/menu'; // TODO: 他のタブと永続化されたstateを同期 @@ -101,57 +101,57 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr 'Content-Type': 'application/json', }, }) - .then(res => new Promise }>((done2, fail2) => { - if (res.status >= 500 && res.status < 600) { + .then(res => new Promise }>((done2, fail2) => { + if (res.status >= 500 && res.status < 600) { // サーバーエラー(5xx)の場合をrejectとする // (認証エラーなど4xxはresolve) - return fail2(res); - } - res.json().then(done2, fail2); - })) - .then(async res => { - if (res.error) { - if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') { + return fail2(res); + } + res.json().then(done2, fail2); + })) + .then(async res => { + if (res.error) { + if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') { // SUSPENDED - if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { - await showSuspendedDialog(); - } - } else if (res.error.id === 'e5b3b9f0-2b8f-4b9f-9c1f-8c5c1b2e1b1a') { + if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + await showSuspendedDialog(); + } + } else if (res.error.id === 'e5b3b9f0-2b8f-4b9f-9c1f-8c5c1b2e1b1a') { // USER_IS_DELETED // アカウントが削除されている - if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { - await alert({ - type: 'error', - title: i18n.ts.accountDeleted, - text: i18n.ts.accountDeletedDescription, - }); - } - } else if (res.error.id === 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14') { + if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + await alert({ + type: 'error', + title: i18n.ts.accountDeleted, + text: i18n.ts.accountDeletedDescription, + }); + } + } else if (res.error.id === 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14') { // AUTHENTICATION_FAILED // トークンが無効化されていたりアカウントが削除されたりしている - if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + await alert({ + type: 'error', + title: i18n.ts.tokenRevoked, + text: i18n.ts.tokenRevokedDescription, + }); + } + } else { await alert({ type: 'error', - title: i18n.ts.tokenRevoked, - text: i18n.ts.tokenRevokedDescription, + title: i18n.ts.failedToFetchAccountInformation, + text: JSON.stringify(res.error), }); } - } else { - await alert({ - type: 'error', - title: i18n.ts.failedToFetchAccountInformation, - text: JSON.stringify(res.error), - }); - } - // rejectかつ理由がtrueの場合、削除対象であることを示す - fail(true); - } else { - (res as Account).token = token; - done(res as Account); - } - }) - .catch(fail); + // rejectかつ理由がtrueの場合、削除対象であることを示す + fail(true); + } else { + (res as Account).token = token; + done(res as Account); + } + }) + .catch(fail); }); } @@ -305,3 +305,7 @@ export async function openAccountMenu(opts: { }); } } + +if (_DEV_) { + (window as any).$i = $i; +} diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 9eaf16374b..093a1ec6d4 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -69,8 +69,8 @@ {{ category || i18n.ts.other }} @@ -102,6 +102,7 @@ import { deviceKind } from '@/scripts/device-kind'; import { i18n } from '@/i18n'; import { defaultStore } from '@/store'; import { customEmojiCategories, customEmojis } from '@/custom-emojis'; +import { $i } from '@/account'; const props = withDefaults(defineProps<{ showPinned?: boolean; @@ -274,10 +275,14 @@ watch(q, () => { return matches; }; - searchResultCustom.value = Array.from(searchCustom()); + searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); searchResultUnicode.value = Array.from(searchUnicode()); }); +function filterAvailable(emoji: Misskey.entities.CustomEmoji): boolean { + return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); +} + function focus() { if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { searchEl.value?.focus({ diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 24b72b6f7f..e36a26638f 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -41,16 +41,19 @@ -
- {{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription }} + +
{{ i18n.ts.add }}
- +
+ + {{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription }} + {{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}
isSensitive @@ -128,8 +131,8 @@ async function removeRole(role, ev) { async function done() { const params = { name, - category, - aliases: aliases.split(' '), + category: category === '' ? null : category, + aliases: aliases.split(' ').filter(x => x !== ''), license: license === '' ? null : license, isSensitive, localOnly,