diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2025-04-03 15:28:10 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-03 15:28:10 +0900 |
| commit | e07bb1dcbce6eaa2bfe157a6c9f1361dbf9aa280 (patch) | |
| tree | 1d9f9e6d6450caa717e83ca8fc0eadf09b413129 /packages/frontend/src/pages/chat/XMessage.vue | |
| parent | perf(frontend): avoid main thread scroll repaint (diff) | |
| download | sharkey-e07bb1dcbce6eaa2bfe157a6c9f1361dbf9aa280.tar.gz sharkey-e07bb1dcbce6eaa2bfe157a6c9f1361dbf9aa280.tar.bz2 sharkey-e07bb1dcbce6eaa2bfe157a6c9f1361dbf9aa280.zip | |
fix: チャット周りの修正 (#15741)
* fix(misskey-js): チャットのChannel型定義を追加
* fix(backend); canChatで塞いでいない書き込み系のAPIを塞ぐ
* fix(frontend): チャット周りのフロントエンド型修正
* lint fix
* fix broken lockfile
* fix
* refactor
* wip
* wip
* wip
* clean up
---------
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/pages/chat/XMessage.vue')
| -rw-r--r-- | packages/frontend/src/pages/chat/XMessage.vue | 62 |
1 files changed, 35 insertions, 27 deletions
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue index 33741b1845..eb8b0d79ee 100644 --- a/packages/frontend/src/pages/chat/XMessage.vue +++ b/packages/frontend/src/pages/chat/XMessage.vue @@ -5,33 +5,28 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="[$style.root, { [$style.isMe]: isMe }]"> - <MkAvatar :class="$style.avatar" :user="message.fromUser" :link="!isMe" :preview="false"/> + <MkAvatar :class="$style.avatar" :user="message.fromUser!" :link="!isMe" :preview="false"/> <div :class="$style.body" @contextmenu.stop="onContextmenu"> - <div :class="$style.header"><MkUserName v-if="!isMe && prefer.s['chat.showSenderName']" :user="message.fromUser"/></div> + <div :class="$style.header"><MkUserName v-if="!isMe && prefer.s['chat.showSenderName'] && message.fromUser != null" :user="message.fromUser"/></div> <MkFukidashi :class="$style.fukidashi" :tail="isMe ? 'right' : 'left'" :accented="isMe"> - <div v-if="!message.isDeleted" :class="$style.content"> - <Mfm - v-if="message.text" - ref="text" - class="_selectable" - :text="message.text" - :i="$i" - :nyaize="'respect'" - :enableEmojiMenu="true" - :enableEmojiMenuReaction="true" - /> - <MkMediaList v-if="message.file" :mediaList="[message.file]" :class="$style.file"/> - </div> - <div v-else :class="$style.content"> - <p>{{ i18n.ts.deleted }}</p> - </div> + <Mfm + v-if="message.text" + ref="text" + class="_selectable" + :text="message.text" + :i="$i" + :nyaize="'respect'" + :enableEmojiMenu="true" + :enableEmojiMenuReaction="true" + /> + <MkMediaList v-if="message.file" :mediaList="[message.file]" :class="$style.file"/> </MkFukidashi> <MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/> <div :class="$style.footer"> <button class="_textButton" style="color: currentColor;" @click="showMenu"><i class="ti ti-dots-circle-horizontal"></i></button> <MkTime :class="$style.time" :time="message.createdAt"/> - <MkA v-if="isSearchResult && message.toRoomId" :to="`/chat/room/${message.toRoomId}`">{{ message.toRoom.name }}</MkA> - <MkA v-if="isSearchResult && message.toUserId && isMe" :to="`/chat/user/${message.toUserId}`">@{{ message.toUser.username }}</MkA> + <MkA v-if="isSearchResult && 'toRoom' in message && message.toRoom != null" :to="`/chat/room/${message.toRoomId}`">{{ message.toRoom.name }}</MkA> + <MkA v-if="isSearchResult && 'toUser' in message && message.toUser != null && isMe" :to="`/chat/user/${message.toUserId}`">@{{ message.toUser.username }}</MkA> </div> <TransitionGroup :enterActiveClass="prefer.s.animation ? $style.transition_reaction_enterActive : ''" @@ -62,6 +57,7 @@ import * as Misskey from 'misskey-js'; import { url } from '@@/js/config.js'; import { isLink } from '@@/js/is-link.js'; import type { MenuItem } from '@/types/menu.js'; +import type { NormalizedChatMessage } from './room.vue'; import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js'; import MkUrlPreview from '@/components/MkUrlPreview.vue'; import { ensureSignin } from '@/i.js'; @@ -76,11 +72,12 @@ import * as sound from '@/utility/sound.js'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import { prefer } from '@/preferences.js'; import { DI } from '@/di.js'; +import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js'; const $i = ensureSignin(); const props = defineProps<{ - message: Misskey.entities.ChatMessageLite | Misskey.entities.ChatMessage; + message: NormalizedChatMessage | Misskey.entities.ChatMessage; isSearchResult?: boolean; }>(); @@ -88,6 +85,8 @@ const isMe = computed(() => props.message.fromUserId === $i.id); const urls = computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []); provide(DI.mfmEmojiReactCallback, (reaction) => { + if (!$i.policies.canChat) return; + sound.playMisskeySfx('reaction'); misskeyApi('chat/messages/react', { messageId: props.message.id, @@ -96,7 +95,12 @@ provide(DI.mfmEmojiReactCallback, (reaction) => { }); function react(ev: MouseEvent) { - reactionPicker.show(ev.currentTarget ?? ev.target, null, async (reaction) => { + if (!$i.policies.canChat) return; + + const targetEl = getHTMLElementOrNull(ev.currentTarget ?? ev.target); + if (!targetEl) return; + + reactionPicker.show(targetEl, null, async (reaction) => { sound.playMisskeySfx('reaction'); misskeyApi('chat/messages/react', { messageId: props.message.id, @@ -106,6 +110,8 @@ function react(ev: MouseEvent) { } function onReactionClick(record: Misskey.entities.ChatMessage['reactions'][0]) { + if (!$i.policies.canChat) return; + if (record.user.id === $i.id) { misskeyApi('chat/messages/unreact', { messageId: props.message.id, @@ -132,7 +138,7 @@ function onContextmenu(ev: MouseEvent) { function showMenu(ev: MouseEvent, contextmenu = false) { const menu: MenuItem[] = []; - if (!isMe.value) { + if (!isMe.value && $i.policies.canChat) { menu.push({ text: i18n.ts.reaction, icon: 'ti ti-mood-plus', @@ -150,7 +156,7 @@ function showMenu(ev: MouseEvent, contextmenu = false) { text: i18n.ts.copyContent, icon: 'ti ti-copy', action: () => { - copyToClipboard(props.message.text); + copyToClipboard(props.message.text ?? ''); }, }); @@ -158,7 +164,7 @@ function showMenu(ev: MouseEvent, contextmenu = false) { type: 'divider', }); - if (isMe.value) { + if (isMe.value && $i.policies.canChat) { menu.push({ text: i18n.ts.delete, icon: 'ti ti-trash', @@ -169,14 +175,16 @@ function showMenu(ev: MouseEvent, contextmenu = false) { }); }, }); - } else { + } + + if (!isMe.value && props.message.fromUser != null) { menu.push({ text: i18n.ts.reportAbuse, icon: 'ti ti-exclamation-circle', action: () => { const localUrl = `${url}/chat/messages/${props.message.id}`; const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { - user: props.message.fromUser, + user: props.message.fromUser!, initialComment: `${localUrl}\n-----\n`, }, { closed: () => dispose(), |