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 { | ||||
| 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
	 syuilo
					syuilo