Unifying the endpoints for contact flows (#3379)

* fix: Contact requests flows

fix: pending CR notification
fix: use CR as message with provided text

* fix: Remove legacy default contact request

* fix: Add test case sending CR after removal from contacts

* fix: refactor contact request tests to have common steps

* fix: activate chat on sender side after receiveing the CR

* chore: Return defaultContactRequestID function

* fix: force activate chat on reciever's side

* fix: ensure AC notification's name for CR notifiaction match contact's primary name
This commit is contained in:
Mikhail Rogachev 2023-04-25 15:27:15 +04:00 committed by GitHub
parent dd7c59423f
commit 96532f97c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 779 additions and 1647 deletions

View File

@ -1 +1 @@
0.146.6
0.147.0

View File

@ -2312,7 +2312,7 @@ func (s *MessengerCommunitiesSuite) TestShareCommunity() {
s.Require().Len(response.Messages(), 1)
// Add bob to contacts so it does not go on activity center
bobPk := common.PubkeyToHex(&s.alice.identity.PublicKey)
bobPk := common.PubkeyToHex(&s.bob.identity.PublicKey)
request := &requests.AddContact{ID: bobPk}
_, err = s.alice.AddContact(context.Background(), request)
s.Require().NoError(err)

File diff suppressed because it is too large Load Diff

View File

@ -135,7 +135,7 @@ func (s *MessengerVerificationRequests) mutualContact(theirMessenger *Messenger)
resp, err = WaitOnMessengerResponse(
s.m,
func(r *MessengerResponse) bool {
return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1
return len(r.Contacts) == 1 && len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 1
},
"no messages",
)
@ -148,18 +148,9 @@ func (s *MessengerVerificationRequests) mutualContact(theirMessenger *Messenger)
// Make sure the message is updated, sender s2de
s.Require().NotNil(resp)
s.Require().Len(resp.Messages(), 2)
var message *common.Message
for _, m := range resp.Messages() {
if m.ID == contactRequests[0].ID {
message = m
}
}
s.Require().NotNil(message)
s.Require().Equal(message.ID, contactRequests[0].ID)
s.Require().Equal(common.ContactRequestStateAccepted, message.ContactRequestState)
s.Require().Len(resp.Messages(), 1)
s.Require().Equal(resp.Messages()[0].ID, contactRequests[0].ID)
s.Require().Equal(common.ContactRequestStateAccepted, resp.Messages()[0].ContactRequestState)
// Make sure we consider them a mutual contact, sender side
mutualContacts = s.m.MutualContacts()
@ -168,7 +159,6 @@ func (s *MessengerVerificationRequests) mutualContact(theirMessenger *Messenger)
// Check the contact state is correctly set
s.Require().Len(resp.Contacts, 1)
s.Require().True(resp.Contacts[0].mutual())
}
func (s *MessengerVerificationRequests) TestAcceptVerificationRequests() {

View File

@ -16,7 +16,7 @@ import (
"github.com/status-im/status-go/protocol/transport"
)
func (m *Messenger) acceptContactRequest(requestID string, syncing bool) (*MessengerResponse, error) {
func (m *Messenger) acceptContactRequest(ctx context.Context, requestID string, syncing bool) (*MessengerResponse, error) {
contactRequest, err := m.persistence.MessageByID(requestID)
if err != nil {
m.logger.Error("could not find contact request message", zap.Error(err))
@ -24,9 +24,30 @@ func (m *Messenger) acceptContactRequest(requestID string, syncing bool) (*Messe
}
m.logger.Info("acceptContactRequest")
// We send a contact update for compatibility with 0.90 desktop, once that's
// not an issue anymore, we can set the last bool flag to `false`
return m.addContact(contactRequest.From, "", "", "", contactRequest.ID, "", syncing, true, false)
response, err := m.addContact(ctx, contactRequest.From, "", "", "", contactRequest.ID, "", syncing, false, false)
if err != nil {
return nil, err
}
// Force activate chat
chat, ok := m.allChats.Load(contactRequest.From)
if !ok {
publicKey, err := common.HexToPubkey(contactRequest.From)
if err != nil {
return nil, err
}
chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
}
chat.Active = true
if err := m.saveChat(chat); err != nil {
return nil, err
}
response.AddChat(chat)
return response, nil
}
func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*MessengerResponse, error) {
@ -35,7 +56,7 @@ func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.
return nil, err
}
response, err := m.acceptContactRequest(request.ID.String(), false)
response, err := m.acceptContactRequest(ctx, request.ID.String(), false)
if err != nil {
return nil, err
}
@ -89,6 +110,7 @@ func (m *Messenger) declineContactRequest(requestID string, syncing bool) (*Mess
return nil, err
}
if notification != nil {
notification.Name = contact.PrimaryName()
notification.Message = contactRequest
notification.Read = true
notification.Dismissed = true
@ -133,7 +155,8 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se
return nil, err
}
response, err := m.addContact(
return m.addContact(
ctx,
chatID,
"",
"",
@ -142,50 +165,8 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se
request.Message,
false,
false,
false,
true,
)
if err != nil {
return nil, err
}
publicKey, err := common.HexToPubkey(chatID)
if err != nil {
return nil, err
}
// A valid added chat is required.
_, ok := m.allChats.Load(chatID)
if !ok {
// Create a one to one chat and set active to false
chat := CreateOneToOneChat(chatID, publicKey, m.getTimesource())
chat.Active = false
err = m.initChatSyncFields(chat)
if err != nil {
return nil, err
}
err = m.saveChat(chat)
if err != nil {
return nil, err
}
}
chatMessage := &common.Message{}
chatMessage.ChatId = chatID
chatMessage.Text = request.Message
chatMessage.ContentType = protobuf.ChatMessage_CONTACT_REQUEST
chatMessage.ContactRequestState = common.ContactRequestStatePending
messageResponse, err := m.sendChatMessage(ctx, chatMessage)
if err != nil {
return nil, err
}
err = response.Merge(messageResponse)
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, contactRequestID string) (*MessengerResponse, error) {
@ -239,6 +220,7 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
}
if notification != nil {
notification.Name = contact.PrimaryName()
notification.Message = contactRequest
notification.Read = true
notification.Accepted = true
@ -256,7 +238,7 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
return response, nil
}
func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRequestID string, contactRequestText string, syncing bool, sendContactUpdate bool, createOutgoingContactRequestNotification bool) (*MessengerResponse, error) {
func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, displayName, contactRequestID string, contactRequestText string, syncing bool, sendContactUpdate bool, createOutgoingContactRequestNotification bool) (*MessengerResponse, error) {
contact, err := m.BuildContact(&requests.BuildContact{PublicKey: pubKey})
if err != nil {
return nil, err
@ -323,6 +305,7 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
if err != nil {
return nil, err
}
if err := m.saveChat(profileChat); err != nil {
return nil, err
}
@ -403,13 +386,17 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
// Add outgoing contact request notification
if createOutgoingContactRequestNotification {
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
contactRequest, err := m.generateContactRequest(clock, timestamp, contact, contactRequestText)
contactRequest, err := m.generateContactRequest(clock, timestamp, contact, contactRequestText, true)
if err != nil {
return nil, err
}
response.AddMessage(contactRequest)
err = m.persistence.SaveMessages([]*common.Message{contactRequest})
messageResponse, err := m.sendChatMessage(ctx, contactRequest)
if err != nil {
return nil, err
}
err = response.Merge(messageResponse)
if err != nil {
return nil, err
}
@ -423,24 +410,27 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
// Add contact
response.AddContact(contact)
return response, nil
}
func (m *Messenger) generateContactRequest(clock uint64, timestamp uint64, contact *Contact, text string) (*common.Message, error) {
func (m *Messenger) generateContactRequest(clock uint64, timestamp uint64, contact *Contact, text string, outgoing bool) (*common.Message, error) {
if contact == nil {
return nil, errors.New("contact cannot be nil")
}
contactRequest := &common.Message{}
contactRequest.ChatId = contact.ID
contactRequest.WhisperTimestamp = timestamp
contactRequest.Seen = true
contactRequest.Text = text
contactRequest.From = contact.ID
if outgoing {
contactRequest.From = m.myHexIdentity()
} else {
contactRequest.From = contact.ID
}
contactRequest.LocalChatID = contact.ID
contactRequest.ContentType = protobuf.ChatMessage_CONTACT_REQUEST
contactRequest.Clock = clock
contactRequest.ID = defaultContactRequestID(contact.ID)
if contact.mutual() {
contactRequest.ContactRequestState = common.ContactRequestStateAccepted
} else {
@ -459,7 +449,9 @@ func (m *Messenger) generateOutgoingContactRequestNotification(contact *Contact,
Message: contactRequest,
Timestamp: m.getTimesource().GetCurrentTime(),
ChatID: contact.ID,
Read: contactRequest.ContactRequestState == common.ContactRequestStateAccepted || contactRequest.ContactRequestState == common.ContactRequestStateDismissed,
Read: contactRequest.ContactRequestState == common.ContactRequestStateAccepted ||
contactRequest.ContactRequestState == common.ContactRequestStateDismissed ||
contactRequest.ContactRequestState == common.ContactRequestStatePending,
Accepted: contactRequest.ContactRequestState == common.ContactRequestStateAccepted,
Dismissed: contactRequest.ContactRequestState == common.ContactRequestStateDismissed,
}
@ -477,6 +469,7 @@ func (m *Messenger) AddContact(ctx context.Context, request *requests.AddContact
}
return m.addContact(
ctx,
id,
request.ENSName,
request.Nickname,
@ -944,9 +937,6 @@ func (m *Messenger) AcceptLatestContactRequestForContact(ctx context.Context, re
if err != nil {
return nil, err
}
if contactRequestID == "" {
contactRequestID = defaultContactRequestID(request.ID.String())
}
return m.AcceptContactRequest(ctx, &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequestID)})
}
@ -961,10 +951,6 @@ func (m *Messenger) DismissLatestContactRequestForContact(ctx context.Context, r
return nil, err
}
if contactRequestID == "" {
contactRequestID = defaultContactRequestID(request.ID.String())
}
return m.DeclineContactRequest(ctx, &requests.DeclineContactRequest{ID: types.Hex2Bytes(contactRequestID)})
}

View File

@ -270,8 +270,33 @@ func (m *Messenger) PendingNotificationContactRequest(contactID string) (*Activi
return m.persistence.ActiveContactRequestNotification(contactID)
}
func (m *Messenger) createContactRequestForContactUpdate(contact *Contact, messageState *ReceivedMessageState) (*common.Message, error) {
contactRequest, err := m.generateContactRequest(
messageState.CurrentMessageState.Message.Clock,
messageState.CurrentMessageState.WhisperTimestamp,
contact,
"Please add me to your contacts",
false,
)
if err != nil {
return nil, err
}
contactRequest.ID = defaultContactRequestID(contact.ID)
// save this message
messageState.Response.AddMessage(contactRequest)
err = m.persistence.SaveMessages([]*common.Message{contactRequest})
if err != nil {
return nil, err
}
return contactRequest, nil
}
func (m *Messenger) createIncomingContactRequestNotification(contact *Contact, messageState *ReceivedMessageState, contactRequest *common.Message, createNewNotification bool) error {
if contactRequest != nil && contactRequest.ContactRequestState == common.ContactRequestStateAccepted {
if contactRequest.ContactRequestState == common.ContactRequestStateAccepted {
// Pull one from the db if there
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
if err != nil {
@ -279,6 +304,7 @@ func (m *Messenger) createIncomingContactRequestNotification(contact *Contact, m
}
if notification != nil {
notification.Name = contact.PrimaryName()
notification.Message = contactRequest
notification.Read = true
notification.Accepted = true
@ -293,82 +319,6 @@ func (m *Messenger) createIncomingContactRequestNotification(contact *Contact, m
return nil
}
if contactRequest == nil || contactRequest.ContactRequestState == common.ContactRequestStatePending {
notification, err := m.PendingNotificationContactRequest(contact.ID)
if err != nil {
return err
}
// If there's already a notification, we will check whether is a default notification
// that has not been dismissed (nor accepted???)
// If it is, we replace it with a non-default, since it contains a message
if notification != nil {
// Check if it's the default notification
if notification.Message.ID == defaultContactRequestID(contact.ID) {
// Nothing to do, we already have a default notification
if contactRequest == nil {
return nil
}
// We first dismiss it in the database
err := m.persistence.DismissActivityCenterNotifications([]types.HexBytes{types.Hex2Bytes(notification.Message.ID)})
if err != nil {
return err
}
// we mark the notification as dismissed & read
notification.Dismissed = true
notification.Read = true
// We remove it from the response, since the client has never seen it, better to just remove it
found := messageState.Response.RemoveActivityCenterNotification(notification.Message.ID)
// Otherwise, it means we have already passed it to the client, so we add it with a `dismissed` flag
// so it can clean up
if !found {
messageState.Response.AddActivityCenterNotification(notification)
}
}
}
}
// Legacy//ContactUpdate contact request
if contactRequest == nil {
if messageState.CurrentMessageState == nil || messageState.CurrentMessageState.MessageID == "" {
return errors.New("no available id")
}
// We use a known id so that we can check if already in the database
defaultID := defaultContactRequestID(contact.ID)
// Pull one from the db if there
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(defaultID))
if err != nil {
return err
}
// if the notification is accepted, we clear it, as this one will replace it
if notification != nil && notification.Accepted {
err = m.persistence.DeleteActivityCenterNotification(types.FromHex(defaultID))
if err != nil {
return err
}
}
// generate request message
contactRequest, err = m.generateContactRequest(
messageState.CurrentMessageState.Message.Clock,
messageState.CurrentMessageState.WhisperTimestamp,
contact,
"Please add me to your contacts",
)
if err != nil {
return err
}
// save this message
messageState.Response.AddMessage(contactRequest)
err = m.persistence.SaveMessages([]*common.Message{contactRequest})
if err != nil {
return err
}
}
if !createNewNotification {
return nil
}
@ -829,7 +779,7 @@ func (m *Messenger) handleAcceptContactRequestMessage(state *ReceivedMessageStat
// If the state has changed from non-mutual contact, to mutual contact
// we want to notify the user
if processingResponse.newContactRequestReceived && contact.mutual() {
if contact.mutual() {
// We set the chat as active, this is currently the expected behavior
// for mobile, it might change as we implement further the activity
// center
@ -874,14 +824,7 @@ func (m *Messenger) handleAcceptContactRequestMessage(state *ReceivedMessageStat
}
func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, message protobuf.AcceptContactRequest, senderID string) error {
// outgoing contact requests are created on the side of a sender
err := m.handleAcceptContactRequestMessage(state, message.Clock, defaultContactRequestID(senderID), true)
if err != nil {
m.logger.Warn("could not accept contact request", zap.Error(err))
}
// legacy contact requests: the ones that are send with SendContactRequest
err = m.handleAcceptContactRequestMessage(state, message.Clock, message.Id, false)
err := m.handleAcceptContactRequestMessage(state, message.Clock, message.Id, false)
if err != nil {
m.logger.Warn("could not accept contact request", zap.Error(err))
}
@ -965,12 +908,17 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
}
if result.newContactRequestReceived {
err = m.createIncomingContactRequestNotification(contact, state, nil, true)
contactRequest, err := m.createContactRequestForContactUpdate(contact, state)
if err != nil {
return err
}
err = m.createIncomingContactRequestNotification(contact, state, contactRequest, true)
if err != nil {
return err
}
}
logger.Debug("handled propagated state", zap.Any("state after update", contact.ContactRequestPropagatedState()))
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)