From 86f2ababd19275ce3f794318b0773c2076112943 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 17 Mar 2025 18:59:22 +0900 Subject: [PATCH] wip --- packages/backend/src/core/ChatService.ts | 24 ++++++- .../src/models/json-schema/chat-message.ts | 4 ++ .../api/endpoints/chat/messages/history.ts | 15 +++- packages/frontend/src/pages/chat/home.vue | 69 ++++++++++++++----- 4 files changed, 93 insertions(+), 19 deletions(-) diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 9eedfb6acb..33b15795af 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -283,7 +283,7 @@ export class ChatService { } @bindThis - public async userHistory(meId: MiUser['id'], limit: number) { + public async userHistory(meId: MiUser['id'], limit: number): Promise { const history: MiChatMessage[] = []; const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') @@ -324,7 +324,7 @@ export class ChatService { } @bindThis - public async roomHistory(meId: MiUser['id'], limit: number) { + public async roomHistory(meId: MiUser['id'], limit: number): Promise { /* const rooms = await this.userRoomJoiningsRepository.findBy({ userId: meId, @@ -359,4 +359,24 @@ export class ChatService { return history; */ } + + @bindThis + public async getUserReadStateMap(userId: MiUser['id'], otherIds: MiUser['id'][]) { + const readStateMap: Record = {}; + + const redisPipeline = this.redisClient.pipeline(); + + for (const otherId of otherIds) { + redisPipeline.get(`newChatMessageExists:${userId}:${otherId}`); + } + + const markers = await redisPipeline.exec(); + + for (let i = 0; i < otherIds.length; i++) { + const marker = markers[i][1]; + readStateMap[otherIds[i]] = marker == null; + } + + return readStateMap; + } } diff --git a/packages/backend/src/models/json-schema/chat-message.ts b/packages/backend/src/models/json-schema/chat-message.ts index 58e9af1bd3..f3907a3677 100644 --- a/packages/backend/src/models/json-schema/chat-message.ts +++ b/packages/backend/src/models/json-schema/chat-message.ts @@ -46,6 +46,10 @@ export const packedChatMessageSchema = { optional: true, nullable: true, ref: 'DriveFile', }, + isRead: { + type: 'boolean', + optional: true, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/chat/messages/history.ts b/packages/backend/src/server/api/endpoints/chat/messages/history.ts index 163c286be3..366ea9c96f 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/history.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/history.ts @@ -48,7 +48,20 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const history = ps.room ? await this.chatService.roomHistory(me.id, ps.limit) : await this.chatService.userHistory(me.id, ps.limit); - return await this.chatMessageEntityService.packMany(history, me); + const packedMessages = await this.chatMessageEntityService.packMany(history, me); + + if (ps.room) { + } else { + const otherIds = history.map(m => m.fromUserId === me.id ? m.toUserId! : m.fromUserId!); + const readStateMap = await this.chatService.getUserReadStateMap(me.id, otherIds); + + for (const message of packedMessages) { + const otherId = message.fromUserId === me.id ? message.toUserId! : message.fromUserId!; + message.isRead = readStateMap[otherId] ?? false; + } + } + + return packedMessages; }); } } diff --git a/packages/frontend/src/pages/chat/home.vue b/packages/frontend/src/pages/chat/home.vue index 6b627613bc..6336470a56 100644 --- a/packages/frontend/src/pages/chat/home.vue +++ b/packages/frontend/src/pages/chat/home.vue @@ -10,34 +10,34 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.startChat }} -
+
- -
- {{ message.room.name }} - + +
+ {{ item.message.room.name }} +
- - @{{ acct(message.other) }} - + + @{{ acct(item.other) }} +
-

{{ i18n.ts.you }}:{{ message.text }}

+

{{ i18n.ts.you }}:{{ item.message.text }}

-
+
-
{{ $ts.noHistory }}
+
{{ i18n.ts.noHistory }}
@@ -46,11 +46,48 @@ SPDX-License-Identifier: AGPL-3.0-only