diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 9210f4367..67aea0aa8 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1657,14 +1657,13 @@ typedef enum : NSUInteger { }]; [actionSheetController addAction:deleteMessageAction]; - UIAlertAction *resendMessageAction = [UIAlertAction + UIAlertAction *retryAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"MESSAGES_VIEW_FAILED_DOWNLOAD_RETRY_ACTION", @"Action sheet button text") style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) { OWSAttachmentsProcessor *processor = [[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer - networkManager:self.networkManager - primaryStorage:self.primaryStorage]; + networkManager:self.networkManager]; [processor fetchAttachmentsForMessage:message primaryStorage:self.primaryStorage success:^(TSAttachmentStream *_Nonnull attachmentStream) { @@ -1676,7 +1675,7 @@ typedef enum : NSUInteger { }]; }]; - [actionSheetController addAction:resendMessageAction]; + [actionSheetController addAction:retryAction]; [self dismissKeyBoard]; [self presentViewController:actionSheetController animated:YES completion:nil]; diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index 6ce9ca627..44f19d2c0 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -81,7 +81,6 @@ NS_ASSUME_NONNULL_BEGIN [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:transcript.attachmentPointerProtos relay:transcript.relay networkManager:self.networkManager - primaryStorage:self.primaryStorage transaction:transaction]; // TODO group updates. Currently desktop doesn't support group updates, so not a problem yet. @@ -96,6 +95,7 @@ NS_ASSUME_NONNULL_BEGIN groupMetaMessage:TSGroupMessageNone quotedMessage:nil]; + // TODO synced quoted replies if (transcript.isExpirationTimerUpdate) { [OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage contactsManager:self.contactsManager]; diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h index dbab5b8a4..8860ed2a3 100644 --- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h +++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h @@ -29,24 +29,22 @@ extern NSString *const kAttachmentDownloadAttachmentIDKey; - (instancetype)init NS_UNAVAILABLE; +// TODO make this take one and loop over? - (instancetype)initWithAttachmentProtos:(NSArray *)attachmentProtos relay:(nullable NSString *)relay networkManager:(TSNetworkManager *)networkManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage transaction:(YapDatabaseReadWriteTransaction *)transaction NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithQuotedAttachmentProtos:(NSArray *)attachmentProtos - relay:(nullable NSString *)relay - networkManager:(TSNetworkManager *)networkManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage - transaction:(YapDatabaseReadWriteTransaction *)transaction NS_DESIGNATED_INITIALIZER; - /* * Retry fetching failed attachment download */ - (instancetype)initWithAttachmentPointer:(TSAttachmentPointer *)attachmentPointer - networkManager:(TSNetworkManager *)networkManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER; + networkManager:(TSNetworkManager *)networkManager NS_DESIGNATED_INITIALIZER; + + ++ (TSAttachmentPointer *)buildPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto + relay:(NSString *_Nullable)relay; + - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message primaryStorage:(OWSPrimaryStorage *)primaryStorage diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m index b25995670..f61ed5088 100644 --- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m +++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m @@ -35,7 +35,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; @interface OWSAttachmentsProcessor () @property (nonatomic, readonly) TSNetworkManager *networkManager; -@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage; @end @@ -43,7 +42,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; - (instancetype)initWithAttachmentPointer:(TSAttachmentPointer *)attachmentPointer networkManager:(TSNetworkManager *)networkManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage { self = [super init]; if (!self) { @@ -51,7 +49,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; } _networkManager = networkManager; - _primaryStorage = primaryStorage; _attachmentPointers = @[ attachmentPointer ]; _attachmentIds = @[ attachmentPointer.uniqueId ]; @@ -62,7 +59,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; - (instancetype)initWithAttachmentProtos:(NSArray *)attachmentProtos relay:(nullable NSString *)relay networkManager:(TSNetworkManager *)networkManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage transaction:(YapDatabaseReadWriteTransaction *)transaction { self = [super init]; @@ -71,35 +67,12 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; } _networkManager = networkManager; - _primaryStorage = primaryStorage; NSMutableArray *attachmentIds = [NSMutableArray new]; NSMutableArray *attachmentPointers = [NSMutableArray new]; for (OWSSignalServiceProtosAttachmentPointer *attachmentProto in attachmentProtos) { - OWSAssert(attachmentProto.id != 0); - OWSAssert(attachmentProto.key != nil); - OWSAssert(attachmentProto.contentType != nil); - - // digest will be empty for old clients. - NSData *digest = attachmentProto.hasDigest ? attachmentProto.digest : nil; - - TSAttachmentType attachmentType = TSAttachmentTypeDefault; - if ([attachmentProto hasFlags]) { - UInt32 flags = attachmentProto.flags; - if ((flags & (UInt32)OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage) > 0) { - attachmentType = TSAttachmentTypeVoiceMessage; - } - } - - TSAttachmentPointer *pointer = [[TSAttachmentPointer alloc] initWithServerId:attachmentProto.id - key:attachmentProto.key - digest:digest - byteCount:attachmentProto.size - contentType:attachmentProto.contentType - relay:relay - sourceFilename:attachmentProto.fileName - attachmentType:attachmentType]; + TSAttachmentPointer *pointer = [self.class buildPointerFromProto:attachmentProto relay:relay]; [attachmentIds addObject:pointer.uniqueId]; [pointer saveWithTransaction:transaction]; @@ -112,7 +85,36 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; return self; } -// Remove this? ++ (TSAttachmentPointer *)buildPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto + relay:(NSString *_Nullable)relay +{ + OWSAssert(attachmentProto.id != 0); + OWSAssert(attachmentProto.key != nil); + OWSAssert(attachmentProto.contentType != nil); + + // digest will be empty for old clients. + NSData *digest = attachmentProto.hasDigest ? attachmentProto.digest : nil; + + TSAttachmentType attachmentType = TSAttachmentTypeDefault; + if ([attachmentProto hasFlags]) { + UInt32 flags = attachmentProto.flags; + if ((flags & (UInt32)OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage) > 0) { + attachmentType = TSAttachmentTypeVoiceMessage; + } + } + + TSAttachmentPointer *pointer = [[TSAttachmentPointer alloc] initWithServerId:attachmentProto.id + key:attachmentProto.key + digest:digest + byteCount:attachmentProto.size + contentType:attachmentProto.contentType + relay:relay + sourceFilename:attachmentProto.fileName + attachmentType:attachmentType]; + return pointer; +} + +// PERF: Remove this and use a pre-existing dbConnection - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message primaryStorage:(OWSPrimaryStorage *)primaryStorage success:(void (^)(TSAttachmentStream *attachmentStream))successHandler diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index 97dfc8dd2..f08f13acf 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -11,7 +11,9 @@ NS_ASSUME_NONNULL_BEGIN */ @class TSAttachment; +@class TSAttachmentStream; @class TSQuotedMessage; +@class YapDatabaseReadWriteTransaction; @interface TSMessage : TSInteraction @@ -38,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasAttachments; - (nullable TSAttachment *)attachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; - (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction; +- (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream; - (BOOL)shouldStartExpireTimer; - (BOOL)shouldStartExpireTimer:(YapDatabaseReadTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 7cc7b27aa..c86a95b83 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -7,7 +7,8 @@ #import "NSDate+OWS.h" #import "NSString+SSK.h" #import "TSAttachment.h" -#import "TSAttachmentPointer.h" +#import "TSAttachmentStream.h" +#import "TSQuotedMessage.h" #import "TSThread.h" #import #import @@ -50,8 +51,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; // they were received & decrypted, not by when they were sent. @property (nonatomic) uint64_t receivedAtTimestamp; -@property (nonatomic, nullable) TSQuotedMessage *quotedMessage; - @end #pragma mark - @@ -311,6 +310,15 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return _body.filterStringForDisplay; } +- (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream +{ + OWSAssert([attachmentStream isKindOfClass:[TSAttachmentStream class]]); + OWSAssert(self.quotedMessage); + OWSAssert(self.quotedMessage.quotedAttachments.count == 1); + + [self.quotedMessage setThumbnailAttachmentStream:attachmentStream]; +} + #pragma mark - Update With... Methods - (void)updateWithExpireStartedAt:(uint64_t)expireStartedAt transaction:(YapDatabaseReadWriteTransaction *)transaction diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 6883fe452..9189ecd33 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -480,9 +480,9 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec quotedAttachmentBuilder.contentType = attachment.contentType; quotedAttachmentBuilder.fileName = attachment.sourceFilename; - if (attachment.thumbnailAttachmentId) { + if (attachment.thumbnailAttachmentStreamId) { quotedAttachmentBuilder.thumbnail = - [self buildProtoForAttachmentId:attachment.thumbnailAttachmentId]; + [self buildProtoForAttachmentId:attachment.thumbnailAttachmentStreamId]; } [quoteBuilder addAttachments:[quotedAttachmentBuilder build]]; diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h index 25f9b9f3b..c6042c8e0 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h @@ -56,8 +56,11 @@ NS_ASSUME_NONNULL_BEGIN // TODO: rename to pendingAttachmentId or maybe pendingAttachmentStream? @property (nonatomic, readonly, nullable) NSString *attachmentId; -// This is set once we've persisted a thumbnail -@property (atomic, nullable) NSString *thumbnailAttachmentId; +// References a yet-to-be downloaded thumbnail file +@property (atomic, nullable) NSString *thumbnailAttachmentPointerId; + +// References an already downloaded or locally generated thumbnail file +@property (atomic, nullable) NSString *thumbnailAttachmentStreamId; - (instancetype)init NS_UNAVAILABLE; @@ -88,11 +91,15 @@ NS_ASSUME_NONNULL_BEGIN - (nullable NSString *)contentType; - (nullable NSString *)sourceFilename; +// References a yet-to-be downloaded thumbnail file +- (nullable NSString *)thumbnailAttachmentPointerId; + +// References an already downloaded or locally generated thumbnail file +- (nullable NSString *)thumbnailAttachmentStreamId; +- (void)setThumbnailAttachmentStream:(TSAttachment *)thumbnailAttachmentStream; + @property (atomic, readonly) NSArray *quotedAttachments; - -- (NSArray *)fetchAttachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction; - // Before sending, persist a thumbnail attachment derived from the quoted attachment - (NSArray *)createThumbnailAttachmentsIfNecessaryWithTransaction: (YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m index d2431cf82..87f5dae03 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m @@ -90,9 +90,9 @@ NS_ASSUME_NONNULL_BEGIN OWSAttachmentInfo *attachmentInfo = quotedMessage.quotedAttachments.firstObject; UIImage *_Nullable thumbnailImage; - if (attachmentInfo.thumbnailAttachmentId) { + if (attachmentInfo.thumbnailAttachmentStreamId) { TSAttachment *attachment = - [TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentId transaction:transaction]; + [TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentStreamId transaction:transaction]; TSAttachmentStream *attachmentStream; if ([attachment isKindOfClass:[TSAttachmentStream class]]) { @@ -182,6 +182,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable OWSAttachmentInfo *)firstAttachmentInfo { + OWSAssert(self.quotedAttachments.count <= 1); return self.quotedAttachments.firstObject; } @@ -199,62 +200,29 @@ NS_ASSUME_NONNULL_BEGIN return firstAttachment.sourceFilename; } -//- (NSArray *)fetchThumbnailAttachmentsWithTransaction:(YapDatabaseReadTransaction *)transaction -//{ -// NSMutableArray *attachments = [NSMutableArray new]; -// -// for (OWSAttachmentInfo *attachmentInfo in self.quotedAttachments) { -// TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentInfo.attachmentId -// transaction:transaction]; -// -// } -//} +- (nullable NSString *)thumbnailAttachmentStreamId +{ + OWSAttachmentInfo *firstAttachment = self.firstAttachmentInfo; -#pragma mark - Thumbnail + return firstAttachment.thumbnailAttachmentStreamId; +} + +- (nullable NSString *)thumbnailAttachmentPointerId +{ + OWSAttachmentInfo *firstAttachment = self.firstAttachmentInfo; + + return firstAttachment.thumbnailAttachmentPointerId; +} + +- (void)setThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream +{ + OWSAssert([attachmentStream isKindOfClass:[TSAttachmentStream class]]); + OWSAssert(self.quotedAttachments.count == 1); + + OWSAttachmentInfo *firstAttachment = self.firstAttachmentInfo; + firstAttachment.thumbnailAttachmentStreamId = attachmentStream.uniqueId; +} -//- (nullable TSAttachment *)firstAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction -//{ -// OWSAttachmentInfo *attachmentInfo = self.firstAttachmentInfo; -// if (!attachmentInfo) { -// return nil; -// } -// -// return [TSAttachment fetchObjectWithUniqueID:attachmentInfo.attachmentId]; -//} -//- (nullable UIImage *)thumbnailImageWithTransaction:(YapDatabaseReadTransaction *)transaction -//{ -// TSAttachmentStream *firstAttachment = (TSAttachmentStream *)self.firstAttachmentIn; -// if (![firstAttachment isKindOfClass:[TSAttachmentStream class]]) { -// return nil; -// } -// -// return firstAttachment.thumbnailImage; -//} -//- (BOOL)hasThumbnailAttachments -//{ -// return self.thumbnailAttachments.count > 0; -//} -// -//- (void)addThumbnailAttachment:(TSAttachmentStream *)attachment -//{ -// NSMutableArray *existingAttachments = [self.thumbnailAttachments mutableCopy]; -// -// OWSAttachmentInfo *attachmentInfo = [[OWSAttachmentInfo alloc] initWithAttachment:attachment]; -// [existingAttachments addObject:attachmentInfo]; -// -// self.thumbnailAttachments = [existingAttachments copy]; -//} -// -//- (nullable OWSAttachmentInfo *)firstThumbnailAttachment -//{ -// return self.thumbnailAttachments.firstObject; -//} -// -//- (TSAttachmentStream *)thumbnailAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction -//{ -// -//} -// - (NSArray *)createThumbnailAttachmentsIfNecessaryWithTransaction: (YapDatabaseReadWriteTransaction *)transaction { @@ -275,7 +243,7 @@ NS_ASSUME_NONNULL_BEGIN } [thumbnailStream saveWithTransaction:transaction]; - info.thumbnailAttachmentId = thumbnailStream.uniqueId; + info.thumbnailAttachmentStreamId = thumbnailStream.uniqueId; [thumbnailAttachments addObject:thumbnailStream]; } diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 422c940c8..274ec66ff 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -517,7 +517,6 @@ NS_ASSUME_NONNULL_BEGIN [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ] relay:envelope.relay networkManager:self.networkManager - primaryStorage:self.primaryStorage transaction:transaction]; if (!attachmentsProcessor.hasSupportedAttachments) { @@ -555,7 +554,6 @@ NS_ASSUME_NONNULL_BEGIN [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments relay:envelope.relay networkManager:self.networkManager - primaryStorage:self.primaryStorage transaction:transaction]; if (!attachmentsProcessor.hasSupportedAttachments) { DDLogWarn(@"%@ received unsupported media envelope", self.logTag); @@ -1052,6 +1050,7 @@ NS_ASSUME_NONNULL_BEGIN attachmentIds:attachmentIds expiresInSeconds:dataMessage.expireTimer quotedMessage:quotedMessage]; + [self finalizeIncomingMessage:incomingMessage thread:thread envelope:envelope @@ -1102,73 +1101,42 @@ NS_ASSUME_NONNULL_BEGIN contentType:quotedAttachment.contentType sourceFilename:quotedAttachment.fileName]; - TSMessage *_Nullable quotedMessage = (TSMessage *)[TSInteraction - interactionsWithTimestamp:timestamp - filter:^BOOL(TSInteraction *interaction) { + // We prefer deriving any thumbnail locally rather than fetching one from the network. + TSAttachmentStream *_Nullable thumbnailStream = + [self tryToDeriveLocalThumbnailWithAttachmentInfo:attachmentInfo + timestamp:timestamp + threadId:thread.uniqueId + authorId:authorId + transaction:transaction]; - if (![thread.uniqueId isEqual:interaction.uniqueThreadId]) { - return NO; - } + if (thumbnailStream) { + DDLogDebug(@"%@ Generated local thumbnail for quoted quoted message: %@:%zu", + self.logTag, + thread.uniqueId, + timestamp); - if ([interaction isKindOfClass:[TSIncomingMessage class]]) { - TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction; - return [authorId isEqual:incomingMessage.messageAuthorId]; - } else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { - return [authorId isEqual:[TSAccountManager localNumber]]; - } else { - // ignore other interaction types - return NO; - } + [thumbnailStream saveWithTransaction:transaction]; - } - withTransaction:transaction] - .firstObject; + attachmentInfo.thumbnailAttachmentStreamId = thumbnailStream.uniqueId; + } else if (quotedAttachment.hasThumbnail) { + DDLogDebug(@"%@ Saving reference for fetching remote thumbnail for quoted message: %@:%zu", + self.logTag, + thread.uniqueId, + timestamp); - // We still have the existing quoted message locally. - // Derive any thumbnail locally rather than fetching one over the network. - if (quotedMessage) { - TSAttachment *attachment = [quotedMessage attachmentWithTransaction:transaction]; - if ([attachment isKindOfClass:[TSAttachmentStream class]]) { - TSAttachmentStream *sourceStream = (TSAttachmentStream *)attachment; + OWSSignalServiceProtosAttachmentPointer *thumbnailAttachmentProto = quotedAttachment.thumbnail; + TSAttachmentPointer *thumbnailPointer = + [OWSAttachmentsProcessor buildPointerFromProto:thumbnailAttachmentProto relay:envelope.relay]; + [thumbnailPointer saveWithTransaction:transaction]; - TSAttachmentStream *thumbnailStream = [sourceStream cloneAsThumbnail]; - [thumbnailStream saveWithTransaction:transaction]; - attachmentInfo.thumbnailAttachmentId = thumbnailStream.uniqueId; - } + attachmentInfo.thumbnailAttachmentPointerId = thumbnailPointer.uniqueId; + } else { + DDLogDebug(@"%@ No thumbnail for quoted message: %@:%zu", self.logTag, thread.uniqueId, timestamp); } [attachmentInfos addObject:attachmentInfo]; } - - // TODO - but only if the attachment can't be found locally. - // OWSAttachmentsProcessor *attachmentsProcessor = - // [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:quoteProto.attachments - // relay:envelope.relay - // networkManager:self.networkManager - // primaryStorage:self.primaryStorage - // transaction:transaction]; - // - // if (!attachmentsProcessor.hasSupportedAttachments) { - // attachments = @[]; - // } else { - // attachments = attachmentsProcessor.attachmentPointers; - // } - // - // [attachmentsProcessor fetchAttachmentsForMessage:nil - // transaction:transaction - // success:^(TSAttachmentStream *attachmentStream) { - // [groupThread - // updateAvatarWithAttachmentStream:attachmentStream]; - // } - // failure:^(NSError *error) { - // DDLogError(@"%@ failed to fetch attachments for group - // avatar sent at: %llu. with error: %@", - // self.logTag, - // envelope.timestamp, - // error); - // }]; - if (!hasText && !hasAttachment) { OWSFail(@"%@ quoted message has neither text nor attachment", self.logTag); return nil; @@ -1180,6 +1148,58 @@ NS_ASSUME_NONNULL_BEGIN quotedAttachmentInfos:attachmentInfos]; } +- (nullable TSAttachmentStream *)tryToDeriveLocalThumbnailWithAttachmentInfo:(OWSAttachmentInfo *)attachmentInfo + timestamp:(uint64_t)timestamp + threadId:(NSString *)threadId + authorId:(NSString *)authorId + transaction: + (YapDatabaseReadWriteTransaction *)transaction +{ + if (![TSAttachmentStream hasThumbnailForMimeType:attachmentInfo.contentType]) { + return nil; + } + + NSArray *quotedMessages = (NSArray *)[TSInteraction + interactionsWithTimestamp:timestamp + filter:^BOOL(TSInteraction *interaction) { + + if (![threadId isEqual:interaction.uniqueThreadId]) { + return NO; + } + + if ([interaction isKindOfClass:[TSIncomingMessage class]]) { + TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction; + return [authorId isEqual:incomingMessage.messageAuthorId]; + } else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { + return [authorId isEqual:[TSAccountManager localNumber]]; + } else { + // ignore other interaction types + return NO; + } + + } + withTransaction:transaction]; + + TSMessage *_Nullable quotedMessage = quotedMessages.firstObject; + + if (!quotedMessage) { + return nil; + } + + TSAttachment *attachment = [quotedMessage attachmentWithTransaction:transaction]; + if (![attachment isKindOfClass:[TSAttachmentStream class]]) { + return nil; + } + TSAttachmentStream *sourceStream = (TSAttachmentStream *)attachment; + + TSAttachmentStream *_Nullable thumbnailStream = [sourceStream cloneAsThumbnail]; + if (!thumbnailStream) { + return nil; + } + + return thumbnailStream; +} + - (void)finalizeIncomingMessage:(TSIncomingMessage *)incomingMessage thread:(TSThread *)thread envelope:(OWSSignalServiceProtosEnvelope *)envelope @@ -1212,7 +1232,36 @@ NS_ASSUME_NONNULL_BEGIN [incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:YES]; } - DDLogDebug(@"%@ shouldMarkMessageAsRead: %d (%@)", self.logTag, shouldMarkMessageAsRead, envelope.source); + TSQuotedMessage *_Nullable quotedMessage = incomingMessage.quotedMessage; + if (quotedMessage && quotedMessage.thumbnailAttachmentPointerId) { + // We weren't able to derive a local thumbnail, so we'll fetch the referenced attachment. + TSAttachmentPointer *attachmentPointer = + [TSAttachmentPointer fetchObjectWithUniqueID:quotedMessage.thumbnailAttachmentPointerId + transaction:transaction]; + + if ([attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) { + OWSAttachmentsProcessor *attachmentProcessor = + [[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer + networkManager:self.networkManager]; + + DDLogDebug(@"%@ downloading thumbnail for message: %tu", self.logTag, incomingMessage.timestamp); + [attachmentProcessor fetchAttachmentsForMessage:incomingMessage + transaction:transaction + success:^(TSAttachmentStream *_Nonnull attachmentStream) { + [self.dbConnection + asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [incomingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream]; + [incomingMessage saveWithTransaction:transaction]; + }]; + } + failure:^(NSError *_Nonnull error) { + DDLogWarn(@"%@ failed to fetch thumbnail for message: %tu with error: %@", + self.logTag, + incomingMessage.timestamp, + error); + }]; + } + } // In case we already have a read receipt for this new message (this happens sometimes). [OWSReadReceiptManager.sharedManager applyEarlyReadReceiptsForIncomingMessage:incomingMessage diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 5991de786..5e9afd799 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -326,7 +326,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (message.quotedMessage) { - // TODO do we want a different thumbnail size for quotes vs the gallery? This seems reasonable, // and has the advantage of already having been generated. __block NSArray *thumbnailAttachments; @@ -338,7 +337,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } }]; - // Though we currently only ever expect at most one thumbnail, the proto data model // suggests this could change. The logic is intended to work with multiple, but // if we ever actually want to send multiple, we should do more testing.