wip
This commit is contained in:
		@@ -255,20 +255,27 @@ export class ChatService {
 | 
			
		||||
		await redisPipeline.exec();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public findMyMessageById(userId: MiUser['id'], messageId: MiChatMessage['id']) {
 | 
			
		||||
		return this.chatMessagesRepository.findOneBy({ id: messageId, fromUserId: userId });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async deleteMessage(message: MiChatMessage) {
 | 
			
		||||
		await this.chatMessagesRepository.delete(message.id);
 | 
			
		||||
 | 
			
		||||
		if (message.toUserId) {
 | 
			
		||||
			const fromUser = await this.usersRepository.findOneByOrFail({ id: message.fromUserId });
 | 
			
		||||
			const toUser = await this.usersRepository.findOneByOrFail({ id: message.toUserId });
 | 
			
		||||
			const [fromUser, toUser] = await Promise.all([
 | 
			
		||||
				this.usersRepository.findOneByOrFail({ id: message.fromUserId }),
 | 
			
		||||
				this.usersRepository.findOneByOrFail({ id: message.toUserId }),
 | 
			
		||||
			]);
 | 
			
		||||
 | 
			
		||||
			if (this.userEntityService.isLocalUser(fromUser)) this.globalEventService.publishChatStream(message.fromUserId, message.toUserId, 'deleted', message.id);
 | 
			
		||||
			if (this.userEntityService.isLocalUser(toUser)) this.globalEventService.publishChatStream(message.toUserId, message.fromUserId, 'deleted', message.id);
 | 
			
		||||
 | 
			
		||||
			if (this.userEntityService.isLocalUser(fromUser) && this.userEntityService.isRemoteUser(toUser)) {
 | 
			
		||||
				const activity = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), fromUser));
 | 
			
		||||
				this.queueService.deliver(fromUser, activity, toUser.inbox);
 | 
			
		||||
				//const activity = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), fromUser));
 | 
			
		||||
				//this.queueService.deliver(fromUser, activity, toUser.inbox);
 | 
			
		||||
			}
 | 
			
		||||
		}/* else if (message.toRoomId) {
 | 
			
		||||
			this.globalEventService.publishRoomChatStream(message.toRoomId, 'deleted', message.id);
 | 
			
		||||
 
 | 
			
		||||
@@ -397,6 +397,7 @@ export * as 'users/search-by-username-and-host' from './endpoints/users/search-b
 | 
			
		||||
export * as 'users/show' from './endpoints/users/show.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/delete' from './endpoints/chat/messages/delete.js';
 | 
			
		||||
export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js';
 | 
			
		||||
export * as 'chat/history' from './endpoints/chat/history.js';
 | 
			
		||||
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,54 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	tags: ['chat'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'write:chat',
 | 
			
		||||
 | 
			
		||||
	res: {
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	errors: {
 | 
			
		||||
		noSuchMessage: {
 | 
			
		||||
			message: 'No such message.',
 | 
			
		||||
			code: 'NO_SUCH_MESSAGE',
 | 
			
		||||
			id: '36b67f0e-66a6-414b-83df-992a55294f17',
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
} 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,
 | 
			
		||||
	) {
 | 
			
		||||
		super(meta, paramDef, async (ps, me) => {
 | 
			
		||||
			const message = await this.chatService.findMyMessageById(me.id, ps.messageId);
 | 
			
		||||
			if (message == null) {
 | 
			
		||||
				throw new ApiError(meta.errors.noSuchMessage);
 | 
			
		||||
			}
 | 
			
		||||
			await this.chatService.deleteMessage(message);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -26,15 +26,18 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { computed, defineAsyncComponent } from 'vue';
 | 
			
		||||
import * as mfm from 'mfm-js';
 | 
			
		||||
import * as Misskey from 'misskey-js';
 | 
			
		||||
import { url } from '@@/js/config.js';
 | 
			
		||||
import type { MenuItem } from '@/types/menu.js';
 | 
			
		||||
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
 | 
			
		||||
import MkUrlPreview from '@/components/MkUrlPreview.vue';
 | 
			
		||||
import { ensureSignin } from '@/i.js';
 | 
			
		||||
import { misskeyApi } from '@/utility/misskey-api.js';
 | 
			
		||||
import { i18n } from '@/i18n.js';
 | 
			
		||||
import MkFukidashi from '@/components/MkFukidashi.vue';
 | 
			
		||||
import * as os from '@/os.js';
 | 
			
		||||
 | 
			
		||||
const $i = ensureSignin();
 | 
			
		||||
 | 
			
		||||
@@ -47,10 +50,36 @@ const props = defineProps<{
 | 
			
		||||
const isMe = computed(() => props.message.fromUserId === $i.id);
 | 
			
		||||
const urls = computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []);
 | 
			
		||||
 | 
			
		||||
function del(): void {
 | 
			
		||||
	misskeyApi('chat/messages/delete', {
 | 
			
		||||
		messageId: props.message.id,
 | 
			
		||||
	});
 | 
			
		||||
function showMenu(ev: MouseEvent) {
 | 
			
		||||
	const menu: MenuItem[] = [];
 | 
			
		||||
	if (isMe.value) {
 | 
			
		||||
		menu.push({
 | 
			
		||||
			text: i18n.ts.delete,
 | 
			
		||||
			icon: 'ti ti-trash',
 | 
			
		||||
			danger: true,
 | 
			
		||||
			action: () => {
 | 
			
		||||
				misskeyApi('chat/messages/delete', {
 | 
			
		||||
					messageId: props.message.id,
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
	} else {
 | 
			
		||||
		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.user,
 | 
			
		||||
					initialComment: `${localUrl}\n-----\n`,
 | 
			
		||||
				}, {
 | 
			
		||||
					closed: () => dispose(),
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	os.popupMenu(menu, ev.currentTarget ?? ev.target);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -965,6 +965,12 @@ type ChatMessagesCreateRequest = operations['chat___messages___create']['request
 | 
			
		||||
// @public (undocumented)
 | 
			
		||||
type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json'];
 | 
			
		||||
 | 
			
		||||
// @public (undocumented)
 | 
			
		||||
type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
 | 
			
		||||
 | 
			
		||||
// @public (undocumented)
 | 
			
		||||
type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
 | 
			
		||||
 | 
			
		||||
// @public (undocumented)
 | 
			
		||||
type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json'];
 | 
			
		||||
 | 
			
		||||
@@ -1475,6 +1481,8 @@ declare namespace entities {
 | 
			
		||||
        ChatHistoryResponse,
 | 
			
		||||
        ChatMessagesCreateRequest,
 | 
			
		||||
        ChatMessagesCreateResponse,
 | 
			
		||||
        ChatMessagesDeleteRequest,
 | 
			
		||||
        ChatMessagesDeleteResponse,
 | 
			
		||||
        ChatMessagesTimelineRequest,
 | 
			
		||||
        ChatMessagesTimelineResponse,
 | 
			
		||||
        ClipsAddNoteRequest,
 | 
			
		||||
 
 | 
			
		||||
@@ -1556,6 +1556,17 @@ declare module '../api.js' {
 | 
			
		||||
      credential?: string | null,
 | 
			
		||||
    ): Promise<SwitchCaseResponseType<E, P>>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * No description provided.
 | 
			
		||||
     * 
 | 
			
		||||
     * **Credential required**: *Yes* / **Permission**: *write:chat*
 | 
			
		||||
     */
 | 
			
		||||
    request<E extends 'chat/messages/delete', P extends Endpoints[E]['req']>(
 | 
			
		||||
      endpoint: E,
 | 
			
		||||
      params: P,
 | 
			
		||||
      credential?: string | null,
 | 
			
		||||
    ): Promise<SwitchCaseResponseType<E, P>>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * No description provided.
 | 
			
		||||
     * 
 | 
			
		||||
 
 | 
			
		||||
@@ -211,6 +211,8 @@ import type {
 | 
			
		||||
	ChatHistoryResponse,
 | 
			
		||||
	ChatMessagesCreateRequest,
 | 
			
		||||
	ChatMessagesCreateResponse,
 | 
			
		||||
	ChatMessagesDeleteRequest,
 | 
			
		||||
	ChatMessagesDeleteResponse,
 | 
			
		||||
	ChatMessagesTimelineRequest,
 | 
			
		||||
	ChatMessagesTimelineResponse,
 | 
			
		||||
	ClipsAddNoteRequest,
 | 
			
		||||
@@ -734,6 +736,7 @@ export type Endpoints = {
 | 
			
		||||
	'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse };
 | 
			
		||||
	'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse };
 | 
			
		||||
	'chat/messages/create': { req: ChatMessagesCreateRequest; res: ChatMessagesCreateResponse };
 | 
			
		||||
	'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse };
 | 
			
		||||
	'chat/messages/timeline': { req: ChatMessagesTimelineRequest; res: ChatMessagesTimelineResponse };
 | 
			
		||||
	'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse };
 | 
			
		||||
	'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse };
 | 
			
		||||
 
 | 
			
		||||
@@ -214,6 +214,8 @@ export type ChatHistoryRequest = operations['chat___history']['requestBody']['co
 | 
			
		||||
export type ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json'];
 | 
			
		||||
export type ChatMessagesCreateRequest = operations['chat___messages___create']['requestBody']['content']['application/json'];
 | 
			
		||||
export type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json'];
 | 
			
		||||
export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
 | 
			
		||||
export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
 | 
			
		||||
export type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json'];
 | 
			
		||||
export type ChatMessagesTimelineResponse = operations['chat___messages___timeline']['responses']['200']['content']['application/json'];
 | 
			
		||||
export type ClipsAddNoteRequest = operations['clips___add-note']['requestBody']['content']['application/json'];
 | 
			
		||||
 
 | 
			
		||||
@@ -1376,6 +1376,15 @@ export type paths = {
 | 
			
		||||
     */
 | 
			
		||||
    post: operations['chat___messages___create'];
 | 
			
		||||
  };
 | 
			
		||||
  '/chat/messages/delete': {
 | 
			
		||||
    /**
 | 
			
		||||
     * chat/messages/delete
 | 
			
		||||
     * @description No description provided.
 | 
			
		||||
     *
 | 
			
		||||
     * **Credential required**: *Yes* / **Permission**: *write:chat*
 | 
			
		||||
     */
 | 
			
		||||
    post: operations['chat___messages___delete'];
 | 
			
		||||
  };
 | 
			
		||||
  '/chat/messages/timeline': {
 | 
			
		||||
    /**
 | 
			
		||||
     * chat/messages/timeline
 | 
			
		||||
@@ -13844,6 +13853,60 @@ export type operations = {
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  /**
 | 
			
		||||
   * chat/messages/delete
 | 
			
		||||
   * @description No description provided.
 | 
			
		||||
   *
 | 
			
		||||
   * **Credential required**: *Yes* / **Permission**: *write:chat*
 | 
			
		||||
   */
 | 
			
		||||
  chat___messages___delete: {
 | 
			
		||||
    requestBody: {
 | 
			
		||||
      content: {
 | 
			
		||||
        'application/json': {
 | 
			
		||||
          /** Format: misskey:id */
 | 
			
		||||
          messageId: string;
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
    responses: {
 | 
			
		||||
      /** @description OK (with results) */
 | 
			
		||||
      200: {
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': unknown;
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      /** @description Client error */
 | 
			
		||||
      400: {
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': components['schemas']['Error'];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      /** @description Authentication error */
 | 
			
		||||
      401: {
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': components['schemas']['Error'];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      /** @description Forbidden error */
 | 
			
		||||
      403: {
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': components['schemas']['Error'];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      /** @description I'm Ai */
 | 
			
		||||
      418: {
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': components['schemas']['Error'];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      /** @description Internal server error */
 | 
			
		||||
      500: {
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': components['schemas']['Error'];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  /**
 | 
			
		||||
   * chat/messages/timeline
 | 
			
		||||
   * @description No description provided.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user