status-go/protocol/messenger_contacts.go

366 lines
9.0 KiB
Go
Raw Normal View History

package protocol
import (
"context"
"crypto/ecdsa"
"github.com/golang/protobuf/proto"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
2021-02-17 23:14:48 +00:00
"github.com/status-im/status-go/protocol/transport"
)
func (m *Messenger) SaveContact(contact *Contact) error {
return m.saveContact(contact)
}
func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerResponse, error) {
2021-03-29 15:41:30 +00:00
contact, ok := m.allContacts.Load(pubKey)
if !ok {
var err error
contact, err = buildContactFromPkString(pubKey)
if err != nil {
return nil, err
}
}
2021-10-01 14:50:16 +00:00
if !contact.Added {
2021-03-24 08:04:03 +00:00
contact.Added = true
}
// We sync the contact with the other devices
err := m.syncContact(context.Background(), contact)
if err != nil {
return nil, err
}
err = m.persistence.SaveContact(contact, nil)
if err != nil {
return nil, err
}
2021-03-29 15:41:30 +00:00
// 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()
if err != nil {
return nil, err
}
2021-02-17 23:14:48 +00:00
// Reset last published time for ChatIdentity so new contact can receive data
err = m.resetLastPublishedTimeForChatIdentity()
2021-02-17 23:14:48 +00:00
if err != nil {
return nil, err
}
2021-03-25 15:15:22 +00:00
// Create the corresponding chat
profileChat := m.buildProfileChat(contact.ID)
2021-03-25 15:15:22 +00:00
_, err = m.Join(profileChat)
if err != nil {
return nil, err
}
// Finally we send a contact update so they are notified we added them
// TODO: ens and picture are both blank for now
response, err := m.sendContactUpdate(context.Background(), pubKey, "", "")
if err != nil {
return nil, err
}
2021-01-11 10:32:51 +00:00
response.AddChat(profileChat)
publicKey, err := contact.PublicKey()
if err != nil {
return nil, err
}
// TODO: Add filters to response
_, err = m.transport.InitFilters([]string{profileChat.ID}, []*ecdsa.PublicKey{publicKey})
if err != nil {
return nil, err
}
2021-09-29 08:21:05 +00:00
// Publish contact code
err = m.publishContactCode()
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) resetLastPublishedTimeForChatIdentity() error {
// Reset last published time for ChatIdentity so new contact can receive data
contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
m.logger.Debug("contact state changed ResetWhenChatIdentityLastPublished")
return m.persistence.ResetWhenChatIdentityLastPublished(contactCodeTopic)
}
func (m *Messenger) removeContact(ctx context.Context, response *MessengerResponse, pubKey string) error {
2021-03-29 15:41:30 +00:00
contact, ok := m.allContacts.Load(pubKey)
if !ok {
return ErrContactNotFound
}
contact.Remove()
err := m.persistence.SaveContact(contact, nil)
if err != nil {
return err
}
err = m.syncContact(context.Background(), contact)
if err != nil {
return err
}
2021-03-29 15:41:30 +00:00
// 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()
if err != nil {
return err
}
// Create the corresponding profile chat
profileChatID := buildProfileChatID(contact.ID)
2021-03-29 15:41:30 +00:00
_, ok = m.allChats.Load(profileChatID)
if ok {
2021-10-05 17:26:02 +00:00
chatResponse, err := m.deactivateChat(profileChatID, false)
if err != nil {
return err
}
err = response.Merge(chatResponse)
if err != nil {
return err
}
}
response.Contacts = []*Contact{contact}
return nil
}
func (m *Messenger) RemoveContact(ctx context.Context, pubKey string) (*MessengerResponse, error) {
response := new(MessengerResponse)
err := m.removeContact(ctx, response, pubKey)
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) Contacts() []*Contact {
var contacts []*Contact
2021-03-29 15:41:30 +00:00
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
2021-03-24 08:04:03 +00:00
contacts = append(contacts, contact)
2021-03-29 15:41:30 +00:00
return true
})
return contacts
}
// GetContactByID assumes pubKey includes 0x prefix
func (m *Messenger) GetContactByID(pubKey string) *Contact {
2021-03-29 15:41:30 +00:00
contact, _ := m.allContacts.Load(pubKey)
return contact
}
func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) {
contact.Block()
chats, err := m.persistence.BlockContact(contact)
if err != nil {
return nil, err
}
2021-03-29 15:41:30 +00:00
m.allContacts.Store(contact.ID, contact)
for _, chat := range chats {
2021-03-29 15:41:30 +00:00
m.allChats.Store(chat.ID, chat)
}
2021-03-29 15:41:30 +00:00
m.allChats.Delete(contact.ID)
err = m.syncContact(context.Background(), contact)
if err != nil {
return nil, err
}
// re-register for push notifications
err = m.reregisterForPushNotifications()
if err != nil {
return nil, err
}
return chats, nil
}
func (m *Messenger) saveContact(contact *Contact) error {
name, identicon, err := generateAliasAndIdenticon(contact.ID)
if err != nil {
return err
}
contact.Identicon = identicon
contact.Alias = name
if m.shouldSyncContact(contact) {
2021-03-25 15:15:22 +00:00
if m.isNewContact(contact) {
publicKey, err := contact.PublicKey()
if err != nil {
return err
}
filter, err := m.transport.JoinPrivate(publicKey)
if err != nil {
return err
}
m.scheduleSyncFilter(filter)
}
err := m.syncContact(context.Background(), contact)
if err != nil {
return err
}
}
// We check if it should re-register with the push notification server
shouldReregisterForPushNotifications := (m.isNewContact(contact) || m.removedContact(contact))
err = m.persistence.SaveContact(contact, nil)
if err != nil {
return err
}
2021-03-29 15:41:30 +00:00
m.allContacts.Store(contact.ID, contact)
// Reregister only when data has changed
if shouldReregisterForPushNotifications {
err := m.resetLastPublishedTimeForChatIdentity()
2021-09-29 08:21:05 +00:00
if err != nil {
return err
}
// Publish contact code
err = m.publishContactCode()
2021-09-29 08:21:05 +00:00
if err != nil {
return err
}
return m.reregisterForPushNotifications()
}
return nil
}
// Send contact updates to all contacts added by us
2021-03-29 15:41:30 +00:00
func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImage string) (err error) {
myID := contactIDFromPublicKey(&m.identity.PublicKey)
2021-03-29 15:41:30 +00:00
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
2021-03-29 15:41:30 +00:00
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
2021-10-01 14:50:16 +00:00
if contact.Added {
2021-03-29 15:41:30 +00:00
if _, err = m.sendContactUpdate(ctx, contact.ID, ensName, profileImage); err != nil {
return false
}
}
2021-03-29 15:41:30 +00:00
return true
})
return err
}
// NOTE: this endpoint does not add the contact, the reason being is that currently
// that's left as a responsibility to the client, which will call both `SendContactUpdate`
// and `SaveContact` with the correct system tag.
// Ideally we have a single endpoint that does both, but probably best to bring `ENS` name
// on the messenger first.
// 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) {
return m.sendContactUpdate(ctx, chatID, ensName, profileImage)
}
func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, profileImage string) (*MessengerResponse, error) {
var response MessengerResponse
2021-03-29 15:41:30 +00:00
contact, ok := m.allContacts.Load(chatID)
if !ok {
var err error
contact, err = buildContactFromPkString(chatID)
if err != nil {
return nil, err
}
}
2021-03-29 15:41:30 +00:00
chat, ok := m.allChats.Load(chatID)
if !ok {
publicKey, err := contact.PublicKey()
if err != nil {
return nil, err
}
chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
// We don't want to show the chat to the user
chat.Active = false
}
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
m.allChats.Store(chat.ID, chat)
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
contactUpdate := &protobuf.ContactUpdate{
Clock: clock,
EnsName: ensName,
ProfileImage: profileImage}
encodedMessage, err := proto.Marshal(contactUpdate)
if err != nil {
return nil, err
}
_, err = m.dispatchMessage(ctx, common.RawMessage{
LocalChatID: chatID,
Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
ResendAutomatically: true,
})
if err != nil {
return nil, err
}
response.Contacts = []*Contact{contact}
2021-01-11 10:32:51 +00:00
response.AddChat(chat)
chat.LastClockValue = clock
err = m.saveChat(chat)
if err != nil {
return nil, err
}
return &response, m.saveContact(contact)
}
func (m *Messenger) isNewContact(contact *Contact) bool {
2021-03-29 15:41:30 +00:00
previousContact, ok := m.allContacts.Load(contact.ID)
2021-10-01 14:50:16 +00:00
return contact.Added && (!ok || !previousContact.Added)
}
func (m *Messenger) shouldSyncContact(contact *Contact) bool {
2021-03-29 15:41:30 +00:00
previousContact, ok := m.allContacts.Load(contact.ID)
if !ok {
2021-10-01 14:50:16 +00:00
return contact.Added
}
return contact.LocalNickname != previousContact.LocalNickname ||
2021-10-01 14:50:16 +00:00
contact.Added != previousContact.Added ||
previousContact.Blocked != contact.Blocked
}
func (m *Messenger) removedContact(contact *Contact) bool {
2021-03-29 15:41:30 +00:00
previousContact, ok := m.allContacts.Load(contact.ID)
if !ok {
return false
}
2021-10-01 14:50:16 +00:00
return previousContact.Added && !contact.Added
}