Enhance(frontend): フロント側でもリアクション権限のチェックをするように (#13134)
* フロント側でもリアクション権限のチェックをするように * update CHANGELOG.md * lint fixes * remove unrelated diffs * deny -> reject denyは「(信用しないことを理由に)拒否する」という意味らしい * allow -> accept * EmojiSimpleにlocalOnlyを含めるように * リアクション権限のない絵文字は打てないように(ダイアログを出すのではなく) * regenerate type definitions * lint fix * remove unused locales * remove unnecessary async
This commit is contained in:
		| @@ -118,6 +118,7 @@ import { i18n } from '@/i18n.js'; | ||||
| import { defaultStore } from '@/store.js'; | ||||
| import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js'; | ||||
| import { $i } from '@/account.js'; | ||||
| import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js'; | ||||
|  | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	showPinned?: boolean; | ||||
| @@ -126,6 +127,7 @@ const props = withDefaults(defineProps<{ | ||||
| 	asDrawer?: boolean; | ||||
| 	asWindow?: boolean; | ||||
| 	asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう | ||||
| 	targetNote?: Misskey.entities.Note; | ||||
| }>(), { | ||||
| 	showPinned: true, | ||||
| }); | ||||
| @@ -340,7 +342,7 @@ watch(q, () => { | ||||
| }); | ||||
|  | ||||
| function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { | ||||
| 	return ((emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction?.includes(r.id)))) ?? false; | ||||
| 	return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji); | ||||
| } | ||||
|  | ||||
| function focus() { | ||||
|   | ||||
| @@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 		:showPinned="showPinned" | ||||
| 		:pinnedEmojis="pinnedEmojis" | ||||
| 		:asReactionPicker="asReactionPicker" | ||||
| 		:targetNote="targetNote" | ||||
| 		:asDrawer="type === 'drawer'" | ||||
| 		:max-height="maxHeight" | ||||
| 		@chosen="chosen" | ||||
| @@ -32,6 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import * as Misskey from 'misskey-js'; | ||||
| import { shallowRef } from 'vue'; | ||||
| import MkModal from '@/components/MkModal.vue'; | ||||
| import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; | ||||
| @@ -43,6 +45,7 @@ const props = withDefaults(defineProps<{ | ||||
| 	showPinned?: boolean; | ||||
|   pinnedEmojis?: string[], | ||||
| 	asReactionPicker?: boolean; | ||||
| 	targetNote?: Misskey.entities.Note; | ||||
|   choseAndClose?: boolean; | ||||
| }>(), { | ||||
| 	manualShowing: null, | ||||
|   | ||||
| @@ -13,12 +13,13 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 	:front="true" | ||||
| 	@closed="emit('closed')" | ||||
| > | ||||
| 	<MkEmojiPicker :showPinned="showPinned" :asReactionPicker="asReactionPicker" asWindow :class="$style.picker" @chosen="chosen"/> | ||||
| 	<MkEmojiPicker :showPinned="showPinned" :asReactionPicker="asReactionPicker" :targetNote="targetNote" asWindow :class="$style.picker" @chosen="chosen"/> | ||||
| </MkWindow> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import { } from 'vue'; | ||||
| import * as Misskey from 'misskey-js'; | ||||
| import MkWindow from '@/components/MkWindow.vue'; | ||||
| import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; | ||||
|  | ||||
| @@ -26,6 +27,7 @@ withDefaults(defineProps<{ | ||||
| 	src?: HTMLElement; | ||||
| 	showPinned?: boolean; | ||||
| 	asReactionPicker?: boolean; | ||||
| 	targetNote?: Misskey.entities.Note | ||||
| }>(), { | ||||
| 	showPinned: true, | ||||
| }); | ||||
|   | ||||
| @@ -385,7 +385,7 @@ function react(viaKeyboard = false): void { | ||||
| 		} | ||||
| 	} else { | ||||
| 		blur(); | ||||
| 		reactionPicker.show(reactButton.value ?? null, reaction => { | ||||
| 		reactionPicker.show(reactButton.value ?? null, note.value, reaction => { | ||||
| 			sound.playMisskeySfx('reaction'); | ||||
|  | ||||
| 			if (props.mock) { | ||||
|   | ||||
| @@ -385,7 +385,7 @@ function react(viaKeyboard = false): void { | ||||
| 		} | ||||
| 	} else { | ||||
| 		blur(); | ||||
| 		reactionPicker.show(reactButton.value ?? null, reaction => { | ||||
| 		reactionPicker.show(reactButton.value ?? null, note.value, reaction => { | ||||
| 			sound.playMisskeySfx('reaction'); | ||||
|  | ||||
| 			misskeyApi('notes/reactions/create', { | ||||
|   | ||||
| @@ -32,6 +32,8 @@ 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'; | ||||
| import { customEmojis } from '@/custom-emojis.js'; | ||||
|  | ||||
| const props = defineProps<{ | ||||
| 	reaction: string; | ||||
| @@ -48,13 +50,19 @@ const emit = defineEmits<{ | ||||
|  | ||||
| const buttonEl = shallowRef<HTMLElement>(); | ||||
|  | ||||
| const canToggle = computed(() => !props.reaction.match(/@\w/) && $i); | ||||
| const isCustomEmoji = computed(() => props.reaction.includes(':')); | ||||
| const emoji = computed(() => isCustomEmoji.value ? customEmojis.value.find(emoji => emoji.name === props.reaction.replace(/:/g, '').replace(/@\./, '')) : null); | ||||
|  | ||||
| const canToggle = computed(() => { | ||||
| 	return !props.reaction.match(/@\w/) && $i | ||||
| 			&& (emoji.value && checkReactionPermissions($i, props.note, emoji.value)) | ||||
| 			|| !isCustomEmoji.value; | ||||
| }); | ||||
| const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':')); | ||||
|  | ||||
| async function toggleReaction() { | ||||
| 	if (!canToggle.value) return; | ||||
|  | ||||
| 	// TODO: その絵文字を使う権限があるかどうか確認 | ||||
|  | ||||
| 	const oldReaction = props.note.myReaction; | ||||
| 	if (oldReaction) { | ||||
| 		const confirm = await os.confirm({ | ||||
| @@ -101,8 +109,8 @@ async function toggleReaction() { | ||||
| } | ||||
|  | ||||
| async function menu(ev) { | ||||
| 	if (!canToggle.value) return; | ||||
| 	if (!props.reaction.includes(':')) return; | ||||
| 	if (!canGetInfo.value) return; | ||||
|  | ||||
| 	os.popupMenu([{ | ||||
| 		text: i18n.ts.info, | ||||
| 		icon: 'ti ti-info-circle', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 1Step621
					1Step621