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

GO-5344: Refactor working with message model

This commit is contained in:
Sergey 2025-03-26 14:31:29 +01:00
parent 62742dd41d
commit cce319c20d
No known key found for this signature in database
GPG key ID: 3B6BEF79160221C6
9 changed files with 741 additions and 707 deletions

View file

@ -228,7 +228,7 @@ func (s *service) AddMessage(ctx context.Context, sessionCtx session.Context, ch
var messageId string
err := cache.Do(s.objectGetter, chatObjectId, func(sb chatobject.StoreObject) error {
var err error
messageId, err = sb.AddMessage(ctx, sessionCtx, message)
messageId, err = sb.AddMessage(ctx, sessionCtx, &chatobject.Message{ChatMessage: message})
return err
})
return messageId, err
@ -236,7 +236,7 @@ func (s *service) AddMessage(ctx context.Context, sessionCtx session.Context, ch
func (s *service) EditMessage(ctx context.Context, chatObjectId string, messageId string, newMessage *model.ChatMessage) error {
return cache.Do(s.objectGetter, chatObjectId, func(sb chatobject.StoreObject) error {
return sb.EditMessage(ctx, messageId, newMessage)
return sb.EditMessage(ctx, messageId, &chatobject.Message{ChatMessage: newMessage})
})
}

View file

@ -44,45 +44,46 @@ func (d *ChatHandler) Init(ctx context.Context, s *storestate.StoreState) (err e
return
}
func (d *ChatHandler) BeforeCreate(ctx context.Context, ch storestate.ChangeOp) (err error) {
msg := newMessageWrapper(ch.Arena, ch.Value)
msg.setCreatedAt(ch.Change.Timestamp)
msg.setCreator(ch.Change.Creator)
func (d *ChatHandler) BeforeCreate(ctx context.Context, ch storestate.ChangeOp) error {
msg := unmarshalMessage(ch.Value)
msg.CreatedAt = ch.Change.Timestamp
msg.Creator = ch.Change.Creator
if d.forceNotRead {
msg.setRead(false)
msg.setMentionRead(false)
msg.Read = false
msg.MentionRead = false
} else {
if ch.Change.Creator == d.currentIdentity {
// msg.setRead(true)
msg.setRead(false)
msg.setMentionRead(false)
// TODO Return to true
msg.Read = false
msg.MentionRead = false
} else {
msg.setRead(false)
msg.setMentionRead(false)
msg.Read = false
msg.MentionRead = false
}
}
msg.setAddedAt(timeid.NewNano())
msgModel := msg.toModel()
msg.AddedAt = timeid.NewNano()
hasMention := false
for _, mark := range msgModel.Message.Marks {
msg.HasMention = false
for _, mark := range msg.Message.Marks {
if mark.Type == model.BlockContentTextMark_Mention && mark.Param == d.myParticipantId {
hasMention = true
msg.HasMention = true
break
}
}
msg.setHasMention(hasMention)
msgModel.OrderId = ch.Change.Order
msg.OrderId = ch.Change.Order
prevOrderId, err := getPrevOrderId(ctx, d.collection, ch.Change.Order)
if err != nil {
return fmt.Errorf("get prev order id: %w", err)
}
d.subscription.add(prevOrderId, msgModel)
return
d.subscription.add(prevOrderId, msg)
msg.MarshalAnyenc(ch.Value, ch.Arena)
return nil
}
func (d *ChatHandler) BeforeModify(ctx context.Context, ch storestate.ChangeOp) (mode storestate.ModifyMode, err error) {
@ -102,8 +103,8 @@ func (d *ChatHandler) BeforeDelete(ctx context.Context, ch storestate.ChangeOp)
return storestate.DeleteModeDelete, fmt.Errorf("get message: %w", err)
}
message := newMessageWrapper(ch.Arena, doc.Value())
if message.getCreator() != ch.Change.Creator {
message := unmarshalMessage(doc.Value())
if message.Creator != ch.Change.Creator {
return storestate.DeleteModeDelete, errors.New("can't delete not own message")
}
@ -125,8 +126,7 @@ func (d *ChatHandler) UpgradeKeyModifier(ch storestate.ChangeOp, key *pb.KeyModi
}
if modified {
msg := newMessageWrapper(a, result)
model := msg.toModel()
msg := unmarshalMessage(result)
switch path {
case reactionsKey:
@ -137,15 +137,15 @@ func (d *ChatHandler) UpgradeKeyModifier(ch storestate.ChangeOp, key *pb.KeyModi
}
// TODO Count validation
d.subscription.updateReactions(model)
d.subscription.updateReactions(msg)
case contentKey:
creator := model.Creator
creator := msg.Creator
if creator != ch.Change.Creator {
return v, false, errors.Join(storestate.ErrValidation, fmt.Errorf("can't modify someone else's message"))
}
result.Set(modifiedAtKey, a.NewNumberInt(int(ch.Change.Timestamp)))
model.ModifiedAt = ch.Change.Timestamp
d.subscription.updateFull(model)
msg.ModifiedAt = ch.Change.Timestamp
msg.MarshalAnyenc(result, a)
d.subscription.updateFull(msg)
default:
return nil, false, fmt.Errorf("invalid key path %s", key.KeyPath)
}

View file

@ -12,6 +12,7 @@ import (
"github.com/anyproto/any-store/query"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/util/slice"
"github.com/samber/lo"
"golang.org/x/exp/slices"
"github.com/anyproto/anytype-heart/core/block/editor/anystoredebug"
@ -42,10 +43,10 @@ type StoreObject interface {
smartblock.SmartBlock
anystoredebug.AnystoreDebug
AddMessage(ctx context.Context, sessionCtx session.Context, message *model.ChatMessage) (string, error)
AddMessage(ctx context.Context, sessionCtx session.Context, message *Message) (string, error)
GetMessages(ctx context.Context, req GetMessagesRequest) (*GetMessagesResponse, error)
GetMessagesByIds(ctx context.Context, messageIds []string) ([]*model.ChatMessage, error)
EditMessage(ctx context.Context, messageId string, newMessage *model.ChatMessage) error
EditMessage(ctx context.Context, messageId string, newMessage *Message) error
ToggleMessageReaction(ctx context.Context, messageId string, emoji string) error
DeleteMessage(ctx context.Context, messageId string) error
SubscribeLastMessages(ctx context.Context, subId string, limit int, asyncInit bool) (*SubscribeLastMessagesResponse, error)
@ -174,7 +175,7 @@ func (s *storeObject) GetMessagesByIds(ctx context.Context, messageIds []string)
if err != nil {
return nil, fmt.Errorf("start read tx: %w", err)
}
messages := make([]*model.ChatMessage, 0, len(messageIds))
messages := make([]*Message, 0, len(messageIds))
for _, messageId := range messageIds {
obj, err := s.collection.FindId(txn.Context(), messageId)
if errors.Is(err, anystore.ErrDocNotFound) {
@ -183,10 +184,9 @@ func (s *storeObject) GetMessagesByIds(ctx context.Context, messageIds []string)
if err != nil {
return nil, errors.Join(txn.Commit(), fmt.Errorf("find id: %w", err))
}
msg := newMessageWrapper(nil, obj.Value())
messages = append(messages, msg.toModel())
messages = append(messages, unmarshalMessage(obj.Value()))
}
return messages, txn.Commit()
return messagesToProto(messages), txn.Commit()
}
type GetMessagesResponse struct {
@ -221,12 +221,14 @@ func (s *storeObject) GetMessages(ctx context.Context, req GetMessagesRequest) (
})
return &GetMessagesResponse{
Messages: msgs,
Messages: lo.Map(msgs, func(item *Message, index int) *model.ChatMessage {
return item.ChatMessage
}),
ChatState: s.subscription.getChatState(),
}, nil
}
func (s *storeObject) queryMessages(ctx context.Context, query anystore.Query) ([]*model.ChatMessage, error) {
func (s *storeObject) queryMessages(ctx context.Context, query anystore.Query) ([]*Message, error) {
arena := s.arenaPool.Get()
defer func() {
arena.Reset()
@ -239,27 +241,28 @@ func (s *storeObject) queryMessages(ctx context.Context, query anystore.Query) (
}
defer iter.Close()
var res []*model.ChatMessage
var res []*Message
for iter.Next() {
doc, err := iter.Doc()
if err != nil {
return nil, fmt.Errorf("get doc: %w", err)
}
message := newMessageWrapper(arena, doc.Value()).toModel()
res = append(res, message)
res = append(res, unmarshalMessage(doc.Value()))
}
return res, nil
}
func (s *storeObject) AddMessage(ctx context.Context, sessionCtx session.Context, message *model.ChatMessage) (string, error) {
func (s *storeObject) AddMessage(ctx context.Context, sessionCtx session.Context, message *Message) (string, error) {
arena := s.arenaPool.Get()
defer func() {
arena.Reset()
s.arenaPool.Put(arena)
}()
message.Read = true
obj := marshalModel(arena, message)
obj := arena.NewObject()
message.MarshalAnyenc(obj, arena)
builder := storestate.Builder{}
err := builder.Create(collectionName, storestate.IdFromChange, obj)
@ -293,13 +296,15 @@ func (s *storeObject) DeleteMessage(ctx context.Context, messageId string) error
return nil
}
func (s *storeObject) EditMessage(ctx context.Context, messageId string, newMessage *model.ChatMessage) error {
func (s *storeObject) EditMessage(ctx context.Context, messageId string, newMessage *Message) error {
arena := s.arenaPool.Get()
defer func() {
arena.Reset()
s.arenaPool.Put(arena)
}()
obj := marshalModel(arena, newMessage)
obj := arena.NewObject()
newMessage.MarshalAnyenc(obj, arena)
builder := storestate.Builder{}
err := builder.Modify(collectionName, messageId, []string{contentKey}, pb.ModifyOp_Set, obj.Get(contentKey))
@ -361,9 +366,8 @@ func (s *storeObject) hasMyReaction(ctx context.Context, arena *anyenc.Arena, me
}
myIdentity := s.accountService.AccountID()
msg := newMessageWrapper(arena, doc.Value())
reactions := msg.reactionsToModel()
if v, ok := reactions.GetReactions()[emoji]; ok {
msg := unmarshalMessage(doc.Value())
if v, ok := msg.GetReactions().GetReactions()[emoji]; ok {
if slices.Contains(v.GetIds(), myIdentity) {
return true, nil
}
@ -414,7 +418,7 @@ func (s *storeObject) SubscribeLastMessages(ctx context.Context, subId string, l
return nil, nil
} else {
return &SubscribeLastMessagesResponse{
Messages: messages,
Messages: messagesToProto(messages),
ChatState: s.subscription.getChatState(),
}, nil
}

View file

@ -19,53 +19,30 @@ const (
orderKey = "_o"
)
type messageWrapper struct {
val *anyenc.Value
arena *anyenc.Arena
type Message struct {
*model.ChatMessage
HasMention bool
}
func newMessageWrapper(arena *anyenc.Arena, val *anyenc.Value) *messageWrapper {
return &messageWrapper{arena: arena, val: val}
func unmarshalMessage(val *anyenc.Value) *Message {
return newMessageWrapper(val).toModel()
}
func (m *messageWrapper) getCreator() string {
return string(m.val.GetStringBytes(creatorKey))
type messageUnmarshaller struct {
val *anyenc.Value
}
func (m *messageWrapper) setCreator(v string) {
m.val.Set(creatorKey, m.arena.NewString(v))
}
func (m *messageWrapper) setRead(v bool) {
if v {
m.val.Set(readKey, m.arena.NewTrue())
} else {
m.val.Set(readKey, m.arena.NewFalse())
func messagesToProto(msgs []*Message) []*model.ChatMessage {
res := make([]*model.ChatMessage, 0, len(msgs))
for _, m := range msgs {
res = append(res, m.ChatMessage)
}
return res
}
func (m *messageWrapper) setHasMention(v bool) {
if v {
m.val.Set(hasMentionKey, m.arena.NewTrue())
} else {
m.val.Del(hasMentionKey)
}
}
func (m *messageWrapper) setMentionRead(v bool) {
if v {
m.val.Set(mentionReadKey, m.arena.NewTrue())
} else {
m.val.Set(mentionReadKey, m.arena.NewFalse())
}
}
func (m *messageWrapper) setCreatedAt(v int64) {
m.val.Set(createdAtKey, m.arena.NewNumberInt(int(v)))
}
func (m *messageWrapper) setAddedAt(v int64) {
m.val.Set(addedKey, m.arena.NewNumberInt(int(v)))
func newMessageWrapper(val *anyenc.Value) *messageUnmarshaller {
return &messageUnmarshaller{val: val}
}
/*
@ -102,12 +79,12 @@ func (m *messageWrapper) setAddedAt(v int64) {
*/
func marshalModel(arena *anyenc.Arena, msg *model.ChatMessage) *anyenc.Value {
func (m *Message) MarshalAnyenc(marshalTo *anyenc.Value, arena *anyenc.Arena) {
message := arena.NewObject()
message.Set("text", arena.NewString(msg.Message.Text))
message.Set("style", arena.NewNumberInt(int(msg.Message.Style)))
message.Set("text", arena.NewString(m.Message.Text))
message.Set("style", arena.NewNumberInt(int(m.Message.Style)))
marks := arena.NewArray()
for i, inMark := range msg.Message.Marks {
for i, inMark := range m.Message.Marks {
mark := arena.NewObject()
mark.Set("from", arena.NewNumberInt(int(inMark.Range.From)))
mark.Set("to", arena.NewNumberInt(int(inMark.Range.To)))
@ -120,7 +97,7 @@ func marshalModel(arena *anyenc.Arena, msg *model.ChatMessage) *anyenc.Value {
message.Set("marks", marks)
attachments := arena.NewObject()
for i, inAttachment := range msg.Attachments {
for i, inAttachment := range m.Attachments {
attachment := arena.NewObject()
attachment.Set("type", arena.NewNumberInt(int(inAttachment.Type)))
attachments.Set(inAttachment.Target, attachment)
@ -132,7 +109,7 @@ func marshalModel(arena *anyenc.Arena, msg *model.ChatMessage) *anyenc.Value {
content.Set("attachments", attachments)
reactions := arena.NewObject()
for emoji, inReaction := range msg.GetReactions().GetReactions() {
for emoji, inReaction := range m.GetReactions().GetReactions() {
identities := arena.NewArray()
for j, identity := range inReaction.Ids {
identities.SetArrayItem(j, arena.NewString(identity))
@ -140,41 +117,48 @@ func marshalModel(arena *anyenc.Arena, msg *model.ChatMessage) *anyenc.Value {
reactions.Set(emoji, identities)
}
root := arena.NewObject()
root.Set(creatorKey, arena.NewString(msg.Creator))
root.Set(createdAtKey, arena.NewNumberInt(int(msg.CreatedAt)))
root.Set(modifiedAtKey, arena.NewNumberInt(int(msg.ModifiedAt)))
root.Set("replyToMessageId", arena.NewString(msg.ReplyToMessageId))
root.Set(contentKey, content)
var read *anyenc.Value
if msg.Read {
read = arena.NewTrue()
marshalTo.Set("id", arena.NewString(m.Id))
marshalTo.Set(creatorKey, arena.NewString(m.Creator))
marshalTo.Set(createdAtKey, arena.NewNumberInt(int(m.CreatedAt)))
marshalTo.Set(modifiedAtKey, arena.NewNumberInt(int(m.ModifiedAt)))
marshalTo.Set("replyToMessageId", arena.NewString(m.ReplyToMessageId))
marshalTo.Set(contentKey, content)
marshalTo.Set(readKey, arenaNewBool(arena, m.Read))
marshalTo.Set(mentionReadKey, arenaNewBool(arena, m.MentionRead))
marshalTo.Set(hasMentionKey, arenaNewBool(arena, m.HasMention))
marshalTo.Set(reactionsKey, reactions)
}
func arenaNewBool(a *anyenc.Arena, value bool) *anyenc.Value {
if value {
return a.NewTrue()
} else {
read = arena.NewFalse()
}
root.Set(readKey, read)
root.Set(reactionsKey, reactions)
return root
}
func (m *messageWrapper) toModel() *model.ChatMessage {
return &model.ChatMessage{
Id: string(m.val.GetStringBytes("id")),
Creator: string(m.val.GetStringBytes(creatorKey)),
CreatedAt: int64(m.val.GetInt(createdAtKey)),
ModifiedAt: int64(m.val.GetInt(modifiedAtKey)),
AddedAt: int64(m.val.GetInt(addedKey)),
OrderId: string(m.val.GetStringBytes("_o", "id")),
ReplyToMessageId: string(m.val.GetStringBytes("replyToMessageId")),
Message: m.contentToModel(),
Read: m.val.GetBool(readKey),
Attachments: m.attachmentsToModel(),
Reactions: m.reactionsToModel(),
return a.NewFalse()
}
}
func (m *messageWrapper) contentToModel() *model.ChatMessageMessageContent {
func (m *messageUnmarshaller) toModel() *Message {
return &Message{
ChatMessage: &model.ChatMessage{
Id: string(m.val.GetStringBytes("id")),
Creator: string(m.val.GetStringBytes(creatorKey)),
CreatedAt: int64(m.val.GetInt(createdAtKey)),
ModifiedAt: int64(m.val.GetInt(modifiedAtKey)),
AddedAt: int64(m.val.GetInt(addedKey)),
OrderId: string(m.val.GetStringBytes("_o", "id")),
ReplyToMessageId: string(m.val.GetStringBytes("replyToMessageId")),
Message: m.contentToModel(),
Read: m.val.GetBool(readKey),
MentionRead: m.val.GetBool(mentionReadKey),
Attachments: m.attachmentsToModel(),
Reactions: m.reactionsToModel(),
},
HasMention: m.val.GetBool(hasMentionKey),
}
}
func (m *messageUnmarshaller) contentToModel() *model.ChatMessageMessageContent {
inMarks := m.val.GetArray(contentKey, "message", "marks")
marks := make([]*model.BlockContentTextMark, 0, len(inMarks))
for _, inMark := range inMarks {
@ -195,7 +179,7 @@ func (m *messageWrapper) contentToModel() *model.ChatMessageMessageContent {
}
}
func (m *messageWrapper) attachmentsToModel() []*model.ChatMessageAttachment {
func (m *messageUnmarshaller) attachmentsToModel() []*model.ChatMessageAttachment {
inAttachments := m.val.GetObject(contentKey, "attachments")
var attachments []*model.ChatMessageAttachment
if inAttachments != nil {
@ -210,7 +194,7 @@ func (m *messageWrapper) attachmentsToModel() []*model.ChatMessageAttachment {
return attachments
}
func (m *messageWrapper) reactionsToModel() *model.ChatMessageReactions {
func (m *messageUnmarshaller) reactionsToModel() *model.ChatMessageReactions {
inReactions := m.val.GetObject(reactionsKey)
reactions := &model.ChatMessageReactions{
Reactions: map[string]*model.ChatMessageReactionsIdentityList{},

View file

@ -275,7 +275,10 @@ func (s *storeObject) getOldestOrderId(txn anystore.ReadTx, opts *counterOptions
if err != nil {
return "", fmt.Errorf("get doc: %w", err)
}
return doc.Value().GetObject(orderKey).Get("id").GetString(), nil
orders := doc.Value().GetObject(orderKey)
if orders != nil {
return orders.Get("id").GetString(), nil
}
}
return "", nil
}

View file

@ -125,7 +125,7 @@ func (s *subscription) getIdentityDetails(identity string) (*domain.Details, err
return details, nil
}
func (s *subscription) add(prevOrderId string, message *model.ChatMessage) {
func (s *subscription) add(prevOrderId string, message *Message) {
s.updateChatState(func(state *model.ChatState) {
if !message.Read {
if message.OrderId < state.Messages.OldestOrderId || state.Messages.OldestOrderId == "" {
@ -155,7 +155,7 @@ func (s *subscription) add(prevOrderId string, message *model.ChatMessage) {
ev := &pb.EventChatAdd{
Id: message.Id,
Message: message,
Message: message.ChatMessage,
OrderId: message.OrderId,
AfterOrderId: prevOrderId,
SubIds: slices.Clone(s.ids),
@ -193,13 +193,13 @@ func (s *subscription) delete(messageId string) {
}))
}
func (s *subscription) updateFull(message *model.ChatMessage) {
func (s *subscription) updateFull(message *Message) {
if !s.canSend() {
return
}
ev := &pb.EventChatUpdate{
Id: message.Id,
Message: message,
Message: message.ChatMessage,
SubIds: slices.Clone(s.ids),
}
s.eventsBuffer = append(s.eventsBuffer, event.NewMessage(s.spaceId, &pb.EventMessageValueOfChatUpdate{
@ -207,7 +207,7 @@ func (s *subscription) updateFull(message *model.ChatMessage) {
}))
}
func (s *subscription) updateReactions(message *model.ChatMessage) {
func (s *subscription) updateReactions(message *Message) {
if !s.canSend() {
return
}

View file

@ -30669,6 +30669,7 @@ Used to decode block meta only, without the content itself
| attachments | [ChatMessage.Attachment](#anytype-model-ChatMessage-Attachment) | repeated | Attachments slice |
| reactions | [ChatMessage.Reactions](#anytype-model-ChatMessage-Reactions) | | Reactions to the message |
| read | [bool](#bool) | | Message read status |
| mentionRead | [bool](#bool) | | |

File diff suppressed because it is too large Load diff

View file

@ -1408,6 +1408,7 @@ message ChatMessage {
repeated Attachment attachments = 7; // Attachments slice
Reactions reactions = 8; // Reactions to the message
bool read = 10; // Message read status
bool mentionRead = 12;
message MessageContent {
string text = 1; // The text content of the message part
Block.Content.Text.Style style = 2; // The style/type of the message part