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>
This commit is contained in:
parent
7cecaa5c54
commit
e07bb1dcbc
29 changed files with 453 additions and 153 deletions
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue