Added granual locking on Messenger

This commit is contained in:
Samuel Hawksby-Robinson 2021-03-29 16:41:30 +01:00 committed by Andrea Maria Piana
parent 9d09cb3e9a
commit 759f7bbeb3
9 changed files with 507 additions and 349 deletions

View file

@ -11,14 +11,19 @@ import (
v1protocol "github.com/status-im/status-go/protocol/v1"
)
var defaultSystemMessagesTranslations = map[protobuf.MembershipUpdateEvent_EventType]string{
protobuf.MembershipUpdateEvent_CHAT_CREATED: "{{from}} created the group {{name}}",
protobuf.MembershipUpdateEvent_NAME_CHANGED: "{{from}} changed the group's name to {{name}}",
protobuf.MembershipUpdateEvent_MEMBERS_ADDED: "{{from}} has invited {{members}}",
protobuf.MembershipUpdateEvent_MEMBER_JOINED: "{{from}} joined the group",
protobuf.MembershipUpdateEvent_ADMINS_ADDED: "{{from}} has made {{members}} admin",
protobuf.MembershipUpdateEvent_MEMBER_REMOVED: "{{member}} left the group",
protobuf.MembershipUpdateEvent_ADMIN_REMOVED: "{{member}} is not admin anymore",
var defaultSystemMessagesTranslations = new(systemMessageTranslationsMap)
func init() {
defaultSystemMessagesTranslationSet := map[protobuf.MembershipUpdateEvent_EventType]string{
protobuf.MembershipUpdateEvent_CHAT_CREATED: "{{from}} created the group {{name}}",
protobuf.MembershipUpdateEvent_NAME_CHANGED: "{{from}} changed the group's name to {{name}}",
protobuf.MembershipUpdateEvent_MEMBERS_ADDED: "{{from}} has invited {{members}}",
protobuf.MembershipUpdateEvent_MEMBER_JOINED: "{{from}} joined the group",
protobuf.MembershipUpdateEvent_ADMINS_ADDED: "{{from}} has made {{members}} admin",
protobuf.MembershipUpdateEvent_MEMBER_REMOVED: "{{member}} left the group",
protobuf.MembershipUpdateEvent_ADMIN_REMOVED: "{{member}} is not admin anymore",
}
defaultSystemMessagesTranslations.Init(defaultSystemMessagesTranslationSet)
}
func tsprintf(format string, params map[string]string) string {
@ -28,32 +33,39 @@ func tsprintf(format string, params map[string]string) string {
return format
}
func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) *common.Message {
func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations *systemMessageTranslationsMap) *common.Message {
var text string
switch e.Type {
case protobuf.MembershipUpdateEvent_CHAT_CREATED:
text = tsprintf(translations[protobuf.MembershipUpdateEvent_CHAT_CREATED], map[string]string{"from": "@" + e.From, "name": e.Name})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_CHAT_CREATED)
text = tsprintf(message, map[string]string{"from": "@" + e.From, "name": e.Name})
case protobuf.MembershipUpdateEvent_NAME_CHANGED:
text = tsprintf(translations[protobuf.MembershipUpdateEvent_NAME_CHANGED], map[string]string{"from": "@" + e.From, "name": e.Name})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_NAME_CHANGED)
text = tsprintf(message, map[string]string{"from": "@" + e.From, "name": e.Name})
case protobuf.MembershipUpdateEvent_MEMBERS_ADDED:
var memberMentions []string
for _, s := range e.Members {
memberMentions = append(memberMentions, "@"+s)
}
text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBERS_ADDED], map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_MEMBERS_ADDED)
text = tsprintf(message, map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")})
case protobuf.MembershipUpdateEvent_MEMBER_JOINED:
text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBER_JOINED], map[string]string{"from": "@" + e.From})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_MEMBER_JOINED)
text = tsprintf(message, map[string]string{"from": "@" + e.From})
case protobuf.MembershipUpdateEvent_ADMINS_ADDED:
var memberMentions []string
for _, s := range e.Members {
memberMentions = append(memberMentions, "@"+s)
}
text = tsprintf(translations[protobuf.MembershipUpdateEvent_ADMINS_ADDED], map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_ADMINS_ADDED)
text = tsprintf(message, map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")})
case protobuf.MembershipUpdateEvent_MEMBER_REMOVED:
text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBER_REMOVED], map[string]string{"member": "@" + e.Members[0]})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_MEMBER_REMOVED)
text = tsprintf(message, map[string]string{"member": "@" + e.Members[0]})
case protobuf.MembershipUpdateEvent_ADMIN_REMOVED:
text = tsprintf(translations[protobuf.MembershipUpdateEvent_ADMIN_REMOVED], map[string]string{"member": "@" + e.Members[0]})
message, _ := translations.Load(protobuf.MembershipUpdateEvent_ADMIN_REMOVED)
text = tsprintf(message, map[string]string{"member": "@" + e.Members[0]})
}
timestamp := v1protocol.TimestampInMsFromTime(time.Now())
@ -76,7 +88,7 @@ func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[p
return message
}
func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*common.Message {
func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations *systemMessageTranslationsMap) []*common.Message {
var messages []*common.Message
for _, e := range events {

View file

@ -47,7 +47,7 @@ func (n NotificationBody) MarshalJSON() ([]byte, error) {
return json.Marshal(item)
}
func NewMessageNotification(id string, message *common.Message, chat *Chat, contact *Contact, contacts map[string]*Contact) (*localnotifications.Notification, error) {
func NewMessageNotification(id string, message *common.Message, chat *Chat, contact *Contact, contacts *contactMap) (*localnotifications.Notification, error) {
body := &NotificationBody{
Message: message,
Chat: chat,
@ -66,7 +66,7 @@ func NewCommunityRequestToJoinNotification(id string, community *communities.Com
return body.toCommunityRequestToJoinNotification(id)
}
func (n NotificationBody) toMessageNotification(id string, contacts map[string]*Contact) (*localnotifications.Notification, error) {
func (n NotificationBody) toMessageNotification(id string, contacts *contactMap) (*localnotifications.Notification, error) {
var title string
if n.Chat.PrivateGroupChat() || n.Chat.Public() || n.Chat.CommunityChat() {
title = n.Chat.Name
@ -76,16 +76,16 @@ func (n NotificationBody) toMessageNotification(id string, contacts map[string]*
}
canonicalNames := make(map[string]string)
for _, id := range n.Message.Mentions {
contact, ok := contacts[id]
for _, mentionID := range n.Message.Mentions {
contact, ok := contacts.Load(mentionID)
if !ok {
var err error
contact, err = buildContactFromPkString(id)
contact, err = buildContactFromPkString(mentionID)
if err != nil {
return nil, err
}
}
canonicalNames[id] = contact.CanonicalName()
canonicalNames[mentionID] = contact.CanonicalName()
}
simplifiedText, err := n.Message.GetSimplifiedText(canonicalNames)

View file

@ -49,7 +49,7 @@ func newMessageHandler(identity *ecdsa.PrivateKey, logger *zap.Logger, persisten
// HandleMembershipUpdate updates a Chat instance according to the membership updates.
// It retrieves chat, if exists, and merges membership updates from the message.
// Finally, the Chat is updated with the new group events.
func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageState, chat *Chat, rawMembershipUpdate protobuf.MembershipUpdateMessage, translations map[protobuf.MembershipUpdateEvent_EventType]string) error {
func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageState, chat *Chat, rawMembershipUpdate protobuf.MembershipUpdateMessage, translations *systemMessageTranslationsMap) error {
var group *v1protocol.Group
var err error
@ -142,7 +142,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta
}
// Store in chats map as it might be a new one
messageState.AllChats[chat.ID] = chat
messageState.AllChats.Store(chat.ID, chat)
messageState.Response.AddChat(chat)
if message.Message != nil {
@ -179,7 +179,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
// Set the LocalChatID for the message
message.LocalChatID = chat.ID
if c, ok := state.AllChats[chat.ID]; ok {
if c, ok := state.AllChats.Load(chat.ID); ok {
chat = c
}
@ -204,7 +204,8 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
chat.Active = true
// Set in the modified maps chat
state.Response.AddChat(chat)
state.AllChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, chat)
// Add to response
if message != nil {
@ -214,14 +215,14 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
}
func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageState, message protobuf.SyncInstallationContact) error {
chat, ok := state.AllChats[state.CurrentMessageState.Contact.ID]
chat, ok := state.AllChats.Load(state.CurrentMessageState.Contact.ID)
if !ok {
chat = OneToOneFromPublicKey(state.CurrentMessageState.PublicKey, state.Timesource)
// We don't want to show the chat to the user
chat.Active = false
}
contact, ok := state.AllContacts[message.Id]
contact, ok := state.AllContacts.Load(message.Id)
if !ok {
var err error
contact, err = buildContactFromPkString(message.Id)
@ -241,25 +242,26 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta
contact.LastUpdated = message.Clock
contact.LocalNickname = message.LocalNickname
state.ModifiedContacts[contact.ID] = true
state.AllContacts[contact.ID] = contact
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)
}
state.AllChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, chat)
return nil
}
func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) bool {
chatID := message.Id
_, ok := state.AllChats[chatID]
_, ok := state.AllChats.Load(chatID)
if ok {
return false
}
chat := CreatePublicChat(chatID, state.Timesource)
state.AllChats[chat.ID] = chat
state.AllChats.Store(chat.ID, chat)
state.Response.AddChat(chat)
return true
@ -268,7 +270,7 @@ func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessage
func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error {
logger := m.logger.With(zap.String("site", "HandleContactUpdate"))
contact := state.CurrentMessageState.Contact
chat, ok := state.AllChats[contact.ID]
chat, ok := state.AllChats.Load(contact.ID)
if !ok {
chat = OneToOneFromPublicKey(state.CurrentMessageState.PublicKey, state.Timesource)
// We don't want to show the chat to the user
@ -287,8 +289,8 @@ func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, messag
contact.ENSVerified = false
}
contact.LastUpdated = message.Clock
state.ModifiedContacts[contact.ID] = true
state.AllContacts[contact.ID] = contact
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)
}
if chat.LastClockValue < message.Clock {
@ -296,7 +298,8 @@ func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, messag
}
state.Response.AddChat(chat)
state.AllChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, chat)
return nil
}
@ -308,7 +311,7 @@ func (m *MessageHandler) HandlePairInstallation(state *ReceivedMessageState, mes
return err
}
installation, ok := state.AllInstallations[message.InstallationId]
installation, ok := state.AllInstallations.Load(message.InstallationId)
if !ok {
return errors.New("installation not found")
}
@ -319,8 +322,9 @@ func (m *MessageHandler) HandlePairInstallation(state *ReceivedMessageState, mes
}
installation.InstallationMetadata = metadata
state.AllInstallations[message.InstallationId] = installation
state.ModifiedInstallations[message.InstallationId] = true
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllInstallations.Store(message.InstallationId, installation)
state.ModifiedInstallations.Store(message.InstallationId, true)
return nil
}
@ -348,16 +352,18 @@ func (m *MessageHandler) HandleCommunityDescription(state *ReceivedMessageState,
var chatIDs []string
for i, chat := range chats {
oldChat, ok := state.AllChats[chat.ID]
oldChat, ok := state.AllChats.Load(chat.ID)
if !ok {
// Beware, don't use the reference in the range (i.e chat) as it's a shallow copy
state.AllChats[chat.ID] = chats[i]
state.AllChats.Store(chat.ID, chats[i])
state.Response.AddChat(chat)
chatIDs = append(chatIDs, chat.ID)
// Update name, currently is the only field is mutable
} else if oldChat.Name != chat.Name {
state.AllChats[chat.ID].Name = chat.Name
oldChat.Name = chat.Name
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, oldChat)
state.Response.AddChat(chat)
}
}
@ -422,7 +428,7 @@ func (m *MessageHandler) HandleCommunityRequestToJoin(state *ReceivedMessageStat
contactID := contactIDFromPublicKey(signer)
contact := state.AllContacts[contactID]
contact, _ := state.AllContacts.Load(contactID)
state.Response.AddNotification(NewCommunityRequestToJoinNotification(requestToJoin.ID.String(), community, contact))
@ -478,7 +484,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
// Set the LocalChatID for the message
receivedMessage.LocalChatID = chat.ID
if c, ok := state.AllChats[chat.ID]; ok {
if c, ok := state.AllChats.Load(chat.ID); ok {
chat = c
}
@ -502,7 +508,8 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
chat.Active = true
// Set in the modified maps chat
state.Response.AddChat(chat)
state.AllChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, chat)
contact := state.CurrentMessageState.Contact
if receivedMessage.EnsName != "" {
@ -513,8 +520,8 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
// If oldRecord is nil, a new verification process will take place
// so we reset the record
contact.ENSVerified = false
state.ModifiedContacts[contact.ID] = true
state.AllContacts[contact.ID] = contact
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)
}
}
@ -738,7 +745,7 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM
return m.handleCommandMessage(messageState, oldMessage)
}
func (m *MessageHandler) matchChatEntity(chatEntity common.ChatEntity, chats map[string]*Chat, timesource common.TimeSource) (*Chat, error) {
func (m *MessageHandler) matchChatEntity(chatEntity common.ChatEntity, chats *chatMap, timesource common.TimeSource) (*Chat, error) {
if chatEntity.GetSigPubKey() == nil {
m.logger.Error("public key can't be empty")
return nil, errors.New("received a chatEntity with empty public key")
@ -749,8 +756,8 @@ func (m *MessageHandler) matchChatEntity(chatEntity common.ChatEntity, chats map
// For public messages, all outgoing and incoming messages have the same chatID
// equal to a public chat name.
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
chat, ok := chats.Load(chatID)
if !ok {
return nil, errors.New("received a public chatEntity from non-existing chat")
}
return chat, nil
@ -758,8 +765,8 @@ func (m *MessageHandler) matchChatEntity(chatEntity common.ChatEntity, chats map
// It's a private message coming from us so we rely on Message.ChatID
// If chat does not exist, it should be created to support multidevice synchronization.
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
chat, ok := chats.Load(chatID)
if !ok {
if len(chatID) != PubKeyStringLength {
return nil, errors.New("invalid pubkey length")
}
@ -780,16 +787,16 @@ func (m *MessageHandler) matchChatEntity(chatEntity common.ChatEntity, chats map
// It's an incoming private chatEntity. ChatID is calculated from the signature.
// If a chat does not exist, a new one is created and saved.
chatID := contactIDFromPublicKey(chatEntity.GetSigPubKey())
chat := chats[chatID]
if chat == nil {
chat, ok := chats.Load(chatID)
if !ok {
// TODO: this should be a three-word name used in the mobile client
chat = CreateOneToOneChat(chatID[:8], chatEntity.GetSigPubKey(), timesource)
}
return chat, nil
case chatEntity.GetMessageType() == protobuf.MessageType_COMMUNITY_CHAT:
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
chat, ok := chats.Load(chatID)
if !ok {
return nil, errors.New("received community chat chatEntity for non-existing chat")
}
@ -817,8 +824,8 @@ func (m *MessageHandler) matchChatEntity(chatEntity common.ChatEntity, chats map
// In the case of a group chatEntity, ChatID is the same for all messages belonging to a group.
// It needs to be verified if the signature public key belongs to the chat.
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
chat, ok := chats.Load(chatID)
if !ok {
return nil, errors.New("received group chat chatEntity for non-existing chat")
}
@ -906,7 +913,8 @@ func (m *MessageHandler) HandleEmojiReaction(state *ReceivedMessageState, pbEmoj
}
state.Response.AddChat(chat)
state.AllChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, chat)
// save emoji reaction
err = m.persistence.SaveEmojiReaction(emojiReaction)
@ -980,8 +988,8 @@ func (m *MessageHandler) HandleChatIdentity(state *ReceivedMessageState, ci prot
contact.Images[imageType] = images.IdentityImage{Name: imageType, Payload: image.Payload}
}
state.ModifiedContacts[contact.ID] = true
state.AllContacts[contact.ID] = contact
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)
}
return nil

View file

@ -74,6 +74,7 @@ var messageCacheIntervalMs uint64 = 1000 * 60 * 60 * 48
// because installations are managed by the user.
// Similarly, it needs to expose an interface to manage
// mailservers because they can also be managed by the user.
// TODO we may want to change the maps into sync.Maps if we start getting unexpected locks or weird collision bugs
type Messenger struct {
node types.Node
config *config
@ -92,11 +93,11 @@ type Messenger struct {
featureFlags common.FeatureFlags
shutdownTasks []func() error
shouldPublishContactCode bool
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
allChats map[string]*Chat
allContacts map[string]*Contact
allInstallations map[string]*multidevice.Installation
modifiedInstallations map[string]bool
systemMessagesTranslations *systemMessageTranslationsMap
allChats *chatMap
allContacts *contactMap
allInstallations *installationMap
modifiedInstallations *stringBoolMap
installationID string
mailserver []byte
database *sql.DB
@ -106,6 +107,7 @@ type Messenger struct {
mailserversDatabase *mailservers.Database
quit chan struct{}
// TODO(samyoul) Determine if/how the remaining usage of this mutex can be removed
mutex sync.Mutex
}
@ -299,11 +301,11 @@ func NewMessenger(
ensVerifier: ensVerifier,
featureFlags: c.featureFlags,
systemMessagesTranslations: c.systemMessagesTranslations,
allChats: make(map[string]*Chat),
allContacts: make(map[string]*Contact),
allInstallations: make(map[string]*multidevice.Installation),
allChats: new(chatMap),
allContacts: new(contactMap),
allInstallations: new(installationMap),
installationID: installationID,
modifiedInstallations: make(map[string]bool),
modifiedInstallations: new(stringBoolMap),
verifyTransactionClient: c.verifyTransactionClient,
database: database,
multiAccounts: c.multiAccount,
@ -780,9 +782,9 @@ func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) error {
func (m *Messenger) handleInstallations(installations []*multidevice.Installation) {
for _, installation := range installations {
if installation.Identity == contactIDFromPublicKey(&m.identity.PublicKey) {
if _, ok := m.allInstallations[installation.ID]; !ok {
m.allInstallations[installation.ID] = installation
m.modifiedInstallations[installation.ID] = true
if _, ok := m.allInstallations.Load(installation.ID); !ok {
m.allInstallations.Store(installation.ID, installation)
m.modifiedInstallations.Store(installation.ID, true)
}
}
}
@ -811,13 +813,10 @@ func (m *Messenger) handleEncryptionLayerSubscriptions(subscriptions *encryption
}
func (m *Messenger) handleENSVerified(records []*ens.VerificationRecord) {
m.mutex.Lock()
defer m.mutex.Unlock()
var contacts []*Contact
for _, record := range records {
m.logger.Info("handling record", zap.Any("record", record))
contact, ok := m.allContacts[record.PublicKey]
contact, ok := m.allContacts.Load(record.PublicKey)
if !ok {
m.logger.Info("contact not found")
continue
@ -1003,7 +1002,7 @@ func (m *Messenger) Init() error {
continue
}
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
if !chat.Active || chat.Timeline() {
continue
}
@ -1038,7 +1037,7 @@ func (m *Messenger) Init() error {
return err
}
for _, contact := range contacts {
m.allContacts[contact.ID] = contact
m.allContacts.Store(contact.ID, contact)
// We only need filters for contacts added by us and not blocked.
if !contact.IsAdded() || contact.IsBlocked() {
continue
@ -1057,7 +1056,7 @@ func (m *Messenger) Init() error {
}
for _, installation := range installations {
m.allInstallations[installation.ID] = installation
m.allInstallations.Store(installation.ID, installation)
}
_, err = m.transport.InitFilters(publicChatIDs, publicKeys)
@ -1086,10 +1085,7 @@ func (m *Messenger) Shutdown() (err error) {
}
func (m *Messenger) EnableInstallation(id string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
installation, ok := m.allInstallations[id]
installation, ok := m.allInstallations.Load(id)
if !ok {
return errors.New("no installation found")
}
@ -1099,15 +1095,13 @@ func (m *Messenger) EnableInstallation(id string) error {
return err
}
installation.Enabled = true
m.allInstallations[id] = installation
// TODO(samyoul) remove storing of an updated reference pointer?
m.allInstallations.Store(id, installation)
return nil
}
func (m *Messenger) DisableInstallation(id string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
installation, ok := m.allInstallations[id]
installation, ok := m.allInstallations.Load(id)
if !ok {
return errors.New("no installation found")
}
@ -1117,25 +1111,25 @@ func (m *Messenger) DisableInstallation(id string) error {
return err
}
installation.Enabled = false
m.allInstallations[id] = installation
// TODO(samyoul) remove storing of an updated reference pointer?
m.allInstallations.Store(id, installation)
return nil
}
func (m *Messenger) Installations() []*multidevice.Installation {
m.mutex.Lock()
defer m.mutex.Unlock()
installations := make([]*multidevice.Installation, len(m.allInstallations))
installations := make([]*multidevice.Installation, m.allInstallations.Len())
var i = 0
for _, installation := range m.allInstallations {
m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
installations[i] = installation
i++
}
return true
})
return installations
}
func (m *Messenger) setInstallationMetadata(id string, data *multidevice.InstallationMetadata) error {
installation, ok := m.allInstallations[id]
installation, ok := m.allInstallations.Load(id)
if !ok {
return errors.New("no installation found")
}
@ -1145,8 +1139,6 @@ func (m *Messenger) setInstallationMetadata(id string, data *multidevice.Install
}
func (m *Messenger) SetInstallationMetadata(id string, data *multidevice.InstallationMetadata) error {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.setInstallationMetadata(id, data)
}
@ -1194,9 +1186,6 @@ func (m *Messenger) Leave(chat Chat) error {
}
func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, members []string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers"))
logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members))
@ -1239,7 +1228,8 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string,
if err != nil {
return nil, err
}
m.allChats[chat.ID] = &chat
m.allChats.Store(chat.ID, &chat)
_, err = m.dispatchMessage(ctx, common.RawMessage{
LocalChatID: chat.ID,
@ -1265,9 +1255,6 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string,
}
func (m *Messenger) CreateGroupChatFromInvitation(name string, chatID string, adminPK string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
logger := m.logger.With(zap.String("site", "CreateGroupChatFromInvitation"))
logger.Info("Creating group chat from invitation", zap.String("name", name))
@ -1282,13 +1269,10 @@ func (m *Messenger) CreateGroupChatFromInvitation(name string, chatID string, ad
}
func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string, member string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
logger := m.logger.With(zap.String("site", "RemoveMemberFromGroupChat"))
logger.Info("Removing member form group chat", zap.String("chatID", chatID), zap.String("member", member))
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1346,13 +1330,10 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string
}
func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
logger := m.logger.With(zap.String("site", "AddMembersFromGroupChat"))
logger.Info("Adding members form group chat", zap.String("chatID", chatID), zap.Any("members", members))
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1438,10 +1419,7 @@ func (m *Messenger) ChangeGroupChatName(ctx context.Context, chatID string, name
logger := m.logger.With(zap.String("site", "ChangeGroupChatName"))
logger.Info("Changing group chat name", zap.String("chatID", chatID), zap.String("name", name))
m.mutex.Lock()
defer m.mutex.Unlock()
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1505,13 +1483,10 @@ func (m *Messenger) SendGroupChatInvitationRequest(ctx context.Context, chatID s
logger.Info("Sending group chat invitation request", zap.String("chatID", chatID),
zap.String("adminPK", adminPK), zap.String("message", message))
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
// Get chat and clock
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1579,9 +1554,6 @@ func (m *Messenger) SendGroupChatInvitationRejection(ctx context.Context, invita
logger := m.logger.With(zap.String("site", "SendGroupChatInvitationRejection"))
logger.Info("Sending group chat invitation reject", zap.String("invitationRequestID", invitationRequestID))
m.mutex.Lock()
defer m.mutex.Unlock()
invitationR, err := m.persistence.InvitationByID(invitationRequestID)
if err != nil {
return nil, err
@ -1590,7 +1562,7 @@ func (m *Messenger) SendGroupChatInvitationRejection(ctx context.Context, invita
invitationR.State = protobuf.GroupChatInvitation_REJECTED
// Get chat and clock
chat, ok := m.allChats[invitationR.ChatId]
chat, ok := m.allChats.Load(invitationR.ChatId)
if !ok {
return nil, ErrChatNotFound
}
@ -1645,14 +1617,11 @@ func (m *Messenger) SendGroupChatInvitationRejection(ctx context.Context, invita
}
func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
logger := m.logger.With(zap.String("site", "AddAdminsToGroupChat"))
logger.Info("Add admins to group chat", zap.String("chatID", chatID), zap.Any("members", members))
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1709,12 +1678,9 @@ func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, mem
}
func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1775,12 +1741,9 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me
}
func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bool) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -1861,7 +1824,7 @@ func (m *Messenger) reSendRawMessage(ctx context.Context, messageID string) erro
return err
}
chat, ok := m.allChats[message.LocalChatID]
chat, ok := m.allChats.Load(message.LocalChatID)
if !ok {
return errors.New("chat not found")
}
@ -1879,19 +1842,17 @@ func (m *Messenger) reSendRawMessage(ctx context.Context, messageID string) erro
// ReSendChatMessage pulls a message from the database and sends it again
func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.reSendRawMessage(ctx, messageID)
}
func (m *Messenger) hasPairedDevices() bool {
var count int
for _, i := range m.allInstallations {
if i.Enabled {
m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
if installation.Enabled {
count++
}
}
return true
})
return count > 1
}
@ -1931,7 +1892,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage)
var err error
var id []byte
logger := m.logger.With(zap.String("site", "dispatchMessage"), zap.String("chatID", spec.LocalChatID))
chat, ok := m.allChats[spec.LocalChatID]
chat, ok := m.allChats.Load(spec.LocalChatID)
if !ok {
return spec, errors.New("no chat found")
}
@ -2051,20 +2012,15 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage)
// SendChatMessage takes a minimal message and sends it based on the corresponding chat
func (m *Messenger) SendChatMessage(ctx context.Context, message *common.Message) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.sendChatMessage(ctx, message)
}
// SendChatMessages takes a array of messages and sends it based on the corresponding chats
func (m *Messenger) SendChatMessages(ctx context.Context, messages []*common.Message) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
for _, message := range messages {
messageResponse, err := m.sendChatMessage(ctx, message)
messageResponse, err := m.SendChatMessage(ctx, message)
if err != nil {
return nil, err
}
@ -2143,7 +2099,7 @@ func (m *Messenger) sendChatMessage(ctx context.Context, message *common.Message
var response MessengerResponse
// A valid added chat is required.
chat, ok := m.allChats[message.ChatId]
chat, ok := m.allChats.Load(message.ChatId)
if !ok {
return nil, errors.New("Chat not found")
}
@ -2205,44 +2161,45 @@ func (m *Messenger) sendChatMessage(ctx context.Context, message *common.Message
// SyncDevices sends all public chats and contacts to paired devices
// TODO remove use of photoPath in contacts
func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string) (err error) {
myID := contactIDFromPublicKey(&m.identity.PublicKey)
if _, err := m.sendContactUpdate(ctx, myID, ensName, photoPath); err != nil {
if _, err = m.sendContactUpdate(ctx, myID, ensName, photoPath); err != nil {
return err
}
for _, chat := range m.allChats {
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
if !chat.Timeline() && !chat.ProfileUpdates() && chat.Public() && chat.Active {
if err := m.syncPublicChat(ctx, chat); err != nil {
return err
err = m.syncPublicChat(ctx, chat)
if err != nil {
return false
}
}
return true
})
if err != nil {
return err
}
for _, contact := range m.allContacts {
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.IsAdded() && contact.ID != myID {
if err := m.syncContact(ctx, contact); err != nil {
return err
if err = m.syncContact(ctx, contact); err != nil {
return false
}
}
}
return true
})
return nil
return err
}
// SendPairInstallation sends a pair installation message
func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var err error
var response MessengerResponse
installation, ok := m.allInstallations[m.installationID]
installation, ok := m.allInstallations.Load(m.installationID)
if !ok {
return nil, errors.New("no installation found")
}
@ -2253,14 +2210,14 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons
chatID := contactIDFromPublicKey(&m.identity.PublicKey)
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
chat = OneToOneFromPublicKey(&m.identity.PublicKey, m.getTimesource())
// We don't want to show the chat to the user
chat.Active = false
}
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
pairMessage := &protobuf.PairInstallation{
@ -2301,14 +2258,14 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
}
chatID := contactIDFromPublicKey(&m.identity.PublicKey)
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
chat = OneToOneFromPublicKey(&m.identity.PublicKey, m.getTimesource())
// We don't want to show the chat to the user
chat.Active = false
}
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
syncMessage := &protobuf.SyncInstallationPublicChat{
@ -2342,14 +2299,14 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
}
chatID := contactIDFromPublicKey(&m.identity.PublicKey)
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
chat = OneToOneFromPublicKey(&m.identity.PublicKey, m.getTimesource())
// We don't want to show the chat to the user
chat.Active = false
}
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
syncMessage := &protobuf.SyncInstallationContact{
@ -2405,16 +2362,15 @@ type ReceivedMessageState struct {
// State on the message being processed
CurrentMessageState *CurrentMessageState
// AllChats in memory
AllChats map[string]*Chat
AllChats *chatMap
// All contacts in memory
AllContacts map[string]*Contact
AllContacts *contactMap
// List of contacts modified
ModifiedContacts map[string]bool
ModifiedContacts *stringBoolMap
// All installations in memory
AllInstallations map[string]*multidevice.Installation
AllInstallations *installationMap
// List of communities modified
ModifiedInstallations map[string]bool
ModifiedInstallations *stringBoolMap
// List of filters
AllFilters map[string]*transport.Filter
// Map of existing messages
@ -2472,8 +2428,15 @@ func (r *ReceivedMessageState) addNewMessageNotification(publicKey ecdsa.PublicK
}
contactID := contactIDFromPublicKey(pubKey)
chat := r.AllChats[m.LocalChatID]
contact := r.AllContacts[contactID]
chat, ok := r.AllChats.Load(m.LocalChatID)
if !ok {
return fmt.Errorf("chat ID '%s' not present", m.LocalChatID)
}
contact, ok := r.AllContacts.Load(contactID)
if !ok {
return fmt.Errorf("contact ID '%s' not present", contactID)
}
if showMessageNotification(publicKey, m, chat, responseTo) {
notification, err := NewMessageNotification(m.ID, m, chat, contact, r.AllContacts)
@ -2487,12 +2450,10 @@ func (r *ReceivedMessageState) addNewMessageNotification(publicKey ecdsa.PublicK
}
func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
messageState := &ReceivedMessageState{
AllChats: m.allChats,
AllContacts: m.allContacts,
ModifiedContacts: make(map[string]bool),
ModifiedContacts: new(stringBoolMap),
AllInstallations: m.allInstallations,
ModifiedInstallations: m.modifiedInstallations,
ExistingMessagesMap: make(map[string]bool),
@ -2531,7 +2492,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
// Check for messages from blocked users
senderID := contactIDFromPublicKey(publicKey)
if _, ok := messageState.AllContacts[senderID]; ok && messageState.AllContacts[senderID].IsBlocked() {
if contact, ok := messageState.AllContacts.Load(senderID); ok && contact.IsBlocked() {
continue
}
@ -2547,7 +2508,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
}
var contact *Contact
if c, ok := messageState.AllContacts[senderID]; ok {
if c, ok := messageState.AllContacts.Load(senderID); ok {
contact = c
} else {
c, err := buildContact(senderID, publicKey)
@ -2557,8 +2518,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
continue
}
contact = c
messageState.AllContacts[senderID] = c
messageState.ModifiedContacts[contact.ID] = true
messageState.AllContacts.Store(senderID, contact)
messageState.ModifiedContacts.Store(contact.ID, true)
}
messageState.CurrentMessageState = &CurrentMessageState{
MessageID: messageID,
@ -2576,7 +2537,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
rawMembershipUpdate := msg.ParsedMessage.Interface().(protobuf.MembershipUpdateMessage)
err = m.handler.HandleMembershipUpdate(messageState, messageState.AllChats[rawMembershipUpdate.ChatId], rawMembershipUpdate, m.systemMessagesTranslations)
chat, _ := messageState.AllChats.Load(rawMembershipUpdate.ChatId)
err = m.handler.HandleMembershipUpdate(messageState, chat, rawMembershipUpdate, m.systemMessagesTranslations)
if err != nil {
logger.Warn("failed to handle MembershipUpdate", zap.Error(err))
allMessagesProcessed = false
@ -2923,9 +2885,9 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
}
var contactsToSave []*Contact
for id := range messageState.ModifiedContacts {
contact := messageState.AllContacts[id]
if contact != nil {
messageState.ModifiedContacts.Range(func(id string, value bool) (shouldContinue bool) {
contact, ok := messageState.AllContacts.Load(id)
if ok {
// We save all contacts so we can pull back name/image,
// but we only send to client those
// that have some custom fields
@ -2934,7 +2896,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
messageState.Response.Contacts = append(messageState.Response.Contacts, contact)
}
}
}
return true
})
for _, filter := range messageState.AllFilters {
messageState.Response.Filters = append(messageState.Response.Filters, filter)
@ -2942,9 +2905,9 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
// Hydrate chat alias and identicon
for id := range messageState.Response.chats {
chat := messageState.AllChats[id]
chat, _ := messageState.AllChats.Load(id)
if chat.OneToOne() {
contact, ok := m.allContacts[chat.ID]
contact, ok := m.allContacts.Load(chat.ID)
if ok {
chat.Alias = contact.Alias
chat.Identicon = contact.Identicon
@ -2954,18 +2917,23 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
messageState.Response.AddChat(chat)
}
for id := range messageState.ModifiedInstallations {
installation := messageState.AllInstallations[id]
var err error
messageState.ModifiedInstallations.Range(func(id string, value bool) (shouldContinue bool) {
installation, _ := messageState.AllInstallations.Load(id)
messageState.Response.Installations = append(messageState.Response.Installations, installation)
if installation.InstallationMetadata != nil {
err := m.setInstallationMetadata(id, installation.InstallationMetadata)
err = m.setInstallationMetadata(id, installation.InstallationMetadata)
if err != nil {
return nil, err
return false
}
}
return true
})
if err != nil {
return nil, err
}
var err error
if len(messageState.Response.chats) > 0 {
err = m.saveChats(messageState.Response.Chats())
if err != nil {
@ -3028,7 +2996,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
}
// Reset installations
m.modifiedInstallations = make(map[string]bool)
m.modifiedInstallations = new(stringBoolMap)
return messageState.Response, nil
}
@ -3109,14 +3077,11 @@ func (m *Messenger) DeleteMessagesByChatID(id string) error {
}
func (m *Messenger) ClearHistory(id string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.clearHistory(id)
}
func (m *Messenger) clearHistory(id string) (*MessengerResponse, error) {
chat, ok := m.allChats[id]
chat, ok := m.allChats.Load(id)
if !ok {
return nil, ErrChatNotFound
}
@ -3128,7 +3093,7 @@ func (m *Messenger) clearHistory(id string) (*MessengerResponse, error) {
return nil, err
}
m.allChats[id] = chat
m.allChats.Store(id, chat)
response := &MessengerResponse{}
response.AddChat(chat)
@ -3139,9 +3104,6 @@ func (m *Messenger) clearHistory(id string) (*MessengerResponse, error) {
// It returns the number of affected messages or error. If there is an error,
// the number of affected messages is always zero.
func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
count, err := m.persistence.MarkMessagesSeen(chatID, ids)
if err != nil {
return 0, err
@ -3150,14 +3112,12 @@ func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, error
if err != nil {
return 0, err
}
m.allChats[chatID] = chat
m.allChats.Store(chatID, chat)
return count, nil
}
func (m *Messenger) MarkAllRead(chatID string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return errors.New("chat not found")
}
@ -3168,16 +3128,15 @@ func (m *Messenger) MarkAllRead(chatID string) error {
}
chat.UnviewedMessagesCount = 0
m.allChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chat.ID, chat)
return nil
}
// 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 {
m.mutex.Lock()
defer m.mutex.Unlock()
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return errors.New("chat not found")
}
@ -3188,7 +3147,8 @@ func (m *Messenger) MuteChat(chatID string) error {
}
chat.Muted = true
m.allChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chat.ID, chat)
return m.reregisterForPushNotifications()
}
@ -3196,9 +3156,7 @@ func (m *Messenger) MuteChat(chatID string) error {
// UnmuteChat signals to the messenger that we want to be notified
// on new messages from this chat
func (m *Messenger) UnmuteChat(chatID string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return errors.New("chat not found")
}
@ -3209,7 +3167,8 @@ func (m *Messenger) UnmuteChat(chatID string) error {
}
chat.Muted = false
m.allChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chat.ID, chat)
return m.reregisterForPushNotifications()
}
@ -3228,13 +3187,10 @@ func GenerateAlias(id string) (string, error) {
}
func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contract, address string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3306,13 +3262,10 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr
}
func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, from, value, contract string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3383,9 +3336,6 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr
}
func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, messageID, address string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
message, err := m.MessageByID(messageID)
@ -3400,7 +3350,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
chatID := message.LocalChatID
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3479,9 +3429,6 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
}
func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
message, err := m.MessageByID(messageID)
@ -3496,7 +3443,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
chatID := message.LocalChatID
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3562,9 +3509,6 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
}
func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, messageID string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
message, err := m.MessageByID(messageID)
@ -3579,7 +3523,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
chatID := message.LocalChatID
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3645,9 +3589,6 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
}
func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHash, messageID string, signature []byte) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
message, err := m.MessageByID(messageID)
@ -3662,7 +3603,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
chatID := message.LocalChatID
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3745,13 +3686,10 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
}
func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract, transactionHash string, signature []byte) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
// A valid added chat is required.
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, errors.New("Chat not found")
}
@ -3830,8 +3768,6 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
if m.verifyTransactionClient == nil {
return nil, nil
}
m.mutex.Lock()
defer m.mutex.Unlock()
logger := m.logger.With(zap.String("site", "ValidateTransactions"))
logger.Debug("Validating transactions")
@ -3851,7 +3787,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
for _, validationResult := range responses {
var message *common.Message
chatID := contactIDFromPublicKey(validationResult.Transaction.From)
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
chat = OneToOneFromPublicKey(validationResult.Transaction.From, m.transport)
}
@ -3917,7 +3853,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
}
response.Messages = append(response.Messages, message)
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
response.AddChat(chat)
contact, err := m.getOrBuildContactFromMessage(message)
@ -4017,19 +3953,21 @@ func (m *Messenger) pushNotificationOptions() *pushnotificationclient.Registrati
var mutedChatIDs []string
var publicChatIDs []string
for _, contact := range m.allContacts {
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.IsAdded() && !contact.IsBlocked() {
pk, err := contact.PublicKey()
if err != nil {
m.logger.Warn("could not parse contact public key")
continue
return true
}
contactIDs = append(contactIDs, pk)
} else if contact.IsBlocked() {
mutedChatIDs = append(mutedChatIDs, contact.ID)
}
}
for _, chat := range m.allChats {
return true
})
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
if chat.Muted {
mutedChatIDs = append(mutedChatIDs, chat.ID)
}
@ -4037,7 +3975,8 @@ func (m *Messenger) pushNotificationOptions() *pushnotificationclient.Registrati
publicChatIDs = append(publicChatIDs, chat.ID)
}
}
return true
})
return &pushnotificationclient.RegistrationOptions{
ContactIDs: contactIDs,
MutedChatIDs: mutedChatIDs,
@ -4157,12 +4096,9 @@ func generateAliasAndIdenticon(pk string) (string, string, error) {
}
func (m *Messenger) SendEmojiReaction(ctx context.Context, chatID, messageID string, emojiID protobuf.EmojiReaction_Type) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -4215,20 +4151,18 @@ func (m *Messenger) EmojiReactionsByChatID(chatID string, cursor string, limit i
if chat.Timeline() {
var chatIDs = []string{"@" + contactIDFromPublicKey(&m.identity.PublicKey)}
for _, contact := range m.allContacts {
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.IsAdded() {
chatIDs = append(chatIDs, "@"+contact.ID)
}
}
return true
})
return m.persistence.EmojiReactionsByChatIDs(chatIDs, cursor, limit)
}
return m.persistence.EmojiReactionsByChatID(chatID, cursor, limit)
}
func (m *Messenger) SendEmojiReactionRetraction(ctx context.Context, emojiReactionID string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
emojiR, err := m.persistence.EmojiReactionByID(emojiReactionID)
if err != nil {
return nil, err
@ -4245,7 +4179,7 @@ func (m *Messenger) SendEmojiReactionRetraction(ctx context.Context, emojiReacti
}
// Get chat and clock
chat, ok := m.allChats[emojiR.GetChatId()]
chat, ok := m.allChats.Load(emojiR.GetChatId())
if !ok {
return nil, ErrChatNotFound
}
@ -4346,7 +4280,7 @@ func (m *Messenger) encodeChatEntity(chat *Chat, message common.ChatEntity) ([]b
}
func (m *Messenger) getOrBuildContactFromMessage(msg *common.Message) (*Contact, error) {
if c, ok := m.allContacts[msg.From]; ok {
if c, ok := m.allContacts.Load(msg.From); ok {
return c, nil
}
@ -4360,6 +4294,7 @@ func (m *Messenger) getOrBuildContactFromMessage(msg *common.Message) (*Contact,
return nil, err
}
m.allContacts[msg.From] = c
// TODO(samyoul) remove storing of an updated reference pointer?
m.allContacts.Store(msg.From, c)
return c, nil
}

View file

@ -10,22 +10,17 @@ import (
)
func (m *Messenger) Chats() []*Chat {
m.mutex.Lock()
defer m.mutex.Unlock()
var chats []*Chat
for _, c := range m.allChats {
chats = append(chats, c)
}
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
chats = append(chats, chat)
return true
})
return chats
}
func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
if err := request.Validate(); err != nil {
return nil, err
}
@ -36,7 +31,7 @@ func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*M
return nil, err
}
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
chat = CreateOneToOneChat(chatID, pk, m.getTimesource())
}
@ -52,7 +47,8 @@ func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*M
return nil, err
}
m.allChats[chatID] = chat
// TODO(Samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chatID, chat)
response := &MessengerResponse{
Filters: filters,
@ -64,9 +60,6 @@ func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*M
}
func (m *Messenger) DeleteChat(chatID string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.deleteChat(chatID)
}
@ -75,10 +68,10 @@ func (m *Messenger) deleteChat(chatID string) error {
if err != nil {
return err
}
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if ok && chat.Active && chat.Public() {
delete(m.allChats, chatID)
m.allChats.Delete(chatID)
return m.reregisterForPushNotifications()
}
@ -86,20 +79,16 @@ func (m *Messenger) deleteChat(chatID string) error {
}
func (m *Messenger) SaveChat(chat *Chat) error {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.saveChat(chat)
}
func (m *Messenger) DeactivateChat(chatID string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.deactivateChat(chatID)
}
func (m *Messenger) deactivateChat(chatID string) (*MessengerResponse, error) {
var response MessengerResponse
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
@ -121,7 +110,8 @@ func (m *Messenger) deactivateChat(chatID string) (*MessengerResponse, error) {
}
}
m.allChats[chatID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chatID, chat)
response.AddChat(chat)
// TODO: Remove filters
@ -135,7 +125,7 @@ func (m *Messenger) saveChats(chats []*Chat) error {
return err
}
for _, chat := range chats {
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
}
return nil
@ -143,7 +133,7 @@ func (m *Messenger) saveChats(chats []*Chat) error {
}
func (m *Messenger) saveChat(chat *Chat) error {
previousChat, ok := m.allChats[chat.ID]
previousChat, ok := m.allChats.Load(chat.ID)
if chat.OneToOne() {
name, identicon, err := generateAliasAndIdenticon(chat.ID)
if err != nil {
@ -170,7 +160,8 @@ func (m *Messenger) saveChat(chat *Chat) error {
if err != nil {
return err
}
m.allChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chat.ID, chat)
if shouldRegisterForPushNotifications {
// Re-register for push notifications, as we want to receive mentions

View file

@ -24,7 +24,7 @@ type config struct {
onContactENSVerified func(*MessengerResponse)
// systemMessagesTranslations holds translations for system-messages
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
systemMessagesTranslations *systemMessageTranslationsMap
// Config for the envelopes monitor
envelopesMonitorConfig *transport.EnvelopesMonitorConfig
@ -56,7 +56,7 @@ type Option func(*config) error
// nolint: unused
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
return func(c *config) error {
c.systemMessagesTranslations = t
c.systemMessagesTranslations.Init(t)
return nil
}
}

View file

@ -11,15 +11,11 @@ import (
)
func (m *Messenger) SaveContact(contact *Contact) error {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.saveContact(contact)
}
func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
contact, ok := m.allContacts[pubKey]
contact, ok := m.allContacts.Load(pubKey)
if !ok {
var err error
contact, err = buildContactFromPkString(pubKey)
@ -43,7 +39,8 @@ func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerRe
return nil, err
}
m.allContacts[contact.ID] = contact
// TODO(samyoul) remove storing of an updated reference pointer?
m.allContacts.Store(contact.ID, contact)
// And we re-register for push notications
err = m.reregisterForPushNotifications()
@ -53,7 +50,7 @@ func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerRe
// Create the corresponding profile chat
profileChatID := buildProfileChatID(contact.ID)
profileChat, ok := m.allChats[profileChatID]
profileChat, ok := m.allChats.Load(profileChatID)
if !ok {
profileChat = CreateProfileChat(profileChatID, contact.ID, m.getTimesource())
@ -89,11 +86,9 @@ func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerRe
}
func (m *Messenger) RemoveContact(ctx context.Context, pubKey string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response *MessengerResponse
response := new(MessengerResponse)
contact, ok := m.allContacts[pubKey]
contact, ok := m.allContacts.Load(pubKey)
if !ok {
return nil, ErrContactNotFound
}
@ -105,7 +100,8 @@ func (m *Messenger) RemoveContact(ctx context.Context, pubKey string) (*Messenge
return nil, err
}
m.allContacts[contact.ID] = contact
// TODO(samyoul) remove storing of an updated reference pointer?
m.allContacts.Store(contact.ID, contact)
// And we re-register for push notications
err = m.reregisterForPushNotifications()
@ -115,7 +111,7 @@ func (m *Messenger) RemoveContact(ctx context.Context, pubKey string) (*Messenge
// Create the corresponding profile chat
profileChatID := buildProfileChatID(contact.ID)
_, ok = m.allChats[profileChatID]
_, ok = m.allChats.Load(profileChatID)
if ok {
chatResponse, err := m.deactivateChat(profileChatID)
@ -133,38 +129,33 @@ func (m *Messenger) RemoveContact(ctx context.Context, pubKey string) (*Messenge
}
func (m *Messenger) Contacts() []*Contact {
m.mutex.Lock()
defer m.mutex.Unlock()
var contacts []*Contact
for _, contact := range m.allContacts {
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.HasCustomFields() {
contacts = append(contacts, contact)
}
}
return true
})
return contacts
}
// GetContactByID assumes pubKey includes 0x prefix
func (m *Messenger) GetContactByID(pubKey string) *Contact {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.allContacts[pubKey]
contact, _ := m.allContacts.Load(pubKey)
return contact
}
func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
chats, err := m.persistence.BlockContact(contact)
if err != nil {
return nil, err
}
m.allContacts[contact.ID] = contact
m.allContacts.Store(contact.ID, contact)
for _, chat := range chats {
m.allChats[chat.ID] = chat
m.allChats.Store(chat.ID, chat)
}
delete(m.allChats, contact.ID)
m.allChats.Delete(contact.ID)
// re-register for push notifications
err = m.reregisterForPushNotifications()
@ -199,7 +190,7 @@ func (m *Messenger) saveContact(contact *Contact) error {
return err
}
m.allContacts[contact.ID] = contact
m.allContacts.Store(contact.ID, contact)
// Reregister only when data has changed
if shouldReregisterForPushNotifications {
@ -210,25 +201,23 @@ func (m *Messenger) saveContact(contact *Contact) error {
}
// Send contact updates to all contacts added by us
func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImage string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImage string) (err error) {
myID := contactIDFromPublicKey(&m.identity.PublicKey)
if _, err := m.sendContactUpdate(ctx, myID, ensName, profileImage); err != nil {
if _, err = m.sendContactUpdate(ctx, myID, ensName, profileImage); err != nil {
return err
}
// TODO: This should not be sending paired messages, as we do it above
for _, contact := range m.allContacts {
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.IsAdded() {
if _, err := m.sendContactUpdate(ctx, contact.ID, ensName, profileImage); err != nil {
return err
if _, err = m.sendContactUpdate(ctx, contact.ID, ensName, profileImage); err != nil {
return false
}
}
}
return nil
return true
})
return err
}
// NOTE: this endpoint does not add the contact, the reason being is that currently
@ -239,15 +228,13 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag
// SendContactUpdate sends a contact update to a user and adds the user to contacts
func (m *Messenger) SendContactUpdate(ctx context.Context, chatID, ensName, profileImage string) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.sendContactUpdate(ctx, chatID, ensName, profileImage)
}
func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, profileImage string) (*MessengerResponse, error) {
var response MessengerResponse
contact, ok := m.allContacts[chatID]
contact, ok := m.allContacts.Load(chatID)
if !ok {
var err error
contact, err = buildContactFromPkString(chatID)
@ -256,7 +243,7 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
}
}
chat, ok := m.allChats[chatID]
chat, ok := m.allChats.Load(chatID)
if !ok {
publicKey, err := contact.PublicKey()
if err != nil {
@ -267,7 +254,8 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
chat.Active = false
}
m.allChats[chat.ID] = chat
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chat.ID, chat)
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
contactUpdate := &protobuf.ContactUpdate{
@ -301,12 +289,12 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
}
func (m *Messenger) isNewContact(contact *Contact) bool {
previousContact, ok := m.allContacts[contact.ID]
previousContact, ok := m.allContacts.Load(contact.ID)
return contact.IsAdded() && (!ok || !previousContact.IsAdded())
}
func (m *Messenger) hasNicknameChanged(contact *Contact) bool {
previousContact, ok := m.allContacts[contact.ID]
previousContact, ok := m.allContacts.Load(contact.ID)
if !ok {
return false
}
@ -314,7 +302,7 @@ func (m *Messenger) hasNicknameChanged(contact *Contact) bool {
}
func (m *Messenger) removedContact(contact *Contact) bool {
previousContact, ok := m.allContacts[contact.ID]
previousContact, ok := m.allContacts.Load(contact.ID)
if !ok {
return false
}

View file

@ -0,0 +1,224 @@
package protocol
import (
"sync"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/protobuf"
)
/*
|--------------------------------------------------------------------------
| chatMap
|--------------------------------------------------------------------------
|
| A sync.Map wrapper for a specific mapping of map[string]*Chat
|
*/
type chatMap struct {
sm sync.Map
}
func (cm *chatMap) Load(chatID string) (*Chat, bool) {
chat, ok := cm.sm.Load(chatID)
if chat == nil {
return nil, ok
}
return chat.(*Chat), ok
}
func (cm *chatMap) Store(chatID string, chat *Chat) {
cm.sm.Store(chatID, chat)
}
func (cm *chatMap) Range(f func(chatID string, chat *Chat) (shouldContinue bool)) {
nf := func(key, value interface{}) (shouldContinue bool) {
return f(key.(string), value.(*Chat))
}
cm.sm.Range(nf)
}
func (cm *chatMap) Delete(chatID string) {
cm.sm.Delete(chatID)
}
/*
|--------------------------------------------------------------------------
| contactMap
|--------------------------------------------------------------------------
|
| A sync.Map wrapper for a specific mapping of map[string]*Contact
|
*/
type contactMap struct {
sm sync.Map
}
func (cm *contactMap) Load(contactID string) (*Contact, bool) {
contact, ok := cm.sm.Load(contactID)
if contact == nil {
return nil, ok
}
return contact.(*Contact), ok
}
func (cm *contactMap) Store(contactID string, contact *Contact) {
cm.sm.Store(contactID, contact)
}
func (cm *contactMap) Range(f func(contactID string, contact *Contact) (shouldContinue bool)) {
nf := func(key, value interface{}) (shouldContinue bool) {
return f(key.(string), value.(*Contact))
}
cm.sm.Range(nf)
}
func (cm *contactMap) Delete(contactID string) {
cm.sm.Delete(contactID)
}
/*
|--------------------------------------------------------------------------
| systemMessageTranslationsMap
|--------------------------------------------------------------------------
|
| A sync.Map wrapper for the specific mapping of map[protobuf.MembershipUpdateEvent_EventType]string
|
*/
type systemMessageTranslationsMap struct {
sm sync.Map
}
func (smtm *systemMessageTranslationsMap) Init(set map[protobuf.MembershipUpdateEvent_EventType]string) {
for eventType, message := range set {
smtm.Store(eventType, message)
}
}
func (smtm *systemMessageTranslationsMap) Load(eventType protobuf.MembershipUpdateEvent_EventType) (string, bool) {
message, ok := smtm.sm.Load(eventType)
if message == nil {
return "", ok
}
return message.(string), ok
}
func (smtm *systemMessageTranslationsMap) Store(eventType protobuf.MembershipUpdateEvent_EventType, message string) {
smtm.sm.Store(eventType, message)
}
func (smtm *systemMessageTranslationsMap) Range(f func(eventType protobuf.MembershipUpdateEvent_EventType, message string) (shouldContinue bool)) {
nf := func(key, value interface{}) (shouldContinue bool) {
return f(key.(protobuf.MembershipUpdateEvent_EventType), value.(string))
}
smtm.sm.Range(nf)
}
func (smtm *systemMessageTranslationsMap) Delete(eventType protobuf.MembershipUpdateEvent_EventType) {
smtm.sm.Delete(eventType)
}
/*
|--------------------------------------------------------------------------
| installationMap
|--------------------------------------------------------------------------
|
| A sync.Map wrapper for the specific mapping of map[string]*multidevice.Installation
|
*/
type installationMap struct {
sm sync.Map
}
func (im *installationMap) Load(installationID string) (*multidevice.Installation, bool) {
installation, ok := im.sm.Load(installationID)
if installation == nil {
return nil, ok
}
return installation.(*multidevice.Installation), ok
}
func (im *installationMap) Store(installationID string, installation *multidevice.Installation) {
im.sm.Store(installationID, installation)
}
func (im *installationMap) Range(f func(installationID string, installation *multidevice.Installation) (shouldContinue bool)) {
nf := func(key, value interface{}) (shouldContinue bool) {
return f(key.(string), value.(*multidevice.Installation))
}
im.sm.Range(nf)
}
func (im *installationMap) Delete(installationID string) {
im.sm.Delete(installationID)
}
func (im *installationMap) Empty() bool {
count := 0
im.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
count++
return false
})
return count == 0
}
func (im *installationMap) Len() int {
count := 0
im.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
count++
return true
})
return count
}
/*
|--------------------------------------------------------------------------
| stringBoolMap
|--------------------------------------------------------------------------
|
| A sync.Map wrapper for the specific mapping of map[string]bool
|
*/
type stringBoolMap struct {
sm sync.Map
}
func (sbm *stringBoolMap) Load(key string) (bool, bool) {
state, ok := sbm.sm.Load(key)
if state == nil {
return false, ok
}
return state.(bool), ok
}
func (sbm *stringBoolMap) Store(key string, value bool) {
sbm.sm.Store(key, value)
}
func (sbm *stringBoolMap) Range(f func(key string, value bool) (shouldContinue bool)) {
nf := func(key, value interface{}) (shouldContinue bool) {
return f(key.(string), value.(bool))
}
sbm.sm.Range(nf)
}
func (sbm *stringBoolMap) Delete(key string) {
sbm.sm.Delete(key)
}
func (sbm *stringBoolMap) Len() int {
count := 0
sbm.Range(func(key string, value bool) (shouldContinue bool) {
count++
return true
})
return count
}

View file

@ -2508,9 +2508,9 @@ func (s *MessageHandlerSuite) TestRun() {
for idx, tc := range testCases {
s.Run(tc.Name, func() {
chatsMap := make(map[string]*Chat)
chatsMap := new(chatMap)
if tc.Chat != nil && tc.Chat.ID != "" {
chatsMap[tc.Chat.ID] = tc.Chat
chatsMap.Store(tc.Chat.ID, tc.Chat)
}
message := tc.Message