Support outgoing contact requests (#3120)

* Outgoing contact requests

* Test fix

* Test fix

* Fixes

* Bugfixes

* Bugfixes

* Almost there

* Removed the activity center notification

* Test update

* Almost ready

* Fixes

* Fixes
This commit is contained in:
Alexander 2023-02-21 19:08:11 +01:00 committed by GitHub
parent 78c677742e
commit 27730057d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 368 additions and 172 deletions

View File

@ -95,7 +95,7 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit
if notification.Type == ActivityCenterNotificationTypeNewOneToOne || if notification.Type == ActivityCenterNotificationTypeNewOneToOne ||
notification.Type == ActivityCenterNotificationTypeNewPrivateGroupChat { notification.Type == ActivityCenterNotificationTypeNewPrivateGroupChat {
// Delete other notifications so it pop us again if not currently dismissed // Delete other notifications, so it pops us again if it was not dismissed
_, err = tx.Exec(`DELETE FROM activity_center_notifications WHERE id = ? AND (dismissed OR accepted)`, notification.ID) _, err = tx.Exec(`DELETE FROM activity_center_notifications WHERE id = ? AND (dismissed OR accepted)`, notification.ID)
if err != nil { if err != nil {
return err return err
@ -871,12 +871,11 @@ func (db sqlitePersistence) ActiveContactRequestNotification(contactID string) (
} }
func (db sqlitePersistence) RemoveAllContactRequestActivityCenterNotifications(chatID string) error { func (db sqlitePersistence) RemoveAllContactRequestActivityCenterNotifications(chatID string) error {
_, err := db.db.Exec(` _, err := db.db.Exec(
DELETE FROM activity_center_notifications `DELETE FROM activity_center_notifications WHERE chat_id = ? AND notification_type = ?`,
WHERE chatID,
chat_id = ? ActivityCenterNotificationTypeContactRequest,
AND notification_type = ? )
`, chatID, ActivityCenterNotificationTypeContactRequest)
return err return err
} }

View File

@ -3781,7 +3781,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
logger.Debug("Handling AcceptContactRequest") logger.Debug("Handling AcceptContactRequest")
message := msg.ParsedMessage.Interface().(protobuf.AcceptContactRequest) message := msg.ParsedMessage.Interface().(protobuf.AcceptContactRequest)
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message) m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
err = m.HandleAcceptContactRequest(messageState, message) err = m.HandleAcceptContactRequest(messageState, message, senderID)
if err != nil { if err != nil {
logger.Warn("failed to handle AcceptContactRequest", zap.Error(err)) logger.Warn("failed to handle AcceptContactRequest", zap.Error(err))
allMessagesProcessed = false allMessagesProcessed = false

View File

@ -270,7 +270,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAndDismissContactRequest() {
s.Require().Equal(contactRequests[0].ContactRequestState, common.ContactRequestStatePending) s.Require().Equal(contactRequests[0].ContactRequestState, common.ContactRequestStatePending)
// Dismiss contact request, receiver side // Dismiss contact request, receiver side
resp, err = theirMessenger.DismissContactRequest(context.Background(), &requests.DismissContactRequest{ID: types.Hex2Bytes(contactRequests[0].ID)}) resp, err = theirMessenger.DeclineContactRequest(context.Background(), &requests.DeclineContactRequest{ID: types.Hex2Bytes(contactRequests[0].ID)})
s.Require().NoError(err) s.Require().NoError(err)
// Check the contact state is correctly set // Check the contact state is correctly set
@ -427,7 +427,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAcceptAndRetractContactRequest
mutualContacts = s.m.MutualContacts() mutualContacts = s.m.MutualContacts()
s.Require().Len(mutualContacts, 1) s.Require().Len(mutualContacts, 1)
resp, err = s.m.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(contactID)}) resp, err = s.m.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(contactID)})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(resp) s.Require().NotNil(resp)
s.Require().Len(resp.Contacts, 1) s.Require().Len(resp.Contacts, 1)
@ -681,7 +681,7 @@ func (s *MessengerContactRequestSuite) TestAcceptLatestContactRequestForContact(
s.Require().Len(resp.Messages(), 1) s.Require().Len(resp.Messages(), 1)
s.Require().Equal(common.ContactRequestStatePending, resp.Messages()[0].ContactRequestState) s.Require().Equal(common.ContactRequestStatePending, resp.Messages()[0].ContactRequestState)
// Make sure it's not returned as coming from us // Make sure it's not returned
contactRequests, _, err := s.m.PendingContactRequests("", 10) contactRequests, _, err := s.m.PendingContactRequests("", 10)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(contactRequests, 0) s.Require().Len(contactRequests, 0)
@ -1038,7 +1038,7 @@ func (s *MessengerContactRequestSuite) TestReceiveMultipleLegacy() {
// Remove contact // Remove contact
_, err = s.m.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(contactID)}) _, err = s.m.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(contactID)})
s.Require().NoError(err) s.Require().NoError(err)
// Wait for the message to reach its destination // Wait for the message to reach its destination
@ -1277,7 +1277,7 @@ func (s *MessengerContactRequestSuite) TestPairedDevicesRemoveContact() {
s.Require().Len(resp.Contacts, 1) s.Require().Len(resp.Contacts, 1)
s.Require().True(resp.Contacts[0].mutual()) s.Require().True(resp.Contacts[0].mutual())
resp, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(bob.myHexIdentity())}) resp, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(resp) s.Require().NotNil(resp)
s.Require().Len(resp.Contacts, 1) s.Require().Len(resp.Contacts, 1)
@ -1603,7 +1603,7 @@ func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsCorrectOrd
s.Require().Len(resp.Contacts, 1) s.Require().Len(resp.Contacts, 1)
s.Require().True(resp.Contacts[0].mutual()) s.Require().True(resp.Contacts[0].mutual())
_, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(bob.myHexIdentity())}) _, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())})
s.Require().NoError(err) s.Require().NoError(err)
// adds bob again to her device // adds bob again to her device
@ -1689,7 +1689,7 @@ func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsWrongOrder
s.Require().Len(resp.Contacts, 1) s.Require().Len(resp.Contacts, 1)
s.Require().True(resp.Contacts[0].mutual()) s.Require().True(resp.Contacts[0].mutual())
_, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(bob.myHexIdentity())}) _, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())})
s.Require().NoError(err) s.Require().NoError(err)
// adds bob again to her device // adds bob again to her device

View File

@ -25,7 +25,7 @@ func (m *Messenger) acceptContactRequest(requestID string, syncing bool) (*Messe
m.logger.Info("acceptContactRequest") m.logger.Info("acceptContactRequest")
// We send a contact update for compatibility with 0.90 desktop, once that's // 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` // not an issue anymore, we can set the last bool flag to `false`
return m.addContact(contactRequest.From, "", "", "", contactRequest.ID, syncing, true) return m.addContact(contactRequest.From, "", "", "", contactRequest.ID, "", syncing, true, false)
} }
func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*MessengerResponse, error) { func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*MessengerResponse, error) {
@ -47,6 +47,140 @@ func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.
return response, nil return response, nil
} }
func (m *Messenger) declineContactRequest(requestID string, syncing bool) (*MessengerResponse, error) {
m.logger.Info("declineContactRequest")
contactRequest, err := m.persistence.MessageByID(requestID)
if err != nil {
return nil, err
}
contact, err := m.BuildContact(contactRequest.From)
if err != nil {
return nil, err
}
response := &MessengerResponse{}
if !syncing {
_, clock, err := m.getOneToOneAndNextClock(contact)
if err != nil {
return nil, err
}
contact.DismissContactRequest(clock)
err = m.persistence.SaveContact(contact, nil)
if err != nil {
return nil, err
}
response.AddContact(contact)
}
contactRequest.ContactRequestState = common.ContactRequestStateDismissed
err = m.persistence.SetContactRequestState(contactRequest.ID, contactRequest.ContactRequestState)
if err != nil {
return nil, err
}
// update notification with the correct status
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
if err != nil {
return nil, err
}
if notification != nil {
notification.Message = contactRequest
notification.Read = true
notification.Dismissed = true
err = m.addActivityCenterNotification(response, notification)
if err != nil {
m.logger.Error("failed to save notification", zap.Error(err))
return nil, err
}
}
response.AddMessage(contactRequest)
return response, nil
}
func (m *Messenger) DeclineContactRequest(ctx context.Context, request *requests.DeclineContactRequest) (*MessengerResponse, error) {
err := request.Validate()
if err != nil {
return nil, err
}
response, err := m.declineContactRequest(request.ID.String(), false)
if err != nil {
return nil, err
}
err = m.syncContactRequestDecision(ctx, request.ID.String(), false, m.dispatchMessage)
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) cancelOutgoingContactRequest(ctx context.Context, ID string) (*MessengerResponse, error) {
response := &MessengerResponse{}
// remove contact
err := m.removeContact(ctx, response, ID, true)
if err != nil {
return nil, err
}
// remove notification
notificationID := types.FromHex(defaultContactRequestID(ID))
notification, err := m.persistence.GetActivityCenterNotificationByID(notificationID)
if err != nil {
return nil, err
}
if notification != nil {
err := m.persistence.DeleteActivityCenterNotification(notificationID)
if err != nil {
return nil, err
}
}
// retract contact
clock, _ := m.getLastClockWithRelatedChat()
retractContactRequest := &protobuf.RetractContactRequest{
Clock: clock,
}
encodedMessage, err := proto.Marshal(retractContactRequest)
if err != nil {
return nil, err
}
_, err = m.dispatchMessage(context.Background(), common.RawMessage{
LocalChatID: ID,
Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_RETRACT_CONTACT_REQUEST,
ResendAutomatically: true,
})
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) CancelOutgoingContactRequest(ctx context.Context, request *requests.CancelOutgoingContactRequest) (*MessengerResponse, error) {
err := request.Validate()
if err != nil {
return nil, err
}
response, err := m.cancelOutgoingContactRequest(ctx, request.ID.String())
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*MessengerResponse, error) { func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*MessengerResponse, error) {
err := request.Validate() err := request.Validate()
if err != nil { if err != nil {
@ -55,7 +189,17 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se
chatID := request.ID.String() chatID := request.ID.String()
response, err := m.addContact(chatID, "", "", "", "", false, false) response, err := m.addContact(
chatID,
"",
"",
"",
"",
request.Message,
false,
false,
false,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -100,82 +244,6 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se
return response, nil return response, nil
} }
func (m *Messenger) dismissContactRequest(requestID string, syncing bool) (*MessengerResponse, error) {
m.logger.Info("dismissContactRequest")
contactRequest, err := m.persistence.MessageByID(requestID)
if err != nil {
return nil, err
}
contact, err := m.BuildContact(contactRequest.From)
if err != nil {
return nil, err
}
response := &MessengerResponse{}
if !syncing {
_, clock, err := m.getOneToOneAndNextClock(contact)
if err != nil {
return nil, err
}
contact.DismissContactRequest(clock)
err = m.persistence.SaveContact(contact, nil)
if err != nil {
return nil, err
}
response.AddContact(contact)
}
contactRequest.ContactRequestState = common.ContactRequestStateDismissed
err = m.persistence.SetContactRequestState(contactRequest.ID, contactRequest.ContactRequestState)
if err != nil {
return nil, err
}
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
if err != nil {
return nil, err
}
if notification != nil {
notification.Message = contactRequest
notification.Read = true
notification.Dismissed = true
err = m.addActivityCenterNotification(response, notification)
if err != nil {
m.logger.Error("failed to save notification", zap.Error(err))
return nil, err
}
}
response.AddMessage(contactRequest)
return response, nil
}
func (m *Messenger) DismissContactRequest(ctx context.Context, request *requests.DismissContactRequest) (*MessengerResponse, error) {
err := request.Validate()
if err != nil {
return nil, err
}
response, err := m.dismissContactRequest(request.ID.String(), false)
if err != nil {
return nil, err
}
err = m.syncContactRequestDecision(ctx, request.ID.String(), false, m.dispatchMessage)
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, contactRequestID string) (*MessengerResponse, error) { func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, contactRequestID string) (*MessengerResponse, error) {
contactRequest, err := m.persistence.MessageByID(contactRequestID) contactRequest, err := m.persistence.MessageByID(contactRequestID)
if err != nil { if err != nil {
@ -244,7 +312,7 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
return response, nil return response, nil
} }
func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRequestID string, syncing bool, sendContactUpdate bool) (*MessengerResponse, error) { func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRequestID string, contactRequestText string, syncing bool, sendContactUpdate bool, createOutgoingContactRequestNotification bool) (*MessengerResponse, error) {
contact, err := m.BuildContact(pubKey) contact, err := m.BuildContact(pubKey)
if err != nil { if err != nil {
return nil, err return nil, err
@ -256,13 +324,11 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
} }
if ensName != "" { if ensName != "" {
clock := m.getTimesource().GetCurrentTime()
err := m.ensVerifier.ENSVerified(pubKey, ensName, clock) err := m.ensVerifier.ENSVerified(pubKey, ensName, clock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
if err := m.addENSNameToContact(contact); err != nil { if err := m.addENSNameToContact(contact); err != nil {
return nil, err return nil, err
} }
@ -276,7 +342,6 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
} }
contact.LastUpdatedLocally = clock contact.LastUpdatedLocally = clock
contact.ContactRequestSent(clock) contact.ContactRequestSent(clock)
if !syncing { if !syncing {
@ -332,11 +397,13 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
return nil, err return nil, err
} }
// Get ENS name of a current user
ensName, err = m.settings.ENSName() ensName, err = m.settings.ENSName()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Get display name of a current user
displayName, err = m.settings.DisplayName() displayName, err = m.settings.DisplayName()
if err != nil { if err != nil {
return nil, err return nil, err
@ -372,11 +439,13 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
} }
} }
// Sends a standalone ChatIdentity message
err = m.handleStandaloneChatIdentity(chat) err = m.handleStandaloneChatIdentity(chat)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Add chat
response.AddChat(profileChat) response.AddChat(profileChat)
_, err = m.transport.InitFilters([]string{profileChat.ID}, []*ecdsa.PublicKey{publicKey}) _, err = m.transport.InitFilters([]string{profileChat.ID}, []*ecdsa.PublicKey{publicKey})
@ -390,18 +459,88 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
return nil, err return nil, err
} }
// Add outgoing contact request notification
if createOutgoingContactRequestNotification {
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
contactRequest, err := m.generateContactRequest(clock, timestamp, contact, contactRequestText)
if err != nil {
return nil, err
}
response.AddMessage(contactRequest)
err = m.persistence.SaveMessages([]*common.Message{contactRequest})
if err != nil {
return nil, err
}
notification := m.generateOutgoingContactRequestNotification(contact, contactRequest)
err = m.addActivityCenterNotification(response, notification)
if err != nil {
return nil, err
}
}
// Add contact
response.AddContact(contact) response.AddContact(contact)
return response, nil return response, nil
} }
func (m *Messenger) generateContactRequest(clock uint64, timestamp uint64, contact *Contact, text string) (*common.Message, error) {
if contact == nil {
return nil, errors.New("contact cannot be nil")
}
contactRequest := &common.Message{}
contactRequest.WhisperTimestamp = timestamp
contactRequest.Seen = true
contactRequest.Text = text
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 {
contactRequest.ContactRequestState = common.ContactRequestStatePending
}
err := contactRequest.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
return contactRequest, err
}
func (m *Messenger) generateOutgoingContactRequestNotification(contact *Contact, contactRequest *common.Message) *ActivityCenterNotification {
return &ActivityCenterNotification{
ID: types.FromHex(contactRequest.ID),
Type: ActivityCenterNotificationTypeContactRequest,
Name: contact.CanonicalName(),
Author: m.myHexIdentity(),
Message: contactRequest,
Timestamp: m.getTimesource().GetCurrentTime(),
ChatID: contact.ID,
Read: contactRequest.ContactRequestState == common.ContactRequestStateAccepted || contactRequest.ContactRequestState == common.ContactRequestStateDismissed,
Accepted: contactRequest.ContactRequestState == common.ContactRequestStateAccepted,
Dismissed: contactRequest.ContactRequestState == common.ContactRequestStateDismissed,
}
}
func (m *Messenger) AddContact(ctx context.Context, request *requests.AddContact) (*MessengerResponse, error) { func (m *Messenger) AddContact(ctx context.Context, request *requests.AddContact) (*MessengerResponse, error) {
err := request.Validate() err := request.Validate()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return m.addContact(request.ID.String(), request.ENSName, request.Nickname, request.DisplayName, "", false, true) return m.addContact(
request.ID.String(),
request.ENSName,
request.Nickname,
request.DisplayName,
"",
"Please add me to your contacts",
false,
true,
true,
)
} }
func (m *Messenger) resetLastPublishedTimeForChatIdentity() error { func (m *Messenger) resetLastPublishedTimeForChatIdentity() error {
@ -801,7 +940,7 @@ func (m *Messenger) RetractContactRequest(request *requests.RetractContactReques
if err != nil { if err != nil {
return nil, err return nil, err
} }
contact, ok := m.allContacts.Load(request.ContactID.String()) contact, ok := m.allContacts.Load(request.ID.String())
if !ok { if !ok {
return nil, errors.New("contact not found") return nil, errors.New("contact not found")
} }
@ -874,7 +1013,7 @@ func (m *Messenger) DismissLatestContactRequestForContact(ctx context.Context, r
contactRequestID = defaultContactRequestID(request.ID.String()) contactRequestID = defaultContactRequestID(request.ID.String())
} }
return m.DismissContactRequest(ctx, &requests.DismissContactRequest{ID: types.Hex2Bytes(contactRequestID)}) return m.DeclineContactRequest(ctx, &requests.DeclineContactRequest{ID: types.Hex2Bytes(contactRequestID)})
} }
func (m *Messenger) PendingContactRequests(cursor string, limit int) ([]*common.Message, string, error) { func (m *Messenger) PendingContactRequests(cursor string, limit int) ([]*common.Message, string, error) {

View File

@ -262,7 +262,28 @@ func (m *Messenger) PendingNotificationContactRequest(contactID string) (*Activi
return m.persistence.ActiveContactRequestNotification(contactID) return m.persistence.ActiveContactRequestNotification(contactID)
} }
func (m *Messenger) createContactRequestNotification(contact *Contact, messageState *ReceivedMessageState, contactRequest *common.Message, createNewNotification bool) error { func (m *Messenger) createIncomingContactRequestNotification(contact *Contact, messageState *ReceivedMessageState, contactRequest *common.Message, createNewNotification bool) error {
if contactRequest != nil && contactRequest.ContactRequestState == common.ContactRequestStateAccepted {
// Pull one from the db if there
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
if err != nil {
return err
}
if notification != nil {
notification.Message = contactRequest
notification.Read = true
notification.Accepted = true
notification.Dismissed = false
err = m.persistence.SaveActivityCenterNotification(notification)
if err != nil {
return err
}
messageState.Response.AddMessage(contactRequest)
messageState.Response.AddActivityCenterNotification(notification)
}
return nil
}
if contactRequest == nil || contactRequest.ContactRequestState == common.ContactRequestStatePending { if contactRequest == nil || contactRequest.ContactRequestState == common.ContactRequestStatePending {
notification, err := m.PendingNotificationContactRequest(contact.ID) notification, err := m.PendingNotificationContactRequest(contact.ID)
@ -321,28 +342,19 @@ func (m *Messenger) createContactRequestNotification(contact *Contact, messageSt
} }
} }
contactRequest = &common.Message{} // generate request message
contactRequest, err = m.generateContactRequest(
contactRequest.WhisperTimestamp = messageState.CurrentMessageState.WhisperTimestamp messageState.CurrentMessageState.Message.Clock,
contactRequest.Seen = true messageState.CurrentMessageState.WhisperTimestamp,
contactRequest.Text = "Please add me to your contacts" contact,
contactRequest.From = contact.ID "Please add me to your contacts",
contactRequest.ContentType = protobuf.ChatMessage_CONTACT_REQUEST )
contactRequest.Clock = messageState.CurrentMessageState.Message.Clock
contactRequest.ID = defaultID
if contact.mutual() {
contactRequest.ContactRequestState = common.ContactRequestStateAccepted
} else {
contactRequest.ContactRequestState = common.ContactRequestStatePending
}
err = contactRequest.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
if err != nil { if err != nil {
return err return err
} }
// save this message
messageState.Response.AddMessage(contactRequest) messageState.Response.AddMessage(contactRequest)
err = m.persistence.SaveMessages([]*common.Message{contactRequest}) err = m.persistence.SaveMessages([]*common.Message{contactRequest})
if err != nil { if err != nil {
return err return err
@ -762,10 +774,10 @@ func (m *Messenger) handleAcceptContactRequest(
response *MessengerResponse, response *MessengerResponse,
contact *Contact, contact *Contact,
originalRequest *common.Message, originalRequest *common.Message,
message protobuf.AcceptContactRequest) (ContactRequestProcessingResponse, error) { clock uint64) (ContactRequestProcessingResponse, error) {
m.logger.Debug("received contact request", zap.Uint64("clock-sent", message.Clock), zap.Uint64("current-clock", contact.ContactRequestRemoteClock), zap.Uint64("current-state", uint64(contact.ContactRequestRemoteState))) m.logger.Debug("received contact request", zap.Uint64("clock-sent", clock), zap.Uint64("current-clock", contact.ContactRequestRemoteClock), zap.Uint64("current-state", uint64(contact.ContactRequestRemoteState)))
if contact.ContactRequestRemoteClock > message.Clock { if contact.ContactRequestRemoteClock > clock {
m.logger.Debug("not handling accept since clock lower") m.logger.Debug("not handling accept since clock lower")
return ContactRequestProcessingResponse{}, nil return ContactRequestProcessingResponse{}, nil
} }
@ -774,14 +786,14 @@ func (m *Messenger) handleAcceptContactRequest(
// be that we sent a legacy contact request/contact-update, or another // be that we sent a legacy contact request/contact-update, or another
// device has sent it, and we haven't synchronized it // device has sent it, and we haven't synchronized it
if originalRequest == nil { if originalRequest == nil {
return contact.ContactRequestAccepted(message.Clock), nil return contact.ContactRequestAccepted(clock), nil
} }
if originalRequest.LocalChatID != contact.ID { if originalRequest.LocalChatID != contact.ID {
return ContactRequestProcessingResponse{}, errors.New("can't accept contact request not sent to user") return ContactRequestProcessingResponse{}, errors.New("can't accept contact request not sent to user")
} }
contact.ContactRequestAccepted(message.Clock) contact.ContactRequestAccepted(clock)
originalRequest.ContactRequestState = common.ContactRequestStateAccepted originalRequest.ContactRequestState = common.ContactRequestStateAccepted
@ -794,15 +806,15 @@ func (m *Messenger) handleAcceptContactRequest(
return ContactRequestProcessingResponse{}, nil return ContactRequestProcessingResponse{}, nil
} }
func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, message protobuf.AcceptContactRequest) error { func (m *Messenger) handleAcceptContactRequestMessage(state *ReceivedMessageState, clock uint64, contactRequestID string, isOutgoing bool) error {
originalRequest, err := m.persistence.MessageByID(message.Id) request, err := m.persistence.MessageByID(contactRequestID)
if err != nil && err != common.ErrRecordNotFound { if err != nil && err != common.ErrRecordNotFound {
return err return err
} }
contact := state.CurrentMessageState.Contact contact := state.CurrentMessageState.Contact
processingResponse, err := m.handleAcceptContactRequest(state.Response, contact, originalRequest, message) processingResponse, err := m.handleAcceptContactRequest(state.Response, contact, request, clock)
if err != nil { if err != nil {
return err return err
} }
@ -818,14 +830,14 @@ func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, mess
return err return err
} }
if chat.LastClockValue < message.Clock { if chat.LastClockValue < clock {
chat.LastClockValue = message.Clock chat.LastClockValue = clock
} }
// NOTE(cammellos): This will re-enable the chat if it was deleted, and only // NOTE(cammellos): This will re-enable the chat if it was deleted, and only
// after we became contact, currently seems safe, but that needs // after we became contact, currently seems safe, but that needs
// discussing with UX. // discussing with UX.
if chat.DeletedAtClockValue < message.Clock { if chat.DeletedAtClockValue < clock {
chat.Active = true chat.Active = true
} }
@ -833,9 +845,16 @@ func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, mess
state.AllChats.Store(chat.ID, chat) state.AllChats.Store(chat.ID, chat)
} }
if originalRequest != nil { if request != nil {
// Update contact requests if existing, or create a new one if isOutgoing {
err = m.createContactRequestNotification(contact, state, originalRequest, processingResponse.newContactRequestReceived) notification := m.generateOutgoingContactRequestNotification(contact, request)
err = m.addActivityCenterNotification(state.Response, notification)
if err != nil {
return err
}
} else {
err = m.createIncomingContactRequestNotification(contact, state, request, processingResponse.newContactRequestReceived)
}
if err != nil { if err != nil {
m.logger.Warn("could not create contact request notification", zap.Error(err)) m.logger.Warn("could not create contact request notification", zap.Error(err))
} }
@ -846,6 +865,22 @@ func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, mess
return nil return nil
} }
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)
if err != nil {
m.logger.Warn("could not accept contact request", zap.Error(err))
}
return nil
}
func (m *Messenger) handleRetractContactRequest(contact *Contact, message protobuf.RetractContactRequest) error { func (m *Messenger) handleRetractContactRequest(contact *Contact, message protobuf.RetractContactRequest) error {
if contact.ID == m.myHexIdentity() { if contact.ID == m.myHexIdentity() {
m.logger.Debug("retraction coming from us, ignoring") m.logger.Debug("retraction coming from us, ignoring")
@ -922,7 +957,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
} }
if result.newContactRequestReceived { if result.newContactRequestReceived {
err = m.createContactRequestNotification(contact, state, nil, true) err = m.createIncomingContactRequestNotification(contact, state, nil, true)
if err != nil { if err != nil {
return err return err
} }
@ -946,7 +981,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
r := contact.ContactRequestReceived(message.ContactRequestClock) r := contact.ContactRequestReceived(message.ContactRequestClock)
if r.newContactRequestReceived { if r.newContactRequestReceived {
err = m.createContactRequestNotification(contact, state, nil, true) err = m.createIncomingContactRequestNotification(contact, state, nil, true)
if err != nil { if err != nil {
m.logger.Warn("could not create contact request notification", zap.Error(err)) m.logger.Warn("could not create contact request notification", zap.Error(err))
} }
@ -1255,7 +1290,7 @@ func (m *Messenger) HandleCommunityRequestToJoin(state *ReceivedMessageState, si
return err return err
} }
} else { } else {
// Activity Center notification, updating existing for accespted/declined // Activity Center notification, updating existing for accepted/declined
notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID) notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID)
if err != nil { if err != nil {
return err return err
@ -1751,7 +1786,7 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
receivedMessage.ContactRequestState = common.ContactRequestStatePending receivedMessage.ContactRequestState = common.ContactRequestStatePending
} }
err = m.createContactRequestNotification(contact, state, receivedMessage, true) err = m.createIncomingContactRequestNotification(contact, state, receivedMessage, true)
if err != nil { if err != nil {
return err return err
} }
@ -1762,7 +1797,6 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
} }
if receivedMessage.ContentType == protobuf.ChatMessage_CONTACT_REQUEST && chat.OneToOne() { if receivedMessage.ContentType == protobuf.ChatMessage_CONTACT_REQUEST && chat.OneToOne() {
chatContact := contact chatContact := contact
if isSyncMessage { if isSyncMessage {
chatContact, err = m.BuildContact(chat.ID) chatContact, err = m.BuildContact(chat.ID)
@ -1780,7 +1814,7 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
state.AllContacts.Store(chatContact.ID, chatContact) state.AllContacts.Store(chatContact.ID, chatContact)
if sendNotification { if sendNotification {
err = m.createContactRequestNotification(chatContact, state, receivedMessage, true) err = m.createIncomingContactRequestNotification(chatContact, state, receivedMessage, true)
if err != nil { if err != nil {
return err return err
} }
@ -2668,13 +2702,12 @@ func (m *Messenger) HandleSyncWalletAccount(state *ReceivedMessageState, message
func (m *Messenger) HandleSyncContactRequestDecision(state *ReceivedMessageState, message protobuf.SyncContactRequestDecision) error { func (m *Messenger) HandleSyncContactRequestDecision(state *ReceivedMessageState, message protobuf.SyncContactRequestDecision) error {
var err error var err error
var response *MessengerResponse var response *MessengerResponse
if message.DecisionStatus == protobuf.SyncContactRequestDecision_ACCEPTED { if message.DecisionStatus == protobuf.SyncContactRequestDecision_ACCEPTED {
response, err = m.updateAcceptedContactRequest(nil, message.RequestId) response, err = m.updateAcceptedContactRequest(nil, message.RequestId)
} else { } else {
response, err = m.dismissContactRequest(message.RequestId, true) response, err = m.declineContactRequest(message.RequestId, true)
} }
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,21 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrCancelOutgoingContactRequestInvalidID = errors.New("cancel-outgoing-contact-request: invalid id")
type CancelOutgoingContactRequest struct {
ID types.HexBytes `json:"id"`
}
func (a *CancelOutgoingContactRequest) Validate() error {
if len(a.ID) == 0 {
return ErrCancelOutgoingContactRequestInvalidID
}
return nil
}

View File

@ -0,0 +1,21 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrDeclineContactRequestInvalidID = errors.New("decline-contact-request: invalid id")
type DeclineContactRequest struct {
ID types.HexBytes `json:"id"`
}
func (a *DeclineContactRequest) Validate() error {
if len(a.ID) == 0 {
return ErrDeclineContactRequestInvalidID
}
return nil
}

View File

@ -1,21 +0,0 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrDismissContactRequestInvalidID = errors.New("dismiss-contact-request: invalid id")
type DismissContactRequest struct {
ID types.HexBytes `json:"id"`
}
func (a *DismissContactRequest) Validate() error {
if len(a.ID) == 0 {
return ErrDismissContactRequestInvalidID
}
return nil
}

View File

@ -9,11 +9,11 @@ import (
var ErrRetractContactRequestInvalidContactID = errors.New("retract-contact-request: invalid id") var ErrRetractContactRequestInvalidContactID = errors.New("retract-contact-request: invalid id")
type RetractContactRequest struct { type RetractContactRequest struct {
ContactID types.HexBytes `json:"contactId"` ID types.HexBytes `json:"id"`
} }
func (a *RetractContactRequest) Validate() error { func (a *RetractContactRequest) Validate() error {
if len(a.ContactID) == 0 { if len(a.ID) == 0 {
return ErrRetractContactRequestInvalidContactID return ErrRetractContactRequestInvalidContactID
} }

View File

@ -695,18 +695,26 @@ func (api *PublicAPI) MarkAllReadInCommunity(communityID string) ([]string, erro
return api.service.messenger.MarkAllReadInCommunity(communityID) return api.service.messenger.MarkAllReadInCommunity(communityID)
} }
func (api *PublicAPI) AddContact(ctx context.Context, request *requests.AddContact) (*protocol.MessengerResponse, error) {
return api.service.messenger.AddContact(ctx, request)
}
func (api *PublicAPI) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*protocol.MessengerResponse, error) { func (api *PublicAPI) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendContactRequest(ctx, request) return api.service.messenger.SendContactRequest(ctx, request)
} }
func (api *PublicAPI) AddContact(ctx context.Context, request *requests.AddContact) (*protocol.MessengerResponse, error) {
return api.service.messenger.AddContact(ctx, request)
}
func (api *PublicAPI) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*protocol.MessengerResponse, error) { func (api *PublicAPI) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.AcceptContactRequest(ctx, request) return api.service.messenger.AcceptContactRequest(ctx, request)
} }
func (api *PublicAPI) DeclineContactRequest(ctx context.Context, request *requests.DeclineContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.DeclineContactRequest(ctx, request)
}
func (api *PublicAPI) CancelOutgoingContactRequest(ctx context.Context, request *requests.CancelOutgoingContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.CancelOutgoingContactRequest(ctx, request)
}
func (api *PublicAPI) AcceptLatestContactRequestForContact(ctx context.Context, request *requests.AcceptLatestContactRequestForContact) (*protocol.MessengerResponse, error) { func (api *PublicAPI) AcceptLatestContactRequestForContact(ctx context.Context, request *requests.AcceptLatestContactRequestForContact) (*protocol.MessengerResponse, error) {
return api.service.messenger.AcceptLatestContactRequestForContact(ctx, request) return api.service.messenger.AcceptLatestContactRequestForContact(ctx, request)
} }
@ -719,10 +727,6 @@ func (api *PublicAPI) RetractContactRequest(ctx context.Context, request *reques
return api.service.messenger.RetractContactRequest(request) return api.service.messenger.RetractContactRequest(request)
} }
func (api *PublicAPI) DismissContactRequest(ctx context.Context, request *requests.DismissContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.DismissContactRequest(ctx, request)
}
func (api *PublicAPI) RemoveContact(ctx context.Context, pubKey string) (*protocol.MessengerResponse, error) { func (api *PublicAPI) RemoveContact(ctx context.Context, pubKey string) (*protocol.MessengerResponse, error) {
return api.service.messenger.RemoveContact(ctx, pubKey) return api.service.messenger.RemoveContact(ctx, pubKey)
} }