From 27730057d0914b0c53c3e87e1442def7edc76998 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 21 Feb 2023 19:08:11 +0100 Subject: [PATCH] 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 --- protocol/activity_center_persistence.go | 13 +- protocol/messenger.go | 2 +- protocol/messenger_contact_requests_test.go | 14 +- protocol/messenger_contacts.go | 309 +++++++++++++----- protocol/messenger_handler.go | 115 ++++--- .../cancel_outgoing_contact_request.go | 21 ++ protocol/requests/decline_contact_request.go | 21 ++ protocol/requests/dismiss_contact_request.go | 21 -- protocol/requests/retract_contact_request.go | 4 +- services/ext/api.go | 20 +- 10 files changed, 368 insertions(+), 172 deletions(-) create mode 100644 protocol/requests/cancel_outgoing_contact_request.go create mode 100644 protocol/requests/decline_contact_request.go delete mode 100644 protocol/requests/dismiss_contact_request.go diff --git a/protocol/activity_center_persistence.go b/protocol/activity_center_persistence.go index bcc5ffbdb..b46b0a0eb 100644 --- a/protocol/activity_center_persistence.go +++ b/protocol/activity_center_persistence.go @@ -95,7 +95,7 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit if notification.Type == ActivityCenterNotificationTypeNewOneToOne || 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) if err != nil { return err @@ -871,12 +871,11 @@ func (db sqlitePersistence) ActiveContactRequestNotification(contactID string) ( } func (db sqlitePersistence) RemoveAllContactRequestActivityCenterNotifications(chatID string) error { - _, err := db.db.Exec(` - DELETE FROM activity_center_notifications - WHERE - chat_id = ? - AND notification_type = ? - `, chatID, ActivityCenterNotificationTypeContactRequest) + _, err := db.db.Exec( + `DELETE FROM activity_center_notifications WHERE chat_id = ? AND notification_type = ?`, + chatID, + ActivityCenterNotificationTypeContactRequest, + ) return err } diff --git a/protocol/messenger.go b/protocol/messenger.go index 16d63275f..67ac2a1c7 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3781,7 +3781,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte logger.Debug("Handling AcceptContactRequest") message := msg.ParsedMessage.Interface().(protobuf.AcceptContactRequest) 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 { logger.Warn("failed to handle AcceptContactRequest", zap.Error(err)) allMessagesProcessed = false diff --git a/protocol/messenger_contact_requests_test.go b/protocol/messenger_contact_requests_test.go index b6ab3d5d8..2b51c1294 100644 --- a/protocol/messenger_contact_requests_test.go +++ b/protocol/messenger_contact_requests_test.go @@ -270,7 +270,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAndDismissContactRequest() { s.Require().Equal(contactRequests[0].ContactRequestState, common.ContactRequestStatePending) // 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) // Check the contact state is correctly set @@ -427,7 +427,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAcceptAndRetractContactRequest mutualContacts = s.m.MutualContacts() 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().NotNil(resp) s.Require().Len(resp.Contacts, 1) @@ -681,7 +681,7 @@ func (s *MessengerContactRequestSuite) TestAcceptLatestContactRequestForContact( s.Require().Len(resp.Messages(), 1) 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) s.Require().NoError(err) s.Require().Len(contactRequests, 0) @@ -1038,7 +1038,7 @@ func (s *MessengerContactRequestSuite) TestReceiveMultipleLegacy() { // 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) // Wait for the message to reach its destination @@ -1277,7 +1277,7 @@ func (s *MessengerContactRequestSuite) TestPairedDevicesRemoveContact() { s.Require().Len(resp.Contacts, 1) 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().NotNil(resp) s.Require().Len(resp.Contacts, 1) @@ -1603,7 +1603,7 @@ func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsCorrectOrd s.Require().Len(resp.Contacts, 1) 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) // adds bob again to her device @@ -1689,7 +1689,7 @@ func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsWrongOrder s.Require().Len(resp.Contacts, 1) 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) // adds bob again to her device diff --git a/protocol/messenger_contacts.go b/protocol/messenger_contacts.go index 53fe98df8..ab367f640 100644 --- a/protocol/messenger_contacts.go +++ b/protocol/messenger_contacts.go @@ -25,7 +25,7 @@ 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) + return m.addContact(contactRequest.From, "", "", "", contactRequest.ID, "", syncing, true, false) } 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 } +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) { err := request.Validate() if err != nil { @@ -55,7 +189,17 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se chatID := request.ID.String() - response, err := m.addContact(chatID, "", "", "", "", false, false) + response, err := m.addContact( + chatID, + "", + "", + "", + "", + request.Message, + false, + false, + false, + ) if err != nil { return nil, err } @@ -100,82 +244,6 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se 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) { contactRequest, err := m.persistence.MessageByID(contactRequestID) if err != nil { @@ -244,7 +312,7 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co 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) if err != nil { return nil, err @@ -256,13 +324,11 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe } if ensName != "" { - clock := m.getTimesource().GetCurrentTime() err := m.ensVerifier.ENSVerified(pubKey, ensName, clock) if err != nil { return nil, err } } - if err := m.addENSNameToContact(contact); err != nil { return nil, err } @@ -276,7 +342,6 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe } contact.LastUpdatedLocally = clock - contact.ContactRequestSent(clock) if !syncing { @@ -332,11 +397,13 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe return nil, err } + // Get ENS name of a current user ensName, err = m.settings.ENSName() if err != nil { return nil, err } + // Get display name of a current user displayName, err = m.settings.DisplayName() if err != nil { 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) if err != nil { return nil, err } + // Add chat response.AddChat(profileChat) _, 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 } + // 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) 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) { err := request.Validate() if err != nil { 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 { @@ -801,7 +940,7 @@ func (m *Messenger) RetractContactRequest(request *requests.RetractContactReques if err != nil { return nil, err } - contact, ok := m.allContacts.Load(request.ContactID.String()) + contact, ok := m.allContacts.Load(request.ID.String()) if !ok { return nil, errors.New("contact not found") } @@ -874,7 +1013,7 @@ func (m *Messenger) DismissLatestContactRequestForContact(ctx context.Context, r 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) { diff --git a/protocol/messenger_handler.go b/protocol/messenger_handler.go index de15a64fc..17c1037a0 100644 --- a/protocol/messenger_handler.go +++ b/protocol/messenger_handler.go @@ -262,7 +262,28 @@ func (m *Messenger) PendingNotificationContactRequest(contactID string) (*Activi 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 { notification, err := m.PendingNotificationContactRequest(contact.ID) @@ -321,28 +342,19 @@ func (m *Messenger) createContactRequestNotification(contact *Contact, messageSt } } - contactRequest = &common.Message{} - - contactRequest.WhisperTimestamp = messageState.CurrentMessageState.WhisperTimestamp - contactRequest.Seen = true - contactRequest.Text = "Please add me to your contacts" - contactRequest.From = contact.ID - 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)) + // 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 @@ -762,10 +774,10 @@ func (m *Messenger) handleAcceptContactRequest( response *MessengerResponse, contact *Contact, 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))) - if contact.ContactRequestRemoteClock > message.Clock { + 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 > clock { m.logger.Debug("not handling accept since clock lower") return ContactRequestProcessingResponse{}, nil } @@ -774,14 +786,14 @@ func (m *Messenger) handleAcceptContactRequest( // be that we sent a legacy contact request/contact-update, or another // device has sent it, and we haven't synchronized it if originalRequest == nil { - return contact.ContactRequestAccepted(message.Clock), nil + return contact.ContactRequestAccepted(clock), nil } if originalRequest.LocalChatID != contact.ID { return ContactRequestProcessingResponse{}, errors.New("can't accept contact request not sent to user") } - contact.ContactRequestAccepted(message.Clock) + contact.ContactRequestAccepted(clock) originalRequest.ContactRequestState = common.ContactRequestStateAccepted @@ -794,15 +806,15 @@ func (m *Messenger) handleAcceptContactRequest( return ContactRequestProcessingResponse{}, nil } -func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, message protobuf.AcceptContactRequest) error { - originalRequest, err := m.persistence.MessageByID(message.Id) +func (m *Messenger) handleAcceptContactRequestMessage(state *ReceivedMessageState, clock uint64, contactRequestID string, isOutgoing bool) error { + request, err := m.persistence.MessageByID(contactRequestID) if err != nil && err != common.ErrRecordNotFound { return err } 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 { return err } @@ -818,14 +830,14 @@ func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, mess return err } - if chat.LastClockValue < message.Clock { - chat.LastClockValue = message.Clock + if chat.LastClockValue < clock { + chat.LastClockValue = clock } // NOTE(cammellos): This will re-enable the chat if it was deleted, and only // after we became contact, currently seems safe, but that needs // discussing with UX. - if chat.DeletedAtClockValue < message.Clock { + if chat.DeletedAtClockValue < clock { chat.Active = true } @@ -833,9 +845,16 @@ func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, mess state.AllChats.Store(chat.ID, chat) } - if originalRequest != nil { - // Update contact requests if existing, or create a new one - err = m.createContactRequestNotification(contact, state, originalRequest, processingResponse.newContactRequestReceived) + if request != nil { + if isOutgoing { + 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 { 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 } +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 { if contact.ID == m.myHexIdentity() { m.logger.Debug("retraction coming from us, ignoring") @@ -922,7 +957,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro } if result.newContactRequestReceived { - err = m.createContactRequestNotification(contact, state, nil, true) + err = m.createIncomingContactRequestNotification(contact, state, nil, true) if err != nil { return err } @@ -946,7 +981,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro r := contact.ContactRequestReceived(message.ContactRequestClock) if r.newContactRequestReceived { - err = m.createContactRequestNotification(contact, state, nil, true) + err = m.createIncomingContactRequestNotification(contact, state, nil, true) if err != nil { 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 } } else { - // Activity Center notification, updating existing for accespted/declined + // Activity Center notification, updating existing for accepted/declined notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID) if err != nil { return err @@ -1751,7 +1786,7 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error { receivedMessage.ContactRequestState = common.ContactRequestStatePending } - err = m.createContactRequestNotification(contact, state, receivedMessage, true) + err = m.createIncomingContactRequestNotification(contact, state, receivedMessage, true) if err != nil { return err } @@ -1762,7 +1797,6 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error { } if receivedMessage.ContentType == protobuf.ChatMessage_CONTACT_REQUEST && chat.OneToOne() { - chatContact := contact if isSyncMessage { chatContact, err = m.BuildContact(chat.ID) @@ -1780,7 +1814,7 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error { state.AllContacts.Store(chatContact.ID, chatContact) if sendNotification { - err = m.createContactRequestNotification(chatContact, state, receivedMessage, true) + err = m.createIncomingContactRequestNotification(chatContact, state, receivedMessage, true) if err != nil { return err } @@ -2668,13 +2702,12 @@ func (m *Messenger) HandleSyncWalletAccount(state *ReceivedMessageState, message func (m *Messenger) HandleSyncContactRequestDecision(state *ReceivedMessageState, message protobuf.SyncContactRequestDecision) error { var err error var response *MessengerResponse + if message.DecisionStatus == protobuf.SyncContactRequestDecision_ACCEPTED { response, err = m.updateAcceptedContactRequest(nil, message.RequestId) - } else { - response, err = m.dismissContactRequest(message.RequestId, true) + response, err = m.declineContactRequest(message.RequestId, true) } - if err != nil { return err } diff --git a/protocol/requests/cancel_outgoing_contact_request.go b/protocol/requests/cancel_outgoing_contact_request.go new file mode 100644 index 000000000..d52ffb6cf --- /dev/null +++ b/protocol/requests/cancel_outgoing_contact_request.go @@ -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 +} diff --git a/protocol/requests/decline_contact_request.go b/protocol/requests/decline_contact_request.go new file mode 100644 index 000000000..f05bd25bf --- /dev/null +++ b/protocol/requests/decline_contact_request.go @@ -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 +} diff --git a/protocol/requests/dismiss_contact_request.go b/protocol/requests/dismiss_contact_request.go deleted file mode 100644 index 24f2563ef..000000000 --- a/protocol/requests/dismiss_contact_request.go +++ /dev/null @@ -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 -} diff --git a/protocol/requests/retract_contact_request.go b/protocol/requests/retract_contact_request.go index 5ea3f8685..614bc8102 100644 --- a/protocol/requests/retract_contact_request.go +++ b/protocol/requests/retract_contact_request.go @@ -9,11 +9,11 @@ import ( var ErrRetractContactRequestInvalidContactID = errors.New("retract-contact-request: invalid id") type RetractContactRequest struct { - ContactID types.HexBytes `json:"contactId"` + ID types.HexBytes `json:"id"` } func (a *RetractContactRequest) Validate() error { - if len(a.ContactID) == 0 { + if len(a.ID) == 0 { return ErrRetractContactRequestInvalidContactID } diff --git a/services/ext/api.go b/services/ext/api.go index 50f609d49..fd4ad4ccd 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -695,18 +695,26 @@ func (api *PublicAPI) MarkAllReadInCommunity(communityID string) ([]string, erro 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) { 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) { 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) { 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) } -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) { return api.service.messenger.RemoveContact(ctx, pubKey) }