diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 9bd4f2155..3e05346ce 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -499,7 +499,7 @@ NS_ASSUME_NONNULL_BEGIN // Ignore taps on links in outgoing messages that haven't been sent yet, as // this interferes with "tap to retry". TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; - shouldIgnoreEvents = outgoingMessage.messageState != TSOutgoingMessageStateSentToService; + shouldIgnoreEvents = outgoingMessage.messageState != TSOutgoingMessageStateSent; } [self.class loadForTextDisplay:self.bodyTextView text:self.displayableBodyText.displayText @@ -1026,7 +1026,7 @@ NS_ASSUME_NONNULL_BEGIN return NO; } TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; - return outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut; + return outgoingMessage.messageState == TSOutgoingMessageStateSending; } - (OWSMessagesBubbleImageFactory *)bubbleFactory @@ -1085,9 +1085,9 @@ NS_ASSUME_NONNULL_BEGIN if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; - if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { + if (outgoingMessage.messageState == TSOutgoingMessageStateFailed) { return; - } else if (outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { + } else if (outgoingMessage.messageState == TSOutgoingMessageStateSending) { // Ignore taps on outgoing messages being sent. return; } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 6c19879c3..784a50c27 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -108,7 +108,7 @@ NS_ASSUME_NONNULL_BEGIN return NO; } TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; - return outgoingMessage.messageState == TSOutgoingMessageStateUnsent; + return outgoingMessage.messageState == TSOutgoingMessageStateFailed; } - (UIImage *)failedSendBadge @@ -527,10 +527,10 @@ NS_ASSUME_NONNULL_BEGIN if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; - if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { + if (outgoingMessage.messageState == TSOutgoingMessageStateFailed) { // Ignore long press on unsent messages. return; - } else if (outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { + } else if (outgoingMessage.messageState == TSOutgoingMessageStateSending) { // Ignore long press on outgoing messages being sent. return; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index ebc7cdaf7..d3f0287ee 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -4663,7 +4663,7 @@ typedef enum : NSUInteger { MessageRecipientStatus recipientStatus = [MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage]; - if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { + if (outgoingMessage.messageState == TSOutgoingMessageStateFailed) { // always show "failed to send" status shouldHideRecipientStatus = NO; } else { diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 2a50d9e2e..4070f192a 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -662,8 +662,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } else if (action == self.replyActionSelector) { if ([self.interaction isKindOfClass:[TSOutgoingMessage class]]) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.interaction; - if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent - || outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { + if (outgoingMessage.messageState == TSOutgoingMessageStateFailed + || outgoingMessage.messageState == TSOutgoingMessageStateSending) { // Don't let users reply to messages which aren't yet delivered to the service. return NO; } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index ef3ffc1dd..1d6b883ec 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -1148,146 +1148,126 @@ NS_ASSUME_NONNULL_BEGIN if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Jpeg ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:YES], - [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Gif ⚠️"]]; } [actions addObjectsFromArray:@[ // Don't bother with multiple GIF states. - [self fakeOutgoingGifAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingLargeGifAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], + [self fakeOutgoingGifAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingLargeGifAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Mp3 ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:YES], - [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Mp4 ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:YES], - [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Compact Landscape Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingCompactLandscapePngAction:thread - messageState:TSOutgoingMessageStateAttemptingOut - hasCaption:NO], - [self fakeOutgoingCompactLandscapePngAction:thread - messageState:TSOutgoingMessageStateAttemptingOut - hasCaption:YES], - [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingCompactLandscapePngAction:thread - messageState:TSOutgoingMessageStateSentToService - hasCaption:NO], - [self fakeOutgoingCompactLandscapePngAction:thread - messageState:TSOutgoingMessageStateSentToService - hasCaption:YES], + [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Compact Portrait Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingCompactPortraitPngAction:thread - messageState:TSOutgoingMessageStateAttemptingOut - hasCaption:NO], - [self fakeOutgoingCompactPortraitPngAction:thread - messageState:TSOutgoingMessageStateAttemptingOut - hasCaption:YES], - [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingCompactPortraitPngAction:thread - messageState:TSOutgoingMessageStateSentToService - hasCaption:NO], - [self fakeOutgoingCompactPortraitPngAction:thread - messageState:TSOutgoingMessageStateSentToService - hasCaption:YES], + [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Wide Landscape Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingWideLandscapePngAction:thread - messageState:TSOutgoingMessageStateAttemptingOut - hasCaption:YES], - [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingWideLandscapePngAction:thread - messageState:TSOutgoingMessageStateSentToService - hasCaption:YES], + [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Tall Portrait Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:YES], - [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Large Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingLargePngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingLargePngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingLargePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingLargePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Tiny Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingTinyPngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingTinyPngAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingTinyPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingTinyPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Reserved Color Png ⚠️"]]; } [actions addObjectsFromArray:@[ @@ -1297,7 +1277,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[UIColor whiteColor] textColor:[UIColor ows_signalBrandBlueColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing White Png" @@ -1305,7 +1285,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[UIColor whiteColor] textColor:[UIColor ows_signalBrandBlueColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing White Png" @@ -1313,7 +1293,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[UIColor whiteColor] textColor:[UIColor ows_signalBrandBlueColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent hasCaption:YES], [self fakeOutgoingPngAction:thread @@ -1322,7 +1302,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingUnsent] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing 'Outgoing Unsent' Png" @@ -1330,7 +1310,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingUnsent] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing 'Outgoing Unsent' Png" @@ -1338,7 +1318,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingUnsent] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent hasCaption:YES], [self fakeOutgoingPngAction:thread @@ -1347,7 +1327,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingSending] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing 'Outgoing Sending' Png" @@ -1355,7 +1335,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingSending] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing 'Outgoing Sending' Png" @@ -1363,7 +1343,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingSending] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent hasCaption:YES], [self fakeOutgoingPngAction:thread @@ -1372,7 +1352,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingSent] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing 'Outgoing Sent' Png" @@ -1380,7 +1360,7 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingSent] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending hasCaption:YES], [self fakeOutgoingPngAction:thread actionLabel:@"Fake Outgoing 'Outgoing Sent' Png" @@ -1388,55 +1368,55 @@ NS_ASSUME_NONNULL_BEGIN backgroundColor:[OWSMessagesBubbleImageFactory bubbleColorOutgoingSent] textColor:[UIColor whiteColor] imageLabel:@"W" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Tiny Pdf ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:YES], - [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:YES], - [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], - [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:YES], + [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES], + [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES], + [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], + [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Large Pdf ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingLargePdfAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], + [self fakeOutgoingLargePdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Missing Png ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingMissingPngAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], + [self fakeOutgoingMissingPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Large Pdf ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingMissingPdfAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], + [self fakeOutgoingMissingPdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Oversize Text ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateUnsent hasCaption:NO], - [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateAttemptingOut hasCaption:NO], - [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateSentToService hasCaption:NO], + [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO], + [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO], + [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO], ]]; // Incoming @@ -1837,31 +1817,31 @@ NS_ASSUME_NONNULL_BEGIN if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Statuses ⚠️"]]; } [actions addObjectsFromArray:@[ - [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateUnsent], - [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateAttemptingOut], - [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSentToService], + [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateFailed], + [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSending], + [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSent], [self fakeShortOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:NO], [self fakeShortOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:YES], ]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Outgoing Message Bodies ⚠️"]]; } for (NSString *messageBody in messageBodies) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:messageBody]]; } return actions; @@ -1905,7 +1885,7 @@ NS_ASSUME_NONNULL_BEGIN // Used fixed values for properties that shouldn't matter much. BOOL quotedMessageIsDelivered = NO; BOOL quotedMessageIsRead = NO; - TSOutgoingMessageState quotedMessageMessageState = TSOutgoingMessageStateSentToService; + TSOutgoingMessageState quotedMessageMessageState = TSOutgoingMessageStateSent; BOOL replyIsDelivered = NO; BOOL replyIsRead = NO; @@ -2063,7 +2043,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Short Text" @@ -2074,7 +2054,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Medium Text" @@ -2085,7 +2065,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Medium Text" @@ -2096,7 +2076,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Long Text" @@ -2107,7 +2087,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:longText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], ]]; if (includeLabels) { @@ -2124,7 +2104,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Jpg" @@ -2135,7 +2115,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Mp3" @@ -2146,7 +2126,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Mp3" @@ -2157,7 +2137,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Mp4" @@ -2168,7 +2148,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Mp4" @@ -2179,7 +2159,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Gif" @@ -2190,7 +2170,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Gif" @@ -2201,7 +2181,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Pdf" @@ -2212,7 +2192,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Missing Pdf" @@ -2223,7 +2203,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Tiny Png" @@ -2234,7 +2214,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Missing Png" @@ -2245,7 +2225,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], ]]; if (includeLabels) { @@ -2262,7 +2242,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Tall Portrait Png" @@ -2273,7 +2253,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Tall Portrait Png" @@ -2284,7 +2264,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Wide Landscape Png" @@ -2295,7 +2275,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Wide Landscape Png" @@ -2306,7 +2286,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Wide Landscape Png" @@ -2317,7 +2297,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Tiny Png" @@ -2328,7 +2308,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Tiny Png" @@ -2339,7 +2319,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], ]]; void (^directionActions)(BOOL, BOOL) = ^(BOOL isQuotedMessageIncoming, BOOL isReplyIncoming) { @@ -2353,7 +2333,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:isReplyIncoming replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], ]]; }; @@ -2380,7 +2360,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Mp3" @@ -2391,7 +2371,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Mp4" @@ -2402,7 +2382,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Gif" @@ -2413,7 +2393,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Pdf" @@ -2424,7 +2404,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Missing Pdf" @@ -2435,7 +2415,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Tiny Png" @@ -2446,7 +2426,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Missing Png" @@ -2457,7 +2437,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Short Text" @@ -2468,7 +2448,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateAttemptingOut], + replyMessageState:TSOutgoingMessageStateSending], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Short Text" @@ -2479,7 +2459,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], [self fakeQuotedReplyAction:thread quotedMessageLabel:@"Short Text" @@ -2490,7 +2470,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateUnsent], + replyMessageState:TSOutgoingMessageStateFailed], ]]; @@ -2509,7 +2489,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Text -> Png + Text [self fakeQuotedReplyAction:thread @@ -2521,7 +2501,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:nil - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Text -> Png [self fakeQuotedReplyAction:thread @@ -2533,7 +2513,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:nil replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Png + Text [self fakeQuotedReplyAction:thread @@ -2545,7 +2525,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Portrait Png + Text [self fakeQuotedReplyAction:thread @@ -2557,7 +2537,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Landscape Png + Text [self fakeQuotedReplyAction:thread @@ -2569,7 +2549,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Landscape Png + Text @@ -2582,7 +2562,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Landscape Png + Text [self fakeQuotedReplyAction:thread @@ -2594,7 +2574,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:shortText replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Landscape Png + Text [self fakeQuotedReplyAction:thread @@ -2606,7 +2586,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], // Png -> Landscape Png + Text [self fakeQuotedReplyAction:thread @@ -2618,7 +2598,7 @@ NS_ASSUME_NONNULL_BEGIN isReplyIncoming:NO replyMessageBody:mediumText replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance] - replyMessageState:TSOutgoingMessageStateSentToService], + replyMessageState:TSOutgoingMessageStateSent], ]]; return actions; @@ -2710,60 +2690,59 @@ NS_ASSUME_NONNULL_BEGIN if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Short Message Sequences ⚠️"]]; } [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming"]]; - [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService - text:@"Outgoing"]]; + [actions + addObject:[self fakeOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSent text:@"Outgoing"]]; [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming 1"]]; [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming 2"]]; [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming 3"]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed text:@"Outgoing Unsent 1"]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed text:@"Outgoing Unsent 2"]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending text:@"Outgoing Sending 1"]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending text:@"Outgoing Sending 2"]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"Outgoing Sent 1"]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"Outgoing Sent 2"]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:@"Outgoing Delivered 1" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:NO]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:@"Outgoing Delivered 2" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:NO]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:@"Outgoing Read 1" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:YES]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:@"Outgoing Read 2" - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:YES]]; [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming"]]; if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Long Message Sequences ⚠️"]]; } @@ -2772,7 +2751,7 @@ NS_ASSUME_NONNULL_BEGIN [actions addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:[@"Outgoing" stringByAppendingString:longText]]]; [actions addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming 1" stringByAppendingString:longText]]]; @@ -2781,43 +2760,43 @@ NS_ASSUME_NONNULL_BEGIN [actions addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming 3" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed text:[@"Outgoing Unsent 1" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed text:[@"Outgoing Unsent 2" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending text:[@"Outgoing Sending 1" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateAttemptingOut + messageState:TSOutgoingMessageStateSending text:[@"Outgoing Sending 2" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:[@"Outgoing Sent 1" stringByAppendingString:longText]]]; [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:[@"Outgoing Sent 2" stringByAppendingString:longText]]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:[@"Outgoing Delivered 1" stringByAppendingString:longText] - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:NO]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:[@"Outgoing Delivered 2" stringByAppendingString:longText] - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:NO]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:[@"Outgoing Read 1" stringByAppendingString:longText] - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:YES]]; [actions addObject:[self fakeShortOutgoingTextMessageAction:thread text:[@"Outgoing Read 2" stringByAppendingString:longText] - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:YES isRead:YES]]; [actions addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming" stringByAppendingString:longText]]]; @@ -2849,7 +2828,7 @@ NS_ASSUME_NONNULL_BEGIN TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread messageBody:messageBody fakeAssetLoader:nil - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent isDelivered:NO isRead:NO quotedMessage:nil @@ -2867,7 +2846,7 @@ NS_ASSUME_NONNULL_BEGIN if (includeLabels) { [actions addObject:[self fakeOutgoingTextMessageAction:thread - messageState:TSOutgoingMessageStateSentToService + messageState:TSOutgoingMessageStateSent text:@"⚠️ Back-Dated ⚠️"]]; } @@ -3285,7 +3264,7 @@ NS_ASSUME_NONNULL_BEGIN [self createFakeOutgoingMessage:thread messageBody:randomText fakeAssetLoader:nil - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed isDelivered:NO isRead:NO quotedMessage:nil @@ -3336,7 +3315,7 @@ NS_ASSUME_NONNULL_BEGIN messageBody:nil attachmentId:attachmentStream.uniqueId filename:filename - messageState:TSOutgoingMessageStateUnsent + messageState:TSOutgoingMessageStateFailed isDelivered:NO isRead:NO isVoiceMessage:NO @@ -3707,7 +3686,7 @@ NS_ASSUME_NONNULL_BEGIN } for (TSOutgoingMessage *message in messages) { - [message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:initialTransaction]; + [message updateWithFakeMessageState:TSOutgoingMessageStateSending transaction:initialTransaction]; [message saveWithTransaction:initialTransaction]; } @@ -3781,11 +3760,9 @@ NS_ASSUME_NONNULL_BEGIN groupMetaMessage:TSGroupMessageUnspecified quotedMessage:nil]; [message saveWithTransaction:transaction]; - [message updateWithMessageState:TSOutgoingMessageStateSentToService transaction:transaction]; + [message updateWithFakeMessageState:TSOutgoingMessageStateSent transaction:transaction]; [message updateWithSentRecipient:recipientId transaction:transaction]; - [message updateWithDeliveredToRecipientId:recipientId - deliveryTimestamp:timestamp - transaction:transaction]; + [message updateWithDeliveredRecipient:recipientId deliveryTimestamp:timestamp transaction:transaction]; [message updateWithReadRecipientId:recipientId readTimestamp:timestamp.unsignedLongLongValue transaction:transaction]; @@ -3935,11 +3912,11 @@ NS_ASSUME_NONNULL_BEGIN if (hasCaption) { [label appendString:@" 🔤"]; } - if (outgoingMessageState == TSOutgoingMessageStateUnsent) { + if (outgoingMessageState == TSOutgoingMessageStateFailed) { [label appendString:@" (Unsent)"]; - } else if (outgoingMessageState == TSOutgoingMessageStateAttemptingOut) { + } else if (outgoingMessageState == TSOutgoingMessageStateSending) { [label appendString:@" (Sending)"]; - } else if (outgoingMessageState == TSOutgoingMessageStateSentToService) { + } else if (outgoingMessageState == TSOutgoingMessageStateSent) { if (isRead) { [label appendString:@" (Read)"]; } else if (isDelivered) { @@ -4024,13 +4001,13 @@ NS_ASSUME_NONNULL_BEGIN } [message saveWithTransaction:transaction]; - [message updateWithMessageState:messageState transaction:transaction]; + [message updateWithFakeMessageState:messageState transaction:transaction]; if (isDelivered) { NSString *_Nullable recipientId = thread.recipientIdentifiers.lastObject; OWSAssert(recipientId.length > 0); - [message updateWithDeliveredToRecipientId:recipientId - deliveryTimestamp:@([NSDate ows_millisecondTimeStamp]) - transaction:transaction]; + [message updateWithDeliveredRecipient:recipientId + deliveryTimestamp:@([NSDate ows_millisecondTimeStamp]) + transaction:transaction]; } if (isRead) { NSString *_Nullable recipientId = thread.recipientIdentifiers.lastObject; diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index a78178b2a..317bb9d38 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -212,7 +212,9 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele groupRows.append(divider) } - for recipientId in thread.recipientIdentifiers { + let messageRecipientIds = outgoingMessage.recipientIds() + + for recipientId in messageRecipientIds { let (recipientStatus, shortStatusMessage, _) = MessageRecipientStatusUtils.recipientStatusAndStatusMessage(outgoingMessage: outgoingMessage, recipientId: recipientId, referenceView: self.view) guard recipientStatus == recipientStatusGroup else { @@ -560,7 +562,10 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele comment: "Status label for messages which are read.") case .failed: return NSLocalizedString("MESSAGE_METADATA_VIEW_MESSAGE_STATUS_FAILED", - comment: "Status label for messages which are failed.") + comment: "Status label for messages which are failed.") + case .skipped: + return NSLocalizedString("MESSAGE_METADATA_VIEW_MESSAGE_STATUS_SKIPPED", + comment: "Status label for messages which were skipped.") } } diff --git a/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift b/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift index b955b4678..80ad3b4ce 100644 --- a/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift +++ b/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift @@ -13,6 +13,7 @@ import SignalMessaging case delivered case read case failed + case skipped } // Our per-recipient status messages are "biased towards success" @@ -93,57 +94,57 @@ class MessageRecipientStatusUtils: NSObject { // so we fall back to `TSOutgoingMessageState` which is not per-recipient and therefore // might be misleading. - let recipientReadMap = outgoingMessage.recipientReadMap - if let readTimestamp = recipientReadMap[recipientId] { - assert(outgoingMessage.messageState == .sentToService) - let timestampString = DateUtil.formatPastTimestampRelativeToNow(readTimestamp.uint64Value, - isRTL:referenceView.isRTL()) - let shortStatusMessage = timestampString - let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_READ", comment:"message footer for read messages").rtlSafeAppend(" ", referenceView:referenceView) - .rtlSafeAppend(timestampString, referenceView:referenceView) - return (status:.read, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) + guard let recipientState = outgoingMessage.recipientState(forRecipientId: recipientId) else { + let shortStatusMessage = NSLocalizedString("MESSAGE_STATUS_FAILED_SHORT", comment: "status message for failed messages") + let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_FAILED", comment: "message footer for failed messages") + return (status:.failed, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) } - let recipientDeliveryMap = outgoingMessage.recipientDeliveryMap - if let deliveryTimestamp = recipientDeliveryMap[recipientId] { - assert(outgoingMessage.messageState == .sentToService) - let timestampString = DateUtil.formatPastTimestampRelativeToNow(deliveryTimestamp.uint64Value, - isRTL:referenceView.isRTL()) - let shortStatusMessage = timestampString - let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_DELIVERED", - comment:"message status for message delivered to their recipient.").rtlSafeAppend(" ", referenceView:referenceView) - .rtlSafeAppend(timestampString, referenceView:referenceView) - return (status:.delivered, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) - } + switch recipientState.state { + case .failed: + let shortStatusMessage = NSLocalizedString("MESSAGE_STATUS_FAILED_SHORT", comment: "status message for failed messages") + let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_FAILED", comment: "message footer for failed messages") + return (status:.failed, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) + case .sending: + if outgoingMessage.hasAttachments() { + assert(outgoingMessage.messageState == .sending) - if outgoingMessage.wasDelivered { - let statusMessage = NSLocalizedString("MESSAGE_STATUS_DELIVERED", - comment:"message status for message delivered to their recipient.") - return (status:.delivered, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) - } + let statusMessage = NSLocalizedString("MESSAGE_STATUS_UPLOADING", + comment: "message footer while attachment is uploading") + return (status:.uploading, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) + } else { + assert(outgoingMessage.messageState == .sending) - if outgoingMessage.messageState == .unsent { - let shortStatusMessage = NSLocalizedString("MESSAGE_STATUS_FAILED_SHORT", comment:"status message for failed messages") - let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_FAILED", comment:"message footer for failed messages") - return (status:.failed, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) - } else if outgoingMessage.messageState == .sentToService || - outgoingMessage.wasSent(toRecipient:recipientId) { + let statusMessage = NSLocalizedString("MESSAGE_STATUS_SENDING", + comment: "message status while message is sending.") + return (status:.sending, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) + } + case .sent: + if let readTimestamp = recipientState.readTimestamp { + let timestampString = DateUtil.formatPastTimestampRelativeToNow(readTimestamp.uint64Value, + isRTL: referenceView.isRTL()) + let shortStatusMessage = timestampString + let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_READ", comment: "message footer for read messages").rtlSafeAppend(" ", referenceView: referenceView) + .rtlSafeAppend(timestampString, referenceView: referenceView) + return (status:.read, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) + } + if let deliveryTimestamp = recipientState.deliveryTimestamp { + let timestampString = DateUtil.formatPastTimestampRelativeToNow(deliveryTimestamp.uint64Value, + isRTL: referenceView.isRTL()) + let shortStatusMessage = timestampString + let longStatusMessage = NSLocalizedString("MESSAGE_STATUS_DELIVERED", + comment: "message status for message delivered to their recipient.").rtlSafeAppend(" ", referenceView: referenceView) + .rtlSafeAppend(timestampString, referenceView: referenceView) + return (status:.delivered, shortStatusMessage:shortStatusMessage, longStatusMessage:longStatusMessage) + } let statusMessage = NSLocalizedString("MESSAGE_STATUS_SENT", - comment:"message footer for sent messages") + comment: "message footer for sent messages") return (status:.sent, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) - } else if outgoingMessage.hasAttachments() { - assert(outgoingMessage.messageState == .attemptingOut) - - let statusMessage = NSLocalizedString("MESSAGE_STATUS_UPLOADING", - comment:"message footer while attachment is uploading") - return (status:.uploading, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) - } else { - assert(outgoingMessage.messageState == .attemptingOut) - - let statusMessage = NSLocalizedString("MESSAGE_STATUS_SENDING", - comment:"message status while message is sending.") - return (status:.sending, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) + case .skipped: + let statusMessage = NSLocalizedString("MESSAGE_STATUS_RECIPIENT_SKIPPED", + comment: "message status if message delivery to a recipient is skipped.") + return (status:.skipped, shortStatusMessage:statusMessage, longStatusMessage:statusMessage) } } @@ -153,40 +154,31 @@ class MessageRecipientStatusUtils: NSObject { referenceView: UIView) -> String { switch outgoingMessage.messageState { - case .unsent: + case .failed: // Use the "long" version of this message here. - return NSLocalizedString("MESSAGE_STATUS_FAILED", comment:"message footer for failed messages") - case .attemptingOut: + return NSLocalizedString("MESSAGE_STATUS_FAILED", comment: "message footer for failed messages") + case .sending: if outgoingMessage.hasAttachments() { return NSLocalizedString("MESSAGE_STATUS_UPLOADING", - comment:"message footer while attachment is uploading") + comment: "message footer while attachment is uploading") } else { return NSLocalizedString("MESSAGE_STATUS_SENDING", - comment:"message status while message is sending.") + comment: "message status while message is sending.") } - case .sentToService: - let recipientReadMap = outgoingMessage.recipientReadMap - if recipientReadMap.count > 0 { - return NSLocalizedString("MESSAGE_STATUS_READ", comment:"message footer for read messages") + case .sent: + if outgoingMessage.readRecipientIds().count > 0 { + return NSLocalizedString("MESSAGE_STATUS_READ", comment: "message footer for read messages") } - - let recipientDeliveryMap = outgoingMessage.recipientDeliveryMap - if recipientDeliveryMap.count > 0 { + if outgoingMessage.deliveredRecipientIds().count > 0 { return NSLocalizedString("MESSAGE_STATUS_DELIVERED", - comment:"message status for message delivered to their recipient.") + comment: "message status for message delivered to their recipient.") } - - if outgoingMessage.wasDelivered { - return NSLocalizedString("MESSAGE_STATUS_DELIVERED", - comment:"message status for message delivered to their recipient.") - } - return NSLocalizedString("MESSAGE_STATUS_SENT", - comment:"message footer for sent messages") + comment: "message footer for sent messages") default: owsFail("Message has unexpected status: \(outgoingMessage.messageState).") return NSLocalizedString("MESSAGE_STATUS_SENT", - comment:"message footer for sent messages") + comment: "message footer for sent messages") } } @@ -194,26 +186,19 @@ class MessageRecipientStatusUtils: NSObject { // See comments above. public class func recipientStatus(outgoingMessage: TSOutgoingMessage) -> MessageRecipientStatus { switch outgoingMessage.messageState { - case .unsent: + case .failed: return .failed - case .attemptingOut: + case .sending: if outgoingMessage.hasAttachments() { return .uploading } else { return .sending } - case .sentToService: - let recipientReadMap = outgoingMessage.recipientReadMap - if recipientReadMap.count > 0 { + case .sent: + if outgoingMessage.readRecipientIds().count > 0 { return .read } - - let recipientDeliveryMap = outgoingMessage.recipientDeliveryMap - if recipientDeliveryMap.count > 0 { - return .delivered - } - - if outgoingMessage.wasDelivered { + if outgoingMessage.deliveredRecipientIds().count > 0 { return .delivered } diff --git a/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift b/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift index 83582cf4b..5c2e50ead 100644 --- a/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift +++ b/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift @@ -45,9 +45,9 @@ public class OWSMessagesBubbleImageFactory: NSObject { return self.incoming } else if let outgoingMessage = message as? TSOutgoingMessage { switch outgoingMessage.messageState { - case .unsent: + case .failed: return outgoingFailed - case .attemptingOut: + case .sending: return currentlyOutgoing default: return outgoing @@ -75,9 +75,9 @@ public class OWSMessagesBubbleImageFactory: NSObject { return OWSMessagesBubbleImageFactory.bubbleColorIncoming } else if let outgoingMessage = message as? TSOutgoingMessage { switch outgoingMessage.messageState { - case .unsent: + case .failed: return OWSMessagesBubbleImageFactory.bubbleColorOutgoingUnsent - case .attemptingOut: + case .sending: return OWSMessagesBubbleImageFactory.bubbleColorOutgoingSending default: return OWSMessagesBubbleImageFactory.bubbleColorOutgoingSent diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m index 0508fef5f..e3d78babe 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m @@ -1,10 +1,11 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSOutgoingSentMessageTranscript.h" #import "OWSSignalServiceProtos.pb.h" #import "TSOutgoingMessage.h" +#import "TSThread.h" NS_ASSUME_NONNULL_BEGIN @@ -48,8 +49,8 @@ NS_ASSUME_NONNULL_BEGIN OWSSignalServiceProtosSyncMessageSentBuilder *sentBuilder = [OWSSignalServiceProtosSyncMessageSentBuilder new]; [sentBuilder setTimestamp:self.message.timestamp]; - [sentBuilder setDestination:self.message.recipientIdentifier]; - [sentBuilder setMessage:[self.message buildDataMessage:self.message.recipientIdentifier]]; + [sentBuilder setDestination:self.recipientIdentifierForMessage]; + [sentBuilder setMessage:[self.message buildDataMessage:self.recipientIdentifierForMessage]]; [sentBuilder setExpirationStartTimestamp:self.message.timestamp]; [syncMessageBuilder setSentBuilder:sentBuilder]; @@ -57,6 +58,12 @@ NS_ASSUME_NONNULL_BEGIN return syncMessageBuilder; } +// Note that this will return nil for group messages. +- (nullable NSString *)recipientIdentifierForMessage +{ + return self.message.thread.contactIdentifier; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index dff04e3fd..5ca809b29 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -11,14 +11,30 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) { // a) Enqueued for sending. // b) Waiting on attachment upload(s). // c) Being sent to the service. - TSOutgoingMessageStateAttemptingOut, + TSOutgoingMessageStateSending, // The failure state. - TSOutgoingMessageStateUnsent, - // These two enum values have been combined into TSOutgoingMessageStateSentToService. + TSOutgoingMessageStateFailed, + // These two enum values have been combined into TSOutgoingMessageStateSent. TSOutgoingMessageStateSent_OBSOLETE, TSOutgoingMessageStateDelivered_OBSOLETE, // The message has been sent to the service. - TSOutgoingMessageStateSentToService, + TSOutgoingMessageStateSent, +}; + +// Used +typedef NS_ENUM(NSInteger, OWSOutgoingMessageRecipientState) { + // Message could not be sent to recipient. + OWSOutgoingMessageRecipientStateFailed = 0, + // Message is being sent to the recipient (enqueued, uploading or sending). + OWSOutgoingMessageRecipientStateSending, + // The message was not sent because the recipient is not valid. + // For example, this recipient may have left the group. + OWSOutgoingMessageRecipientStateSkipped, + // The message has been sent to the service. It may also have been delivered or read. + OWSOutgoingMessageRecipientStateSent, + + OWSOutgoingMessageRecipientStateMin = OWSOutgoingMessageRecipientStateFailed, + OWSOutgoingMessageRecipientStateMax = OWSOutgoingMessageRecipientStateSent, }; typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @@ -35,6 +51,16 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @class OWSSignalServiceProtosDataMessageBuilder; @class SignalRecipient; +@interface TSOutgoingMessageRecipientState : NSObject + +@property (atomic, readonly) OWSOutgoingMessageRecipientState state; +@property (atomic, nullable, readonly) NSNumber *deliveryTimestamp; +@property (atomic, nullable, readonly) NSNumber *readTimestamp; + +@end + +#pragma mark - + @interface TSOutgoingMessage : TSMessage - (instancetype)initMessageWithTimestamp:(uint64_t)timestamp @@ -75,11 +101,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { + (instancetype)outgoingMessageInThread:(nullable TSThread *)thread groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage; -@property (atomic, readonly) TSOutgoingMessageState messageState; - -// The message has been sent to the service and received by at least one recipient client. -// A recipient may have more than one client, and group message may have more than one recipient. -@property (atomic, readonly) BOOL wasDelivered; +@property (readonly) TSOutgoingMessageState messageState; @property (atomic, readonly) BOOL hasSyncedTranscript; @property (atomic, readonly) NSString *customMessage; @@ -89,27 +111,13 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @property (atomic, readonly) TSGroupMetaMessage groupMetaMessage; -// If set, this group message should only be sent to a single recipient. -@property (atomic, readonly) NSString *singleGroupRecipient; - @property (nonatomic, readonly) BOOL isVoiceMessage; // This property won't be accurate for legacy messages. @property (atomic, readonly) BOOL isFromLinkedDevice; -// Map of "recipient id"-to-"delivery time" of the recipients who have received the message. -@property (atomic, readonly) NSDictionary *recipientDeliveryMap; - -// Map of "recipient id"-to-"read time" of the recipients who have read the message. -@property (atomic, readonly) NSDictionary *recipientReadMap; - @property (nonatomic, readonly) BOOL isSilent; -/** - * Signal Identifier (e.g. e164 number) or nil if in a group thread. - */ -- (nullable NSString *)recipientIdentifier; - /** * The data representation of this message, to be encrypted, before being sent. */ @@ -143,36 +151,70 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { - (BOOL)shouldBeSaved; +- (NSArray *)recipientIds; + +- (NSArray *)sendingRecipientIds; + +- (NSArray *)deliveredRecipientIds; + +- (NSArray *)readRecipientIds; + +- (NSUInteger)sentRecipientsCount; + +- (nullable TSOutgoingMessageRecipientState *)recipientStateForRecipientId:(NSString *)recipientId; + #pragma mark - Update With... Methods -- (void)updateWithMessageState:(TSOutgoingMessageState)messageState; -- (void)updateWithMessageState:(TSOutgoingMessageState)messageState - transaction:(YapDatabaseReadWriteTransaction *)transaction; +// This method is used to record a successful send to one recipient. +- (void)updateWithSentRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; + +// This method is used to record a skipped send to one recipient. +- (void)updateWithSkippedRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; + +// On app launch, all "sending" recipients should be marked as "failed". +- (void)updateWithAllSendingRecipientsMarkedAsFailedWithTansaction:(YapDatabaseReadWriteTransaction *)transaction; + +// When we start a message send, all "failed" recipients should be marked as "sending". +- (void)updateWithMarkingAllUnsentRecipientsAsSendingWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; + +#ifdef DEBUG +// This method is used to forge the message state for fake messages. +- (void)updateWithFakeMessageState:(TSOutgoingMessageState)messageState + transaction:(YapDatabaseReadWriteTransaction *)transaction; +#endif + +// This method is used to record a failed send to all "sending" recipients. - (void)updateWithSendingError:(NSError *)error; + - (void)updateWithHasSyncedTranscript:(BOOL)hasSyncedTranscript transaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)updateWithCustomMessage:(NSString *)customMessage transaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)updateWithCustomMessage:(NSString *)customMessage; + +// This method is used to record a successful delivery to one recipient. +// // deliveryTimestamp is an optional parameter, since legacy // delivery receipts don't have a "delivery timestamp". Those // messages repurpose the "timestamp" field to indicate when the // corresponding message was originally sent. -- (void)updateWithDeliveredToRecipientId:(NSString *)recipientId - deliveryTimestamp:(NSNumber *_Nullable)deliveryTimestamp - transaction:(YapDatabaseReadWriteTransaction *)transaction; +- (void)updateWithDeliveredRecipient:(NSString *)recipientId + deliveryTimestamp:(NSNumber *_Nullable)deliveryTimestamp + transaction:(YapDatabaseReadWriteTransaction *)transaction; + - (void)updateWithWasSentFromLinkedDeviceWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; + +// This method is used to rewrite the recipient list with a single recipient. +// It is used to reply to a "group info request", which should only be +// delivered to the requestor. - (void)updateWithSingleGroupRecipient:(NSString *)singleGroupRecipient transaction:(YapDatabaseReadWriteTransaction *)transaction; + +// This method is used to record a successful "read" by one recipient. - (void)updateWithReadRecipientId:(NSString *)recipientId readTimestamp:(uint64_t)readTimestamp transaction:(YapDatabaseReadWriteTransaction *)transaction; -- (nullable NSNumber *)firstRecipientReadTimestamp; - -#pragma mark - Sent Recipients -- (NSUInteger)sentRecipientsCount; -- (BOOL)wasSentToRecipient:(NSString *)contactId; -- (void)updateWithSentRecipient:(NSString *)contactId transaction:(YapDatabaseReadWriteTransaction *)transaction; +- (nullable NSNumber *)firstRecipientReadTimestamp; @end diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 0e95c93af..e9a55e338 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -21,28 +21,31 @@ NS_ASSUME_NONNULL_BEGIN NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRecipientAll"; +@interface TSOutgoingMessageRecipientState () + +@property (atomic) OWSOutgoingMessageRecipientState state; +@property (atomic, nullable) NSNumber *deliveryTimestamp; +@property (atomic, nullable) NSNumber *readTimestamp; + +@end + +#pragma mark - + +@implementation TSOutgoingMessageRecipientState + +@end + +#pragma mark - + @interface TSOutgoingMessage () -@property (atomic) TSOutgoingMessageState messageState; @property (atomic) BOOL hasSyncedTranscript; @property (atomic) NSString *customMessage; @property (atomic) NSString *mostRecentFailureText; -@property (atomic) BOOL wasDelivered; -@property (atomic) NSString *singleGroupRecipient; @property (atomic) BOOL isFromLinkedDevice; - -// For outgoing, non-legacy group messages sent from this client, this -// contains the list of recipients to whom the message has been sent. -// -// This collection can also be tested to avoid repeat delivery to the -// same recipient. -@property (atomic) NSArray *sentRecipients; - @property (atomic) TSGroupMetaMessage groupMetaMessage; -@property (atomic) NSDictionary *recipientDeliveryMap; - -@property (atomic) NSDictionary *recipientReadMap; +@property (atomic, nullable) NSDictionary *recipientStateMap; @end @@ -50,8 +53,6 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec @implementation TSOutgoingMessage -@synthesize sentRecipients = _sentRecipients; - - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; @@ -60,22 +61,95 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec if (!_attachmentFilenameMap) { _attachmentFilenameMap = [NSMutableDictionary new]; } - - // Migrate message state. - if (_messageState == TSOutgoingMessageStateSent_OBSOLETE) { - _messageState = TSOutgoingMessageStateSentToService; - } else if (_messageState == TSOutgoingMessageStateDelivered_OBSOLETE) { - _messageState = TSOutgoingMessageStateSentToService; - _wasDelivered = YES; - } - if (!_sentRecipients) { - _sentRecipients = [NSArray new]; + + if (!self.recipientStateMap) { + [self migrateRecipientStateMapWithCoder:coder]; + OWSAssert(self.recipientStateMap); } } return self; } +- (void)migrateRecipientStateMapWithCoder:(NSCoder *)coder +{ + OWSAssert(!self.recipientStateMap); + OWSAssert(coder); + + // Determine the "overall message state." + TSOutgoingMessageState oldMessageState = TSOutgoingMessageStateFailed; + NSNumber *_Nullable messageStateValue = [coder decodeObjectForKey:@"messageState"]; + if (messageStateValue) { + oldMessageState = (TSOutgoingMessageState)messageStateValue.intValue; + } + + OWSOutgoingMessageRecipientState defaultState; + switch (oldMessageState) { + case TSOutgoingMessageStateFailed: + defaultState = OWSOutgoingMessageRecipientStateFailed; + break; + case TSOutgoingMessageStateSending: + defaultState = OWSOutgoingMessageRecipientStateSending; + break; + case TSOutgoingMessageStateSent: + case TSOutgoingMessageStateSent_OBSOLETE: + case TSOutgoingMessageStateDelivered_OBSOLETE: + // Convert legacy values. + defaultState = OWSOutgoingMessageRecipientStateSent; + break; + } + + // Try to leverage the "per-recipient state." + NSDictionary *_Nullable recipientDeliveryMap = + [coder decodeObjectForKey:@"recipientDeliveryMap"]; + NSDictionary *_Nullable recipientReadMap = [coder decodeObjectForKey:@"recipientReadMap"]; + NSArray *_Nullable sentRecipients = [coder decodeObjectForKey:@"sentRecipients"]; + + NSMutableDictionary *recipientStateMap = [NSMutableDictionary new]; + // Our default recipient list is the current thread members. + NSArray *recipientIds = [self.thread recipientIdentifiers]; + if (sentRecipients) { + // If we have a `sentRecipients` list, prefer that as it is more accurate. + recipientIds = sentRecipients; + } + NSString *_Nullable singleGroupRecipient = [coder decodeObjectForKey:@"singleGroupRecipient"]; + if (singleGroupRecipient) { + // If this is a "single group recipient message", treat it as such. + recipientIds = @[ + singleGroupRecipient, + ]; + } + + for (NSString *recipientId in recipientIds) { + TSOutgoingMessageRecipientState *recipientState = [TSOutgoingMessageRecipientState new]; + + NSNumber *_Nullable readTimestamp = recipientReadMap[recipientId]; + NSNumber *_Nullable deliveryTimestamp = recipientDeliveryMap[recipientId]; + if (readTimestamp) { + // If we have a read timestamp for this recipient, mark it as read. + recipientState.state = OWSOutgoingMessageRecipientStateSent; + recipientState.readTimestamp = readTimestamp; + // deliveryTimestamp might be nil here. + recipientState.deliveryTimestamp = deliveryTimestamp; + } else if (deliveryTimestamp) { + // If we have a delivery timestamp for this recipient, mark it as delivered. + recipientState.state = OWSOutgoingMessageRecipientStateSent; + recipientState.deliveryTimestamp = deliveryTimestamp; + } else if ([sentRecipients containsObject:recipientId]) { + // If this recipient is in `sentRecipients`, mark it as sent. + recipientState.state = OWSOutgoingMessageRecipientStateSent; + } else { + // Use the default state for this message. + recipientState.state = defaultState; + } + + recipientStateMap[recipientId] = recipientState; + } + self.recipientStateMap = [recipientStateMap copy]; + + [self updateMessageState]; +} + + (instancetype)outgoingMessageInThread:(nullable TSThread *)thread messageBody:(nullable NSString *)body attachmentId:(nullable NSString *)attachmentId @@ -156,8 +230,6 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec return self; } - _messageState = TSOutgoingMessageStateAttemptingOut; - _sentRecipients = [NSArray new]; _hasSyncedTranscript = NO; if ([thread isKindOfClass:TSGroupThread.class]) { @@ -177,9 +249,55 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec _attachmentFilenameMap = [NSMutableDictionary new]; + NSMutableDictionary *recipientStateMap = [NSMutableDictionary new]; + NSArray *recipientIds = [self.thread recipientIdentifiers]; + for (NSString *recipientId in recipientIds) { + TSOutgoingMessageRecipientState *recipientState = [TSOutgoingMessageRecipientState new]; + recipientState.state = OWSOutgoingMessageRecipientStateSending; + recipientStateMap[recipientId] = recipientState; + } + self.recipientStateMap = [recipientStateMap copy]; + + [self updateMessageState]; + return self; } +- (void)updateMessageState +{ + // self.messageState = [TSOutgoingMessage messageStateForRecipientStates:self.recipientStateMap.allValues]; +} + +- (TSOutgoingMessageState)messageState +{ + return [TSOutgoingMessage messageStateForRecipientStates:self.recipientStateMap.allValues]; +} + ++ (TSOutgoingMessageState)messageStateForRecipientStates:(NSArray *)recipientStates +{ + OWSAssert(recipientStates); + + // If there are any "sending" recipients, consider this message "sending". + BOOL hasFailed = NO; + for (TSOutgoingMessageRecipientState *recipientState in recipientStates) { + if (recipientState.state == OWSOutgoingMessageRecipientStateSending) { + return TSOutgoingMessageStateSending; + } else if (recipientState.state == OWSOutgoingMessageRecipientStateFailed) { + hasFailed = YES; + } + } + + // If there are any "failed" recipients, consider this message "failed". + if (hasFailed) { + return TSOutgoingMessageStateFailed; + } + + // Otherwise, consider the message "sent". + // + // NOTE: This includes messages with no recipients. + return TSOutgoingMessageStateSent; +} + - (BOOL)shouldBeSaved { if (self.groupMetaMessage == TSGroupMessageDeliver || self.groupMetaMessage == TSGroupMessageUnspecified) { @@ -203,23 +321,39 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec [super saveWithTransaction:transaction]; } -- (nullable NSString *)recipientIdentifier +- (OWSOutgoingMessageRecipientState)maxMessageState +{ + OWSOutgoingMessageRecipientState result = OWSOutgoingMessageRecipientStateMin; + for (TSOutgoingMessageRecipientState *recipientState in self.recipientStateMap.allValues) { + result = MAX(recipientState.state, result); + } + return result; +} + +- (OWSOutgoingMessageRecipientState)minMessageState { - return self.thread.contactIdentifier; + OWSOutgoingMessageRecipientState result = OWSOutgoingMessageRecipientStateMax; + for (TSOutgoingMessageRecipientState *recipientState in self.recipientStateMap.allValues) { + result = MIN(recipientState.state, result); + } + return result; } - (BOOL)shouldStartExpireTimer:(YapDatabaseReadTransaction *)transaction { - switch (self.messageState) { - case TSOutgoingMessageStateSentToService: - return self.isExpiringMessage; - case TSOutgoingMessageStateAttemptingOut: - case TSOutgoingMessageStateUnsent: - return NO; - case TSOutgoingMessageStateSent_OBSOLETE: - case TSOutgoingMessageStateDelivered_OBSOLETE: - OWSFail(@"%@ Obsolete message state.", self.logTag); - return self.isExpiringMessage; + // It's not clear if we should wait until _all_ recipients have reached "sent or later" + // (which could never occur if one group member is unregistered) or only wait until + // the first recipient has reached "sent or later" (which could cause partially delivered + // messages to expire). For now, we'll do the latter. + // + // TODO: Revisit this decision. + + if (!self.isExpiringMessage) { + return NO; + } else if (self.recipientStateMap.count < 1) { + return YES; + } else { + return self.maxMessageState >= OWSOutgoingMessageRecipientStateSent; } } @@ -233,6 +367,67 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec return OWSInteractionType_OutgoingMessage; } +- (NSArray *)recipientIds +{ + return [self.recipientStateMap.allKeys copy]; +} + +- (NSArray *)sendingRecipientIds +{ + NSMutableArray *result = [NSMutableArray new]; + for (NSString *recipientId in self.recipientStateMap) { + TSOutgoingMessageRecipientState *recipientState = self.recipientStateMap[recipientId]; + if (recipientState.state == OWSOutgoingMessageRecipientStateSending) { + [result addObject:recipientId]; + } + } + return result; +} + +- (NSArray *)deliveredRecipientIds +{ + NSMutableArray *result = [NSMutableArray new]; + for (NSString *recipientId in self.recipientStateMap) { + TSOutgoingMessageRecipientState *recipientState = self.recipientStateMap[recipientId]; + if (recipientState.deliveryTimestamp != nil) { + [result addObject:recipientId]; + } + } + return result; +} + +- (NSArray *)readRecipientIds +{ + NSMutableArray *result = [NSMutableArray new]; + for (NSString *recipientId in self.recipientStateMap) { + TSOutgoingMessageRecipientState *recipientState = self.recipientStateMap[recipientId]; + if (recipientState.readTimestamp != nil) { + [result addObject:recipientId]; + } + } + return result; +} + +- (NSUInteger)sentRecipientsCount +{ + return [self.recipientStateMap.allValues + filteredArrayUsingPredicate:[NSPredicate + predicateWithBlock:^BOOL(TSOutgoingMessageRecipientState *recipientState, + NSDictionary *_Nullable bindings) { + return recipientState.state == OWSOutgoingMessageRecipientStateSent; + }]] + .count; +} + +- (nullable TSOutgoingMessageRecipientState *)recipientStateForRecipientId:(NSString *)recipientId +{ + OWSAssert(recipientId.length > 0); + + TSOutgoingMessageRecipientState *_Nullable result = self.recipientStateMap[recipientId]; + OWSAssert(result); + return result; +} + #pragma mark - Update With... Methods - (void)updateWithSendingError:(NSError *)error @@ -242,27 +437,49 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - [message setMessageState:TSOutgoingMessageStateUnsent]; + // Mark any "sending" recipients as "failed." + for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap + .allValues) { + if (recipientState.state == OWSOutgoingMessageRecipientStateSending) { + recipientState.state = OWSOutgoingMessageRecipientStateFailed; + } + } [message setMostRecentFailureText:error.localizedDescription]; }]; }]; } -- (void)updateWithMessageState:(TSOutgoingMessageState)messageState +- (void)updateWithAllSendingRecipientsMarkedAsFailedWithTansaction:(YapDatabaseReadWriteTransaction *)transaction { - [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self updateWithMessageState:messageState transaction:transaction]; - }]; + OWSAssert(transaction); + + [self applyChangeToSelfAndLatestCopy:transaction + changeBlock:^(TSOutgoingMessage *message) { + // Mark any "sending" recipients as "failed." + for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap + .allValues) { + if (recipientState.state == OWSOutgoingMessageRecipientStateSending) { + recipientState.state = OWSOutgoingMessageRecipientStateFailed; + } + } + [message updateMessageState]; + }]; } -- (void)updateWithMessageState:(TSOutgoingMessageState)messageState - transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)updateWithMarkingAllUnsentRecipientsAsSendingWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(transaction); [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - [message setMessageState:messageState]; + // Mark any "sending" recipients as "failed." + for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap + .allValues) { + if (recipientState.state == OWSOutgoingMessageRecipientStateFailed) { + recipientState.state = OWSOutgoingMessageRecipientStateSending; + } + } + [message updateMessageState]; }]; } @@ -293,135 +510,167 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec }]; } -- (void)updateWithDeliveredToRecipientId:(NSString *)recipientId - deliveryTimestamp:(NSNumber *_Nullable)deliveryTimestamp - transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)updateWithSentRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(recipientId.length > 0); OWSAssert(transaction); + // TODO: I suspect we're double-calling this method. + [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - - if (deliveryTimestamp) { - NSMutableDictionary *recipientDeliveryMap - = (message.recipientDeliveryMap ? [message.recipientDeliveryMap mutableCopy] - : [NSMutableDictionary new]); - recipientDeliveryMap[recipientId] = deliveryTimestamp; - message.recipientDeliveryMap = [recipientDeliveryMap copy]; + TSOutgoingMessageRecipientState *_Nullable recipientState + = message.recipientStateMap[recipientId]; + if (!recipientState) { + OWSFail(@"%@ Missing recipient state for recipient: %@", self.logTag, recipientId); + return; } - - [message setWasDelivered:YES]; + recipientState.state = OWSOutgoingMessageRecipientStateSent; + [message updateMessageState]; }]; } -- (void)updateWithWasSentFromLinkedDeviceWithTransaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)updateWithSkippedRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssert(recipientId.length > 0); OWSAssert(transaction); [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - [message setMessageState:TSOutgoingMessageStateSentToService]; - [message setWasDelivered:YES]; - [message setIsFromLinkedDevice:YES]; + TSOutgoingMessageRecipientState *_Nullable recipientState + = message.recipientStateMap[recipientId]; + if (!recipientState) { + OWSFail(@"%@ Missing recipient state for recipient: %@", self.logTag, recipientId); + return; + } + recipientState.state = OWSOutgoingMessageRecipientStateSkipped; + [message updateMessageState]; }]; } -- (void)updateWithSingleGroupRecipient:(NSString *)singleGroupRecipient - transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)updateWithDeliveredRecipient:(NSString *)recipientId + deliveryTimestamp:(NSNumber *_Nullable)deliveryTimestamp + transaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssert(recipientId.length > 0); OWSAssert(transaction); - OWSAssert(singleGroupRecipient.length > 0); [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - [message setSingleGroupRecipient:singleGroupRecipient]; + TSOutgoingMessageRecipientState *_Nullable recipientState + = message.recipientStateMap[recipientId]; + if (!recipientState) { + OWSFail(@"%@ Missing recipient state for delivered recipient: %@", + self.logTag, + recipientId); + return; + } + recipientState.state = OWSOutgoingMessageRecipientStateSent; + recipientState.deliveryTimestamp = deliveryTimestamp; + [message updateMessageState]; }]; } -#pragma mark - Sent Recipients - -- (NSArray *)sentRecipients -{ - @synchronized(self) - { - return _sentRecipients; - } -} - -- (void)setSentRecipients:(NSArray *)sentRecipients -{ - @synchronized(self) - { - _sentRecipients = [sentRecipients copy]; - } -} - -- (void)addSentRecipient:(NSString *)contactId -{ - @synchronized(self) - { - OWSAssert(_sentRecipients); - OWSAssert(contactId.length > 0); - - NSMutableArray *sentRecipients = [_sentRecipients mutableCopy]; - [sentRecipients addObject:contactId]; - _sentRecipients = [sentRecipients copy]; - } -} - -- (BOOL)wasSentToRecipient:(NSString *)contactId -{ - OWSAssert(self.sentRecipients); - OWSAssert(contactId.length > 0); - - return [self.sentRecipients containsObject:contactId]; -} - -- (NSUInteger)sentRecipientsCount +- (void)updateWithReadRecipientId:(NSString *)recipientId + readTimestamp:(uint64_t)readTimestamp + transaction:(YapDatabaseReadWriteTransaction *)transaction { - OWSAssert(self.sentRecipients); + OWSAssert(recipientId.length > 0); + OWSAssert(transaction); - return self.sentRecipients.count; + [self applyChangeToSelfAndLatestCopy:transaction + changeBlock:^(TSOutgoingMessage *message) { + TSOutgoingMessageRecipientState *_Nullable recipientState + = message.recipientStateMap[recipientId]; + if (!recipientState) { + OWSFail(@"%@ Missing recipient state for delivered recipient: %@", + self.logTag, + recipientId); + return; + } + recipientState.state = OWSOutgoingMessageRecipientStateSent; + recipientState.readTimestamp = @(readTimestamp); + [message updateMessageState]; + }]; } -- (void)updateWithSentRecipient:(NSString *)contactId transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)updateWithWasSentFromLinkedDeviceWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(transaction); + [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - [message addSentRecipient:contactId]; + // Mark any "sending" recipients as "sent." + for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap + .allValues) { + if (recipientState.state == OWSOutgoingMessageRecipientStateSending) { + recipientState.state = OWSOutgoingMessageRecipientStateSent; + } + } + [message setIsFromLinkedDevice:YES]; + [message updateMessageState]; }]; } -- (void)updateWithReadRecipientId:(NSString *)recipientId - readTimestamp:(uint64_t)readTimestamp - transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)updateWithSingleGroupRecipient:(NSString *)singleGroupRecipient + transaction:(YapDatabaseReadWriteTransaction *)transaction { - OWSAssert(recipientId.length > 0); OWSAssert(transaction); + OWSAssert(singleGroupRecipient.length > 0); [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - NSMutableDictionary *recipientReadMap - = (message.recipientReadMap ? [message.recipientReadMap mutableCopy] - : [NSMutableDictionary new]); - recipientReadMap[recipientId] = @(readTimestamp); - message.recipientReadMap = [recipientReadMap copy]; + TSOutgoingMessageRecipientState *recipientState = + [TSOutgoingMessageRecipientState new]; + recipientState.state = OWSOutgoingMessageRecipientStateSending; + [message setRecipientStateMap:@{ + singleGroupRecipient : recipientState, + }]; + [message updateMessageState]; }]; } - (nullable NSNumber *)firstRecipientReadTimestamp { NSNumber *result = nil; - for (NSNumber *timestamp in self.recipientReadMap.allValues) { - if (!result || (result.unsignedLongLongValue > timestamp.unsignedLongLongValue)) { - result = timestamp; + for (TSOutgoingMessageRecipientState *recipientState in self.recipientStateMap.allValues) { + if (!recipientState.readTimestamp) { + continue; + } + if (!result || (result.unsignedLongLongValue > recipientState.readTimestamp.unsignedLongLongValue)) { + result = recipientState.readTimestamp; } } return result; } +- (void)updateWithFakeMessageState:(TSOutgoingMessageState)messageState + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + OWSAssert(transaction); + + [self applyChangeToSelfAndLatestCopy:transaction + changeBlock:^(TSOutgoingMessage *message) { + for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap + .allValues) { + switch (messageState) { + case TSOutgoingMessageStateSending: + recipientState.state = OWSOutgoingMessageRecipientStateSending; + break; + case TSOutgoingMessageStateFailed: + recipientState.state = OWSOutgoingMessageRecipientStateFailed; + break; + case TSOutgoingMessageStateSent: + recipientState.state = OWSOutgoingMessageRecipientStateSent; + break; + default: + OWSFail(@"%@ unexpected message state.", self.logTag); + break; + } + } + [message updateMessageState]; + }]; +} #pragma mark - - (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder @@ -437,7 +686,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec [builder setBody:self.body]; } else { OWSFail(@"%@ message body length too long.", self.logTag); - NSString *truncatedBody = self.body; + NSString *truncatedBody = [self.body copy]; while ([truncatedBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > kOversizeTextMessageSizeThreshold) { DDLogError(@"%@ truncating body which is too long: %tu", self.logTag, diff --git a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m index 5ea221472..01ad559c1 100644 --- a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m +++ b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m @@ -44,9 +44,8 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m NSMutableArray *messageIds = [NSMutableArray new]; - NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ == %d", - OWSFailedMessagesJobMessageStateColumn, - (int)TSOutgoingMessageStateAttemptingOut]; + NSString *formattedString = [NSString + stringWithFormat:@"WHERE %@ == %d", OWSFailedMessagesJobMessageStateColumn, (int)TSOutgoingMessageStateSending]; YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString]; [[transaction ext:OWSFailedMessagesJobMessageStateIndex] enumerateKeysMatchingQuery:query @@ -83,8 +82,8 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [self enumerateAttemptingOutMessagesWithBlock:^(TSOutgoingMessage *message) { // sanity check - OWSAssert(message.messageState == TSOutgoingMessageStateAttemptingOut); - if (message.messageState != TSOutgoingMessageStateAttemptingOut) { + OWSAssert(message.messageState == TSOutgoingMessageStateSending); + if (message.messageState != TSOutgoingMessageStateSending) { DDLogError(@"%@ Refusing to mark as unsent message with state: %d", self.logTag, (int)message.messageState); @@ -92,8 +91,8 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m } DDLogDebug(@"%@ marking message as unsent: %@", self.logTag, message.uniqueId); - [message updateWithMessageState:TSOutgoingMessageStateUnsent transaction:transaction]; - OWSAssert(message.messageState == TSOutgoingMessageStateUnsent); + [message updateWithAllSendingRecipientsMarkedAsFailedWithTansaction:transaction]; + OWSAssert(message.messageState == TSOutgoingMessageStateFailed); count++; } diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 901f929b5..a95908a2f 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -262,9 +262,9 @@ NS_ASSUME_NONNULL_BEGIN timestamp); } for (TSOutgoingMessage *outgoingMessage in messages) { - [outgoingMessage updateWithDeliveredToRecipientId:recipientId - deliveryTimestamp:deliveryTimestamp - transaction:transaction]; + [outgoingMessage updateWithDeliveredRecipient:recipientId + deliveryTimestamp:deliveryTimestamp + transaction:transaction]; } } } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index bc0501905..1b4306340 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -186,7 +186,8 @@ void AssertIsOnSendingQueue() - (void)didSucceed { - [self.message updateWithMessageState:TSOutgoingMessageStateSentToService]; + OWSAssert(self.message.messageState == TSOutgoingMessageStateSent); + self.successHandler(); } @@ -311,7 +312,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // All outgoing messages should be saved at the time they are enqueued. [message saveWithTransaction:transaction]; - [message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:transaction]; + // When we start a message send, all "failed" recipients should be marked as "sending". + [message updateWithMarkingAllUnsentRecipientsAsSendingWithTransaction:transaction]; }]; NSOperationQueue *sendingQueue = [self sendingQueueForMessage:message]; @@ -417,11 +419,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; }); } -- (NSArray *)getRecipients:(NSArray *)identifiers error:(NSError **)error +- (NSArray *)getRecipientsForRecipientIds:(NSArray *)recipientIds error:(NSError **)error { + OWSAssert(error); + + error = nil; + NSMutableArray *recipients = [NSMutableArray new]; - for (NSString *recipientId in identifiers) { + for (NSString *recipientId in recipientIds) { SignalRecipient *existingRecipient = [SignalRecipient recipientWithTextSecureIdentifier:recipientId]; if (existingRecipient) { @@ -451,11 +457,38 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; TSThread *thread = message.thread; if ([thread isKindOfClass:[TSGroupThread class]]) { + TSGroupThread *gThread = (TSGroupThread *)thread; + // Send to the intersection of: + // + // * "sending" recipients of the message. + // * members of the group. + // + // I.e. try to send a message IFF: + // + // * The recipient was in the group when the message was first tried to be sent. + // * The recipient is still in the group. + // * The recipient is in the "sending" state. + NSMutableSet *obsoleteRecipientIds = [NSMutableSet setWithArray:message.sendingRecipientIds]; + [obsoleteRecipientIds minusSet:[NSSet setWithArray:gThread.groupModel.groupMemberIds]]; + if (obsoleteRecipientIds.count > 0) { + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + for (NSString *recipientId in obsoleteRecipientIds) { + // Mark this recipient as "skipped". + // + // TODO: We should also mark as "skipped" group members who no longer have signal accounts. + [message updateWithSkippedRecipient:recipientId transaction:transaction]; + } + }]; + } + + NSMutableSet *sendingRecipientIds = [NSMutableSet setWithArray:message.sendingRecipientIds]; + [sendingRecipientIds intersectSet:[NSSet setWithArray:gThread.groupModel.groupMemberIds]]; + NSError *error; NSArray *recipients = - [self getRecipients:gThread.groupModel.groupMemberIds error:&error]; + [self getRecipientsForRecipientIds:sendingRecipientIds.allObjects error:&error]; if (recipients.count == 0) { if (!error) { @@ -491,7 +524,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // you might, for example, have a pending outgoing message when // you block them. OWSAssert(recipientContactId.length > 0); - if ([_blockingManager isRecipientIdBlocked:recipientContactId]) { + if ([self.blockingManager isRecipientIdBlocked:recipientContactId]) { DDLogInfo(@"%@ skipping 1:1 send to blocked contact: %@", self.logTag, recipientContactId); NSError *error = OWSErrorMakeMessageSendFailedToBlockListError(); // No need to retry - the user will continue to be blocked. @@ -562,6 +595,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; success:^{ DDLogInfo(@"%@ Marking group message as sent to recipient: %@", self.logTag, recipient.uniqueId); [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + // Mark this recipient as "sent". [message updateWithSentRecipient:recipient.uniqueId transaction:transaction]; }]; [futureSource trySetResult:@1]; @@ -589,12 +623,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if ([recipientId isEqualToString:[TSAccountManager localNumber]]) { continue; } - // We don't need to sent the message to all group members if - // it has a "single group recipient". - if (message.singleGroupRecipient && ![message.singleGroupRecipient isEqualToString:recipientId]) { - continue; - } - if ([message wasSentToRecipient:recipientId]) { + if (![message.sendingRecipientIds containsObject:recipientId]) { // Skip recipients we have already sent this message to (on an // earlier retry, perhaps). DDLogInfo(@"%@ Skipping group message recipient; already sent: %@", self.logTag, recipient.uniqueId); @@ -904,7 +933,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } dispatch_async([OWSDispatch sendingQueue], ^{ - [recipient save]; + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [recipient saveWithTransaction:transaction]; + [message updateWithSentRecipient:recipient.uniqueId transaction:transaction]; + }]; + [self handleMessageSentLocally:message]; successHandler(); });