wip
This commit is contained in:
@@ -255,6 +255,11 @@ export class ChatService {
|
|||||||
await redisPipeline.exec();
|
await redisPipeline.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public findMessageById(messageId: MiChatMessage['id']) {
|
||||||
|
return this.chatMessagesRepository.findOneBy({ id: messageId });
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public findMyMessageById(userId: MiUser['id'], messageId: MiChatMessage['id']) {
|
public findMyMessageById(userId: MiUser['id'], messageId: MiChatMessage['id']) {
|
||||||
return this.chatMessagesRepository.findOneBy({ id: messageId, fromUserId: userId });
|
return this.chatMessagesRepository.findOneBy({ id: messageId, fromUserId: userId });
|
||||||
|
@@ -398,6 +398,7 @@ export * as 'users/show' from './endpoints/users/show.js';
|
|||||||
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
|
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
|
||||||
export * as 'chat/messages/create' from './endpoints/chat/messages/create.js';
|
export * as 'chat/messages/create' from './endpoints/chat/messages/create.js';
|
||||||
export * as 'chat/messages/delete' from './endpoints/chat/messages/delete.js';
|
export * as 'chat/messages/delete' from './endpoints/chat/messages/delete.js';
|
||||||
|
export * as 'chat/messages/show' from './endpoints/chat/messages/show.js';
|
||||||
export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js';
|
export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js';
|
||||||
export * as 'chat/history' from './endpoints/chat/history.js';
|
export * as 'chat/history' from './endpoints/chat/history.js';
|
||||||
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
|
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
|
||||||
|
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
|
import { ChatMessageEntityService } from '@/core/entities/ChatMessageEntityService.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['chat'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:chat',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'ChatMessage',
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchMessage: {
|
||||||
|
message: 'No such message.',
|
||||||
|
code: 'NO_SUCH_MESSAGE',
|
||||||
|
id: '3710865b-1848-4da9-8d61-cfed15510b93',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
messageId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['messageId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private chatService: ChatService,
|
||||||
|
private roleService: RoleService,
|
||||||
|
private chatMessageEntityService: ChatMessageEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const message = await this.chatService.findMessageById(ps.messageId);
|
||||||
|
if (message == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchMessage);
|
||||||
|
}
|
||||||
|
if (message.fromUserId !== me.id && message.toUserId !== me.id && !(await this.roleService.isModerator(me))) {
|
||||||
|
throw new ApiError(meta.errors.noSuchMessage);
|
||||||
|
}
|
||||||
|
return this.chatMessageEntityService.pack(message, me);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
55
packages/frontend/src/pages/chat/message.vue
Normal file
55
packages/frontend/src/pages/chat/message.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageWithHeader>
|
||||||
|
<MkSpacer :contentMax="700">
|
||||||
|
<div v-if="initializing">
|
||||||
|
<MkLoading/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<XMessage :message="message" :user="message.fromUser"/>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</PageWithHeader>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, useTemplateRef, computed, watch, onMounted, nextTick, onBeforeUnmount, onDeactivated, onActivated } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import XMessage from './room.message.vue';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { useStream } from '@/stream.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { ensureSignin } from '@/i.js';
|
||||||
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
import { definePage } from '@/page.js';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
messageId?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const initializing = ref(true);
|
||||||
|
const message = ref<Misskey.entities.ChatMessage>();
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
initializing.value = true;
|
||||||
|
|
||||||
|
message.value = await misskeyApi('chat/messages/show', {
|
||||||
|
messageId: props.messageId,
|
||||||
|
});
|
||||||
|
|
||||||
|
initializing.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
definePage({
|
||||||
|
title: i18n.ts.chat,
|
||||||
|
});
|
||||||
|
</script>
|
@@ -43,9 +43,19 @@ export const ROUTE_DEF = [{
|
|||||||
}, {
|
}, {
|
||||||
path: '/chat',
|
path: '/chat',
|
||||||
component: page(() => import('@/pages/chat/home.vue')),
|
component: page(() => import('@/pages/chat/home.vue')),
|
||||||
|
loginRequired: true,
|
||||||
}, {
|
}, {
|
||||||
path: '/chat/user/:userId',
|
path: '/chat/user/:userId',
|
||||||
component: page(() => import('@/pages/chat/room.vue')),
|
component: page(() => import('@/pages/chat/room.vue')),
|
||||||
|
loginRequired: true,
|
||||||
|
}, {
|
||||||
|
path: '/chat/room/:roomId',
|
||||||
|
component: page(() => import('@/pages/chat/room.vue')),
|
||||||
|
loginRequired: true,
|
||||||
|
}, {
|
||||||
|
path: '/chat/messages/:messageId',
|
||||||
|
component: page(() => import('@/pages/chat/message.vue')),
|
||||||
|
loginRequired: true,
|
||||||
}, {
|
}, {
|
||||||
path: '/instance-info/:host',
|
path: '/instance-info/:host',
|
||||||
component: page(() => import('@/pages/instance-info.vue')),
|
component: page(() => import('@/pages/instance-info.vue')),
|
||||||
|
Reference in New Issue
Block a user