Merge branch 'charlesmchen/debugUIMultiImageSends'

pull/1/head
Matthew Chen 7 years ago
commit 1e31e6aec5

@ -1650,7 +1650,7 @@ typedef enum : NSUInteger {
networkManager:self.networkManager]; networkManager:self.networkManager];
[processor fetchAttachmentsForMessage:message [processor fetchAttachmentsForMessage:message
primaryStorage:self.primaryStorage primaryStorage:self.primaryStorage
success:^(TSAttachmentStream *attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSLogInfo(@"Successfully redownloaded attachment in thread: %@", message.thread); OWSLogInfo(@"Successfully redownloaded attachment in thread: %@", message.thread);
} }
failure:^(NSError *error) { failure:^(NSError *error) {
@ -2208,7 +2208,9 @@ typedef enum : NSUInteger {
[self.editingDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.editingDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[processor fetchAttachmentsForMessage:nil [processor fetchAttachmentsForMessage:nil
transaction:transaction transaction:transaction
success:^(TSAttachmentStream *attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[self.editingDatabaseConnection [self.editingDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) { asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) {
[message setQuotedMessageThumbnailAttachmentStream:attachmentStream]; [message setQuotedMessageThumbnailAttachmentStream:attachmentStream];

@ -481,6 +481,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
if (message.attachmentIds.count == 0) { if (message.attachmentIds.count == 0) {
return nil; return nil;
} }
// TODO: Support multi-image messages.
NSString *_Nullable attachmentId = message.attachmentIds.firstObject; NSString *_Nullable attachmentId = message.attachmentIds.firstObject;
if (attachmentId.length == 0) { if (attachmentId.length == 0) {
return nil; return nil;

@ -127,6 +127,10 @@ NS_ASSUME_NONNULL_BEGIN
actionBlock:^{ actionBlock:^{
[DebugUIMessages sendNTextMessagesInThread:thread]; [DebugUIMessages sendNTextMessagesInThread:thread];
}], }],
[OWSTableItem itemWithTitle:@"Send Multi-Image Message"
actionBlock:^{
[DebugUIMessages sendMultiImageMessageInThread:thread];
}],
[OWSTableItem itemWithTitle:@"Select Fake" [OWSTableItem itemWithTitle:@"Select Fake"
actionBlock:^{ actionBlock:^{
[DebugUIMessages selectFakeAction:thread]; [DebugUIMessages selectFakeAction:thread];
@ -246,7 +250,6 @@ NS_ASSUME_NONNULL_BEGIN
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self.messageSenderJobQueue addMessage:syncGroupsRequestMessage transaction:transaction]; [self.messageSenderJobQueue addMessage:syncGroupsRequestMessage transaction:transaction];
}]; }];
}], }],
[OWSTableItem itemWithTitle:@"Message with stalled timer" [OWSTableItem itemWithTitle:@"Message with stalled timer"
actionBlock:^{ actionBlock:^{
@ -3357,7 +3360,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
// style them indistinguishably from a separate text message. // style them indistinguishably from a separate text message.
attachment.captionText = [self randomCaptionText]; attachment.captionText = [self randomCaptionText];
} }
[ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil ignoreErrors:YES]; [ThreadUtil enqueueMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil];
} }
+ (SSKProtoEnvelope *)createEnvelopeForThread:(TSThread *)thread + (SSKProtoEnvelope *)createEnvelopeForThread:(TSThread *)thread
@ -4631,6 +4634,41 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
} }
} }
+ (void)sendMultiImageMessageInThread:(TSThread *)thread
{
OWSLogInfo(@"");
const uint32_t kMinImageCount = 2;
const uint32_t kMaxImageCount = 10;
uint32_t imageCount = kMinImageCount + arc4random_uniform(kMaxImageCount - kMinImageCount);
NSMutableArray<SignalAttachment *> *attachments = [NSMutableArray new];
for (uint32_t i = 0; i < imageCount; i++) {
UIColor *imageColor = [UIColor colorWithRed:arc4random_uniform(256) / 255.f
green:arc4random_uniform(256) / 255.f
blue:arc4random_uniform(256) / 255.f
alpha:1.f];
UIImage *image = [UIImage imageWithColor:imageColor size:CGSizeMake(10.f, 10.f)];
OWSAssertDebug(image);
NSData *pngData = UIImagePNGRepresentation(image);
OWSAssertDebug(pngData);
NSString *filePath = [OWSFileSystem temporaryFilePathWithFileExtension:@"png"];
[pngData writeToFile:filePath atomically:YES];
OWSAssertDebug([NSFileManager.defaultManager fileExistsAtPath:filePath]);
DataSource *dataSource = [DataSourcePath dataSourceWithFilePath:filePath shouldDeleteOnDeallocation:YES];
SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource
dataUTI:(NSString *)kUTTypePNG
imageQuality:TSImageQualityOriginal];
[attachments addObject:attachment];
}
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSOutgoingMessage *message =
[ThreadUtil enqueueMessageWithAttachments:attachments inThread:thread quotedReplyModel:nil];
OWSLogError(@"timestamp: %llu.", message.timestamp);
}];
}
#endif #endif
@end @end

@ -634,7 +634,8 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
var hasFetchedMostRecent = false var hasFetchedMostRecent = false
func buildGalleryItem(message: TSMessage, transaction: YapDatabaseReadTransaction) -> MediaGalleryItem? { func buildGalleryItem(message: TSMessage, transaction: YapDatabaseReadTransaction) -> MediaGalleryItem? {
guard let attachmentStream = message.attachment(with: transaction) as? TSAttachmentStream else { // TODO: Support multi-image messages.
guard let attachmentStream = message.attachments(with: transaction).first as? TSAttachmentStream else {
owsFailDebug("attachment was unexpectedly empty") owsFailDebug("attachment was unexpectedly empty")
return nil return nil
} }

@ -398,6 +398,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
} }
private func fetchAttachment(transaction: YapDatabaseReadTransaction) -> TSAttachment? { private func fetchAttachment(transaction: YapDatabaseReadTransaction) -> TSAttachment? {
// TODO: Support multi-image messages.
guard let attachmentId = message.attachmentIds.firstObject as? String else { guard let attachmentId = message.attachmentIds.firstObject as? String else {
return nil return nil
} }

@ -579,6 +579,7 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion);
return; return;
} }
// TODO: Support multi-image messages.
NSString *_Nullable attachmentRecordId = self.outgoingMessage.attachmentIds.firstObject; NSString *_Nullable attachmentRecordId = self.outgoingMessage.attachmentIds.firstObject;
if (!attachmentRecordId) { if (!attachmentRecordId) {
OWSLogDebug(@"Ignoring upload progress until outgoing message has an attachment record id"); OWSLogDebug(@"Ignoring upload progress until outgoing message has an attachment record id");

@ -167,7 +167,7 @@ NS_ASSUME_NONNULL_BEGIN
BOOL hasText = quotedText.length > 0; BOOL hasText = quotedText.length > 0;
BOOL hasAttachment = NO; BOOL hasAttachment = NO;
TSAttachment *_Nullable attachment = [message attachmentWithTransaction:transaction]; TSAttachment *_Nullable attachment = [message attachmentsWithTransaction:transaction].firstObject;
TSAttachmentStream *quotedAttachment; TSAttachmentStream *quotedAttachment;
if (attachment && [attachment isKindOfClass:[TSAttachmentStream class]]) { if (attachment && [attachment isKindOfClass:[TSAttachmentStream class]]) {

@ -239,6 +239,11 @@ public class SignalAttachment: NSObject {
return errorDescription return errorDescription
} }
@objc
public var outgoingAttachmentInfo: OutgoingAttachmentInfo {
return OutgoingAttachmentInfo(dataSource: dataSource, contentType: mimeType, sourceFilename: filenameOrDefault)
}
@objc @objc
public func image() -> UIImage? { public func image() -> UIImage? {
if let cachedImage = cachedImage { if let cachedImage = cachedImage {

@ -53,10 +53,9 @@ NS_ASSUME_NONNULL_BEGIN
inThread:(TSThread *)thread inThread:(TSThread *)thread
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel; quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel;
+ (TSOutgoingMessage *)enqueueMessageWithAttachment:(SignalAttachment *)attachment + (TSOutgoingMessage *)enqueueMessageWithAttachments:(NSArray<SignalAttachment *> *)attachments
inThread:(TSThread *)thread inThread:(TSThread *)thread
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel;
ignoreErrors:(BOOL)ignoreErrors;
+ (TSOutgoingMessage *)enqueueMessageWithContactShare:(OWSContact *)contactShare inThread:(TSThread *)thread; + (TSOutgoingMessage *)enqueueMessageWithContactShare:(OWSContact *)contactShare inThread:(TSThread *)thread;
+ (void)enqueueLeaveGroupMessageInThread:(TSGroupThread *)thread; + (void)enqueueLeaveGroupMessageInThread:(TSGroupThread *)thread;

@ -93,44 +93,49 @@ NS_ASSUME_NONNULL_BEGIN
inThread:(TSThread *)thread inThread:(TSThread *)thread
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
{ {
return [self enqueueMessageWithAttachment:attachment return [self enqueueMessageWithAttachments:@[
inThread:thread attachment,
quotedReplyModel:quotedReplyModel ]
ignoreErrors:NO]; inThread:thread
quotedReplyModel:quotedReplyModel];
} }
+ (TSOutgoingMessage *)enqueueMessageWithAttachment:(SignalAttachment *)attachment + (TSOutgoingMessage *)enqueueMessageWithAttachments:(NSArray<SignalAttachment *> *)attachments
inThread:(TSThread *)thread inThread:(TSThread *)thread
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
ignoreErrors:(BOOL)ignoreErrors
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssertDebug(attachment); OWSAssertDebug(attachments.count > 0);
OWSAssertDebug(!attachment.hasError);
OWSAssertDebug(attachment.mimeType.length > 0);
OWSAssertDebug(thread); OWSAssertDebug(thread);
for (SignalAttachment *attachment in attachments) {
OWSAssertDebug(!attachment.hasError);
OWSAssertDebug(attachment.mimeType.length > 0);
}
OWSDisappearingMessagesConfiguration *configuration = OWSDisappearingMessagesConfiguration *configuration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId];
uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0);
BOOL isVoiceMessage = (attachments.count == 1 && attachments.lastObject.isVoiceMessage);
// TODO: Support multi-image captions.
NSString *_Nullable messageBody = attachments.lastObject.captionText;
TSOutgoingMessage *message = TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread inThread:thread
messageBody:attachment.captionText messageBody:messageBody
attachmentIds:[NSMutableArray new] attachmentIds:[NSMutableArray new]
expiresInSeconds:expiresInSeconds expiresInSeconds:expiresInSeconds
expireStartedAt:0 expireStartedAt:0
isVoiceMessage:[attachment isVoiceMessage] isVoiceMessage:isVoiceMessage
groupMetaMessage:TSGroupMetaMessageUnspecified groupMetaMessage:TSGroupMetaMessageUnspecified
quotedMessage:[quotedReplyModel buildQuotedMessageForSending] quotedMessage:[quotedReplyModel buildQuotedMessageForSending]
contactShare:nil]; contactShare:nil];
[self.messageSenderJobQueue addMediaMessage:message NSMutableArray<OWSOutgoingAttachmentInfo *> *attachmentInfos = [NSMutableArray new];
dataSource:attachment.dataSource for (SignalAttachment *attachment in attachments) {
contentType:attachment.mimeType [attachmentInfos addObject:attachment.outgoingAttachmentInfo];
sourceFilename:attachment.filenameOrDefault }
isTemporaryAttachment:NO]; [self.messageSenderJobQueue addMediaMessage:message attachmentInfos:attachmentInfos isTemporaryAttachment:NO];
return message; return message;
} }

@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
contactsManager:(id<ContactsManagerProtocol>)contactsManager contactsManager:(id<ContactsManagerProtocol>)contactsManager
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
- (void)runWithAttachmentHandler:(void (^)(TSAttachmentStream *attachmentStream))attachmentHandler - (void)runWithAttachmentHandler:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))attachmentHandler
transaction:(YapDatabaseReadWriteTransaction *)transaction; transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end @end

@ -59,7 +59,7 @@ NS_ASSUME_NONNULL_BEGIN
return self; return self;
} }
- (void)runWithAttachmentHandler:(void (^)(TSAttachmentStream *attachmentStream))attachmentHandler - (void)runWithAttachmentHandler:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))attachmentHandler
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssertDebug(transaction); OWSAssertDebug(transaction);
@ -112,7 +112,9 @@ NS_ASSUME_NONNULL_BEGIN
OWSLogDebug(@"downloading thumbnail for transcript: %lu", (unsigned long)transcript.timestamp); OWSLogDebug(@"downloading thumbnail for transcript: %lu", (unsigned long)transcript.timestamp);
[attachmentProcessor fetchAttachmentsForMessage:outgoingMessage [attachmentProcessor fetchAttachmentsForMessage:outgoingMessage
transaction:transaction transaction:transaction
success:^(TSAttachmentStream *_Nonnull attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[self.primaryStorage.newDatabaseConnection [self.primaryStorage.newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[outgoingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream]; [outgoingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream];

@ -41,11 +41,11 @@ extern NSString *const kAttachmentDownloadAttachmentIDKey;
- (void)fetchAttachmentsForMessage:(nullable TSMessage *)message - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message
primaryStorage:(OWSPrimaryStorage *)primaryStorage primaryStorage:(OWSPrimaryStorage *)primaryStorage
success:(void (^)(TSAttachmentStream *attachmentStream))successHandler success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))successHandler
failure:(void (^)(NSError *error))failureHandler; failure:(void (^)(NSError *error))failureHandler;
- (void)fetchAttachmentsForMessage:(nullable TSMessage *)message - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
success:(void (^)(TSAttachmentStream *attachmentStream))successHandler success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))successHandler
failure:(void (^)(NSError *error))failureHandler; failure:(void (^)(NSError *error))failureHandler;
@end @end

@ -20,6 +20,7 @@
#import "TSMessage.h" #import "TSMessage.h"
#import "TSNetworkManager.h" #import "TSNetworkManager.h"
#import "TSThread.h" #import "TSThread.h"
#import <PromiseKit/AnyPromise.h>
#import <SignalCoreKit/Cryptography.h> #import <SignalCoreKit/Cryptography.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h> #import <SignalServiceKit/SignalServiceKit-Swift.h>
#import <YapDatabase/YapDatabaseConnection.h> #import <YapDatabase/YapDatabaseConnection.h>
@ -93,7 +94,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
// PERF: Remove this and use a pre-existing dbConnection // PERF: Remove this and use a pre-existing dbConnection
- (void)fetchAttachmentsForMessage:(nullable TSMessage *)message - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message
primaryStorage:(OWSPrimaryStorage *)primaryStorage primaryStorage:(OWSPrimaryStorage *)primaryStorage
success:(void (^)(TSAttachmentStream *attachmentStream))successHandler success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))successHandler
failure:(void (^)(NSError *error))failureHandler failure:(void (^)(NSError *error))failureHandler
{ {
[[primaryStorage newDatabaseConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [[primaryStorage newDatabaseConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@ -106,18 +107,53 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
- (void)fetchAttachmentsForMessage:(nullable TSMessage *)message - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
success:(void (^)(TSAttachmentStream *attachmentStream))successHandler success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))successHandler
failure:(void (^)(NSError *error))failureHandler failure:(void (^)(NSError *error))failureHandler
{ {
OWSAssertDebug(transaction); OWSAssertDebug(transaction);
OWSAssertDebug(self.attachmentPointers.count > 0);
NSMutableArray<AnyPromise *> *promises = [NSMutableArray array];
NSMutableArray<TSAttachmentStream *> *attachmentStreams = [NSMutableArray array];
for (TSAttachmentPointer *attachmentPointer in self.attachmentPointers) { for (TSAttachmentPointer *attachmentPointer in self.attachmentPointers) {
[self retrieveAttachment:attachmentPointer AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
message:message [self retrieveAttachment:attachmentPointer
transaction:transaction message:message
success:successHandler transaction:transaction
failure:failureHandler]; success:^(TSAttachmentStream *attachmentStream) {
OWSLogVerbose(@"Attachment download succeeded.");
@synchronized(attachmentStreams) {
[attachmentStreams addObject:attachmentStream];
}
resolve(@(1));
}
failure:^(NSError *error) {
OWSLogError(@"Attachment download failed with error: %@", error);
resolve(error);
}];
}];
[promises addObject:promise];
} }
// We use PMKJoin(), not PMKWhen(), because we don't want the
// completion promise to execute until _all_ promises
// have either succeeded or failed. PMKWhen() executes as
// soon as any of its input promises fail.
AnyPromise *completionPromise
= PMKJoin(promises)
.then(^(id value) {
NSArray<TSAttachmentStream *> *attachmentStreamsCopy;
@synchronized(attachmentStreams) {
attachmentStreamsCopy = [attachmentStreams copy];
}
OWSLogInfo(@"Attachment downloads succeeded: %lu.", (unsigned long)attachmentStreamsCopy.count);
successHandler(attachmentStreamsCopy);
})
.catch(^(NSError *error) {
failureHandler(error);
});
[completionPromise retainUntilComplete];
} }
- (void)retrieveAttachment:(TSAttachmentPointer *)attachment - (void)retrieveAttachment:(TSAttachmentPointer *)attachment

@ -60,9 +60,6 @@ typedef void (^OWSThumbnailFailure)(void);
- (BOOL)writeData:(NSData *)data error:(NSError **)error; - (BOOL)writeData:(NSData *)data error:(NSError **)error;
- (BOOL)writeDataSource:(DataSource *)dataSource; - (BOOL)writeDataSource:(DataSource *)dataSource;
- (BOOL)isOversizeText;
- (nullable NSString *)readOversizeText;
+ (void)deleteAttachments; + (void)deleteAttachments;
+ (NSString *)attachmentsFolder; + (NSString *)attachmentsFolder;

@ -630,27 +630,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
return [OWSBackupFragment fetchObjectWithUniqueID:self.lazyRestoreFragmentId]; return [OWSBackupFragment fetchObjectWithUniqueID:self.lazyRestoreFragmentId];
} }
- (BOOL)isOversizeText
{
return [self.contentType isEqualToString:OWSMimeTypeOversizeTextMessage];
}
- (nullable NSString *)readOversizeText
{
if (!self.isOversizeText) {
OWSFailDebug(@"oversize text attachment has unexpected content type.");
return nil;
}
NSError *error;
NSData *_Nullable data = [self readDataFromFileWithError:&error];
if (error || !data) {
OWSFailDebug(@"could not read oversize text attachment: %@.", error);
return nil;
}
NSString *_Nullable string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return string;
}
#pragma mark - Thumbnails #pragma mark - Thumbnails
- (nullable UIImage *)thumbnailImageWithSizeHint:(CGSize)sizeHint - (nullable UIImage *)thumbnailImageWithSizeHint:(CGSize)sizeHint

@ -489,7 +489,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
return [TSAttachment fetchObjectWithUniqueID:self.avatarAttachmentId transaction:transaction]; return [TSAttachment fetchObjectWithUniqueID:self.avatarAttachmentId transaction:transaction];
} }
- (void)saveAvatarImage:(UIImage *)image transaction:(YapDatabaseReadWriteTransaction *)transaction - (void)saveAvatarImage:(UIImage *)image transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
NSData *imageData = UIImageJPEGRepresentation(image, (CGFloat)0.9); NSData *imageData = UIImageJPEGRepresentation(image, (CGFloat)0.9);

@ -41,9 +41,11 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (BOOL)hasAttachments; - (BOOL)hasAttachments;
- (nullable TSAttachment *)attachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; - (NSArray<TSAttachment *> *)attachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction;
- (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream; - (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream;
- (nullable NSString *)oversizeTextWithTransaction:(YapDatabaseReadTransaction *)transaction;
- (nullable NSString *)bodyTextWithTransaction:(YapDatabaseReadTransaction *)transaction; - (nullable NSString *)bodyTextWithTransaction:(YapDatabaseReadTransaction *)transaction;
- (BOOL)shouldStartExpireTimerWithTransaction:(YapDatabaseReadTransaction *)transaction; - (BOOL)shouldStartExpireTimerWithTransaction:(YapDatabaseReadTransaction *)transaction;

@ -199,14 +199,19 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
return self.attachmentIds ? (self.attachmentIds.count > 0) : NO; return self.attachmentIds ? (self.attachmentIds.count > 0) : NO;
} }
- (nullable TSAttachment *)attachmentWithTransaction:(YapDatabaseReadTransaction *)transaction - (NSArray<TSAttachment *> *)attachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction
{ {
if (!self.hasAttachments) { NSMutableArray<TSAttachment *> *attachments = [NSMutableArray new];
return nil; for (NSString *attachmentId in self.attachmentIds) {
TSAttachment *_Nullable attachment =
[TSAttachment fetchObjectWithUniqueID:attachmentId transaction:transaction];
if (attachment) {
[attachments addObject:attachment];
} else {
OWSFailDebug(@"Missing attachment for: %@.", attachmentId);
}
} }
return [attachments copy];
OWSAssertDebug(self.attachmentIds.count == 1);
return [TSAttachment fetchObjectWithUniqueID:self.attachmentIds.firstObject transaction:transaction];
} }
- (NSString *)debugDescription - (NSString *)debugDescription
@ -223,23 +228,40 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
} }
} }
- (nullable NSString *)oversizeTextWithTransaction:(YapDatabaseReadTransaction *)transaction
{
if (self.attachmentIds.count != 1) {
return nil;
}
TSAttachment *_Nullable attachment = [self attachmentsWithTransaction:transaction].firstObject;
OWSAssertDebug(attachment);
if (![OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType]
|| ![attachment isKindOfClass:TSAttachmentStream.class]) {
return nil;
}
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
NSData *_Nullable data = [NSData dataWithContentsOfFile:attachmentStream.originalFilePath];
if (!data) {
OWSFailDebug(@"Can't load oversize text data.");
return nil;
}
NSString *_Nullable text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (!text) {
OWSFailDebug(@"Can't parse oversize text data.");
return nil;
}
return text.filterStringForDisplay;
}
- (nullable NSString *)bodyTextWithTransaction:(YapDatabaseReadTransaction *)transaction - (nullable NSString *)bodyTextWithTransaction:(YapDatabaseReadTransaction *)transaction
{ {
if (self.hasAttachments) { NSString *_Nullable oversizeText = [self oversizeTextWithTransaction:transaction];
TSAttachment *_Nullable attachment = [self attachmentWithTransaction:transaction]; if (oversizeText) {
return oversizeText;
if ([OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType] &&
[attachment isKindOfClass:TSAttachmentStream.class]) {
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
NSData *_Nullable data = [NSData dataWithContentsOfFile:attachmentStream.originalFilePath];
if (data) {
NSString *_Nullable text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (text) {
return text.filterStringForDisplay;
}
}
}
} }
if (self.body.length > 0) { if (self.body.length > 0) {

@ -253,7 +253,8 @@ NS_ASSUME_NONNULL_BEGIN
return nil; return nil;
} }
TSAttachment *attachment = [quotedMessage attachmentWithTransaction:transaction]; // We quote _the first_ attachment, if any.
TSAttachment *_Nullable attachment = [quotedMessage attachmentsWithTransaction:transaction].firstObject;
if (![attachment isKindOfClass:[TSAttachmentStream class]]) { if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
return nil; return nil;
} }

@ -731,7 +731,9 @@ NS_ASSUME_NONNULL_BEGIN
} }
[attachmentsProcessor fetchAttachmentsForMessage:nil [attachmentsProcessor fetchAttachmentsForMessage:nil
transaction:transaction transaction:transaction
success:^(TSAttachmentStream *attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[groupThread updateAvatarWithAttachmentStream:attachmentStream]; [groupThread updateAvatarWithAttachmentStream:attachmentStream];
} }
failure:^(NSError *error) { failure:^(NSError *error) {
@ -786,8 +788,10 @@ NS_ASSUME_NONNULL_BEGIN
[attachmentsProcessor fetchAttachmentsForMessage:createdMessage [attachmentsProcessor fetchAttachmentsForMessage:createdMessage
transaction:transaction transaction:transaction
success:^(TSAttachmentStream *attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSLogDebug(@"successfully fetched attachment: %@ for message: %@", attachmentStream, createdMessage); OWSLogDebug(@"successfully fetched attachments: %lu for message: %@",
(unsigned long)attachmentStreams.count,
createdMessage);
} }
failure:^(NSError *error) { failure:^(NSError *error) {
OWSLogError(@"failed to fetch attachments for message: %@ with error: %@", createdMessage, error); OWSLogError(@"failed to fetch attachments for message: %@ with error: %@", createdMessage, error);
@ -843,23 +847,27 @@ NS_ASSUME_NONNULL_BEGIN
} }
if ([self isDataMessageGroupAvatarUpdate:syncMessage.sent.message]) { if ([self isDataMessageGroupAvatarUpdate:syncMessage.sent.message]) {
[recordJob runWithAttachmentHandler:^(TSAttachmentStream *attachmentStream) { [recordJob
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { runWithAttachmentHandler:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
TSGroupThread *_Nullable groupThread = OWSAssertDebug(attachmentStreams.count == 1);
[TSGroupThread threadWithGroupId:dataMessage.group.id transaction:transaction]; TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
if (!groupThread) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
OWSFailDebug(@"ignoring sync group avatar update for unknown group."); TSGroupThread *_Nullable groupThread =
return; [TSGroupThread threadWithGroupId:dataMessage.group.id transaction:transaction];
} if (!groupThread) {
OWSFailDebug(@"ignoring sync group avatar update for unknown group.");
[groupThread updateAvatarWithAttachmentStream:attachmentStream transaction:transaction]; return;
}]; }
}
transaction:transaction]; [groupThread updateAvatarWithAttachmentStream:attachmentStream transaction:transaction];
}];
}
transaction:transaction];
} else { } else {
[recordJob [recordJob
runWithAttachmentHandler:^(TSAttachmentStream *attachmentStream) { runWithAttachmentHandler:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSLogDebug(@"successfully fetched transcript attachment: %@", attachmentStream); OWSLogDebug(
@"successfully fetched transcript attachments: %lu", (unsigned long)attachmentStreams.count);
} }
transaction:transaction]; transaction:transaction];
} }
@ -1350,7 +1358,9 @@ NS_ASSUME_NONNULL_BEGIN
OWSLogDebug(@"downloading thumbnail for message: %lu", (unsigned long)incomingMessage.timestamp); OWSLogDebug(@"downloading thumbnail for message: %lu", (unsigned long)incomingMessage.timestamp);
[attachmentProcessor fetchAttachmentsForMessage:incomingMessage [attachmentProcessor fetchAttachmentsForMessage:incomingMessage
transaction:transaction transaction:transaction
success:^(TSAttachmentStream *attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[incomingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream]; [incomingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream];
[incomingMessage saveWithTransaction:transaction]; [incomingMessage saveWithTransaction:transaction];
@ -1379,7 +1389,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSLogDebug(@"downloading contact avatar for message: %lu", (unsigned long)incomingMessage.timestamp); OWSLogDebug(@"downloading contact avatar for message: %lu", (unsigned long)incomingMessage.timestamp);
[attachmentProcessor fetchAttachmentsForMessage:incomingMessage [attachmentProcessor fetchAttachmentsForMessage:incomingMessage
transaction:transaction transaction:transaction
success:^(TSAttachmentStream *attachmentStream) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[incomingMessage touchWithTransaction:transaction]; [incomingMessage touchWithTransaction:transaction];
}]; }];

@ -33,6 +33,23 @@ typedef void (^RetryableFailureHandler)(NSError *_Nonnull error);
#pragma mark - #pragma mark -
NS_SWIFT_NAME(OutgoingAttachmentInfo)
@interface OWSOutgoingAttachmentInfo : NSObject
@property (nonatomic, readonly) DataSource *dataSource;
@property (nonatomic, readonly) NSString *contentType;
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithDataSource:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename NS_DESIGNATED_INITIALIZER;
@end
#pragma mark -
NS_SWIFT_NAME(MessageSender) NS_SWIFT_NAME(MessageSender)
@interface OWSMessageSender : NSObject @interface OWSMessageSender : NSObject
@ -71,6 +88,8 @@ NS_SWIFT_NAME(MessageSender)
@end @end
#pragma mark -
@interface OutgoingMessagePreparer : NSObject @interface OutgoingMessagePreparer : NSObject
/// Persists all necessary data to disk before sending, e.g. generate thumbnails /// Persists all necessary data to disk before sending, e.g. generate thumbnails
@ -80,11 +99,10 @@ NS_SWIFT_NAME(MessageSender)
transaction:(YapDatabaseReadWriteTransaction *)transaction; transaction:(YapDatabaseReadWriteTransaction *)transaction;
/// Writes attachment to disk and applies original filename to message attributes /// Writes attachment to disk and applies original filename to message attributes
+ (void)prepareAttachmentWithDataSource:(DataSource *)dataSource + (void)prepareAttachments:(NSArray<OWSOutgoingAttachmentInfo *> *)attachmentInfos
contentType:(NSString *)contentType inMessage:(TSOutgoingMessage *)outgoingMessage
sourceFilename:(nullable NSString *)sourceFilename completionHandler:(void (^)(NSError *_Nullable error))completionHandler
inMessage:(TSOutgoingMessage *)outgoingMessage NS_SWIFT_NAME(prepareAttachments(_:inMessage:completionHandler:));
completionHandler:(void (^)(NSError *_Nullable error))completionHandler;
@end @end

@ -81,6 +81,28 @@ void AssertIsOnSendingQueue()
#pragma mark - #pragma mark -
@implementation OWSOutgoingAttachmentInfo
- (instancetype)initWithDataSource:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
{
self = [super init];
if (!self) {
return self;
}
_dataSource = dataSource;
_contentType = contentType;
_sourceFilename = sourceFilename;
return self;
}
@end
#pragma mark -
/** /**
* OWSSendMessageOperation encapsulates all the work associated with sending a message, e.g. uploading attachments, * OWSSendMessageOperation encapsulates all the work associated with sending a message, e.g. uploading attachments,
* getting proper keys, and retrying upon failure. * getting proper keys, and retrying upon failure.
@ -156,13 +178,14 @@ void AssertIsOnSendingQueue()
// Sanity check preconditions // Sanity check preconditions
if (self.message.hasAttachments) { if (self.message.hasAttachments) {
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction * transaction) { [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
TSAttachmentStream *attachmentStream for (TSAttachment *attachment in [self.message attachmentsWithTransaction:transaction]) {
= (TSAttachmentStream *)[self.message attachmentWithTransaction:transaction]; OWSAssertDebug([attachment isKindOfClass:[TSAttachmentStream class]]);
OWSAssertDebug(attachmentStream); TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
OWSAssertDebug([attachmentStream isKindOfClass:[TSAttachmentStream class]]); OWSAssertDebug(attachmentStream);
OWSAssertDebug(attachmentStream.serverId); OWSAssertDebug(attachmentStream.serverId);
OWSAssertDebug(attachmentStream.isUploaded); OWSAssertDebug(attachmentStream.isUploaded);
}
}]; }];
} }
@ -353,11 +376,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
success:successHandler success:successHandler
failure:failureHandler]; failure:failureHandler];
// TODO de-dupe attachment enque logic. // TODO: de-dupe attachment enqueue logic.
if (message.hasAttachments) { for (NSString *attachmentId in message.attachmentIds) {
OWSUploadOperation *uploadAttachmentOperation = OWSUploadOperation *uploadAttachmentOperation =
[[OWSUploadOperation alloc] initWithAttachmentId:message.attachmentIds.firstObject [[OWSUploadOperation alloc] initWithAttachmentId:attachmentId dbConnection:self.dbConnection];
dbConnection:self.dbConnection];
[sendMessageOperation addDependency:uploadAttachmentOperation]; [sendMessageOperation addDependency:uploadAttachmentOperation];
[sendingQueue addOperation:uploadAttachmentOperation]; [sendingQueue addOperation:uploadAttachmentOperation];
} }
@ -431,17 +453,18 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
failure:(void (^)(NSError *error))failureHandler failure:(void (^)(NSError *error))failureHandler
{ {
OWSAssertDebug(dataSource); OWSAssertDebug(dataSource);
[OutgoingMessagePreparer prepareAttachmentWithDataSource:dataSource OWSOutgoingAttachmentInfo *attachmentInfo = [[OWSOutgoingAttachmentInfo alloc] initWithDataSource:dataSource
contentType:contentType contentType:contentType
sourceFilename:sourceFilename sourceFilename:sourceFilename];
inMessage:message [OutgoingMessagePreparer prepareAttachments:@[ attachmentInfo ]
completionHandler:^(NSError *_Nullable error) { inMessage:message
if (error) { completionHandler:^(NSError *_Nullable error) {
failureHandler(error); if (error) {
return; failureHandler(error);
} return;
[self sendMessage:message success:successHandler failure:failureHandler]; }
}]; [self sendMessage:message success:successHandler failure:failureHandler];
}];
} }
- (void)sendMessageToService:(TSOutgoingMessage *)message - (void)sendMessageToService:(TSOutgoingMessage *)message
@ -1768,35 +1791,42 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[message updateWithMarkingAllUnsentRecipientsAsSendingWithTransaction:transaction]; [message updateWithMarkingAllUnsentRecipientsAsSendingWithTransaction:transaction];
} }
+ (void)prepareAttachmentWithDataSource:(DataSource *)dataSource + (void)prepareAttachments:(NSArray<OWSOutgoingAttachmentInfo *> *)attachmentInfos
contentType:(NSString *)contentType inMessage:(TSOutgoingMessage *)outgoingMessage
sourceFilename:(nullable NSString *)sourceFilename completionHandler:(void (^)(NSError *_Nullable error))completionHandler
inMessage:(TSOutgoingMessage *)outgoingMessage
completionHandler:(void (^)(NSError *_Nullable error))completionHandler
{ {
OWSAssertDebug(dataSource); OWSAssertDebug(attachmentInfos.count > 0);
OWSAssertDebug(outgoingMessage);
dispatch_async([OWSDispatch attachmentsQueue], ^{ dispatch_async([OWSDispatch attachmentsQueue], ^{
TSAttachmentStream *attachmentStream = NSMutableArray<TSAttachmentStream *> *attachmentStreams = [NSMutableArray new];
[[TSAttachmentStream alloc] initWithContentType:contentType for (OWSOutgoingAttachmentInfo *attachmentInfo in attachmentInfos) {
byteCount:(UInt32)dataSource.dataLength TSAttachmentStream *attachmentStream =
sourceFilename:sourceFilename]; [[TSAttachmentStream alloc] initWithContentType:attachmentInfo.contentType
if (outgoingMessage.isVoiceMessage) { byteCount:(UInt32)attachmentInfo.dataSource.dataLength
attachmentStream.attachmentType = TSAttachmentTypeVoiceMessage; sourceFilename:attachmentInfo.sourceFilename];
} if (outgoingMessage.isVoiceMessage) {
attachmentStream.attachmentType = TSAttachmentTypeVoiceMessage;
}
if (![attachmentStream writeDataSource:attachmentInfo.dataSource]) {
OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotWriteAttachment]);
NSError *error = OWSErrorMakeWriteAttachmentDataError();
completionHandler(error);
return;
}
if (![attachmentStream writeDataSource:dataSource]) { [attachmentStreams addObject:attachmentStream];
OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotWriteAttachment]);
NSError *error = OWSErrorMakeWriteAttachmentDataError();
completionHandler(error);
} }
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[attachmentStream saveWithTransaction:transaction]; for (TSAttachmentStream *attachmentStream in attachmentStreams) {
[attachmentStream saveWithTransaction:transaction];
[outgoingMessage.attachmentIds addObject:attachmentStream.uniqueId]; [outgoingMessage.attachmentIds addObject:attachmentStream.uniqueId];
if (sourceFilename) { if (attachmentStream.sourceFilename) {
outgoingMessage.attachmentFilenameMap[attachmentStream.uniqueId] = sourceFilename; outgoingMessage.attachmentFilenameMap[attachmentStream.uniqueId] = attachmentStream.sourceFilename;
}
} }
[outgoingMessage saveWithTransaction:transaction]; [outgoingMessage saveWithTransaction:transaction];
}]; }];

@ -34,20 +34,25 @@ public class MessageSenderJobQueue: NSObject, JobQueue {
@objc(addMediaMessage:dataSource:contentType:sourceFilename:isTemporaryAttachment:) @objc(addMediaMessage:dataSource:contentType:sourceFilename:isTemporaryAttachment:)
public func add(mediaMessage: TSOutgoingMessage, dataSource: DataSource, contentType: String, sourceFilename: String?, isTemporaryAttachment: Bool) { public func add(mediaMessage: TSOutgoingMessage, dataSource: DataSource, contentType: String, sourceFilename: String?, isTemporaryAttachment: Bool) {
OutgoingMessagePreparer.prepareAttachment(with: dataSource, let attachmentInfo = OutgoingAttachmentInfo(dataSource: dataSource, contentType: contentType, sourceFilename: sourceFilename)
contentType: contentType, add(mediaMessage: mediaMessage, attachmentInfos: [attachmentInfo], isTemporaryAttachment: isTemporaryAttachment)
sourceFilename: sourceFilename, }
in: mediaMessage) { error in
if let error = error { @objc(addMediaMessage:attachmentInfos:isTemporaryAttachment:)
self.dbConnection.readWrite { transaction in public func add(mediaMessage: TSOutgoingMessage, attachmentInfos: [OutgoingAttachmentInfo], isTemporaryAttachment: Bool) {
mediaMessage.update(sendingError: error, transaction: transaction) OutgoingMessagePreparer.prepareAttachments(attachmentInfos,
} inMessage: mediaMessage,
} else { completionHandler: { error in
self.dbConnection.readWrite { transaction in if let error = error {
self.add(message: mediaMessage, removeMessageAfterSending: isTemporaryAttachment, transaction: transaction) self.dbConnection.readWrite { transaction in
} mediaMessage.update(sendingError: error, transaction: transaction)
} }
} } else {
self.dbConnection.readWrite { transaction in
self.add(message: mediaMessage, removeMessageAfterSending: isTemporaryAttachment, transaction: transaction)
}
}
})
} }
private func add(message: TSOutgoingMessage, removeMessageAfterSending: Bool, transaction: YapDatabaseReadWriteTransaction) { private func add(message: TSOutgoingMessage, removeMessageAfterSending: Bool, transaction: YapDatabaseReadWriteTransaction) {

@ -194,41 +194,12 @@ public class FullTextSearchFinder: NSObject {
} }
private static let messageIndexer: SearchIndexer<TSMessage> = SearchIndexer { (message: TSMessage, transaction: YapDatabaseReadTransaction) in private static let messageIndexer: SearchIndexer<TSMessage> = SearchIndexer { (message: TSMessage, transaction: YapDatabaseReadTransaction) in
if let body = message.body, body.count > 0 { if let bodyText = message.bodyText(with: transaction) {
return body return bodyText
}
if let oversizeText = oversizeText(forMessage: message, transaction: transaction) {
return oversizeText
} }
return "" return ""
} }
private static func oversizeText(forMessage message: TSMessage, transaction: YapDatabaseReadTransaction) -> String? {
guard message.hasAttachments() else {
return nil
}
guard let attachment = message.attachment(with: transaction) else {
owsFailDebug("attachment was unexpectedly nil")
return nil
}
guard let attachmentStream = attachment as? TSAttachmentStream else {
return nil
}
guard attachmentStream.isOversizeText() else {
return nil
}
guard let text = attachmentStream.readOversizeText() else {
owsFailDebug("Could not load oversize text attachment")
return nil
}
return text
}
private class func indexContent(object: Any, transaction: YapDatabaseReadTransaction) -> String? { private class func indexContent(object: Any, transaction: YapDatabaseReadTransaction) -> String? {
if let groupThread = object as? TSGroupThread { if let groupThread = object as? TSGroupThread {
return self.groupThreadIndexer.index(groupThread, transaction: transaction) return self.groupThreadIndexer.index(groupThread, transaction: transaction)

@ -147,17 +147,20 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin
return nil; return nil;
} }
TSMessage *message = (TSMessage *)object; TSMessage *message = (TSMessage *)object;
OWSAssertDebug(message.attachmentIds.count <= 1); BOOL allAttachmentsAreMedia = message.attachmentIds.count > 0;
NSString *attachmentId = message.attachmentIds.firstObject; for (NSString *attachmentId in message.attachmentIds) {
if (attachmentId.length == 0) { OWSAssertDebug(attachmentId.length > 0);
return nil; if (![self attachmentIdShouldAppearInMediaGallery:attachmentId transaction:transaction]) {
allAttachmentsAreMedia = NO;
break;
}
} }
if ([self attachmentIdShouldAppearInMediaGallery:attachmentId transaction:transaction]) { if (allAttachmentsAreMedia) {
return [self mediaGroupWithThreadId:message.uniqueThreadId]; return [self mediaGroupWithThreadId:message.uniqueThreadId];
} }
return nil; return nil;
}]; }];

@ -609,6 +609,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
} }
// Prefer a URL provider if available // Prefer a URL provider if available
// TODO: Support multi-image messages.
if let preferredAttachment = attachments.first(where: { (attachment: Any) -> Bool in if let preferredAttachment = attachments.first(where: { (attachment: Any) -> Bool in
guard let itemProvider = attachment as? NSItemProvider else { guard let itemProvider = attachment as? NSItemProvider else {
return false return false

Loading…
Cancel
Save