wip
This commit is contained in:
		| @@ -255,20 +255,27 @@ export class ChatService { | |||||||
| 		await redisPipeline.exec(); | 		await redisPipeline.exec(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	@bindThis | ||||||
|  | 	public findMyMessageById(userId: MiUser['id'], messageId: MiChatMessage['id']) { | ||||||
|  | 		return this.chatMessagesRepository.findOneBy({ id: messageId, fromUserId: userId }); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public async deleteMessage(message: MiChatMessage) { | 	public async deleteMessage(message: MiChatMessage) { | ||||||
| 		await this.chatMessagesRepository.delete(message.id); | 		await this.chatMessagesRepository.delete(message.id); | ||||||
|  |  | ||||||
| 		if (message.toUserId) { | 		if (message.toUserId) { | ||||||
| 			const fromUser = await this.usersRepository.findOneByOrFail({ id: message.fromUserId }); | 			const [fromUser, toUser] = await Promise.all([ | ||||||
| 			const toUser = await this.usersRepository.findOneByOrFail({ id: message.toUserId }); | 				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(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(toUser)) this.globalEventService.publishChatStream(message.toUserId, message.fromUserId, 'deleted', message.id); | ||||||
|  |  | ||||||
| 			if (this.userEntityService.isLocalUser(fromUser) && this.userEntityService.isRemoteUser(toUser)) { | 			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)); | 				//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); | 				//this.queueService.deliver(fromUser, activity, toUser.inbox); | ||||||
| 			} | 			} | ||||||
| 		}/* else if (message.toRoomId) { | 		}/* else if (message.toRoomId) { | ||||||
| 			this.globalEventService.publishRoomChatStream(message.toRoomId, 'deleted', message.id); | 			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/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/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,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> | </template> | ||||||
|  |  | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { computed } from 'vue'; | import { computed, defineAsyncComponent } from 'vue'; | ||||||
| import * as mfm from 'mfm-js'; | import * as mfm from 'mfm-js'; | ||||||
| import * as Misskey from 'misskey-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 { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js'; | ||||||
| import MkUrlPreview from '@/components/MkUrlPreview.vue'; | import MkUrlPreview from '@/components/MkUrlPreview.vue'; | ||||||
| import { ensureSignin } from '@/i.js'; | import { ensureSignin } from '@/i.js'; | ||||||
| import { misskeyApi } from '@/utility/misskey-api.js'; | import { misskeyApi } from '@/utility/misskey-api.js'; | ||||||
| import { i18n } from '@/i18n.js'; | import { i18n } from '@/i18n.js'; | ||||||
| import MkFukidashi from '@/components/MkFukidashi.vue'; | import MkFukidashi from '@/components/MkFukidashi.vue'; | ||||||
|  | import * as os from '@/os.js'; | ||||||
|  |  | ||||||
| const $i = ensureSignin(); | const $i = ensureSignin(); | ||||||
|  |  | ||||||
| @@ -47,10 +50,36 @@ const props = defineProps<{ | |||||||
| const isMe = computed(() => props.message.fromUserId === $i.id); | const isMe = computed(() => props.message.fromUserId === $i.id); | ||||||
| const urls = computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []); | const urls = computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []); | ||||||
|  |  | ||||||
| function del(): void { | function showMenu(ev: MouseEvent) { | ||||||
| 	misskeyApi('chat/messages/delete', { | 	const menu: MenuItem[] = []; | ||||||
| 		messageId: props.message.id, | 	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> | </script> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -965,6 +965,12 @@ type ChatMessagesCreateRequest = operations['chat___messages___create']['request | |||||||
| // @public (undocumented) | // @public (undocumented) | ||||||
| type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json']; | 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) | // @public (undocumented) | ||||||
| type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json']; | type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json']; | ||||||
|  |  | ||||||
| @@ -1475,6 +1481,8 @@ declare namespace entities { | |||||||
|         ChatHistoryResponse, |         ChatHistoryResponse, | ||||||
|         ChatMessagesCreateRequest, |         ChatMessagesCreateRequest, | ||||||
|         ChatMessagesCreateResponse, |         ChatMessagesCreateResponse, | ||||||
|  |         ChatMessagesDeleteRequest, | ||||||
|  |         ChatMessagesDeleteResponse, | ||||||
|         ChatMessagesTimelineRequest, |         ChatMessagesTimelineRequest, | ||||||
|         ChatMessagesTimelineResponse, |         ChatMessagesTimelineResponse, | ||||||
|         ClipsAddNoteRequest, |         ClipsAddNoteRequest, | ||||||
|   | |||||||
| @@ -1556,6 +1556,17 @@ declare module '../api.js' { | |||||||
|       credential?: string | null, |       credential?: string | null, | ||||||
|     ): Promise<SwitchCaseResponseType<E, P>>; |     ): 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. |      * No description provided. | ||||||
|      *  |      *  | ||||||
|   | |||||||
| @@ -211,6 +211,8 @@ import type { | |||||||
| 	ChatHistoryResponse, | 	ChatHistoryResponse, | ||||||
| 	ChatMessagesCreateRequest, | 	ChatMessagesCreateRequest, | ||||||
| 	ChatMessagesCreateResponse, | 	ChatMessagesCreateResponse, | ||||||
|  | 	ChatMessagesDeleteRequest, | ||||||
|  | 	ChatMessagesDeleteResponse, | ||||||
| 	ChatMessagesTimelineRequest, | 	ChatMessagesTimelineRequest, | ||||||
| 	ChatMessagesTimelineResponse, | 	ChatMessagesTimelineResponse, | ||||||
| 	ClipsAddNoteRequest, | 	ClipsAddNoteRequest, | ||||||
| @@ -734,6 +736,7 @@ export type Endpoints = { | |||||||
| 	'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse }; | 	'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse }; | ||||||
| 	'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse }; | 	'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse }; | ||||||
| 	'chat/messages/create': { req: ChatMessagesCreateRequest; res: ChatMessagesCreateResponse }; | 	'chat/messages/create': { req: ChatMessagesCreateRequest; res: ChatMessagesCreateResponse }; | ||||||
|  | 	'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse }; | ||||||
| 	'chat/messages/timeline': { req: ChatMessagesTimelineRequest; res: ChatMessagesTimelineResponse }; | 	'chat/messages/timeline': { req: ChatMessagesTimelineRequest; res: ChatMessagesTimelineResponse }; | ||||||
| 	'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse }; | 	'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse }; | ||||||
| 	'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse }; | 	'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 ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json']; | ||||||
| export type ChatMessagesCreateRequest = operations['chat___messages___create']['requestBody']['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 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 ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json']; | ||||||
| export type ChatMessagesTimelineResponse = operations['chat___messages___timeline']['responses']['200']['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']; | export type ClipsAddNoteRequest = operations['clips___add-note']['requestBody']['content']['application/json']; | ||||||
|   | |||||||
| @@ -1376,6 +1376,15 @@ export type paths = { | |||||||
|      */ |      */ | ||||||
|     post: operations['chat___messages___create']; |     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': { | ||||||
|     /** |     /** | ||||||
|      * 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 |    * chat/messages/timeline | ||||||
|    * @description No description provided. |    * @description No description provided. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo