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(frontend): フロント側でもリアクション権限のチェックをするように #13134

Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
- Enhance: MFMの属性でオートコンプリートが使用できるように #12735
- Enhance: 絵文字編集ダイアログをモーダルではなくウィンドウで表示するように
- Enhance: リモートのユーザーはメニューから直接リモートで表示できるように
- Enhance: リアクション権限がない場合、ハートにフォールバックするのではなく、権限がないことをダイアログで表示するように
- リモートのユーザーにローカルのみのカスタム絵文字をリアクションしようとした場合
- センシティブなリアクションを認めていないユーザーにセンシティブなカスタム絵文字をリアクションしようとした場合
- ロールが必要な絵文字をリアクションしようとした場合
- Fix: ネイティブモードの絵文字がモノクロにならないように
- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
- Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正
Expand Down
18 changes: 18 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,24 @@ export interface Locale extends ILocale {
* リアクション
*/
"reactions": string;
/**
* このリアクションをつける権限がありません。
*/
"reactionRejected": string;
"_reactionRejectedReason": {
/**
* 投稿者がセンシティブなリアクションを許可していないため、リアクションできません。
*/
"isSensitive": string;
/**
* この絵文字はリモートから見られないように設定されているため、リアクションできません。
*/
"localOnly": string;
/**
* この絵文字をリアクションとして使うにはロールが必要です。
*/
"roleIdsThatCanBeUsedThisEmojiAsReaction": string;
1Step621 marked this conversation as resolved.
Show resolved Hide resolved
};
/**
* 絵文字ピッカー
*/
Expand Down
5 changes: 5 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ sensitive: "センシティブ"
add: "追加"
reaction: "リアクション"
reactions: "リアクション"
reactionRejected: "このリアクションをつける権限がありません。"
_reactionRejectedReason:
isSensitive: "投稿者がセンシティブなリアクションを許可していないため、リアクションできません。"
localOnly: "この絵文字はリモートから見られないように設定されているため、リアクションできません。"
roleIdsThatCanBeUsedThisEmojiAsReaction: "この絵文字をリアクションとして使うにはロールが必要です。"
emojiPicker: "絵文字ピッカー"
pinnedEmojisForReactionSettingDescription: "リアクション時にピン留め表示する絵文字を設定できます"
pinnedEmojisSettingDescription: "絵文字入力時にピン留め表示する絵文字を設定できます"
Expand Down
17 changes: 16 additions & 1 deletion packages/frontend/src/components/MkNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ import { MenuItem } from '@/types/menu.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { shouldCollapsed } from '@/scripts/collapsed.js';
import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';

const props = withDefaults(defineProps<{
note: Misskey.entities.Note;
Expand Down Expand Up @@ -380,7 +381,21 @@ function react(viaKeyboard = false): void {
}
} else {
blur();
reactionPicker.show(reactButton.value ?? null, reaction => {
reactionPicker.show(reactButton.value ?? null, async reaction => {
if (reaction.includes(':')) {
const permissions = checkReactionPermissions($i!, props.note, await misskeyApi('emoji', {
name: reaction.replace(/:/g, '').replace(/@\./, ''),
}));
if (!permissions.accepted) {
os.alert({
type: "info",
title: i18n.ts.reactionRejected,
text: i18n.ts._reactionRejectedReason[permissions.rejectedReason],
});
return;
}
}

sound.playMisskeySfx('reaction');

if (props.mock) {
Expand Down
17 changes: 16 additions & 1 deletion packages/frontend/src/components/MkNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkPagination, { type Paging } from '@/components/MkPagination.vue';
import MkReactionIcon from '@/components/MkReactionIcon.vue';
import MkButton from '@/components/MkButton.vue';
import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';

const props = defineProps<{
note: Misskey.entities.Note;
Expand Down Expand Up @@ -385,7 +386,21 @@ function react(viaKeyboard = false): void {
}
} else {
blur();
reactionPicker.show(reactButton.value ?? null, reaction => {
reactionPicker.show(reactButton.value ?? null, async reaction => {
if (reaction.includes(':')) {
const permissions = checkReactionPermissions($i!, props.note, await misskeyApi('emoji', {
name: reaction.replace(/:/g, '').replace(/@\./, ''),
}));
if (!permissions.accepted) {
os.alert({
type: "info",
title: i18n.ts.reactionRejected,
text: i18n.ts._reactionRejectedReason[permissions.rejectedReason],
});
return;
}
}

sound.playMisskeySfx('reaction');

misskeyApi('notes/reactions/create', {
Expand Down
15 changes: 14 additions & 1 deletion packages/frontend/src/components/MkReactionsViewer.reaction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { claimAchievement } from '@/scripts/achievements.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import * as sound from '@/scripts/sound.js';
import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';

const props = defineProps<{
reaction: string;
Expand All @@ -53,7 +54,19 @@ const canToggle = computed(() => !props.reaction.match(/@\w/) && $i);
async function toggleReaction() {
if (!canToggle.value) return;

// TODO: その絵文字を使う権限があるかどうか確認
if (props.reaction.includes(':')) {
const permissions = checkReactionPermissions($i!, props.note, await misskeyApi('emoji', {
name: props.reaction.replace(/:/g, '').replace(/@\./, ''),
}));
if (!permissions.accepted) {
os.alert({
type: "info",
title: i18n.ts.reactionRejected,
text: i18n.ts._reactionRejectedReason[permissions.rejectedReason],
});
return;
}
}

const oldReaction = props.note.myReaction;
if (oldReaction) {
Expand Down
19 changes: 19 additions & 0 deletions packages/frontend/src/scripts/check-reaction-permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Misskey from 'misskey-js';

export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiDetailed): {
accepted: true;
} | {
accepted: false;
rejectedReason: 'localOnly' | 'isSensitive' | 'roleIdsThatCanBeUsedThisEmojiAsReaction';
} {
if (emoji.localOnly && note.user.host !== me.host) {
return { accepted: false, rejectedReason: 'localOnly' };
}
if (emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote')) {
return { accepted: false, rejectedReason: 'isSensitive' };
}
if (!(emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)))) {
return { accepted: false, rejectedReason: 'roleIdsThatCanBeUsedThisEmojiAsReaction' };
}
return { accepted: true };
}
Loading