diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 46062e1572..334a798483 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -248,11 +248,11 @@ export class ChatService { public async readUserChatMessage( readerId: MiUser['id'], senderId: MiUser['id'], - ) { + ): Promise { const redisPipeline = this.redisClient.pipeline(); redisPipeline.del(`newChatMessageExists:${readerId}:${senderId}`); redisPipeline.srem(`newChatMessagesExists:${readerId}`, `user:${senderId}`); - redisPipeline.exec(); + await redisPipeline.exec(); } @bindThis @@ -467,6 +467,7 @@ export class ChatService { @bindThis public async hasUnreadMessages(userId: MiUser['id']) { - // TODO + const card = await this.redisClient.scard(`newChatMessagesExists:${userId}`); + return card > 0; } } diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 2a62b41f33..a0b90d62fc 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -74,6 +74,7 @@ export interface MainEventTypes { unreadNotification: Packed<'Notification'>; readAllAntennas: undefined; unreadAntenna: MiAntenna; + newChatMessage: Packed<'ChatMessage'>; readAllAnnouncements: undefined; myTokenRegenerated: undefined; signin: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 8147cc36a1..b10430782b 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -545,6 +545,10 @@ export const packedMeDetailedOnlySchema = { type: 'boolean', nullable: false, optional: false, }, + hasUnreadChatMessages: { + type: 'boolean', + nullable: false, optional: false, + }, hasUnreadNotification: { type: 'boolean', nullable: false, optional: false, diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index b91d294d09..f82d4c6a30 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -133,6 +133,7 @@ describe('ユーザー', () => { hasUnreadAnnouncement: user.hasUnreadAnnouncement, hasUnreadAntenna: user.hasUnreadAntenna, hasUnreadChannel: user.hasUnreadChannel, + hasUnreadChatMessages: user.hasUnreadChatMessages, hasUnreadNotification: user.hasUnreadNotification, unreadNotificationsCount: user.unreadNotificationsCount, hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest, @@ -371,6 +372,7 @@ describe('ユーザー', () => { assert.strictEqual(response.hasUnreadAnnouncement, false); assert.strictEqual(response.hasUnreadAntenna, false); assert.strictEqual(response.hasUnreadChannel, false); + assert.strictEqual(response.hasUnreadChatMessages, false); assert.strictEqual(response.hasUnreadNotification, false); assert.strictEqual(response.unreadNotificationsCount, 0); assert.strictEqual(response.hasPendingReceivedFollowRequest, false); diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 1ee07658fb..7be5cf1f21 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -511,6 +511,11 @@ export async function mainBoot() { sound.playMisskeySfx('antenna'); }); + main.on('newChatMessage', () => { + updateCurrentAccountPartial({ hasUnreadChatMessages: true }); + sound.playMisskeySfx('chat'); + }); + main.on('readAllAnnouncements', () => { updateCurrentAccountPartial({ hasUnreadAnnouncement: false }); }); diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index d3a8560403..894df83721 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -114,6 +114,7 @@ export const navbarItemDef = reactive({ title: i18n.ts.chat, icon: 'ti ti-message', to: '/chat', + indicated: computed(() => $i != null && $i.hasUnreadChatMessages), }, achievements: { title: i18n.ts.achievements, diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 6507eb7681..f258a2474a 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -626,6 +626,7 @@ export type Channels = { readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; unreadAntenna: (payload: Antenna) => void; + newChatMessage: (payload: ChatMessage) => void; readAllAnnouncements: () => void; myTokenRegenerated: () => void; signin: (payload: Signin) => void; @@ -3480,8 +3481,8 @@ type V2AdminEmojiListResponse = operations['v2___admin___emoji___list']['respons // // src/entities.ts:50:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.ts:57:3 - (ae-forgotten-export) The symbol "ReconnectingWebSocket" needs to be exported by the entry point index.d.ts -// src/streaming.types.ts:220:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts -// src/streaming.types.ts:230:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts +// src/streaming.types.ts:222:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts +// src/streaming.types.ts:232:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index c40da6bfc8..6e0346b935 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3914,6 +3914,7 @@ export type components = { unreadAnnouncements: components['schemas']['Announcement'][]; hasUnreadAntenna: boolean; hasUnreadChannel: boolean; + hasUnreadChatMessages: boolean; hasUnreadNotification: boolean; hasPendingReceivedFollowRequest: boolean; unreadNotificationsCount: number; diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index 26a50f9fa4..05b2d28ec8 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -1,5 +1,6 @@ import { Antenna, + ChatMessage, DriveFile, DriveFolder, Note, @@ -53,6 +54,7 @@ export type Channels = { readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; unreadAntenna: (payload: Antenna) => void; + newChatMessage: (payload: ChatMessage) => void; readAllAnnouncements: () => void; myTokenRegenerated: () => void; signin: (payload: Signin) => void;