diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m index c3dfee8f5..e19f5bea3 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m @@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN [sentBuilder setMessage:dataMessage]; [sentBuilder setExpirationStartTimestamp:self.message.timestamp]; - for (NSString *recipientId in self.message.recipientIds) { + for (NSString *recipientId in self.message.sentRecipientIds) { TSOutgoingMessageRecipientState *_Nullable recipientState = [self.message recipientStateForRecipientId:recipientId]; if (!recipientState) { @@ -75,6 +75,7 @@ NS_ASSUME_NONNULL_BEGIN continue; } if (recipientState.state != OWSOutgoingMessageRecipientStateSent) { + OWSFailDebug(@"unexpected recipient state for: %@", recipientId); continue; } diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index 7c73bf122..d3b00bbe2 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -113,6 +113,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @property (readonly) TSOutgoingMessageState messageState; @property (readonly) BOOL wasDeliveredToAnyRecipient; +@property (readonly) BOOL wasSentToAnyRecipient; @property (atomic, readonly) BOOL hasSyncedTranscript; @property (atomic, readonly) NSString *customMessage; @@ -155,7 +156,10 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { // All recipients of this message who we are currently trying to send to (queued, uploading or during send). - (NSArray *)sendingRecipientIds; -// All recipients of this message to whom it has been sent and delivered. +// All recipients of this message to whom it has been sent (and possibly delivered or read). +- (NSArray *)sentRecipientIds; + +// All recipients of this message to whom it has been sent and delivered (and possibly read). - (NSArray *)deliveredRecipientIds; // All recipients of this message to whom it has been sent, delivered and read. diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 1597449e3..c78065105 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -405,6 +405,14 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt return (self.hasLegacyMessageState && self.legacyWasDelivered && self.messageState == TSOutgoingMessageStateSent); } +- (BOOL)wasSentToAnyRecipient +{ + if ([self sentRecipientIds].count > 0) { + return YES; + } + return (self.hasLegacyMessageState && self.messageState == TSOutgoingMessageStateSent); +} + + (TSOutgoingMessageState)messageStateForRecipientStates:(NSArray *)recipientStates { OWSAssertDebug(recipientStates); @@ -509,6 +517,18 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt return result; } +- (NSArray *)sentRecipientIds +{ + NSMutableArray *result = [NSMutableArray new]; + for (NSString *recipientId in self.recipientStateMap) { + TSOutgoingMessageRecipientState *recipientState = self.recipientStateMap[recipientId]; + if (recipientState.state == OWSOutgoingMessageRecipientStateSent) { + [result addObject:recipientId]; + } + } + return result; +} + - (NSArray *)deliveredRecipientIds { NSMutableArray *result = [NSMutableArray new]; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index cc55cedff..ab1969ca0 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -619,11 +619,26 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; - (void)sendMessageToService:(TSOutgoingMessage *)message senderCertificate:(nullable SMKSenderCertificate *)senderCertificate - success:(void (^)(void))successHandler - failure:(RetryableFailureHandler)failureHandler + success:(void (^)(void))successHandlerParam + failure:(RetryableFailureHandler)failureHandlerParam { AssertIsOnSendingQueue(); + void (^successHandler)(void) = ^() { + dispatch_async([OWSDispatch sendingQueue], ^{ + [self handleMessageSentLocally:message senderCertificate:senderCertificate]; + }); + successHandlerParam(); + }; + void (^failureHandler)(NSError *) = ^(NSError *error) { + if (message.wasSentToAnyRecipient) { + dispatch_async([OWSDispatch sendingQueue], ^{ + [self handleMessageSentLocally:message senderCertificate:senderCertificate]; + }); + } + failureHandlerParam(error); + }; + TSThread *_Nullable thread = message.thread; // In the "self-send" special case, we ony need to send a sync message with a delivery receipt. @@ -638,9 +653,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; transaction:transaction]; } }]; - - [self handleMessageSentLocally:message senderCertificate:senderCertificate]; - successHandler(); return; } @@ -1103,8 +1115,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [SignalRecipient markRecipientAsRegisteredAndGet:recipient.recipientId transaction:transaction]; }]; - [self handleMessageSentLocally:messageSend.message - senderCertificate:messageSend.unidentifiedAccess.senderCertificate]; messageSend.success(); }); } @@ -1273,6 +1283,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (message.shouldSyncTranscript) { // TODO: I suspect we shouldn't optimistically set hasSyncedTranscript. // We could set this in a success handler for [sendSyncTranscriptForMessage:]. + // TODO: We might send to a recipient, then to another recipient on retry. + // To ensure desktop receives all "delivery status" info, we might + // want to send a transcript after every send that reaches _any_ + // new recipients. [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [message updateWithHasSyncedTranscript:YES transaction:transaction]; }];