1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-08 05:47:07 +09:00

GO-5344: Fix reading/un-reading

This commit is contained in:
Sergey 2025-03-27 15:36:33 +01:00
parent 13ef3c7998
commit b64be942db
No known key found for this signature in database
GPG key ID: 3B6BEF79160221C6
6 changed files with 1540 additions and 1378 deletions

View file

@ -56,9 +56,8 @@ func (d *ChatHandler) BeforeCreate(ctx context.Context, ch storestate.ChangeOp)
msg.MentionRead = false
} else {
if ch.Change.Creator == d.currentIdentity {
// TODO Return to true
msg.Read = false
msg.MentionRead = false
msg.Read = true
msg.MentionRead = true
} else {
msg.Read = false
msg.MentionRead = false

View file

@ -18,6 +18,7 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/storestate"
"github.com/anyproto/anytype-heart/core/block/source"
"github.com/anyproto/anytype-heart/core/block/source/mock_source"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/event/mock_event"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
@ -25,6 +26,10 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const (
testSpaceId = "spaceId1"
)
type accountServiceStub struct {
accountId string
}
@ -86,7 +91,7 @@ func newFixture(t *testing.T) *fixture {
source := mock_source.NewMockStore(t)
source.EXPECT().Id().Return("chatId1")
source.EXPECT().SpaceID().Return("space1")
source.EXPECT().SpaceID().Return(testSpaceId)
source.EXPECT().ReadStoreDoc(ctx, mock.Anything, mock.Anything).Return(nil)
source.EXPECT().PushStoreChange(mock.Anything, mock.Anything).RunAndReturn(fx.applyToStore).Maybe()
@ -400,13 +405,32 @@ func TestReadMessages(t *testing.T) {
require.NoError(t, err)
}
// All messages forced as not read
messagesResp := fx.assertReadStatus(t, ctx, "", "", false)
messagesResp := fx.assertReadStatus(t, ctx, "", "", false, false)
err := fx.MarkReadMessages(ctx, "", messagesResp.Messages[2].OrderId, messagesResp.ChatState.DbTimestamp, CounterTypeMessage)
require.NoError(t, err)
fx.assertReadStatus(t, ctx, "", messagesResp.Messages[2].OrderId, true)
fx.assertReadStatus(t, ctx, messagesResp.Messages[3].OrderId, "", false)
fx.assertReadStatus(t, ctx, "", messagesResp.Messages[2].OrderId, true, false)
fx.assertReadStatus(t, ctx, messagesResp.Messages[3].OrderId, "", false, false)
}
func TestReadMentions(t *testing.T) {
ctx := context.Background()
fx := newFixture(t)
fx.chatHandler.forceNotRead = true
const n = 10
for i := 0; i < n; i++ {
_, err := fx.AddMessage(ctx, nil, givenMessageWithMention(fmt.Sprintf("message %d", i+1)))
require.NoError(t, err)
}
// All messages forced as not read
messagesResp := fx.assertReadStatus(t, ctx, "", "", false, false)
err := fx.MarkReadMessages(ctx, "", messagesResp.Messages[2].OrderId, messagesResp.ChatState.DbTimestamp, CounterTypeMention)
require.NoError(t, err)
fx.assertReadStatus(t, ctx, "", messagesResp.Messages[2].OrderId, false, true)
fx.assertReadStatus(t, ctx, messagesResp.Messages[3].OrderId, "", false, false)
}
func TestMarkMessagesAsNotRead(t *testing.T) {
@ -419,15 +443,33 @@ func TestMarkMessagesAsNotRead(t *testing.T) {
require.NoError(t, err)
}
// All messages added by myself are read
fx.assertReadStatus(t, ctx, "", "", true)
fx.assertReadStatus(t, ctx, "", "", true, true)
err := fx.MarkMessagesAsUnread(ctx, "", CounterTypeMessage)
require.NoError(t, err)
fx.assertReadStatus(t, ctx, "", "", false)
fx.assertReadStatus(t, ctx, "", "", false, true)
}
func (fx *fixture) assertReadStatus(t *testing.T, ctx context.Context, afterOrderId string, beforeOrderId string, isRead bool) *GetMessagesResponse {
func TestMarkMentionsAsNotRead(t *testing.T) {
ctx := context.Background()
fx := newFixture(t)
const n = 10
for i := 0; i < n; i++ {
_, err := fx.AddMessage(ctx, nil, givenMessageWithMention(fmt.Sprintf("message %d", i+1)))
require.NoError(t, err)
}
// All messages added by myself are read
fx.assertReadStatus(t, ctx, "", "", true, true)
err := fx.MarkMessagesAsUnread(ctx, "", CounterTypeMention)
require.NoError(t, err)
fx.assertReadStatus(t, ctx, "", "", true, false)
}
func (fx *fixture) assertReadStatus(t *testing.T, ctx context.Context, afterOrderId string, beforeOrderId string, isRead bool, isMentionRead bool) *GetMessagesResponse {
messageResp, err := fx.GetMessages(ctx, GetMessagesRequest{
AfterOrderId: afterOrderId,
BeforeOrderId: beforeOrderId,
@ -438,6 +480,7 @@ func (fx *fixture) assertReadStatus(t *testing.T, ctx context.Context, afterOrde
for _, m := range messageResp.Messages {
assert.Equal(t, isRead, m.Read)
assert.Equal(t, isMentionRead, m.MentionRead)
}
return messageResp
}
@ -483,6 +526,29 @@ func givenSimpleMessage(text string) *Message {
}
}
func givenMessageWithMention(text string) *Message {
return &Message{
ChatMessage: &model.ChatMessage{
Id: "",
OrderId: "",
Creator: "",
Read: true,
MentionRead: true,
Message: &model.ChatMessageMessageContent{
Text: text,
Style: model.BlockContentText_Paragraph,
Marks: []*model.BlockContentTextMark{
{
Type: model.BlockContentTextMark_Mention,
Param: domain.NewParticipantId(testSpaceId, testCreator),
Range: &model.Range{From: 0, To: 1},
},
},
},
},
}
}
func givenComplexMessage() *Message {
return &Message{
ChatMessage: &model.ChatMessage{

View file

@ -27,7 +27,8 @@ type counterOptions struct {
readKey string
messagesFilter query.Filter
readMessages func(newOldestOrderId string, idsModified []string)
readMessages func(newOldestOrderId string, idsModified []string)
unreadMessages func(newOldestOrderId string, lastAddedAt int64, msgIds []string)
}
func (o *counterOptions) readModifier(value bool) query.Modifier {
@ -55,6 +56,15 @@ func newCounterOptions(counterType CounterType, subscription *subscription) *cou
})
subscription.updateMessageRead(idsModified, true)
}
opts.unreadMessages = func(newOldestOrderId string, lastAddedAt int64, msgIds []string) {
subscription.updateChatState(func(state *model.ChatState) {
state.Messages.OldestOrderId = newOldestOrderId
state.DbTimestamp = int64(lastAddedAt)
})
subscription.updateMessageRead(msgIds, false)
}
case CounterTypeMention:
opts.unreadFilter = query.And{
query.Key{Path: []string{hasMentionKey}, Filter: query.NewComp(query.CompOpEq, true)},
@ -70,6 +80,14 @@ func newCounterOptions(counterType CounterType, subscription *subscription) *cou
})
subscription.updateMentionRead(idsModified, true)
}
opts.unreadMessages = func(newOldestOrderId string, lastAddedAt int64, msgIds []string) {
subscription.updateChatState(func(state *model.ChatState) {
state.Mentions.OldestOrderId = newOldestOrderId
state.DbTimestamp = int64(lastAddedAt)
})
subscription.updateMentionRead(msgIds, false)
}
default:
panic("unknown counter type")
}
@ -127,11 +145,7 @@ func (s *storeObject) MarkMessagesAsUnread(ctx context.Context, afterOrderId str
return fmt.Errorf("get last added date: %w", err)
}
s.subscription.updateChatState(func(state *model.ChatState) {
state.Messages.OldestOrderId = newOldestOrderId
state.DbTimestamp = int64(lastAdded)
})
s.subscription.updateMessageRead(msgs, false)
opts.unreadMessages(newOldestOrderId, lastAdded, msgs)
s.subscription.flush()
seenHeads, err := s.seenHeadsCollector.collectSeenHeads(ctx, afterOrderId)
@ -286,7 +300,7 @@ func (s *storeObject) countUnreadMessages(txn anystore.ReadTx, opts *counterOpti
return unreadQuery.Limit(1).Count(txn.Context())
}
func (s *storeObject) getLastAddedDate(txn anystore.ReadTx) (int, error) {
func (s *storeObject) getLastAddedDate(txn anystore.ReadTx) (int64, error) {
lastAddedDate := s.collection.Find(nil).Sort(descAdded).Limit(1)
iter, err := lastAddedDate.Iter(txn.Context())
if err != nil {
@ -303,7 +317,7 @@ func (s *storeObject) getLastAddedDate(txn anystore.ReadTx) (int, error) {
if err != nil {
return 0, fmt.Errorf("unmarshal message: %w", err)
}
return int(msg.AddedAt), nil
return msg.AddedAt, nil
}
return 0, nil
}

View file

@ -1476,6 +1476,7 @@
- [Rpc.Chat.SubscribeLastMessages.Response.Error.Code](#anytype-Rpc-Chat-SubscribeLastMessages-Response-Error-Code)
- [Rpc.Chat.SubscribeToMessagePreviews.Response.Error.Code](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error-Code)
- [Rpc.Chat.ToggleMessageReaction.Response.Error.Code](#anytype-Rpc-Chat-ToggleMessageReaction-Response-Error-Code)
- [Rpc.Chat.Unread.ReadType](#anytype-Rpc-Chat-Unread-ReadType)
- [Rpc.Chat.Unread.Response.Error.Code](#anytype-Rpc-Chat-Unread-Response-Error-Code)
- [Rpc.Chat.Unsubscribe.Response.Error.Code](#anytype-Rpc-Chat-Unsubscribe-Response-Error-Code)
- [Rpc.Chat.UnsubscribeFromMessagePreviews.Response.Error.Code](#anytype-Rpc-Chat-UnsubscribeFromMessagePreviews-Response-Error-Code)
@ -11080,7 +11081,8 @@ Get marks list in the selected range in text block.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| chatObjectId | [string](#string) | | id of the chat object |
| type | [Rpc.Chat.Unread.ReadType](#anytype-Rpc-Chat-Unread-ReadType) | | |
| chatObjectId | [string](#string) | | |
| afterOrderId | [string](#string) | | |
@ -23598,8 +23600,8 @@ Middleware-to-front-end response, that can contain a NULL error or a non-NULL er
| Name | Number | Description |
| ---- | ------ | ----------- |
| messages | 0 | |
| replies | 1 | |
| Messages | 0 | |
| Mentions | 1 | |
@ -23656,6 +23658,18 @@ Middleware-to-front-end response, that can contain a NULL error or a non-NULL er
<a name="anytype-Rpc-Chat-Unread-ReadType"></a>
### Rpc.Chat.Unread.ReadType
| Name | Number | Description |
| ---- | ------ | ----------- |
| Messages | 0 | |
| Mentions | 1 | |
<a name="anytype-Rpc-Chat-Unread-Response-Error-Code"></a>
### Rpc.Chat.Unread.Response.Error.Code

File diff suppressed because it is too large Load diff

View file

@ -8283,9 +8283,10 @@ message Rpc {
message ReadMessages {
enum ReadType {
messages = 0;
replies = 1;
Messages = 0;
Mentions = 1;
}
message Request {
ReadType type = 1;
string chatObjectId = 2; // id of the chat object
@ -8314,8 +8315,14 @@ message Rpc {
}
message Unread {
enum ReadType {
Messages = 0;
Mentions = 1;
}
message Request {
string chatObjectId = 2; // id of the chat object
ReadType type = 1;
string chatObjectId = 2;
string afterOrderId = 3;
}