diff --git a/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift b/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift index 9c25b0b25..4d2bc2cd4 100644 --- a/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift +++ b/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift @@ -129,7 +129,7 @@ public class MessageRecipientStatusUtils: NSObject { if outgoingMessage.readRecipientIds().count > 0 { return (.read, NSLocalizedString("MESSAGE_STATUS_READ", comment: "message footer for read messages")) } - if outgoingMessage.deliveredRecipientIds().count > 0 { + if outgoingMessage.wasDeliveredToAnyRecipient { return (.delivered, NSLocalizedString("MESSAGE_STATUS_DELIVERED", comment: "message status for message delivered to their recipient.")) } diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index dcb80ebde..953d58e77 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -109,6 +109,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage; @property (readonly) TSOutgoingMessageState messageState; +@property (readonly) BOOL wasDeliveredToAnyRecipient; @property (atomic, readonly) BOOL hasSyncedTranscript; @property (atomic, readonly) NSString *customMessage; diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index b7338735b..839a14e18 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -79,6 +79,10 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt @property (atomic) BOOL isFromLinkedDevice; @property (atomic) TSGroupMetaMessage groupMetaMessage; +@property (nonatomic, readonly) TSOutgoingMessageState legacyMessageState; +@property (nonatomic, readonly) BOOL legacyWasDelivered; +@property (nonatomic, readonly) BOOL hasLegacyMessageState; + @property (atomic, nullable) NSDictionary *recipientStateMap; @end @@ -116,6 +120,8 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt if (messageStateValue) { oldMessageState = (TSOutgoingMessageState)messageStateValue.intValue; } + _hasLegacyMessageState = YES; + _legacyMessageState = oldMessageState; OWSOutgoingMessageRecipientState defaultState; switch (oldMessageState) { @@ -140,6 +146,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt NSArray *_Nullable sentRecipients = [coder decodeObjectForKey:@"sentRecipients"]; NSMutableDictionary *recipientStateMap = [NSMutableDictionary new]; + __block BOOL isGroupThread = NO; // Our default recipient list is the current thread members. __block NSArray *recipientIds = @[]; // To avoid deadlock while migrating these records, we use a dedicated @@ -149,12 +156,25 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt // always accurate, so not using the same connection for both reads is // acceptable. [TSOutgoingMessage.dbMigrationConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - recipientIds = [[self threadWithTransaction:transaction] recipientIdentifiers]; + TSThread *thread = [self threadWithTransaction:transaction]; + recipientIds = [thread recipientIdentifiers]; + isGroupThread = [thread isGroupThread]; }]; - if (sentRecipients) { + + NSNumber *_Nullable wasDelivered = [coder decodeObjectForKey:@"wasDelivered"]; + _legacyWasDelivered = wasDelivered && wasDelivered.boolValue; + BOOL wasDeliveredToContact = NO; + if (isGroupThread) { // If we have a `sentRecipients` list, prefer that as it is more accurate. - recipientIds = sentRecipients; + if (sentRecipients) { + recipientIds = sentRecipients; + } + } else { + // Special-case messages in contact threads; if "was delivered", we know + // it was delivered to the contact. + wasDeliveredToContact = _legacyWasDelivered; } + NSString *_Nullable singleGroupRecipient = [coder decodeObjectForKey:@"singleGroupRecipient"]; if (singleGroupRecipient) { OWSFail(@"%@ unexpected single group recipient message.", self.logTag); @@ -179,6 +199,11 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt // If we have a delivery timestamp for this recipient, mark it as delivered. recipientState.state = OWSOutgoingMessageRecipientStateSent; recipientState.deliveryTimestamp = deliveryTimestamp; + } else if (wasDeliveredToContact) { + OWSAssert(!isGroupThread); + recipientState.state = OWSOutgoingMessageRecipientStateSent; + // Use message time as an estimate of delivery time. + recipientState.deliveryTimestamp = @(self.timestamp); } else if ([sentRecipients containsObject:recipientId]) { // If this recipient is in `sentRecipients`, mark it as sent. recipientState.state = OWSOutgoingMessageRecipientStateSent; @@ -330,7 +355,22 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt - (TSOutgoingMessageState)messageState { - return [TSOutgoingMessage messageStateForRecipientStates:self.recipientStateMap.allValues]; + TSOutgoingMessageState newMessageState = + [TSOutgoingMessage messageStateForRecipientStates:self.recipientStateMap.allValues]; + if (self.hasLegacyMessageState) { + if (newMessageState == TSOutgoingMessageStateSent || self.legacyMessageState == TSOutgoingMessageStateSent) { + return TSOutgoingMessageStateSent; + } + } + return newMessageState; +} + +- (BOOL)wasDeliveredToAnyRecipient +{ + if ([self deliveredRecipientIds].count > 0) { + return YES; + } + return (self.hasLegacyMessageState && self.legacyWasDelivered && self.messageState == TSOutgoingMessageStateSent); } + (TSOutgoingMessageState)messageStateForRecipientStates:(NSArray *)recipientStates