diff --git a/VERSION b/VERSION index b74e5a1cc80..359ee08a7ce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.86.7 +0.87.0 diff --git a/protocol/message_persistence.go b/protocol/message_persistence.go index 4022d63dc1b..d73723355e0 100644 --- a/protocol/message_persistence.go +++ b/protocol/message_persistence.go @@ -752,6 +752,28 @@ func (db sqlitePersistence) AllMessagesFromChatsAndCommunitiesWhichMatchTerm(com return result, nil } +func (db sqlitePersistence) AllChatIDsByCommunity(communityID string) ([]string, error) { + rows, err := db.db.Query("SELECT id FROM chats WHERE community_id = ?", communityID) + + if err != nil { + return nil, err + } + defer rows.Close() + + var rst []string + + for rows.Next() { + var chatID string + err = rows.Scan(&chatID) + if err != nil { + return nil, err + } + rst = append(rst, chatID) + } + + return rst, nil +} + // PinnedMessageByChatID returns all pinned messages for a given chatID in descending order. // Ordering is accomplished using two concatenated values: ClockValue and ID. // These two values are also used to compose a cursor which is returned to the result. @@ -1254,6 +1276,40 @@ func (db sqlitePersistence) MarkAllRead(chatID string) error { return err } +func (db sqlitePersistence) MarkAllReadMultiple(chatIDs []string) error { + tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) + if err != nil { + return err + } + defer func() { + if err == nil { + err = tx.Commit() + return + } + // don't shadow original error + _ = tx.Rollback() + }() + + idsArgs := make([]interface{}, 0, len(chatIDs)) + for _, id := range chatIDs { + idsArgs = append(idsArgs, id) + } + + inVector := strings.Repeat("?, ", len(chatIDs)-1) + "?" + + q := "UPDATE user_messages SET seen = 1 WHERE local_chat_id IN (%s) AND seen != 1" + q = fmt.Sprintf(q, inVector) + _, err = tx.Exec(q, idsArgs...) + if err != nil { + return err + } + + q = "UPDATE chats SET unviewed_mentions_count = 0, unviewed_message_count = 0 WHERE id IN (%s)" + q = fmt.Sprintf(q, inVector) + _, err = tx.Exec(q, idsArgs...) + return err +} + func (db sqlitePersistence) MarkMessagesSeen(chatID string, ids []string) (uint64, uint64, error) { tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) if err != nil { diff --git a/protocol/messenger.go b/protocol/messenger.go index babb56c3895..57ceaf768c7 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3294,6 +3294,32 @@ func (m *Messenger) MarkAllRead(chatID string) error { return nil } +func (m *Messenger) MarkAllReadInCommunity(communityID string) ([]string, error) { + chatIDs, err := m.persistence.AllChatIDsByCommunity(communityID) + if err != nil { + return nil, err + } + + err = m.persistence.MarkAllReadMultiple(chatIDs) + if err != nil { + return nil, err + } + + for _, chatID := range chatIDs { + chat, ok := m.allChats.Load(chatID) + + if ok { + chat.UnviewedMessagesCount = 0 + chat.UnviewedMentionsCount = 0 + m.allChats.Store(chat.ID, chat) + } else { + err = errors.New(fmt.Sprintf("chat with chatID %s not found", chatID)) + } + } + + return chatIDs, err +} + // MuteChat signals to the messenger that we don't want to be notified // on new messages from this chat func (m *Messenger) MuteChat(chatID string) error { diff --git a/services/ext/api.go b/services/ext/api.go index 32f03b9d82f..b7ae2820cd3 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -575,6 +575,10 @@ func (api *PublicAPI) MarkAllRead(chatID string) error { return api.service.messenger.MarkAllRead(chatID) } +func (api *PublicAPI) MarkAllReadInCommunity(communityID string) ([]string, error) { + return api.service.messenger.MarkAllReadInCommunity(communityID) +} + func (api *PublicAPI) AddContact(ctx context.Context, pubKey string) (*protocol.MessengerResponse, error) { return api.service.messenger.AddContact(ctx, pubKey) }