feat(messages): sync saved message tags
This commit is contained in:
parent
a785ae7491
commit
6b3eba6c5d
42 changed files with 1581 additions and 551 deletions
|
|
@ -632,54 +632,6 @@ func (s *ChannelStore) ClearRecentMessageReactions(_ context.Context, userID int
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ListSavedReactionTags(_ context.Context, userID int64, limit int) ([]domain.SavedReactionTag, error) {
|
||||
if userID == 0 {
|
||||
return nil, domain.ErrChannelInvalid
|
||||
}
|
||||
if limit <= 0 {
|
||||
return []domain.SavedReactionTag{}, nil
|
||||
}
|
||||
if limit > domain.MaxSavedReactionTags {
|
||||
limit = domain.MaxSavedReactionTags
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
rows := make([]domain.SavedReactionTag, 0, len(s.savedTags[userID]))
|
||||
for _, row := range s.savedTags[userID] {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool {
|
||||
if rows[i].Count != rows[j].Count {
|
||||
return rows[i].Count > rows[j].Count
|
||||
}
|
||||
if rows[i].Reaction.Type != rows[j].Reaction.Type {
|
||||
return rows[i].Reaction.Type < rows[j].Reaction.Type
|
||||
}
|
||||
return rows[i].Reaction.Value() < rows[j].Reaction.Value()
|
||||
})
|
||||
if len(rows) > limit {
|
||||
rows = rows[:limit]
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) UpsertSavedReactionTag(_ context.Context, tag domain.SavedReactionTag) error {
|
||||
if tag.UserID == 0 || tag.Reaction.Type != domain.MessageReactionEmoji || strings.TrimSpace(tag.Reaction.Emoticon) == "" {
|
||||
return domain.ErrChannelInvalid
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.savedTags[tag.UserID] == nil {
|
||||
s.savedTags[tag.UserID] = make(map[string]domain.SavedReactionTag)
|
||||
}
|
||||
tag.Reaction.Emoticon = strings.TrimSpace(tag.Reaction.Emoticon)
|
||||
if tag.Count < 0 {
|
||||
tag.Count = 0
|
||||
}
|
||||
s.savedTags[tag.UserID][messageReactionKey(tag.Reaction)] = tag
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ListChannelUnreadReactions(_ context.Context, viewerUserID int64, filter domain.ChannelUnreadReactionsFilter) (domain.ChannelHistory, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ type ChannelStore struct {
|
|||
paidReactions map[int64]map[int]map[int64]memoryPaidReaction
|
||||
top map[int64]map[string]domain.TopMessageReaction
|
||||
recent map[int64]map[string]domain.RecentMessageReaction
|
||||
savedTags map[int64]map[string]domain.SavedReactionTag
|
||||
mentions map[int64]map[int64]map[int]memoryMention
|
||||
msgViews map[int64]map[int]int
|
||||
msgViewers map[int64]map[int]map[int64]struct{}
|
||||
|
|
@ -124,7 +123,6 @@ func NewChannelStore() *ChannelStore {
|
|||
paidReactions: make(map[int64]map[int]map[int64]memoryPaidReaction),
|
||||
top: make(map[int64]map[string]domain.TopMessageReaction),
|
||||
recent: make(map[int64]map[string]domain.RecentMessageReaction),
|
||||
savedTags: make(map[int64]map[string]domain.SavedReactionTag),
|
||||
mentions: make(map[int64]map[int64]map[int]memoryMention),
|
||||
msgViews: make(map[int64]map[int]int),
|
||||
msgViewers: make(map[int64]map[int]map[int64]struct{}),
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ func (s *MessageStore) finishMemoryDeleteLocked(res domain.DeleteMessagesResult,
|
|||
idsByOwner := make(map[int64][]int)
|
||||
peersByOwner := make(map[int64]map[domain.Peer]struct{})
|
||||
for _, row := range deleted {
|
||||
if byMessage := s.savedMessageTags[row.userID]; byMessage != nil {
|
||||
delete(byMessage, row.id)
|
||||
if len(byMessage) == 0 {
|
||||
delete(s.savedMessageTags, row.userID)
|
||||
}
|
||||
}
|
||||
idsByOwner[row.userID] = append(idsByOwner[row.userID], row.id)
|
||||
if peersByOwner[row.userID] == nil {
|
||||
peersByOwner[row.userID] = make(map[domain.Peer]struct{})
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import (
|
|||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"telesrv/internal/domain"
|
||||
"time"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func (s *MessageStore) GetByIDs(_ context.Context, userID int64, ids []int) (domain.MessageList, error) {
|
||||
|
|
@ -282,6 +283,12 @@ func filterMessageList(messages []domain.Message, filter domain.MessageFilter) d
|
|||
if query != "" && !strings.Contains(strings.ToLower(msg.Body), query) {
|
||||
continue
|
||||
}
|
||||
if filter.MinDate > 0 && msg.Date <= filter.MinDate {
|
||||
continue
|
||||
}
|
||||
if filter.MaxDate > 0 && msg.Date >= filter.MaxDate {
|
||||
continue
|
||||
}
|
||||
if filter.MaxID > 0 && msg.ID >= filter.MaxID {
|
||||
continue
|
||||
}
|
||||
|
|
@ -297,6 +304,9 @@ func filterMessageList(messages []domain.Message, filter domain.MessageFilter) d
|
|||
if filter.SavedPeer.ID != 0 && msg.SavedPeer != filter.SavedPeer {
|
||||
continue
|
||||
}
|
||||
if len(filter.SavedReactions) > 0 && !messageHasAnySavedTag(msg, filter.SavedReactions) {
|
||||
continue
|
||||
}
|
||||
base = append(base, msg)
|
||||
}
|
||||
|
||||
|
|
@ -316,6 +326,22 @@ func filterMessageList(messages []domain.Message, filter domain.MessageFilter) d
|
|||
}
|
||||
}
|
||||
|
||||
func messageHasAnySavedTag(msg domain.Message, wanted []domain.MessageReaction) bool {
|
||||
if msg.Reactions == nil || !msg.Reactions.AsTags {
|
||||
return false
|
||||
}
|
||||
have := make(map[string]struct{}, len(msg.Reactions.Results))
|
||||
for _, result := range msg.Reactions.Results {
|
||||
have[result.Reaction.Key()] = struct{}{}
|
||||
}
|
||||
for _, reaction := range wanted {
|
||||
if _, ok := have[reaction.Key()]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pageMessageHistory(base []domain.Message, filter domain.MessageFilter, limit int) []domain.Message {
|
||||
if limit <= 0 || len(base) == 0 {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ func (s *MessageStore) SetMessageReactions(_ context.Context, req domain.SetPriv
|
|||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if req.Peer.ID == req.UserID {
|
||||
return s.setSavedMessageTagsLocked(req)
|
||||
}
|
||||
var target domain.Message
|
||||
for _, msg := range s.m[req.UserID] {
|
||||
if msg.ID == req.MessageID && msg.Peer == req.Peer {
|
||||
|
|
@ -119,6 +122,10 @@ func (s *MessageStore) privateReactionResultLocked(uid int64) domain.PrivateMess
|
|||
}
|
||||
|
||||
func (s *MessageStore) privateMessageReactionsForMessageLocked(msg domain.Message) domain.ChannelMessageReactions {
|
||||
if msg.OwnerUserID != 0 &&
|
||||
msg.Peer == (domain.Peer{Type: domain.PeerTypeUser, ID: msg.OwnerUserID}) {
|
||||
return s.savedMessageTagsForMessageLocked(msg)
|
||||
}
|
||||
reactions := s.privateMessageReactionsLocked(msg.UID, msg.OwnerUserID)
|
||||
if len(reactions.Recent) == 0 || msg.From.ID == 0 {
|
||||
return reactions
|
||||
|
|
@ -232,6 +239,11 @@ func writeMessageReactionsHash(h hash.Hash64, reactions *domain.ChannelMessageRe
|
|||
return
|
||||
}
|
||||
var buf [16]byte
|
||||
if reactions.AsTags {
|
||||
_, _ = h.Write([]byte{1})
|
||||
} else {
|
||||
_, _ = h.Write([]byte{0})
|
||||
}
|
||||
for _, item := range reactions.Results {
|
||||
_, _ = h.Write([]byte(item.Reaction.Type))
|
||||
_, _ = h.Write([]byte{0})
|
||||
|
|
|
|||
159
internal/store/memory/message_saved_reactions.go
Normal file
159
internal/store/memory/message_saved_reactions.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func (s *MessageStore) setSavedMessageTagsLocked(req domain.SetPrivateMessageReactionsRequest) (domain.PrivateMessageReactionsResult, error) {
|
||||
var target domain.Message
|
||||
for _, msg := range s.m[req.UserID] {
|
||||
if msg.ID == req.MessageID &&
|
||||
msg.Peer == (domain.Peer{Type: domain.PeerTypeUser, ID: req.UserID}) {
|
||||
target = msg
|
||||
break
|
||||
}
|
||||
}
|
||||
if target.ID == 0 {
|
||||
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
||||
}
|
||||
for _, reaction := range req.Reactions {
|
||||
if !reaction.Valid() {
|
||||
return domain.PrivateMessageReactionsResult{}, domain.ErrReactionInvalid
|
||||
}
|
||||
}
|
||||
if len(req.Reactions) == 0 {
|
||||
if byMessage := s.savedMessageTags[req.UserID]; byMessage != nil {
|
||||
delete(byMessage, target.ID)
|
||||
if len(byMessage) == 0 {
|
||||
delete(s.savedMessageTags, req.UserID)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if s.savedMessageTags[req.UserID] == nil {
|
||||
s.savedMessageTags[req.UserID] = make(map[int][]domain.MessageReaction)
|
||||
}
|
||||
s.savedMessageTags[req.UserID][target.ID] = append([]domain.MessageReaction(nil), req.Reactions...)
|
||||
}
|
||||
item := cloneMessage(target)
|
||||
reactions := s.savedMessageTagsForMessageLocked(item)
|
||||
item.Reactions = cloneChannelMessageReactionsPtr(&reactions)
|
||||
return domain.PrivateMessageReactionsResult{
|
||||
Messages: []domain.Message{item},
|
||||
Reactions: reactions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *MessageStore) savedMessageTagsForMessageLocked(msg domain.Message) domain.ChannelMessageReactions {
|
||||
out := domain.ChannelMessageReactions{
|
||||
AsTags: true,
|
||||
Results: []domain.ChannelMessageReactionCount{},
|
||||
Recent: []domain.ChannelMessagePeerReaction{},
|
||||
}
|
||||
for i, reaction := range s.savedMessageTags[msg.OwnerUserID][msg.ID] {
|
||||
out.Results = append(out.Results, domain.ChannelMessageReactionCount{
|
||||
Reaction: reaction,
|
||||
Count: 1,
|
||||
ChosenOrder: i + 1,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *MessageStore) ListSavedReactionTags(_ context.Context, req domain.SavedReactionTagsRequest) ([]domain.SavedReactionTag, error) {
|
||||
if req.UserID == 0 {
|
||||
return nil, domain.ErrReactionInvalid
|
||||
}
|
||||
if req.Limit <= 0 || req.Limit > domain.MaxSavedReactionTags {
|
||||
req.Limit = domain.MaxSavedReactionTags
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
visible := make(map[int]domain.Message, len(s.m[req.UserID]))
|
||||
for _, msg := range s.m[req.UserID] {
|
||||
if msg.Peer == (domain.Peer{Type: domain.PeerTypeUser, ID: req.UserID}) {
|
||||
visible[msg.ID] = msg
|
||||
}
|
||||
}
|
||||
byKey := make(map[string]domain.SavedReactionTag)
|
||||
for messageID, reactions := range s.savedMessageTags[req.UserID] {
|
||||
msg, ok := visible[messageID]
|
||||
if !ok || (req.SavedPeer.ID != 0 && msg.SavedPeer != req.SavedPeer) {
|
||||
continue
|
||||
}
|
||||
for _, reaction := range reactions {
|
||||
key := reaction.Key()
|
||||
tag := byKey[key]
|
||||
tag.UserID = req.UserID
|
||||
tag.Reaction = reaction
|
||||
tag.Count++
|
||||
if req.SavedPeer.ID == 0 {
|
||||
tag.Title = s.savedTagTitles[req.UserID][key]
|
||||
}
|
||||
byKey[key] = tag
|
||||
}
|
||||
}
|
||||
out := make([]domain.SavedReactionTag, 0, len(byKey))
|
||||
for _, tag := range byKey {
|
||||
out = append(out, tag)
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].Count != out[j].Count {
|
||||
return out[i].Count > out[j].Count
|
||||
}
|
||||
return out[i].Reaction.Key() > out[j].Reaction.Key()
|
||||
})
|
||||
if len(out) > req.Limit {
|
||||
out = out[:req.Limit]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *MessageStore) UpsertSavedReactionTag(_ context.Context, tag domain.SavedReactionTag) error {
|
||||
if tag.UserID == 0 || !tag.Reaction.Valid() {
|
||||
return domain.ErrReactionInvalid
|
||||
}
|
||||
key := tag.Reaction.Key()
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
found := false
|
||||
for messageID, reactions := range s.savedMessageTags[tag.UserID] {
|
||||
alive := false
|
||||
for _, msg := range s.m[tag.UserID] {
|
||||
if msg.ID == messageID &&
|
||||
msg.Peer == (domain.Peer{Type: domain.PeerTypeUser, ID: tag.UserID}) {
|
||||
alive = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !alive {
|
||||
continue
|
||||
}
|
||||
for _, reaction := range reactions {
|
||||
if reaction.Key() == key {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return domain.ErrReactionInvalid
|
||||
}
|
||||
if tag.Title == "" {
|
||||
if titles := s.savedTagTitles[tag.UserID]; titles != nil {
|
||||
delete(titles, key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if s.savedTagTitles[tag.UserID] == nil {
|
||||
s.savedTagTitles[tag.UserID] = make(map[string]string)
|
||||
}
|
||||
s.savedTagTitles[tag.UserID][key] = tag.Title
|
||||
return nil
|
||||
}
|
||||
150
internal/store/memory/message_saved_reactions_test.go
Normal file
150
internal/store/memory/message_saved_reactions_test.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func TestSavedMessageTagsAssignmentCountsSearchAndDelete(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
const userID int64 = 1001
|
||||
self := domain.Peer{Type: domain.PeerTypeUser, ID: userID}
|
||||
peerA := domain.Peer{Type: domain.PeerTypeUser, ID: 2001}
|
||||
peerB := domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}
|
||||
thumb := domain.MessageReaction{Type: domain.MessageReactionEmoji, Emoticon: "👍"}
|
||||
custom := domain.MessageReaction{Type: domain.MessageReactionCustomEmoji, DocumentID: 90001}
|
||||
|
||||
store := NewMessageStore()
|
||||
create := func(body string, savedPeer domain.Peer) domain.Message {
|
||||
msg, err := store.Create(ctx, domain.Message{
|
||||
OwnerUserID: userID,
|
||||
Peer: self,
|
||||
From: self,
|
||||
SavedPeer: savedPeer,
|
||||
Date: 1_700_000_000,
|
||||
Body: body,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create saved message: %v", err)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
first := create("first", peerA)
|
||||
second := create("second", peerA)
|
||||
third := create("third", peerB)
|
||||
|
||||
set := func(msg domain.Message, reactions ...domain.MessageReaction) {
|
||||
t.Helper()
|
||||
result, err := store.SetMessageReactions(ctx, domain.SetPrivateMessageReactionsRequest{
|
||||
UserID: userID,
|
||||
Peer: self,
|
||||
MessageID: msg.ID,
|
||||
Reactions: reactions,
|
||||
ReactionsPerUserMax: 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("set saved tags for %d: %v", msg.ID, err)
|
||||
}
|
||||
if len(result.Messages) != 1 || result.Messages[0].Reactions == nil ||
|
||||
!result.Messages[0].Reactions.AsTags {
|
||||
t.Fatalf("saved tag result = %+v, want one reactions_as_tags message", result)
|
||||
}
|
||||
}
|
||||
set(first, thumb)
|
||||
set(second, thumb, custom)
|
||||
set(third, custom)
|
||||
if got := store.nextPts[userID]; got != 0 {
|
||||
t.Fatalf("tag mutations pts = %d, want 0", got)
|
||||
}
|
||||
|
||||
if err := store.UpsertSavedReactionTag(ctx, domain.SavedReactionTag{
|
||||
UserID: userID, Reaction: thumb, Title: "Fav",
|
||||
}); err != nil {
|
||||
t.Fatalf("rename saved tag: %v", err)
|
||||
}
|
||||
global, err := store.ListSavedReactionTags(ctx, domain.SavedReactionTagsRequest{UserID: userID, Limit: 100})
|
||||
if err != nil {
|
||||
t.Fatalf("list global saved tags: %v", err)
|
||||
}
|
||||
assertMemorySavedTag(t, global, thumb, 2, "Fav")
|
||||
assertMemorySavedTag(t, global, custom, 2, "")
|
||||
|
||||
perPeer, err := store.ListSavedReactionTags(ctx, domain.SavedReactionTagsRequest{
|
||||
UserID: userID, SavedPeer: peerA, Limit: 100,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("list per-peer saved tags: %v", err)
|
||||
}
|
||||
assertMemorySavedTag(t, perPeer, thumb, 2, "")
|
||||
assertMemorySavedTag(t, perPeer, custom, 1, "")
|
||||
|
||||
found, err := store.ListByUser(ctx, userID, domain.MessageFilter{
|
||||
HasPeer: true,
|
||||
Peer: self,
|
||||
SavedPeer: peerA,
|
||||
SavedReactions: []domain.MessageReaction{custom},
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("search saved tag: %v", err)
|
||||
}
|
||||
if len(found.Messages) != 1 || found.Messages[0].ID != second.ID ||
|
||||
found.Messages[0].Reactions == nil || !found.Messages[0].Reactions.AsTags {
|
||||
t.Fatalf("saved tag search = %+v, want second message", found.Messages)
|
||||
}
|
||||
foundAny, err := store.ListByUser(ctx, userID, domain.MessageFilter{
|
||||
HasPeer: true,
|
||||
Peer: self,
|
||||
SavedReactions: []domain.MessageReaction{thumb, custom},
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("search any saved tag: %v", err)
|
||||
}
|
||||
if len(foundAny.Messages) != 3 {
|
||||
t.Fatalf("saved tag OR search = %+v, want all three messages", foundAny.Messages)
|
||||
}
|
||||
|
||||
if _, err := store.DeleteMessages(ctx, domain.DeleteMessagesRequest{
|
||||
OwnerUserID: userID,
|
||||
IDs: []int{second.ID},
|
||||
Date: 1_700_000_100,
|
||||
}); err != nil {
|
||||
t.Fatalf("delete tagged message: %v", err)
|
||||
}
|
||||
global, err = store.ListSavedReactionTags(ctx, domain.SavedReactionTagsRequest{UserID: userID, Limit: 100})
|
||||
if err != nil {
|
||||
t.Fatalf("list tags after delete: %v", err)
|
||||
}
|
||||
assertMemorySavedTag(t, global, thumb, 1, "Fav")
|
||||
assertMemorySavedTag(t, global, custom, 1, "")
|
||||
|
||||
set(first)
|
||||
global, err = store.ListSavedReactionTags(ctx, domain.SavedReactionTagsRequest{UserID: userID, Limit: 100})
|
||||
if err != nil {
|
||||
t.Fatalf("list tags after clear: %v", err)
|
||||
}
|
||||
if len(global) != 1 || global[0].Reaction.Key() != custom.Key() {
|
||||
t.Fatalf("tags after clear = %+v, want only custom", global)
|
||||
}
|
||||
if err := store.UpsertSavedReactionTag(ctx, domain.SavedReactionTag{
|
||||
UserID: userID, Reaction: thumb, Title: "ghost",
|
||||
}); err != domain.ErrReactionInvalid {
|
||||
t.Fatalf("rename unassigned tag err = %v, want ErrReactionInvalid", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertMemorySavedTag(t *testing.T, tags []domain.SavedReactionTag, reaction domain.MessageReaction, count int, title string) {
|
||||
t.Helper()
|
||||
for _, tag := range tags {
|
||||
if tag.Reaction.Key() == reaction.Key() {
|
||||
if tag.Count != count || tag.Title != title {
|
||||
t.Fatalf("tag %s = %+v, want count=%d title=%q", reaction.Key(), tag, count, title)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("tag %s not found in %+v", reaction.Key(), tags)
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ type MessageStore struct {
|
|||
nextPts map[int64]int
|
||||
readOutboxDates map[readOutboxDateKey]int
|
||||
privateReactions map[int64]map[int64][]domain.ChannelMessagePeerReaction
|
||||
savedMessageTags map[int64]map[int][]domain.MessageReaction
|
||||
savedTagTitles map[int64]map[string]string
|
||||
privateSendDedup map[privateSendDedupKey]privateSendDedupRecord
|
||||
loginCodeDeliveries map[[32]byte]loginCodeDeliveryRecord
|
||||
albumGroups map[albumGroupKey]albumGroupRecord
|
||||
|
|
@ -44,6 +46,8 @@ func NewMessageStore(dialogs ...*DialogStore) *MessageStore {
|
|||
nextPts: make(map[int64]int),
|
||||
readOutboxDates: make(map[readOutboxDateKey]int),
|
||||
privateReactions: make(map[int64]map[int64][]domain.ChannelMessagePeerReaction),
|
||||
savedMessageTags: make(map[int64]map[int][]domain.MessageReaction),
|
||||
savedTagTitles: make(map[int64]map[string]string),
|
||||
privateSendDedup: make(map[privateSendDedupKey]privateSendDedupRecord),
|
||||
loginCodeDeliveries: make(map[[32]byte]loginCodeDeliveryRecord),
|
||||
albumGroups: make(map[albumGroupKey]albumGroupRecord),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue