mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-03-21 03:30:42 +00:00
enhance: チャットのリアクションを削除できるように
This commit is contained in:
@@ -33,6 +33,20 @@ const MAX_ROOM_MEMBERS = 30;
|
||||
const MAX_REACTIONS_PER_MESSAGE = 100;
|
||||
const isCustomEmojiRegexp = /^:([\w+-]+)(?:@\.)?:$/;
|
||||
|
||||
// TODO: ReactionServiceのやつと共通化
|
||||
function normalizeEmojiString(x: string) {
|
||||
const match = emojiRegex.exec(x);
|
||||
if (match) {
|
||||
// 合字を含む1つの絵文字
|
||||
const unicode = match[0];
|
||||
|
||||
// 異体字セレクタ除去
|
||||
return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, '');
|
||||
} else {
|
||||
throw new Error('invalid emoji');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ChatService {
|
||||
constructor(
|
||||
@@ -751,24 +765,10 @@ export class ChatService {
|
||||
public async react(messageId: MiChatMessage['id'], userId: MiUser['id'], reaction_: string) {
|
||||
let reaction;
|
||||
|
||||
// TODO: ReactionServiceのやつと共通化
|
||||
function normalize(x: string) {
|
||||
const match = emojiRegex.exec(x);
|
||||
if (match) {
|
||||
// 合字を含む1つの絵文字
|
||||
const unicode = match[0];
|
||||
|
||||
// 異体字セレクタ除去
|
||||
return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, '');
|
||||
} else {
|
||||
throw new Error('invalid emoji');
|
||||
}
|
||||
}
|
||||
|
||||
const custom = reaction_.match(isCustomEmojiRegexp);
|
||||
|
||||
if (custom == null) {
|
||||
reaction = normalize(reaction_);
|
||||
reaction = normalizeEmojiString(reaction_);
|
||||
} else {
|
||||
const name = custom[1];
|
||||
const emoji = (await this.customEmojiService.localEmojisCache.fetch()).get(name);
|
||||
@@ -827,6 +827,52 @@ export class ChatService {
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async unreact(messageId: MiChatMessage['id'], userId: MiUser['id'], reaction_: string) {
|
||||
let reaction;
|
||||
|
||||
const custom = reaction_.match(isCustomEmojiRegexp);
|
||||
|
||||
if (custom == null) {
|
||||
reaction = normalizeEmojiString(reaction_);
|
||||
} else { // 削除されたカスタム絵文字のリアクションを削除したいかもしれないので絵文字の存在チェックはする必要なし
|
||||
const name = custom[1];
|
||||
reaction = `:${name}:`;
|
||||
}
|
||||
|
||||
// NOTE: 自分のリアクションを(あれば)削除するだけなので諸々の権限チェックは必要なし
|
||||
|
||||
const message = await this.chatMessagesRepository.findOneByOrFail({ id: messageId });
|
||||
|
||||
const room = message.toRoomId ? await this.chatRoomsRepository.findOneByOrFail({ id: message.toRoomId }) : null;
|
||||
|
||||
await this.chatMessagesRepository.createQueryBuilder().update()
|
||||
.set({
|
||||
reactions: () => `array_remove("reactions", '${userId}/${reaction}')`,
|
||||
})
|
||||
.where('id = :id', { id: message.id })
|
||||
.execute();
|
||||
|
||||
// TODO: 実際に削除が行われたときのみイベントを発行する
|
||||
|
||||
if (room) {
|
||||
this.globalEventService.publishChatRoomStream(room.id, 'unreact', {
|
||||
messageId: message.id,
|
||||
user: await this.userEntityService.pack(userId),
|
||||
reaction,
|
||||
});
|
||||
} else {
|
||||
this.globalEventService.publishChatUserStream(message.fromUserId, message.toUserId!, 'unreact', {
|
||||
messageId: message.id,
|
||||
reaction,
|
||||
});
|
||||
this.globalEventService.publishChatUserStream(message.toUserId!, message.fromUserId, 'unreact', {
|
||||
messageId: message.id,
|
||||
reaction,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getMyMemberships(userId: MiUser['id'], limit: number, sinceId?: MiChatRoomMembership['id'] | null, untilId?: MiChatRoomMembership['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatRoomMembershipsRepository.createQueryBuilder('membership'), sinceId, untilId)
|
||||
|
||||
@@ -167,6 +167,11 @@ export interface ChatEventTypes {
|
||||
user?: Packed<'UserLite'>;
|
||||
messageId: MiChatMessage['id'];
|
||||
};
|
||||
unreact: {
|
||||
reaction: string;
|
||||
user?: Packed<'UserLite'>;
|
||||
messageId: MiChatMessage['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReversiEventTypes {
|
||||
|
||||
@@ -401,6 +401,7 @@ export * as 'chat/messages/create-to-room' from './endpoints/chat/messages/creat
|
||||
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/react' from './endpoints/chat/messages/react.js';
|
||||
export * as 'chat/messages/unreact' from './endpoints/chat/messages/unreact.js';
|
||||
export * as 'chat/messages/user-timeline' from './endpoints/chat/messages/user-timeline.js';
|
||||
export * as 'chat/messages/room-timeline' from './endpoints/chat/messages/room-timeline.js';
|
||||
export * as 'chat/messages/search' from './endpoints/chat/messages/search.js';
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { ChatService } from '@/core/ChatService.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: 'c39ea42f-e3ca-428a-ad57-390e0a711595',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
messageId: { type: 'string', format: 'misskey:id' },
|
||||
reaction: { type: 'string' },
|
||||
},
|
||||
required: ['messageId', 'reaction'],
|
||||
} 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) => {
|
||||
await this.chatService.unreact(ps.messageId, me.id, ps.reaction);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user