diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index 8dc9b7921..7acdec646 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -3975,11 +3975,19 @@ export class ConversationModel extends window.Backbone sendReadReceipts: true, } ): Promise { - const unreadCount = await markConversationRead( + const markedUnread = await markConversationRead( this.attributes, newestUnreadId, options ); + + if (!markedUnread) { + return; + } + + const unreadCount = await window.Signal.Data.getUnreadCountForConversation( + this.id + ); this.set({ unreadCount }); window.Signal.Data.updateConversation(this.attributes); } diff --git a/ts/sql/Client.ts b/ts/sql/Client.ts index f4a0a8d3f..82c039d0a 100644 --- a/ts/sql/Client.ts +++ b/ts/sql/Client.ts @@ -167,6 +167,7 @@ const dataInterface: ClientInterface = { saveMessages, removeMessage, removeMessages, + getUnreadCountForConversation, getUnreadByConversationAndMarkRead, getUnreadReactionsAndMarkRead, markReactionAsRead, @@ -1046,6 +1047,10 @@ async function getMessageBySender( return new Message(messages[0]); } +async function getUnreadCountForConversation(conversationId: string) { + return channels.getUnreadCountForConversation(conversationId); +} + async function getUnreadByConversationAndMarkRead( conversationId: string, newestUnreadId: number, diff --git a/ts/sql/Interface.ts b/ts/sql/Interface.ts index 7b39ee894..588f261bb 100644 --- a/ts/sql/Interface.ts +++ b/ts/sql/Interface.ts @@ -343,6 +343,7 @@ export type ServerInterface = DataInterface & { getNextTapToViewMessageToAgeOut: () => Promise; getOutgoingWithoutExpiresAt: () => Promise>; getTapToViewMessagesNeedingErase: () => Promise>; + getUnreadCountForConversation: (conversationId: string) => Promise; getUnreadByConversationAndMarkRead: ( conversationId: string, newestUnreadId: number, @@ -486,6 +487,7 @@ export type ClientInterface = DataInterface & { getTapToViewMessagesNeedingErase: (options: { MessageCollection: typeof MessageModelCollectionType; }) => Promise; + getUnreadCountForConversation: (conversationId: string) => Promise; getUnreadByConversationAndMarkRead: ( conversationId: string, newestUnreadId: number, diff --git a/ts/sql/Server.ts b/ts/sql/Server.ts index 59e62f20d..9fa26c6c1 100644 --- a/ts/sql/Server.ts +++ b/ts/sql/Server.ts @@ -157,6 +157,7 @@ const dataInterface: ServerInterface = { saveMessages, removeMessage, removeMessages, + getUnreadCountForConversation, getUnreadByConversationAndMarkRead, getUnreadReactionsAndMarkRead, markReactionAsRead, @@ -3092,6 +3093,24 @@ function updateExpirationTimers( ); } +async function getUnreadCountForConversation( + conversationId: string +): Promise { + const db = getInstance(); + const row = db + .prepare( + ` + SELECT COUNT(*) AS unreadCount FROM messages + WHERE unread = 1 AND + conversationId = $conversationId + ` + ) + .get({ + conversationId, + }); + return row.unreadCount; +} + async function getUnreadByConversationAndMarkRead( conversationId: string, newestUnreadId: number, @@ -3118,6 +3137,10 @@ async function getUnreadByConversationAndMarkRead( newestUnreadId, }); + if (!rows.length) { + return []; + } + const messagesWithExpireTimer: Map< string, { diff --git a/ts/util/markConversationRead.ts b/ts/util/markConversationRead.ts index 668011b53..73594371f 100644 --- a/ts/util/markConversationRead.ts +++ b/ts/util/markConversationRead.ts @@ -11,7 +11,7 @@ export async function markConversationRead( options: { readAt?: number; sendReadReceipts: boolean } = { sendReadReceipts: true, } -): Promise { +): Promise { const { id: conversationId } = conversationAttrs; window.Whisper.Notifications.removeBy({ conversationId }); @@ -27,6 +27,10 @@ export async function markConversationRead( ), ]); + if (!unreadMessages.length && !unreadReactions.length) { + return false; + } + const unreadReactionSyncData = new Map< string, { @@ -67,22 +71,13 @@ export async function markConversationRead( }); // Some messages we're marking read are local notifications with no sender - const messagesWithSenderId = allReadMessagesSync.filter(syncMessage => - Boolean(syncMessage.senderId) - ); - const incomingUnreadMessages = unreadMessages.filter( - message => message.type === 'incoming' - ); - const unreadCount = - incomingUnreadMessages.length - messagesWithSenderId.length; - // If a message has errors, we don't want to send anything out about it. // read syncs - let's wait for a client that really understands the message // to mark it read. we'll mark our local error read locally, though. // read receipts - here we can run into infinite loops, where each time the // conversation is viewed, another error message shows up for the contact - const unreadMessagesSyncData = messagesWithSenderId.filter( - item => !item.hasErrors + const unreadMessagesSyncData = allReadMessagesSync.filter( + item => Boolean(item.senderId) && !item.hasErrors ); const readSyncs = [ @@ -107,5 +102,5 @@ export async function markConversationRead( await sendReadReceiptsFor(conversationAttrs, unreadMessagesSyncData); } - return unreadCount; + return true; }