diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index c699e63e9..5104aa509 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -3604,10 +3604,16 @@ typedef enum : NSUInteger { } BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - TSOutgoingMessage *message = [ThreadUtil enqueueMessageWithAttachments:attachments - messageBody:messageText - inThread:self.thread - quotedReplyModel:self.inputToolbar.quotedReply]; + + __block TSOutgoingMessage *message; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + message = [ThreadUtil enqueueMessageWithText:messageText + mediaAttachments:attachments + inThread:self.thread + quotedReplyModel:self.inputToolbar.quotedReply + linkPreviewDraft:nil + transaction:transaction]; + }]; [self messageWasSent:message]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index fa13720a0..49844d050 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -592,10 +592,26 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } } - NSArray *attachments = [message attachmentsWithTransaction:transaction]; + NSString *_Nullable bodyText = [message bodyTextWithTransaction:transaction]; + if (bodyText) { + self.displayableBodyText = [self displayableBodyTextForText:bodyText interactionId:message.uniqueId]; + } + + // Even though displayableBodyText could have already been assigned from the oversized text + // attachment, it's also possible that for new messages we'd first cached the truncated body + // text. So if an AttachmentStream now exists we explicitly use the text from the attachment. + TSAttachment *_Nullable oversizeTextAttachment = [message oversizeTextAttachmentWithTransaction:transaction]; + if (oversizeTextAttachment != nil && [oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) { + TSAttachmentStream *oversizeTextAttachmentStream = (TSAttachmentStream *)oversizeTextAttachment; + self.messageCellType = OWSMessageCellType_OversizeTextMessage; + self.displayableBodyText = [self displayableBodyTextForOversizeTextAttachment:oversizeTextAttachmentStream + interactionId:message.uniqueId]; + } + + NSArray *mediaAttachments = [message mediaAttachmentsWithTransaction:transaction]; if ([message isMediaAlbumWithTransaction:transaction]) { - OWSAssertDebug(attachments.count > 0); - NSArray *mediaAlbumItems = [self mediaAlbumItemsForAttachments:attachments]; + OWSAssertDebug(mediaAttachments.count > 0); + NSArray *mediaAlbumItems = [self mediaAlbumItemsForAttachments:mediaAttachments]; if (mediaAlbumItems.count == 1) { ConversationMediaAlbumItem *mediaAlbumItem = mediaAlbumItems.firstObject; @@ -608,30 +624,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.mediaAlbumItems = mediaAlbumItems; self.messageCellType = OWSMessageCellType_MediaAlbum; - NSString *_Nullable albumTitle = [message bodyTextWithTransaction:transaction]; - if (!albumTitle && mediaAlbumItems.count == 1) { - // If the album contains only one option, use its caption as the - // the album title. - albumTitle = mediaAlbumItems.firstObject.caption; - } - if (albumTitle) { - self.displayableBodyText = [self displayableBodyTextForText:albumTitle interactionId:message.uniqueId]; - } return; } + // Only media galleries should have more than one attachment. - OWSAssertDebug(attachments.count <= 1); - - TSAttachment *_Nullable attachment = attachments.firstObject; - if (attachment) { - if ([attachment isKindOfClass:[TSAttachmentStream class]]) { - self.attachmentStream = (TSAttachmentStream *)attachment; - - if ([attachment.contentType isEqualToString:OWSMimeTypeOversizeTextMessage]) { - self.messageCellType = OWSMessageCellType_OversizeTextMessage; - self.displayableBodyText = [self displayableBodyTextForOversizeTextAttachment:self.attachmentStream - interactionId:message.uniqueId]; - } else if ([self.attachmentStream isAudio]) { + OWSAssertDebug(mediaAttachments.count <= 1); + TSAttachment *_Nullable mediaAttachment = mediaAttachments.firstObject; + if (mediaAttachment) { + if ([mediaAttachment isKindOfClass:[TSAttachmentStream class]]) { + self.attachmentStream = (TSAttachmentStream *)mediaAttachment; + if ([self.attachmentStream isAudio]) { CGFloat audioDurationSeconds = [self.attachmentStream audioDurationSeconds]; if (audioDurationSeconds > 0) { self.audioDurationSeconds = audioDurationSeconds; @@ -639,34 +641,28 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } else { self.messageCellType = OWSMessageCellType_GenericAttachment; } - } else { + } else if (self.messageCellType == OWSMessageCellType_Unknown) { self.messageCellType = OWSMessageCellType_GenericAttachment; } - } else if ([attachment isKindOfClass:[TSAttachmentPointer class]]) { + } else if ([mediaAttachment isKindOfClass:[TSAttachmentPointer class]]) { self.messageCellType = OWSMessageCellType_DownloadingAttachment; - self.attachmentPointer = (TSAttachmentPointer *)attachment; + self.attachmentPointer = (TSAttachmentPointer *)mediaAttachment; } else { OWSFailDebug(@"Unknown attachment type"); } } - // Ignore message body for oversize text attachments. - if (message.body.length > 0) { - if (self.hasBodyText) { - OWSFailDebug(@"oversize text message has unexpected caption."); - } - + if (self.hasBodyText) { // If we haven't already assigned an attachment type at this point, message.body isn't a caption, // it's a stand-alone text message. if (self.messageCellType == OWSMessageCellType_Unknown) { OWSAssertDebug(message.attachmentIds.count == 0); self.messageCellType = OWSMessageCellType_TextMessage; } - self.displayableBodyText = [self displayableBodyTextForText:message.body interactionId:message.uniqueId]; OWSAssertDebug(self.displayableBodyText); } - if (self.hasBodyText && attachment == nil && message.linkPreview) { + if (self.hasBodyText && message.linkPreview) { self.linkPreview = message.linkPreview; if (message.linkPreview.imageAttachmentId.length > 0) { TSAttachment *_Nullable linkPreviewAttachment = diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 8a69619e1..660c0362e 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -390,12 +390,12 @@ NS_ASSUME_NONNULL_BEGIN }]; } -+ (void)sendAttachment:(NSString *)filePath - thread:(TSThread *)thread - label:(NSString *)label - hasCaption:(BOOL)hasCaption - success:(nullable void (^)(void))success - failure:(nullable void (^)(void))failure ++ (void)sendAttachmentWithFilePath:(NSString *)filePath + thread:(TSThread *)thread + label:(NSString *)label + hasCaption:(BOOL)hasCaption + success:(nullable void (^)(void))success + failure:(nullable void (^)(void))failure { OWSAssertDebug(filePath); OWSAssertDebug(thread); @@ -425,7 +425,9 @@ NS_ASSUME_NONNULL_BEGIN [DDLog flushLog]; } OWSAssertDebug(![attachment hasError]); - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; + + [self sendAttachment:attachment thread:thread messageBody:messageBody]; + success(); } @@ -551,12 +553,12 @@ NS_ASSUME_NONNULL_BEGIN ActionFailureBlock failure) { dispatch_async(dispatch_get_main_queue(), ^{ OWSAssertDebug(fakeAssetLoader.filePath.length > 0); - [self sendAttachment:fakeAssetLoader.filePath - thread:thread - label:label - hasCaption:hasCaption - success:success - failure:failure]; + [self sendAttachmentWithFilePath:fakeAssetLoader.filePath + thread:thread + label:label + hasCaption:hasCaption + success:success + failure:failure]; }); } prepareBlock:fakeAssetLoader.prepareBlock]; @@ -1732,19 +1734,25 @@ NS_ASSUME_NONNULL_BEGIN return attachment; } -+ (void)sendAttachment:(NSString *)filePath ++ (void)sendAttachment:(nullable SignalAttachment *)attachment thread:(TSThread *)thread - success:(nullable void (^)(void))success - failure:(nullable void (^)(void))failure + messageBody:(nullable NSString *)messageBody { - OWSAssertDebug(filePath); - OWSAssertDebug(thread); - - SignalAttachment *attachment = [self signalAttachmentForFilePath:filePath]; - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; - success(); + [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + NSArray *attachments = @[]; + if (attachment != nil) { + attachments = @[ attachment ]; + } + [ThreadUtil enqueueMessageWithText:messageBody + mediaAttachments:attachments + inThread:thread + quotedReplyModel:nil + linkPreviewDraft:nil + transaction:transaction]; + }]; } + + (DebugUIMessagesAction *)fakeIncomingTextMessageAction:(TSThread *)thread text:(NSString *)text { OWSAssertDebug(thread); @@ -3342,11 +3350,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac + (void)sendOversizeTextMessage:(TSThread *)thread { - NSString *message = [self randomOversizeText]; - DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:message]; - SignalAttachment *attachment = - [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI]; - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; + [self sendAttachment:nil thread:thread messageBody:[self randomOversizeText]]; } + (NSData *)createRandomNSDataOfSize:(size_t)size @@ -3379,7 +3383,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac // style them indistinguishably from a separate text message. attachment.captionText = [self randomCaptionText]; } - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; + [self sendAttachment:attachment thread:thread messageBody:nil]; } + (SSKProtoEnvelope *)createEnvelopeForThread:(TSThread *)thread @@ -4445,7 +4449,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [DDLog flushLog]; } OWSAssertDebug(![attachment hasError]); - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; + [self sendAttachment:attachment thread:thread messageBody:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ sendUnsafeFile(); @@ -4759,12 +4763,14 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [attachments addObject:attachment]; } - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - TSOutgoingMessage *message = [ThreadUtil enqueueMessageWithAttachments:attachments - messageBody:messageBody - inThread:thread - quotedReplyModel:nil]; - OWSLogError(@"timestamp: %llu.", message.timestamp); + [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + TSOutgoingMessage *message = [ThreadUtil enqueueMessageWithText:messageBody + mediaAttachments:attachments + inThread:thread + quotedReplyModel:nil + linkPreviewDraft:nil + transaction:transaction]; + OWSLogDebug(@"timestamp: %llu.", message.timestamp); }]; } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m index c14efff16..a3671bd51 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m @@ -33,6 +33,13 @@ NS_ASSUME_NONNULL_BEGIN @implementation DebugUIMisc +#pragma mark - Dependencies + ++ (YapDatabaseConnection *)dbConnection +{ + return [OWSPrimaryStorage.sharedManager dbReadWriteConnection]; +} + #pragma mark - Factory Methods - (NSString *)name @@ -252,11 +259,23 @@ NS_ASSUME_NONNULL_BEGIN SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:utiType]; NSData *databasePassword = [OWSPrimaryStorage.sharedManager databasePassword]; attachment.captionText = [databasePassword hexadecimalString]; + [self sendAttachment:attachment thread:thread]; +} + ++ (void)sendAttachment:(SignalAttachment *)attachment thread:(TSThread *)thread +{ if (!attachment || [attachment hasError]) { OWSFailDebug(@"attachment[%@]: %@", [attachment sourceFilename], [attachment errorName]); return; } - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; + [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + [ThreadUtil enqueueMessageWithText:nil + mediaAttachments:@[ attachment ] + inThread:thread + quotedReplyModel:nil + linkPreviewDraft:nil + transaction:transaction]; + }]; } + (void)sendUnencryptedDatabase:(TSThread *)thread @@ -274,11 +293,7 @@ NS_ASSUME_NONNULL_BEGIN DataSource *_Nullable dataSource = [DataSourcePath dataSourceWithFilePath:filePath shouldDeleteOnDeallocation:YES]; [dataSource setSourceFilename:fileName]; SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:utiType]; - if (!attachment || [attachment hasError]) { - OWSFailDebug(@"attachment[%@]: %@", [attachment sourceFilename], [attachment errorName]); - return; - } - [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil]; + [self sendAttachment:attachment thread:thread]; } #ifdef DEBUG diff --git a/SignalMessaging/ViewControllers/SharingThreadPickerViewController.m b/SignalMessaging/ViewControllers/SharingThreadPickerViewController.m index f29816b53..b14ddc605 100644 --- a/SignalMessaging/ViewControllers/SharingThreadPickerViewController.m +++ b/SignalMessaging/ViewControllers/SharingThreadPickerViewController.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "SharingThreadPickerViewController.h" diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 47e9e3763..aa1923d3b 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -44,20 +44,18 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Durable Message Enqueue -+ (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)text ++ (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)fullMessageText inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft transaction:(YapDatabaseReadTransaction *)transaction; -+ (TSOutgoingMessage *)enqueueMessageWithAttachment:(SignalAttachment *)attachment - inThread:(TSThread *)thread - quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel; - -+ (TSOutgoingMessage *)enqueueMessageWithAttachments:(NSArray *)attachments - messageBody:(nullable NSString *)messageBody - inThread:(TSThread *)thread - quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel; ++ (TSOutgoingMessage *)enqueueMessageWithText:(nullable NSString *)fullMessageText + mediaAttachments:(NSArray *)attachments + inThread:(TSThread *)thread + quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel + linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft + transaction:(YapDatabaseReadTransaction *)transaction; + (TSOutgoingMessage *)enqueueMessageWithContactShare:(OWSContact *)contactShare inThread:(TSThread *)thread; + (void)enqueueLeaveGroupMessageInThread:(TSGroupThread *)thread; diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 50040b5ae..c4ec5a10c 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -9,6 +9,7 @@ #import "OWSUnreadIndicator.h" #import "TSUnreadIndicatorInteraction.h" #import +#import #import #import #import @@ -65,58 +66,18 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Durable Message Enqueue -+ (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)text ++ (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)fullMessageText inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft transaction:(YapDatabaseReadTransaction *)transaction { - - NSString *truncatedText; - SignalAttachment *_Nullable oversizeTextAttachment; - - if ([text lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= kOversizeTextMessageSizeThreshold) { - truncatedText = [text truncatedToByteCount:kOversizeTextMessageSizeThreshold]; - DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:text]; - if (dataSource) { - oversizeTextAttachment = - [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI]; - } - } else { - truncatedText = text; - oversizeTextAttachment = nil; - } - - OWSDisappearingMessagesConfiguration *configuration = - [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction]; - - uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); - - TSOutgoingMessage *message = - [TSOutgoingMessage outgoingMessageInThread:thread - messageBody:text - attachmentId:nil - expiresInSeconds:expiresInSeconds - quotedMessage:[quotedReplyModel buildQuotedMessageForSending] - linkPreview:nil]; - - [BenchManager benchAsyncWithTitle:@"Saving outgoing message" block:^(void (^benchmarkCompletion)(void)) { - // To avoid blocking the send flow, we dispatch an async write from within this read transaction - [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull writeTransaction) { - [message saveWithTransaction:writeTransaction]; - - OWSLinkPreview *_Nullable linkPreview = - [self linkPreviewForLinkPreviewDraft:linkPreviewDraft transaction:writeTransaction]; - if (linkPreview) { - [message updateWithLinkPreview:linkPreview transaction:writeTransaction]; - } - - [self.messageSenderJobQueue addMessage:message transaction:writeTransaction]; - } - completionBlock:benchmarkCompletion]; - }]; - - return message; + return [self enqueueMessageWithText:fullMessageText + mediaAttachments:@[] + inThread:thread + quotedReplyModel:quotedReplyModel + linkPreviewDraft:linkPreviewDraft + transaction:transaction]; } + (nullable OWSLinkPreview *)linkPreviewForLinkPreviewDraft:(nullable OWSLinkPreviewDraft *)linkPreviewDraft @@ -137,40 +98,48 @@ NS_ASSUME_NONNULL_BEGIN return linkPreview; } -+ (TSOutgoingMessage *)enqueueMessageWithAttachment:(SignalAttachment *)attachment - inThread:(TSThread *)thread - quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel -{ - return [self enqueueMessageWithAttachments:@[ - attachment, - ] - messageBody:attachment.captionText - inThread:thread - quotedReplyModel:quotedReplyModel]; -} - -+ (TSOutgoingMessage *)enqueueMessageWithAttachments:(NSArray *)attachments - messageBody:(nullable NSString *)messageBody - inThread:(TSThread *)thread - quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel ++ (TSOutgoingMessage *)enqueueMessageWithText:(nullable NSString *)fullMessageText + mediaAttachments:(NSArray *)attachmentsParam + inThread:(TSThread *)thread + quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel + linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft + transaction:(YapDatabaseReadTransaction *)transaction { OWSAssertIsOnMainThread(); - OWSAssertDebug(attachments.count > 0); OWSAssertDebug(thread); - for (SignalAttachment *attachment in attachments) { - OWSAssertDebug(!attachment.hasError); - OWSAssertDebug(attachment.mimeType.length > 0); + + NSString *truncatedText; + NSArray *attachments = attachmentsParam; + if ([fullMessageText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] < kOversizeTextMessageSizeThreshold) { + truncatedText = fullMessageText; + } else { + truncatedText = [fullMessageText ows_truncatedToByteCount:kOversizeTextMessageSizeThreshold]; + DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:fullMessageText]; + if (dataSource) { + SignalAttachment *oversizeTextAttachment = + [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI]; + attachments = [attachmentsParam arrayByAddingObject:oversizeTextAttachment]; + } else { + OWSFailDebug(@"dataSource was unexpectedly nil"); + } } OWSDisappearingMessagesConfiguration *configuration = - [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; + [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction]; uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); + + for (SignalAttachment *attachment in attachments) { + OWSAssertDebug(!attachment.hasError); + OWSAssertDebug(attachment.mimeType.length > 0); + } + BOOL isVoiceMessage = (attachments.count == 1 && attachments.lastObject.isVoiceMessage); + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread - messageBody:messageBody + messageBody:truncatedText attachmentIds:[NSMutableArray new] expiresInSeconds:expiresInSeconds expireStartedAt:0 @@ -180,12 +149,40 @@ NS_ASSUME_NONNULL_BEGIN contactShare:nil linkPreview:nil]; - NSMutableArray *attachmentInfos = [NSMutableArray new]; - for (SignalAttachment *attachment in attachments) { - OWSOutgoingAttachmentInfo *attachmentInfo = [attachment buildOutgoingAttachmentInfoWithMessage:message]; - [attachmentInfos addObject:attachmentInfo]; - } - [self.messageSenderJobQueue addMediaMessage:message attachmentInfos:attachmentInfos isTemporaryAttachment:NO]; + [BenchManager + benchAsyncWithTitle:@"Saving outgoing message" + block:^(void (^benchmarkCompletion)(void)) { + // To avoid blocking the send flow, we dispatch an async write from within this read + // transaction + [self.dbConnection + asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull writeTransaction) { + [message saveWithTransaction:writeTransaction]; + + OWSLinkPreview *_Nullable linkPreview = + [self linkPreviewForLinkPreviewDraft:linkPreviewDraft + transaction:writeTransaction]; + if (linkPreview) { + [message updateWithLinkPreview:linkPreview transaction:writeTransaction]; + } + + if (attachments.count == 0) { + [self.messageSenderJobQueue addMessage:message transaction:writeTransaction]; + } else { + NSMutableArray *attachmentInfos = + [NSMutableArray new]; + for (SignalAttachment *attachment in attachments) { + OWSOutgoingAttachmentInfo *attachmentInfo = + [attachment buildOutgoingAttachmentInfoWithMessage:message]; + [attachmentInfos addObject:attachmentInfo]; + } + + [self.messageSenderJobQueue addMediaMessage:message + attachmentInfos:attachmentInfos + isTemporaryAttachment:NO]; + } + } + completionBlock:benchmarkCompletion]; + }]; return message; } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index 2050aa7c7..83155957f 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -45,6 +45,9 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasAttachments; - (NSArray *)attachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction; +- (NSArray *)mediaAttachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction; +- (nullable TSAttachment *)oversizeTextAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; + - (void)removeAttachment:(TSAttachment *)attachment transaction:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(removeAttachment(_:transaction:)); diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index c51a4919e..0b44980da 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -219,6 +219,26 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return [attachments copy]; } +- (NSArray *)attachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction + contentType:(NSString *)contentType +{ + NSArray *attachments = [self attachmentsWithTransaction:transaction]; + return [attachments filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(TSAttachment *evaluatedObject, + NSDictionary *_Nullable bindings) { + return [evaluatedObject.contentType isEqualToString:contentType]; + }]]; +} + +- (NSArray *)attachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction + exceptContentType:(NSString *)contentType +{ + NSArray *attachments = [self attachmentsWithTransaction:transaction]; + return [attachments filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(TSAttachment *evaluatedObject, + NSDictionary *_Nullable bindings) { + return ![evaluatedObject.contentType isEqualToString:contentType]; + }]]; +} + - (void)removeAttachment:(TSAttachment *)attachment transaction:(YapDatabaseReadWriteTransaction *)transaction; { OWSAssertDebug([self.attachmentIds containsObject:attachment.uniqueId]); @@ -257,15 +277,24 @@ static const NSUInteger OWSMessageSchemaVersion = 4; } } +- (nullable TSAttachment *)oversizeTextAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + return [self attachmentsWithTransaction:transaction contentType:OWSMimeTypeOversizeTextMessage].firstObject; +} + +- (NSArray *)mediaAttachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + return [self attachmentsWithTransaction:transaction exceptContentType:OWSMimeTypeOversizeTextMessage]; +} + - (nullable NSString *)oversizeTextWithTransaction:(YapDatabaseReadTransaction *)transaction { - if (self.attachmentIds.count != 1) { + TSAttachment *_Nullable attachment = [self oversizeTextAttachmentWithTransaction:transaction]; + if (!attachment) { return nil; } - TSAttachment *_Nullable attachment = [self attachmentsWithTransaction:transaction].firstObject; - if (![OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType] - || ![attachment isKindOfClass:TSAttachmentStream.class]) { + if (![attachment isKindOfClass:TSAttachmentStream.class]) { return nil; }