From 843bae56597ecf3811d724504bd0216b867979f7 Mon Sep 17 00:00:00 2001 From: Mikhail Rogachev Date: Mon, 25 Dec 2023 13:25:22 +0700 Subject: [PATCH] feat: Return seen/unseen messages count in MessnegerResponse (#4461) --- protocol/messenger.go | 64 ++++++++++++++++++---- protocol/messenger_activity_center.go | 17 +++++- protocol/messenger_activity_center_test.go | 8 +-- protocol/messenger_response.go | 34 ++++++++++++ protocol/messenger_test.go | 23 +++++--- protocol/messenger_unread_test.go | 56 +++++++++++-------- services/ext/api.go | 17 ++---- 7 files changed, 161 insertions(+), 58 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index 62e3a39a7..ebde0c9a7 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -4146,7 +4146,7 @@ func (m *Messenger) DeleteMessagesByChatID(id string) error { return m.persistence.DeleteMessagesByChatID(id) } -func (m *Messenger) MarkMessageAsUnreadImpl(chatID string, messageID string) (uint64, uint64, error) { +func (m *Messenger) markMessageAsUnreadImpl(chatID string, messageID string) (uint64, uint64, error) { count, countWithMentions, err := m.persistence.MarkMessageAsUnread(chatID, messageID) if err != nil { @@ -4161,17 +4161,23 @@ func (m *Messenger) MarkMessageAsUnreadImpl(chatID string, messageID string) (ui return count, countWithMentions, nil } -func (m *Messenger) MarkMessageAsUnread(chatID string, messageID string) (uint64, uint64, []*ActivityCenterNotification, error) { - count, countWithMentions, err := m.MarkMessageAsUnreadImpl(chatID, messageID) - +func (m *Messenger) MarkMessageAsUnread(chatID string, messageID string) (*MessengerResponse, error) { + count, countWithMentions, err := m.markMessageAsUnreadImpl(chatID, messageID) if err != nil { - return 0, 0, nil, err + return nil, err } - ids, err := m.persistence.GetMessageIdsWithGreaterTimestamp(chatID, messageID) + response := &MessengerResponse{} + response.AddSeenAndUnseenMessages(&SeenUnseenMessages{ + ChatID: chatID, + Count: count, + CountWithMentions: countWithMentions, + Seen: false, + }) + ids, err := m.persistence.GetMessageIdsWithGreaterTimestamp(chatID, messageID) if err != nil { - return 0, 0, nil, err + return nil, err } hexBytesIds := []types.HexBytes{} @@ -4181,12 +4187,13 @@ func (m *Messenger) MarkMessageAsUnread(chatID string, messageID string) (uint64 updatedAt := m.GetCurrentTimeInMillis() notifications, err := m.persistence.MarkActivityCenterNotificationsUnread(hexBytesIds, updatedAt) - if err != nil { - return 0, 0, nil, err + return nil, err } - return count, countWithMentions, notifications, nil + response.AddActivityCenterNotifications(notifications) + + return response, nil } // MarkMessagesSeen marks messages with `ids` as seen in the chat `chatID`. @@ -4205,6 +4212,7 @@ func (m *Messenger) markMessagesSeenImpl(chatID string, ids []string) (uint64, u return count, countWithMentions, nil } +// Deprecated: Use MarkMessagesRead instead func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, uint64, []*ActivityCenterNotification, error) { count, countWithMentions, err := m.markMessagesSeenImpl(chatID, ids) if err != nil { @@ -4231,6 +4239,42 @@ func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, uint6 return count, countWithMentions, notifications, nil } +func (m *Messenger) MarkMessagesRead(chatID string, ids []string) (*MessengerResponse, error) { + count, countWithMentions, err := m.markMessagesSeenImpl(chatID, ids) + if err != nil { + return nil, err + } + + response := &MessengerResponse{} + response.AddSeenAndUnseenMessages(&SeenUnseenMessages{ + ChatID: chatID, + Count: count, + CountWithMentions: countWithMentions, + Seen: true, + }) + + hexBytesIds := []types.HexBytes{} + for _, id := range ids { + hexBytesIds = append(hexBytesIds, types.FromHex(id)) + } + + // Mark notifications as read in the database + updatedAt := m.GetCurrentTimeInMillis() + err = m.persistence.MarkActivityCenterNotificationsRead(hexBytesIds, updatedAt) + if err != nil { + return nil, err + } + + notifications, err := m.persistence.GetActivityCenterNotificationsByID(hexBytesIds) + if err != nil { + return nil, err + } + + response.AddActivityCenterNotifications(notifications) + + return response, nil +} + func (m *Messenger) syncChatMessagesRead(ctx context.Context, chatID string, clock uint64, rawMessageHandler RawMessageHandler) error { if !m.hasPairedDevices() { return nil diff --git a/protocol/messenger_activity_center.go b/protocol/messenger_activity_center.go index ffbfd704d..82179fb70 100644 --- a/protocol/messenger_activity_center.go +++ b/protocol/messenger_activity_center.go @@ -143,7 +143,7 @@ func (m *Messenger) MarkActivityCenterNotificationsRead(ctx context.Context, ids response := &MessengerResponse{} repliesAndMentions := make(map[string][]string) - // When marking as read Mention or Reply notification, the corresponding chat message should also be read. + // When marking as read Mention or Reply notification, the corresponding chat message should also be seen. for _, notification := range notifications { response.AddActivityCenterNotification(notification) @@ -155,10 +155,17 @@ func (m *Messenger) MarkActivityCenterNotificationsRead(ctx context.Context, ids // Mark messages as seen for chatID, messageIDs := range repliesAndMentions { - _, _, err := m.markMessagesSeenImpl(chatID, messageIDs) + count, countWithMentions, err := m.markMessagesSeenImpl(chatID, messageIDs) if err != nil { return nil, err } + + response.AddSeenAndUnseenMessages(&SeenUnseenMessages{ + ChatID: chatID, + Count: count, + CountWithMentions: countWithMentions, + Seen: true, + }) } state, err := m.persistence.GetActivityCenterState() @@ -182,12 +189,16 @@ func (m *Messenger) MarkActivityCenterNotificationsRead(ctx context.Context, ids } func (m *Messenger) MarkActivityCenterNotificationsUnread(ctx context.Context, ids []types.HexBytes, updatedAt uint64, sync bool) (*MessengerResponse, error) { - response := &MessengerResponse{} notifications, err := m.persistence.MarkActivityCenterNotificationsUnread(ids, updatedAt) if err != nil { return nil, err } + response := &MessengerResponse{} + response.AddActivityCenterNotifications(notifications) + + // Don't mark messages unseen in chat, that looks weird + state, err := m.persistence.GetActivityCenterState() if err != nil { return nil, err diff --git a/protocol/messenger_activity_center_test.go b/protocol/messenger_activity_center_test.go index f2d801220..f2fe7254f 100644 --- a/protocol/messenger_activity_center_test.go +++ b/protocol/messenger_activity_center_test.go @@ -417,12 +417,12 @@ func (s *MessengerActivityCenterMessageSuite) confirmMentionAndReplyNotification func (s *MessengerActivityCenterMessageSuite) TestMarkMessagesSeenMarksNotificationsRead() { alice, _, mentionMessage, replyMessage, _ := s.prepareCommunityChannelWithMentionAndReply() - _, _, notifications, err := alice.MarkMessagesSeen(replyMessage.ChatId, []string{mentionMessage.ID, replyMessage.ID}) + response, err := alice.MarkMessagesRead(replyMessage.ChatId, []string{mentionMessage.ID, replyMessage.ID}) s.Require().NoError(err) - s.Require().Len(notifications, 2) - s.Require().True(notifications[0].Read) - s.Require().True(notifications[1].Read) + s.Require().Len(response.ActivityCenterNotifications(), 2) + s.Require().True(response.ActivityCenterNotifications()[0].Read) + s.Require().True(response.ActivityCenterNotifications()[1].Read) s.confirmMentionAndReplyNotificationsRead(alice, mentionMessage, replyMessage, true) } diff --git a/protocol/messenger_response.go b/protocol/messenger_response.go index d8817d6a6..be04ec7ae 100644 --- a/protocol/messenger_response.go +++ b/protocol/messenger_response.go @@ -35,6 +35,13 @@ type ClearedHistory struct { ClearedAt uint64 `json:"clearedAt"` } +type SeenUnseenMessages struct { + ChatID string `json:"chatId"` + Count uint64 `json:"count"` + CountWithMentions uint64 `json:"countWithMentions"` + Seen bool `json:"seen"` +} + type MessengerResponse struct { Contacts []*Contact Installations []*multidevice.Installation @@ -81,6 +88,7 @@ type MessengerResponse struct { SocialLinksInfo *identity.SocialLinksInfo ensUsernameDetails []*ensservice.UsernameDetail updatedProfileShowcases map[string]*ProfileShowcase + seenAndUnseenMessages map[string]*SeenUnseenMessages } func (r *MessengerResponse) MarshalJSON() ([]byte, error) { @@ -126,6 +134,7 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) { SocialLinksInfo *identity.SocialLinksInfo `json:"socialLinksInfo,omitempty"` EnsUsernameDetails []*ensservice.UsernameDetail `json:"ensUsernameDetails,omitempty"` UpdatedProfileShowcases []*ProfileShowcase `json:"updatedProfileShowcases,omitempty"` + SeenAndUnseenMessages []*SeenUnseenMessages `json:"seenAndUnseenMessages,omitempty"` }{ Contacts: r.Contacts, Installations: r.Installations, @@ -164,6 +173,7 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) { SocialLinksInfo: r.SocialLinksInfo, EnsUsernameDetails: r.EnsUsernameDetails(), UpdatedProfileShowcases: r.GetUpdatedProfileShowcases(), + SeenAndUnseenMessages: r.GetSeenAndUnseenMessages(), } responseItem.TrustStatus = r.TrustStatus() @@ -292,6 +302,7 @@ func (r *MessengerResponse) IsEmpty() bool { len(r.RequestsToJoinCommunity)+ len(r.savedAddresses)+ len(r.updatedProfileShowcases)+ + len(r.seenAndUnseenMessages)+ len(r.ensUsernameDetails) == 0 && r.currentStatus == nil && r.activityCenterState == nil && @@ -329,6 +340,7 @@ func (r *MessengerResponse) Merge(response *MessengerResponse) error { r.AddRequestsToJoinCommunity(response.RequestsToJoinCommunity) r.AddBookmarks(response.GetBookmarks()) r.AddProfileShowcases(response.GetUpdatedProfileShowcases()) + r.AddSeveralSeenAndUnseenMessages(response.GetSeenAndUnseenMessages()) r.CommunityChanges = append(r.CommunityChanges, response.CommunityChanges...) r.BackupHandled = response.BackupHandled r.CustomizationColor = response.CustomizationColor @@ -812,3 +824,25 @@ func (r *MessengerResponse) GetUpdatedProfileShowcases() []*ProfileShowcase { } return showcases } + +func (r *MessengerResponse) AddSeveralSeenAndUnseenMessages(messages []*SeenUnseenMessages) { + for _, message := range messages { + r.AddSeenAndUnseenMessages(message) + } +} + +func (r *MessengerResponse) AddSeenAndUnseenMessages(message *SeenUnseenMessages) { + if r.seenAndUnseenMessages == nil { + r.seenAndUnseenMessages = make(map[string]*SeenUnseenMessages) + } + + r.seenAndUnseenMessages[message.ChatID] = message +} + +func (r *MessengerResponse) GetSeenAndUnseenMessages() []*SeenUnseenMessages { + var messages []*SeenUnseenMessages + for _, message := range r.seenAndUnseenMessages { + messages = append(messages, message) + } + return messages +} diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index 85c66099d..66e0c6eee 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -268,18 +268,25 @@ func (s *MessengerSuite) TestMarkMessagesSeen() { err = s.m.SaveMessages([]*common.Message{inputMessage1, inputMessage2}) s.Require().NoError(err) - count, countWithMentions, notifications, err := s.m.MarkMessagesSeen(chat.ID, []string{inputMessage1.ID}) + response, err := s.m.MarkMessagesRead(chat.ID, []string{inputMessage1.ID}) s.Require().NoError(err) - s.Require().Equal(uint64(1), count) - s.Require().Equal(uint64(1), countWithMentions) - s.Require().Len(notifications, 0) + + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(chat.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(true, response.GetSeenAndUnseenMessages()[0].Seen) + s.Require().Len(response.ActivityCenterNotifications(), 0) // Make sure that if it's not seen, it does not return a count of 1 - count, countWithMentions, notifications, err = s.m.MarkMessagesSeen(chat.ID, []string{inputMessage1.ID}) + response, err = s.m.MarkMessagesRead(chat.ID, []string{inputMessage1.ID}) s.Require().NoError(err) - s.Require().Equal(uint64(0), count) - s.Require().Equal(uint64(0), countWithMentions) - s.Require().Len(notifications, 0) + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(chat.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(0), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(0), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(true, response.GetSeenAndUnseenMessages()[0].Seen) + s.Require().Len(response.ActivityCenterNotifications(), 0) chats := s.m.Chats() for _, c := range chats { diff --git a/protocol/messenger_unread_test.go b/protocol/messenger_unread_test.go index d70e50b53..ce4d08fce 100644 --- a/protocol/messenger_unread_test.go +++ b/protocol/messenger_unread_test.go @@ -69,11 +69,14 @@ func (s *MessengerSuite) TestMarkMessageAsUnreadWhenMessageListContainsSingleMes s.Require().False(actualChat.Highlight) s.checkMessageSeen(inputMessage1.ID, true) - count, countWithMentions, notifications, err := s.m.MarkMessageAsUnread(chat.ID, inputMessage1.ID) + response, err := s.m.MarkMessageAsUnread(chat.ID, inputMessage1.ID) s.Require().NoError(err) - s.Require().Equal(uint64(1), count) - s.Require().Equal(uint64(1), countWithMentions) - s.Require().Len(notifications, 0) + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(chat.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(false, response.GetSeenAndUnseenMessages()[0].Seen) + s.Require().Len(response.ActivityCenterNotifications(), 0) chats = s.m.allChats @@ -134,7 +137,7 @@ func (s *MessengerSuite) TestMarkMessageAsUnreadWhenMessageListContainsSeveralMe s.checkMessageSeen(inputMessage2.ID, true) s.checkMessageSeen(inputMessage3.ID, true) - count, countWithMentions, notifications, err := s.m.MarkMessageAsUnread(chat.ID, inputMessage2.ID) + response, err := s.m.MarkMessageAsUnread(chat.ID, inputMessage2.ID) s.Require().NoError(err) // count is 2 because the messages are layout the following way : @@ -147,9 +150,11 @@ func (s *MessengerSuite) TestMarkMessageAsUnreadWhenMessageListContainsSeveralMe // And the inputMessage3 has greater timestamp than inputMessage2 // Similarly, the mentioned message is inputMessage1, therefore the // countWithMentions is 0 - s.Require().Equal(uint64(2), count) - s.Require().Equal(uint64(0), countWithMentions) - s.Require().Len(notifications, 0) + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(chat.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(2), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(0), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(false, response.GetSeenAndUnseenMessages()[0].Seen) chats = s.m.allChats actualChat, ok = chats.Load(chat.ID) @@ -190,11 +195,13 @@ func (s *MessengerSuite) TestMarkMessageAsUnreadWhenMessageIsAlreadyInUnreadStat s.Require().True(actualChat.Highlight) s.checkMessageSeen(inputMessage1.ID, false) - count, countWithMentions, notifications, err := s.m.MarkMessageAsUnread(chat.ID, inputMessage1.ID) + response, err := s.m.MarkMessageAsUnread(chat.ID, inputMessage1.ID) s.Require().NoError(err) - s.Require().Equal(uint64(1), count) - s.Require().Equal(uint64(0), countWithMentions) - s.Require().Len(notifications, 0) + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(chat.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(0), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(false, response.GetSeenAndUnseenMessages()[0].Seen) chats = s.m.allChats actualChat, ok = chats.Load(chat.ID) @@ -249,13 +256,15 @@ func (s *MessengerSuite) TestMarkMessageAsUnreadInOneChatDoesNotImpactOtherChats s.checkMessageSeen(inputMessage1.ID, true) s.checkMessageSeen(inputMessage2.ID, true) - count, countWithMentions, notifications, err := s.m.MarkMessageAsUnread(chat1.ID, inputMessage1.ID) + response, err := s.m.MarkMessageAsUnread(chat1.ID, inputMessage1.ID) s.Require().NoError(err) - s.Require().Equal(uint(1), chat2.UnviewedMessagesCount) - s.Require().Equal(uint64(2), count) - s.Require().Equal(uint64(0), countWithMentions) - s.Require().Len(notifications, 0) + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(chat1.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(2), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(0), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(false, response.GetSeenAndUnseenMessages()[0].Seen) + s.Require().Len(response.ActivityCenterNotifications(), 0) chats := s.m.allChats actualChat, ok := chats.Load(chat1.ID) @@ -325,11 +334,14 @@ func (s *MessengerSuite) TestMarkMessageWithNotificationAsUnreadInCommunityChatS s.Require().Len(response.ActivityCenterNotifications(), 1) s.Require().True(response.ActivityCenterNotifications()[0].Read) - count, countWithMentions, notifications, err := s.m.MarkMessageAsUnread(communityChat.ID, inputMessage1.ID) + response, err = s.m.MarkMessageAsUnread(communityChat.ID, inputMessage1.ID) s.Require().NoError(err) - s.Require().Equal(count, uint64(1)) - s.Require().Equal(countWithMentions, uint64(1)) - s.Require().Len(notifications, 1) - s.Require().False(notifications[0].Read) + s.Require().Len(response.GetSeenAndUnseenMessages(), 1) + s.Require().Equal(communityChat.ID, response.GetSeenAndUnseenMessages()[0].ChatID) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].Count) + s.Require().Equal(uint64(1), response.GetSeenAndUnseenMessages()[0].CountWithMentions) + s.Require().Equal(false, response.GetSeenAndUnseenMessages()[0].Seen) + s.Require().Len(response.ActivityCenterNotifications(), 1) + s.Require().False(response.ActivityCenterNotifications()[0].Read) } diff --git a/services/ext/api.go b/services/ext/api.go index d4d7cebda..e6afc83e0 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -797,6 +797,7 @@ func (api *PublicAPI) DeleteMessagesByChatID(id string) error { return api.service.messenger.DeleteMessagesByChatID(id) } +// Deprecated: Use MarkMessagesRead instead func (api *PublicAPI) MarkMessagesSeen(chatID string, ids []string) (*MarkMessageSeenResponse, error) { count, withMentions, notifications, err := api.service.messenger.MarkMessagesSeen(chatID, ids) if err != nil { @@ -811,18 +812,12 @@ func (api *PublicAPI) MarkMessagesSeen(chatID string, ids []string) (*MarkMessag return response, nil } -func (api *PublicAPI) MarkMessageAsUnread(chatID string, messageID string) (*MarkMessageSeenResponse, error) { - count, withMentions, notifications, err := api.service.messenger.MarkMessageAsUnread(chatID, messageID) - if err != nil { - return nil, err - } +func (api *PublicAPI) MarkMessagesRead(chatID string, ids []string) (*protocol.MessengerResponse, error) { + return api.service.messenger.MarkMessagesRead(chatID, ids) +} - response := &MarkMessageSeenResponse{ - Count: count, - CountWithMentions: withMentions, - ActivityCenterNotifications: notifications, - } - return response, nil +func (api *PublicAPI) MarkMessageAsUnread(chatID string, messageID string) (*protocol.MessengerResponse, error) { + return api.service.messenger.MarkMessageAsUnread(chatID, messageID) } func (api *PublicAPI) MarkAllRead(ctx context.Context, chatID string) (*protocol.MessengerResponse, error) {