Add OWSAttachmentDownloads.

pull/1/head
Matthew Chen 7 years ago
parent b25f17a27c
commit 3daf7d4744

@ -86,7 +86,6 @@
#import <SignalServiceKit/NSTimer+OWS.h> #import <SignalServiceKit/NSTimer+OWS.h>
#import <SignalServiceKit/OWSAnalytics.h> #import <SignalServiceKit/OWSAnalytics.h>
#import <SignalServiceKit/OWSAnalyticsEvents.h> #import <SignalServiceKit/OWSAnalyticsEvents.h>
#import <SignalServiceKit/OWSAttachmentsProcessor.h>
#import <SignalServiceKit/OWSBackgroundTask.h> #import <SignalServiceKit/OWSBackgroundTask.h>
#import <SignalServiceKit/OWSCallMessageHandler.h> #import <SignalServiceKit/OWSCallMessageHandler.h>
#import <SignalServiceKit/OWSContactsOutputStream.h> #import <SignalServiceKit/OWSContactsOutputStream.h>

@ -234,7 +234,6 @@ public class ConversationMediaView: UIView {
AssertIsOnMainThread() AssertIsOnMainThread()
guard let loadBlock = loadBlock else { guard let loadBlock = loadBlock else {
owsFailDebug("Missing loadBlock")
return return
} }
loadBlock() loadBlock()
@ -245,7 +244,6 @@ public class ConversationMediaView: UIView {
AssertIsOnMainThread() AssertIsOnMainThread()
guard let unloadBlock = unloadBlock else { guard let unloadBlock = unloadBlock else {
owsFailDebug("Missing unloadBlock")
return return
} }
unloadBlock() unloadBlock()

@ -39,8 +39,7 @@ extern const UIDataDetectorTypes kOWSAllowedDataDetectorTypes;
- (void)didTapTruncatedTextMessage:(id<ConversationViewItem>)conversationItem; - (void)didTapTruncatedTextMessage:(id<ConversationViewItem>)conversationItem;
- (void)didTapFailedIncomingAttachment:(id<ConversationViewItem>)viewItem - (void)didTapFailedIncomingAttachment:(id<ConversationViewItem>)viewItem;
attachmentPointer:(TSAttachmentPointer *)attachmentPointer;
- (void)didTapConversationItem:(id<ConversationViewItem>)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply; - (void)didTapConversationItem:(id<ConversationViewItem>)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply;
- (void)didTapConversationItem:(id<ConversationViewItem>)viewItem - (void)didTapConversationItem:(id<ConversationViewItem>)viewItem

@ -1402,7 +1402,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
OWSAssertDebug(attachmentPointer); OWSAssertDebug(attachmentPointer);
if (attachmentPointer.state == TSAttachmentPointerStateFailed) { if (attachmentPointer.state == TSAttachmentPointerStateFailed) {
[self.delegate didTapFailedIncomingAttachment:self.viewItem attachmentPointer:attachmentPointer]; [self.delegate didTapFailedIncomingAttachment:self.viewItem];
} }
break; break;
} }

@ -67,7 +67,7 @@
#import <SignalServiceKit/NSTimer+OWS.h> #import <SignalServiceKit/NSTimer+OWS.h>
#import <SignalServiceKit/OWSAddToContactsOfferMessage.h> #import <SignalServiceKit/OWSAddToContactsOfferMessage.h>
#import <SignalServiceKit/OWSAddToProfileWhitelistOfferMessage.h> #import <SignalServiceKit/OWSAddToProfileWhitelistOfferMessage.h>
#import <SignalServiceKit/OWSAttachmentsProcessor.h> #import <SignalServiceKit/OWSAttachmentDownloads.h>
#import <SignalServiceKit/OWSBlockingManager.h> #import <SignalServiceKit/OWSBlockingManager.h>
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h> #import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
#import <SignalServiceKit/OWSIdentityManager.h> #import <SignalServiceKit/OWSIdentityManager.h>
@ -297,6 +297,11 @@ typedef enum : NSUInteger {
return SSKEnvironment.shared.typingIndicators; return SSKEnvironment.shared.typingIndicators;
} }
- (OWSAttachmentDownloads *)attachmentDownloads
{
return SSKEnvironment.shared.attachmentDownloads;
}
#pragma mark - #pragma mark -
- (void)addNotificationListeners - (void)addNotificationListeners
@ -1644,13 +1649,11 @@ typedef enum : NSUInteger {
#pragma mark Bubble User Actions #pragma mark Bubble User Actions
- (void)handleFailedDownloadTapForMessage:(TSMessage *)message - (void)handleFailedDownloadTapForMessage:(TSMessage *)message
attachmentPointer:(TSAttachmentPointer *)attachmentPointer
{ {
OWSAttachmentsProcessor *processor = OWSAssert(message);
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:@[ attachmentPointer ]];
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[processor fetchAttachmentsForMessage:message [self.attachmentDownloads downloadAttachmentsForMessage:message
transaction:transaction transaction:transaction
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSLogInfo(@"Successfully redownloaded attachment in thread: %@", message.thread); OWSLogInfo(@"Successfully redownloaded attachment in thread: %@", message.thread);
@ -2159,15 +2162,13 @@ typedef enum : NSUInteger {
} }
- (void)didTapFailedIncomingAttachment:(id<ConversationViewItem>)viewItem - (void)didTapFailedIncomingAttachment:(id<ConversationViewItem>)viewItem
attachmentPointer:(TSAttachmentPointer *)attachmentPointer
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssertDebug(viewItem); OWSAssertDebug(viewItem);
OWSAssertDebug(attachmentPointer);
// Restart failed downloads // Restart failed downloads
TSMessage *message = (TSMessage *)viewItem.interaction; TSMessage *message = (TSMessage *)viewItem.interaction;
[self handleFailedDownloadTapForMessage:message attachmentPointer:attachmentPointer]; [self handleFailedDownloadTapForMessage:message];
} }
- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message - (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message
@ -2192,17 +2193,13 @@ typedef enum : NSUInteger {
return; return;
} }
OWSAttachmentsProcessor *processor = [self.uiDatabaseConnection asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) {
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:@[ attachmentPointer ]]; [self.attachmentDownloads downloadAttachmentPointer:attachmentPointer
[self.editingDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[processor fetchAttachmentsForMessage:nil
transaction:transaction
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1); OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject; TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[self.editingDatabaseConnection [self.editingDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) { readWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) {
[message setQuotedMessageThumbnailAttachmentStream:attachmentStream]; [message setQuotedMessageThumbnailAttachmentStream:attachmentStream];
[message saveWithTransaction:postSuccessTransaction]; [message saveWithTransaction:postSuccessTransaction];
}]; }];
@ -2210,8 +2207,8 @@ typedef enum : NSUInteger {
failure:^(NSError *error) { failure:^(NSError *error) {
OWSLogWarn(@"Failed to redownload thumbnail with error: %@", error); OWSLogWarn(@"Failed to redownload thumbnail with error: %@", error);
[self.editingDatabaseConnection [self.editingDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) { readWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) {
[message touchWithTransaction:transaction]; [message touchWithTransaction:postSuccessTransaction];
}]; }];
}]; }];
}]; }];

@ -694,7 +694,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
navigationController.pushViewController(viewController, animated: true) navigationController.pushViewController(viewController, animated: true)
} }
func didTapFailedIncomingAttachment(_ viewItem: ConversationViewItem, attachmentPointer: TSAttachmentPointer) { func didTapFailedIncomingAttachment(_ viewItem: ConversationViewItem) {
// no - op // no - op
} }

@ -11,6 +11,7 @@
#import <SignalMessaging/SignalMessaging-Swift.h> #import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/ContactDiscoveryService.h> #import <SignalServiceKit/ContactDiscoveryService.h>
#import <SignalServiceKit/OWS2FAManager.h> #import <SignalServiceKit/OWS2FAManager.h>
#import <SignalServiceKit/OWSAttachmentDownloads.h>
#import <SignalServiceKit/OWSBackgroundTask.h> #import <SignalServiceKit/OWSBackgroundTask.h>
#import <SignalServiceKit/OWSBatchMessageProcessor.h> #import <SignalServiceKit/OWSBatchMessageProcessor.h>
#import <SignalServiceKit/OWSBlockingManager.h> #import <SignalServiceKit/OWSBlockingManager.h>
@ -86,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSyncManager *syncManager = [[OWSSyncManager alloc] initDefault]; OWSSyncManager *syncManager = [[OWSSyncManager alloc] initDefault];
id<SSKReachabilityManager> reachabilityManager = [SSKReachabilityManagerImpl new]; id<SSKReachabilityManager> reachabilityManager = [SSKReachabilityManagerImpl new];
id<OWSTypingIndicators> typingIndicators = [[OWSTypingIndicatorsImpl alloc] init]; id<OWSTypingIndicators> typingIndicators = [[OWSTypingIndicatorsImpl alloc] init];
OWSAttachmentDownloads *attachmentDownloads = [[OWSAttachmentDownloads alloc] init];
OWSAudioSession *audioSession = [OWSAudioSession new]; OWSAudioSession *audioSession = [OWSAudioSession new];
OWSSounds *sounds = [[OWSSounds alloc] initWithPrimaryStorage:primaryStorage]; OWSSounds *sounds = [[OWSSounds alloc] initWithPrimaryStorage:primaryStorage];
@ -123,7 +125,8 @@ NS_ASSUME_NONNULL_BEGIN
outgoingReceiptManager:outgoingReceiptManager outgoingReceiptManager:outgoingReceiptManager
reachabilityManager:reachabilityManager reachabilityManager:reachabilityManager
syncManager:syncManager syncManager:syncManager
typingIndicators:typingIndicators]]; typingIndicators:typingIndicators
attachmentDownloads:attachmentDownloads]];
appSpecificSingletonBlock(); appSpecificSingletonBlock();

@ -5,24 +5,14 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class OWSIncomingSentMessageTranscript; @class OWSIncomingSentMessageTranscript;
@class OWSPrimaryStorage;
@class OWSReadReceiptManager;
@class TSAttachmentStream; @class TSAttachmentStream;
@class TSNetworkManager;
@class YapDatabaseReadWriteTransaction; @class YapDatabaseReadWriteTransaction;
@protocol ContactsManagerProtocol;
// This job is used to process "outgoing message" notifications from linked devices. // This job is used to process "outgoing message" notifications from linked devices.
@interface OWSRecordTranscriptJob : NSObject @interface OWSRecordTranscriptJob : NSObject
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithIncomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSentMessageTranscript; - (instancetype)initWithIncomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSentMessageTranscript;
- (instancetype)initWithIncomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSentMessageTranscript
networkManager:(TSNetworkManager *)networkManager
primaryStorage:(OWSPrimaryStorage *)primaryStorage
readReceiptManager:(OWSReadReceiptManager *)readReceiptManager
contactsManager:(id<ContactsManagerProtocol>)contactsManager
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
- (void)runWithAttachmentHandler:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))attachmentHandler - (void)runWithAttachmentHandler:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))attachmentHandler

@ -3,7 +3,7 @@
// //
#import "OWSRecordTranscriptJob.h" #import "OWSRecordTranscriptJob.h"
#import "OWSAttachmentsProcessor.h" #import "OWSAttachmentDownloads.h"
#import "OWSDisappearingMessagesJob.h" #import "OWSDisappearingMessagesJob.h"
#import "OWSIncomingSentMessageTranscript.h" #import "OWSIncomingSentMessageTranscript.h"
#import "OWSPrimaryStorage+SessionStore.h" #import "OWSPrimaryStorage+SessionStore.h"
@ -19,31 +19,15 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSRecordTranscriptJob () @interface OWSRecordTranscriptJob ()
@property (nonatomic, readonly) TSNetworkManager *networkManager;
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
@property (nonatomic, readonly) OWSReadReceiptManager *readReceiptManager;
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) OWSIncomingSentMessageTranscript *incomingSentMessageTranscript; @property (nonatomic, readonly) OWSIncomingSentMessageTranscript *incomingSentMessageTranscript;
@end @end
@implementation OWSRecordTranscriptJob #pragma mark -
- (instancetype)initWithIncomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSentMessageTranscript @implementation OWSRecordTranscriptJob
{
return [self initWithIncomingSentMessageTranscript:incomingSentMessageTranscript
networkManager:TSNetworkManager.sharedManager
primaryStorage:OWSPrimaryStorage.sharedManager
readReceiptManager:OWSReadReceiptManager.sharedManager
contactsManager:SSKEnvironment.shared.contactsManager];
}
- (instancetype)initWithIncomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSentMessageTranscript - (instancetype)initWithIncomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSentMessageTranscript
networkManager:(TSNetworkManager *)networkManager
primaryStorage:(OWSPrimaryStorage *)primaryStorage
readReceiptManager:(OWSReadReceiptManager *)readReceiptManager
contactsManager:(id<ContactsManagerProtocol>)contactsManager
{ {
self = [super init]; self = [super init];
if (!self) { if (!self) {
@ -51,14 +35,47 @@ NS_ASSUME_NONNULL_BEGIN
} }
_incomingSentMessageTranscript = incomingSentMessageTranscript; _incomingSentMessageTranscript = incomingSentMessageTranscript;
_networkManager = networkManager;
_primaryStorage = primaryStorage;
_readReceiptManager = readReceiptManager;
_contactsManager = contactsManager;
return self; return self;
} }
#pragma mark - Dependencies
- (OWSPrimaryStorage *)primaryStorage
{
OWSAssertDebug(SSKEnvironment.shared.primaryStorage);
return SSKEnvironment.shared.primaryStorage;
}
- (TSNetworkManager *)networkManager
{
OWSAssertDebug(SSKEnvironment.shared.networkManager);
return SSKEnvironment.shared.networkManager;
}
- (OWSReadReceiptManager *)readReceiptManager
{
OWSAssert(SSKEnvironment.shared.readReceiptManager);
return SSKEnvironment.shared.readReceiptManager;
}
- (id<ContactsManagerProtocol>)contactsManager
{
OWSAssertDebug(SSKEnvironment.shared.contactsManager);
return SSKEnvironment.shared.contactsManager;
}
- (OWSAttachmentDownloads *)attachmentDownloads
{
return SSKEnvironment.shared.attachmentDownloads;
}
#pragma mark -
- (void)runWithAttachmentHandler:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))attachmentHandler - (void)runWithAttachmentHandler:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))attachmentHandler
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
@ -107,22 +124,19 @@ NS_ASSUME_NONNULL_BEGIN
transaction:transaction]; transaction:transaction];
if ([attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) { if ([attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) {
OWSAttachmentsProcessor *attachmentProcessor = OWSLogDebug(@"downloading attachments for transcript: %lu", (unsigned long)transcript.timestamp);
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:@[ attachmentPointer ]];
OWSLogDebug(@"downloading thumbnail for transcript: %lu", (unsigned long)transcript.timestamp); [self.attachmentDownloads downloadAttachmentPointer:attachmentPointer
[attachmentProcessor fetchAttachmentsForMessage:outgoingMessage
transaction:transaction
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1); OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject; TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[self.primaryStorage.newDatabaseConnection [self.primaryStorage.newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[outgoingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream]; [outgoingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream];
[outgoingMessage saveWithTransaction:transaction]; [outgoingMessage saveWithTransaction:transaction];
}]; }];
} }
failure:^(NSError *_Nonnull error) { failure:^(NSError *error) {
OWSLogWarn(@"failed to fetch thumbnail for transcript: %lu with error: %@", OWSLogWarn(@"failed to fetch thumbnail for transcript: %lu with error: %@",
(unsigned long)transcript.timestamp, (unsigned long)transcript.timestamp,
error); error);
@ -161,15 +175,14 @@ NS_ASSUME_NONNULL_BEGIN
[self.readReceiptManager applyEarlyReadReceiptsForOutgoingMessageFromLinkedDevice:outgoingMessage [self.readReceiptManager applyEarlyReadReceiptsForOutgoingMessageFromLinkedDevice:outgoingMessage
transaction:transaction]; transaction:transaction];
OWSAttachmentsProcessor *attachmentsProcessor = [self.attachmentDownloads
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:attachmentPointers]; downloadAttachmentsForMessage:outgoingMessage
[attachmentsProcessor transaction:transaction
fetchAttachmentsForMessage:outgoingMessage success:attachmentHandler
transaction:transaction failure:^(NSError *error) {
success:attachmentHandler OWSLogError(
failure:^(NSError *_Nonnull error) { @"failed to fetch transcripts attachments for message: %@", outgoingMessage);
OWSLogError(@"failed to fetch transcripts attachments for message: %@", outgoingMessage); }];
}];
} }
@end @end

@ -0,0 +1,46 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kAttachmentDownloadProgressNotification;
extern NSString *const kAttachmentDownloadProgressKey;
extern NSString *const kAttachmentDownloadAttachmentIDKey;
@class SSKProtoAttachmentPointer;
@class TSAttachment;
@class TSAttachmentPointer;
@class TSAttachmentStream;
@class TSMessage;
@class YapDatabaseReadTransaction;
@class YapDatabaseReadWriteTransaction;
#pragma mark -
/**
* Given incoming attachment protos, determines which we support.
* It can download those that we support and notifies threads when it receives unsupported attachments.
*/
@interface OWSAttachmentDownloads : NSObject
// This will try to download all un-downloaded attachments for a given message.
// Any attachments for the message which are already downloaded are skipped BUT
// they are included in the success callback.
//
// success/failure are always called on a worker queue.
- (void)downloadAttachmentsForMessage:(TSMessage *)message
transaction:(YapDatabaseReadTransaction *)transaction
success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))success
failure:(void (^)(NSError *error))failure;
// This will try to download a single attachment.
//
// success/failure are always called on a worker queue.
- (void)downloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer
success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))success
failure:(void (^)(NSError *error))failure;
@end
NS_ASSUME_NONNULL_END

@ -2,7 +2,7 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
#import "OWSAttachmentsProcessor.h" #import "OWSAttachmentDownloads.h"
#import "AppContext.h" #import "AppContext.h"
#import "MIMETypeUtil.h" #import "MIMETypeUtil.h"
#import "NSNotificationCenter+OWS.h" #import "NSNotificationCenter+OWS.h"
@ -36,103 +36,307 @@ NSString *const kAttachmentDownloadAttachmentIDKey = @"kAttachmentDownloadAttach
// indicator shows up as quickly as possible. // indicator shows up as quickly as possible.
static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
@interface OWSAttachmentsProcessor () typedef void (^AttachmentDownloadSuccess)(TSAttachmentStream *attachmentStream);
typedef void (^AttachmentDownloadFailure)(NSError *error);
@property (nonatomic, readonly) TSNetworkManager *networkManager; @interface OWSAttachmentDownloadJob : NSObject
@property (nonatomic, readonly) TSAttachmentPointer *attachmentPointer;
@property (nonatomic, readonly, nullable) TSMessage *message;
@property (nonatomic, readonly) AttachmentDownloadSuccess success;
@property (nonatomic, readonly) AttachmentDownloadFailure failure;
@end @end
@implementation OWSAttachmentsProcessor #pragma mark -
@implementation OWSAttachmentDownloadJob
- (instancetype)initWithAttachmentPointers:(NSArray<TSAttachmentPointer *> *)attachmentPointers - (instancetype)initWithAttachmentPointer:(TSAttachmentPointer *)attachmentPointer
message:(nullable TSMessage *)message
success:(AttachmentDownloadSuccess)success
failure:(AttachmentDownloadFailure)failure
{ {
self = [super init]; self = [super init];
if (!self) { if (!self) {
return self; return self;
} }
_attachmentPointers = attachmentPointers; _attachmentPointer = attachmentPointer;
_message = message;
_success = success;
_failure = failure;
return self; return self;
} }
@end
#pragma mark -
@interface OWSAttachmentDownloads ()
// This property should only be accessed while synchronized on this class.
@property (nonatomic, readonly) NSMutableSet<NSString *> *downloadingAttachmentIdSet;
// This property should only be accessed while synchronized on this class.
@property (nonatomic, readonly) NSMutableArray<OWSAttachmentDownloadJob *> *attachmentDownloadJobQueue;
@end
#pragma mark -
@implementation OWSAttachmentDownloads
#pragma mark - Dependencies #pragma mark - Dependencies
- (OWSPrimaryStorage *)primaryStorage
{
return SSKEnvironment.shared.primaryStorage;
}
- (TSNetworkManager *)networkManager - (TSNetworkManager *)networkManager
{ {
return SSKEnvironment.shared.networkManager; return SSKEnvironment.shared.networkManager;
} }
#pragma mark
- (void)fetchAttachmentsForMessage:(nullable TSMessage *)message #pragma mark -
transaction:(YapDatabaseReadWriteTransaction *)transaction
success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))successHandler - (instancetype)init
failure:(void (^)(NSError *error))failureHandler {
self = [super init];
if (!self) {
return self;
}
_downloadingAttachmentIdSet = [NSMutableSet new];
_attachmentDownloadJobQueue = [NSMutableArray new];
return self;
}
#pragma mark -
- (void)downloadAttachmentsForMessage:(TSMessage *)message
transaction:(YapDatabaseReadTransaction *)transaction
success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))success
failure:(void (^)(NSError *error))failure
{ {
OWSAssertDebug(transaction); OWSAssertDebug(transaction);
OWSAssertDebug(self.attachmentPointers.count > 0); OWSAssertDebug(message);
NSMutableArray<AnyPromise *> *promises = [NSMutableArray array];
NSMutableArray<TSAttachmentStream *> *attachmentStreams = [NSMutableArray array]; NSMutableArray<TSAttachmentStream *> *attachmentStreams = [NSMutableArray array];
NSMutableArray<TSAttachmentPointer *> *attachmentPointers = [NSMutableArray new];
for (TSAttachment *attachment in [message attachmentsWithTransaction:transaction]) {
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
[attachmentStreams addObject:attachmentStream];
} else if ([attachment isKindOfClass:[TSAttachmentPointer class]]) {
TSAttachmentPointer *attachmentPointer = (TSAttachmentPointer *)attachment;
[attachmentPointers addObject:attachmentPointer];
} else {
OWSFailDebug(@"Unexpected attachment type: %@", attachment.class);
}
}
[self enqueueJobsForAttachmentStreams:attachmentStreams
attachmentPointers:attachmentPointers
message:message
success:success
failure:failure];
}
- (void)downloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer
success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))success
failure:(void (^)(NSError *error))failure
{
OWSAssertDebug(attachmentPointer);
[self enqueueJobsForAttachmentStreams:@[]
attachmentPointers:@[
attachmentPointer,
]
message:nil
success:success
failure:failure];
}
- (void)enqueueJobsForAttachmentStreams:(NSArray<TSAttachmentStream *> *)attachmentStreamsParam
attachmentPointers:(NSArray<TSAttachmentPointer *> *)attachmentPointers
message:(nullable TSMessage *)message
success:(void (^)(NSArray<TSAttachmentStream *> *attachmentStreams))successHandler
failure:(void (^)(NSError *error))failureHandler
{
OWSAssertDebug(attachmentStreamsParam);
OWSAssertDebug(attachmentPointers.count > 0);
// To avoid deadlocks, synchronize on self outside of the transaction.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (attachmentPointers.count < 1) {
OWSAssertDebug(attachmentStreamsParam.count > 0);
successHandler(attachmentStreamsParam);
return;
}
NSMutableArray<TSAttachmentStream *> *attachmentStreams = [attachmentStreamsParam mutableCopy];
NSMutableArray<AnyPromise *> *promises = [NSMutableArray array];
for (TSAttachmentPointer *attachmentPointer in attachmentPointers) {
AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
[self enqueueJobForAttachmentPointer:attachmentPointer
message:message
success:^(TSAttachmentStream *attachmentStream) {
@synchronized(attachmentStreams) {
[attachmentStreams addObject:attachmentStream];
}
for (TSAttachmentPointer *attachmentPointer in self.attachmentPointers) { resolve(@(1));
AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
[self retrieveAttachment:attachmentPointer
message:message
transaction:transaction
success:^(TSAttachmentStream *attachmentStream) {
OWSLogVerbose(@"Attachment download succeeded.");
@synchronized(attachmentStreams) {
[attachmentStreams addObject:attachmentStream];
} }
resolve(@(1)); failure:^(NSError *error) {
} resolve(error);
failure:^(NSError *error) { }];
OWSLogError(@"Attachment download failed with error: %@", error); }];
resolve(error); [promises addObject:promise];
}]; }
}];
[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);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
successHandler(attachmentStreamsCopy);
});
})
.catch(^(NSError *error) {
OWSLogError(@"Attachment downloads failed.");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
failureHandler(error);
});
});
[completionPromise retainUntilComplete];
});
}
- (void)enqueueJobForAttachmentPointer:(TSAttachmentPointer *)attachmentPointer
message:(nullable TSMessage *)message
success:(void (^)(TSAttachmentStream *attachmentStream))success
failure:(void (^)(NSError *error))failure
{
OWSAssertDebug(attachmentPointer);
OWSAttachmentDownloadJob *job = [[OWSAttachmentDownloadJob alloc] initWithAttachmentPointer:attachmentPointer
message:message
success:success
failure:failure];
@synchronized(self) {
[self.attachmentDownloadJobQueue addObject:job];
} }
// We use PMKJoin(), not PMKWhen(), because we don't want the [self startDownloadIfPossible];
// 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)startDownloadIfPossible
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OWSAttachmentDownloadJob *_Nullable job;
@synchronized(self) {
const NSUInteger kMaxSimultaneousDownloads = 4;
if (self.downloadingAttachmentIdSet.count >= kMaxSimultaneousDownloads) {
return;
}
job = self.attachmentDownloadJobQueue.firstObject;
if (!job) {
return;
}
if ([self.downloadingAttachmentIdSet containsObject:job.attachmentPointer.uniqueId]) {
// Ensure we only have one download in flight at a time for a given attachment.
OWSLogWarn(@"Ignoring duplicate download.");
return;
}
[self.attachmentDownloadJobQueue removeObjectAtIndex:0];
[self.downloadingAttachmentIdSet addObject:job.attachmentPointer.uniqueId];
}
[self.primaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
job.attachmentPointer.state = TSAttachmentPointerStateDownloading;
[job.attachmentPointer saveWithTransaction:transaction];
if (job.message) {
[job.message touchWithTransaction:transaction];
}
}];
[self retrieveAttachment:job.attachmentPointer
success:^(TSAttachmentStream *attachmentStream) {
OWSLogVerbose(@"Attachment download succeeded.");
[self.primaryStorage.dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[attachmentStream saveWithTransaction:transaction];
if (job.message) {
[job.message touchWithTransaction:transaction];
}
}];
job.success(attachmentStream);
@synchronized(self) {
[self.downloadingAttachmentIdSet removeObject:job.attachmentPointer.uniqueId];
}
[self startDownloadIfPossible];
}
failure:^(NSError *error) {
OWSLogError(@"Attachment download failed with error: %@", error);
[self.primaryStorage.dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
job.attachmentPointer.mostRecentFailureLocalizedText = error.localizedDescription;
job.attachmentPointer.state = TSAttachmentPointerStateFailed;
[job.attachmentPointer saveWithTransaction:transaction];
if (job.message) {
[job.message touchWithTransaction:transaction];
}
}];
@synchronized(self) {
[self.downloadingAttachmentIdSet removeObject:job.attachmentPointer.uniqueId];
}
job.failure(error);
[self startDownloadIfPossible];
}];
});
}
#pragma mark -
- (void)retrieveAttachment:(TSAttachmentPointer *)attachment - (void)retrieveAttachment:(TSAttachmentPointer *)attachment
message:(nullable TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction
success:(void (^)(TSAttachmentStream *attachmentStream))successHandler success:(void (^)(TSAttachmentStream *attachmentStream))successHandler
failure:(void (^)(NSError *error))failureHandler failure:(void (^)(NSError *error))failureHandler
{ {
OWSAssertDebug(transaction); OWSAssertDebug(attachment);
__block OWSBackgroundTask *_Nullable backgroundTask = __block OWSBackgroundTask *_Nullable backgroundTask =
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
[self setAttachment:attachment isDownloadingInMessage:message transaction:transaction];
void (^markAndHandleFailure)(NSError *) = ^(NSError *error) { void (^markAndHandleFailure)(NSError *) = ^(NSError *error) {
// Ensure enclosing transaction is complete.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self setAttachment:attachment didFailInMessage:message error:error];
failureHandler(error); failureHandler(error);
OWSAssertDebug(backgroundTask); OWSAssertDebug(backgroundTask);
@ -141,12 +345,8 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
}; };
void (^markAndHandleSuccess)(TSAttachmentStream *attachmentStream) = ^(TSAttachmentStream *attachmentStream) { void (^markAndHandleSuccess)(TSAttachmentStream *attachmentStream) = ^(TSAttachmentStream *attachmentStream) {
// Ensure enclosing transaction is complete.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
successHandler(attachmentStream); successHandler(attachmentStream);
if (message) {
[message touch];
}
OWSAssertDebug(backgroundTask); OWSAssertDebug(backgroundTask);
backgroundTask = nil; backgroundTask = nil;
@ -263,7 +463,8 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
} }
if (!plaintext) { if (!plaintext) {
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, NSLocalizedString(@"ERROR_MESSAGE_INVALID_MESSAGE", @"")); NSError *error = OWSErrorWithCodeDescription(
OWSErrorCodeFailedToDecryptMessage, NSLocalizedString(@"ERROR_MESSAGE_INVALID_MESSAGE", @""));
failureHandler(error); failureHandler(error);
return; return;
} }
@ -278,7 +479,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
return; return;
} }
[stream save];
successHandler(stream); successHandler(stream);
} }
@ -299,7 +499,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
failure:(void (^)(NSURLSessionTask *_Nullable task, NSError *error))failureHandlerParam failure:(void (^)(NSURLSessionTask *_Nullable task, NSError *error))failureHandlerParam
{ {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFHTTPRequestSerializer serializer]; manager.requestSerializer = [AFHTTPRequestSerializer serializer];
[manager.requestSerializer setValue:OWSMimeTypeApplicationOctetStream forHTTPHeaderField:@"Content-Type"]; [manager.requestSerializer setValue:OWSMimeTypeApplicationOctetStream forHTTPHeaderField:@"Content-Type"];
manager.responseSerializer = [AFHTTPResponseSerializer serializer]; manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
@ -451,31 +651,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
}]; }];
} }
- (void)setAttachment:(TSAttachmentPointer *)pointer
isDownloadingInMessage:(nullable TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssertDebug(transaction);
pointer.state = TSAttachmentPointerStateDownloading;
[pointer saveWithTransaction:transaction];
if (message) {
[message touchWithTransaction:transaction];
}
}
- (void)setAttachment:(TSAttachmentPointer *)pointer
didFailInMessage:(nullable TSMessage *)message
error:(NSError *)error
{
pointer.mostRecentFailureLocalizedText = error.localizedDescription;
pointer.state = TSAttachmentPointerStateFailed;
[pointer save];
if (message) {
[message touch];
}
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -107,6 +107,9 @@ NS_ASSUME_NONNULL_BEGIN
(NSArray<SSKProtoAttachmentPointer *> *)attachmentProtos (NSArray<SSKProtoAttachmentPointer *> *)attachmentProtos
albumMessage:(TSMessage *)albumMessage albumMessage:(TSMessage *)albumMessage
{ {
OWSAssertDebug(attachmentProtos);
OWSAssertDebug(albumMessage);
NSMutableArray *attachmentPointers = [NSMutableArray new]; NSMutableArray *attachmentPointers = [NSMutableArray new];
for (SSKProtoAttachmentPointer *attachmentProto in attachmentProtos) { for (SSKProtoAttachmentPointer *attachmentProto in attachmentProtos) {
TSAttachmentPointer *_Nullable attachmentPointer = TSAttachmentPointer *_Nullable attachmentPointer =

@ -9,7 +9,7 @@
#import "MimeTypeUtil.h" #import "MimeTypeUtil.h"
#import "NSNotificationCenter+OWS.h" #import "NSNotificationCenter+OWS.h"
#import "NotificationsProtocol.h" #import "NotificationsProtocol.h"
#import "OWSAttachmentsProcessor.h" #import "OWSAttachmentDownloads.h"
#import "OWSBlockingManager.h" #import "OWSBlockingManager.h"
#import "OWSCallMessageHandler.h" #import "OWSCallMessageHandler.h"
#import "OWSContact.h" #import "OWSContact.h"
@ -164,6 +164,11 @@ NS_ASSUME_NONNULL_BEGIN
return SSKEnvironment.shared.typingIndicators; return SSKEnvironment.shared.typingIndicators;
} }
- (OWSAttachmentDownloads *)attachmentDownloads
{
return SSKEnvironment.shared.attachmentDownloads;
}
#pragma mark - #pragma mark -
- (void)startObserving - (void)startObserving
@ -729,13 +734,14 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
TSAttachmentPointer *avatarPointer = TSAttachmentPointer *_Nullable avatarPointer =
[TSAttachmentPointer attachmentPointerFromProto:dataMessage.group.avatar albumMessage:nil]; [TSAttachmentPointer attachmentPointerFromProto:dataMessage.group.avatar albumMessage:nil];
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:@[ avatarPointer ]];
[attachmentsProcessor fetchAttachmentsForMessage:nil if (!avatarPointer) {
transaction:transaction OWSLogWarn(@"received unsupported group avatar envelope");
return;
}
[self.attachmentDownloads downloadAttachmentPointer:avatarPointer
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1); OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject; TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
@ -771,35 +777,26 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
TSIncomingMessage *_Nullable createdMessage = [self handleReceivedEnvelope:envelope TSIncomingMessage *_Nullable message =
withDataMessage:dataMessage [self handleReceivedEnvelope:envelope withDataMessage:dataMessage transaction:transaction];
transaction:transaction];
if (!createdMessage) {
return;
}
NSArray<TSAttachmentPointer *> *attachmentPointers = if (!message) {
[TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments albumMessage:createdMessage]; return;
for (TSAttachmentPointer *pointer in attachmentPointers) {
[pointer saveWithTransaction:transaction];
[createdMessage.attachmentIds addObject:pointer.uniqueId];
} }
[createdMessage saveWithTransaction:transaction];
OWSLogDebug(@"incoming attachment message: %@", createdMessage.debugDescription); [message saveWithTransaction:transaction];
OWSAttachmentsProcessor *attachmentsProcessor = OWSLogDebug(@"incoming attachment message: %@", message.debugDescription);
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:attachmentPointers];
[attachmentsProcessor fetchAttachmentsForMessage:createdMessage [self.attachmentDownloads downloadAttachmentsForMessage:message
transaction:transaction transaction:transaction
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSLogDebug(@"successfully fetched attachments: %lu for message: %@", OWSLogDebug(@"successfully fetched attachments: %lu for message: %@",
(unsigned long)attachmentStreams.count, (unsigned long)attachmentStreams.count,
createdMessage); message);
} }
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: %@", message, error);
}]; }];
} }
@ -1264,6 +1261,22 @@ NS_ASSUME_NONNULL_BEGIN
serverTimestamp:serverTimestamp serverTimestamp:serverTimestamp
wasReceivedByUD:wasReceivedByUD]; wasReceivedByUD:wasReceivedByUD];
NSArray<TSAttachmentPointer *> *attachmentPointers =
[TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments
albumMessage:incomingMessage];
for (TSAttachmentPointer *pointer in attachmentPointers) {
[pointer saveWithTransaction:transaction];
[incomingMessage.attachmentIds addObject:pointer.uniqueId];
}
if (body.length == 0 && attachmentPointers.count < 1 && !contact) {
OWSLogWarn(@"ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu",
envelopeAddress(envelope),
groupId,
(unsigned long)timestamp);
return nil;
}
[self finalizeIncomingMessage:incomingMessage [self finalizeIncomingMessage:incomingMessage
thread:oldGroupThread thread:oldGroupThread
envelope:envelope envelope:envelope
@ -1298,6 +1311,20 @@ NS_ASSUME_NONNULL_BEGIN
serverTimestamp:serverTimestamp serverTimestamp:serverTimestamp
wasReceivedByUD:wasReceivedByUD]; wasReceivedByUD:wasReceivedByUD];
NSArray<TSAttachmentPointer *> *attachmentPointers =
[TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments albumMessage:incomingMessage];
for (TSAttachmentPointer *pointer in attachmentPointers) {
[pointer saveWithTransaction:transaction];
[incomingMessage.attachmentIds addObject:pointer.uniqueId];
}
if (body.length == 0 && attachmentPointers.count < 1 && !contact) {
OWSLogWarn(@"ignoring empty incoming message from: %@ with timestamp: %lu",
envelopeAddress(envelope),
(unsigned long)timestamp);
return nil;
}
[self finalizeIncomingMessage:incomingMessage [self finalizeIncomingMessage:incomingMessage
thread:thread thread:thread
envelope:envelope envelope:envelope
@ -1344,16 +1371,12 @@ NS_ASSUME_NONNULL_BEGIN
transaction:transaction]; transaction:transaction];
if ([attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) { if ([attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) {
OWSAttachmentsProcessor *attachmentProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:@[ attachmentPointer ]];
OWSLogDebug(@"downloading thumbnail for message: %lu", (unsigned long)incomingMessage.timestamp); OWSLogDebug(@"downloading thumbnail for message: %lu", (unsigned long)incomingMessage.timestamp);
[attachmentProcessor fetchAttachmentsForMessage:incomingMessage [self.attachmentDownloads downloadAttachmentPointer:attachmentPointer
transaction:transaction
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSAssertDebug(attachmentStreams.count == 1); OWSAssertDebug(attachmentStreams.count == 1);
TSAttachmentStream *attachmentStream = attachmentStreams.firstObject; TSAttachmentStream *attachmentStream = attachmentStreams.firstObject;
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[incomingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream]; [incomingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream];
[incomingMessage saveWithTransaction:transaction]; [incomingMessage saveWithTransaction:transaction];
}]; }];
@ -1374,14 +1397,11 @@ NS_ASSUME_NONNULL_BEGIN
if (![attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) { if (![attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) {
OWSFailDebug(@"avatar attachmentPointer was unexpectedly nil"); OWSFailDebug(@"avatar attachmentPointer was unexpectedly nil");
} else { } else {
OWSAttachmentsProcessor *attachmentProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointers:@[ attachmentPointer ]];
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
transaction:transaction [self.attachmentDownloads downloadAttachmentPointer:attachmentPointer
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[incomingMessage touchWithTransaction:transaction]; [incomingMessage touchWithTransaction:transaction];
}]; }];
} }

@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
@class ContactDiscoveryService; @class ContactDiscoveryService;
@class ContactsUpdater; @class ContactsUpdater;
@class OWS2FAManager; @class OWS2FAManager;
@class OWSAttachmentDownloads;
@class OWSBatchMessageProcessor; @class OWSBatchMessageProcessor;
@class OWSBlockingManager; @class OWSBlockingManager;
@class OWSDisappearingMessagesJob; @class OWSDisappearingMessagesJob;
@ -60,7 +61,8 @@ NS_ASSUME_NONNULL_BEGIN
outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager
reachabilityManager:(id<SSKReachabilityManager>)reachabilityManager reachabilityManager:(id<SSKReachabilityManager>)reachabilityManager
syncManager:(id<OWSSyncManagerProtocol>)syncManager syncManager:(id<OWSSyncManagerProtocol>)syncManager
typingIndicators:(id<OWSTypingIndicators>)typingIndicators NS_DESIGNATED_INITIALIZER; typingIndicators:(id<OWSTypingIndicators>)typingIndicators
attachmentDownloads:(OWSAttachmentDownloads *)attachmentDownloads NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
@ -97,6 +99,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) id<OWSSyncManagerProtocol> syncManager; @property (nonatomic, readonly) id<OWSSyncManagerProtocol> syncManager;
@property (nonatomic, readonly) id<SSKReachabilityManager> reachabilityManager; @property (nonatomic, readonly) id<SSKReachabilityManager> reachabilityManager;
@property (nonatomic, readonly) id<OWSTypingIndicators> typingIndicators; @property (nonatomic, readonly) id<OWSTypingIndicators> typingIndicators;
@property (nonatomic, readonly) OWSAttachmentDownloads *attachmentDownloads;
// This property is configured after Environment is created. // This property is configured after Environment is created.
@property (atomic, nullable) id<OWSCallMessageHandler> callMessageHandler; @property (atomic, nullable) id<OWSCallMessageHandler> callMessageHandler;

@ -35,6 +35,7 @@ static SSKEnvironment *sharedSSKEnvironment;
@property (nonatomic) id<OWSSyncManagerProtocol> syncManager; @property (nonatomic) id<OWSSyncManagerProtocol> syncManager;
@property (nonatomic) id<SSKReachabilityManager> reachabilityManager; @property (nonatomic) id<SSKReachabilityManager> reachabilityManager;
@property (nonatomic) id<OWSTypingIndicators> typingIndicators; @property (nonatomic) id<OWSTypingIndicators> typingIndicators;
@property (nonatomic) OWSAttachmentDownloads *attachmentDownloads;
@end @end
@ -73,6 +74,7 @@ static SSKEnvironment *sharedSSKEnvironment;
reachabilityManager:(id<SSKReachabilityManager>)reachabilityManager reachabilityManager:(id<SSKReachabilityManager>)reachabilityManager
syncManager:(id<OWSSyncManagerProtocol>)syncManager syncManager:(id<OWSSyncManagerProtocol>)syncManager
typingIndicators:(id<OWSTypingIndicators>)typingIndicators typingIndicators:(id<OWSTypingIndicators>)typingIndicators
attachmentDownloads:(OWSAttachmentDownloads *)attachmentDownloads
{ {
self = [super init]; self = [super init];
if (!self) { if (!self) {
@ -103,6 +105,7 @@ static SSKEnvironment *sharedSSKEnvironment;
OWSAssertDebug(syncManager); OWSAssertDebug(syncManager);
OWSAssertDebug(reachabilityManager); OWSAssertDebug(reachabilityManager);
OWSAssertDebug(typingIndicators); OWSAssertDebug(typingIndicators);
OWSAssertDebug(attachmentDownloads);
_contactsManager = contactsManager; _contactsManager = contactsManager;
_messageSender = messageSender; _messageSender = messageSender;
@ -128,6 +131,7 @@ static SSKEnvironment *sharedSSKEnvironment;
_syncManager = syncManager; _syncManager = syncManager;
_reachabilityManager = reachabilityManager; _reachabilityManager = reachabilityManager;
_typingIndicators = typingIndicators; _typingIndicators = typingIndicators;
_attachmentDownloads = attachmentDownloads;
return self; return self;
} }

@ -5,6 +5,7 @@
#import "MockSSKEnvironment.h" #import "MockSSKEnvironment.h"
#import "ContactDiscoveryService.h" #import "ContactDiscoveryService.h"
#import "OWS2FAManager.h" #import "OWS2FAManager.h"
#import "OWSAttachmentDownloads.h"
#import "OWSBatchMessageProcessor.h" #import "OWSBatchMessageProcessor.h"
#import "OWSBlockingManager.h" #import "OWSBlockingManager.h"
#import "OWSDisappearingMessagesJob.h" #import "OWSDisappearingMessagesJob.h"
@ -76,6 +77,7 @@ NS_ASSUME_NONNULL_BEGIN
id<SSKReachabilityManager> reachabilityManager = [SSKReachabilityManagerImpl new]; id<SSKReachabilityManager> reachabilityManager = [SSKReachabilityManagerImpl new];
id<OWSSyncManagerProtocol> syncManager = [[OWSMockSyncManager alloc] init]; id<OWSSyncManagerProtocol> syncManager = [[OWSMockSyncManager alloc] init];
id<OWSTypingIndicators> typingIndicators = [[OWSTypingIndicatorsImpl alloc] init]; id<OWSTypingIndicators> typingIndicators = [[OWSTypingIndicatorsImpl alloc] init];
OWSAttachmentDownloads *attachmentDownloads = [[OWSAttachmentDownloads alloc] init];
self = [super initWithContactsManager:contactsManager self = [super initWithContactsManager:contactsManager
messageSender:messageSender messageSender:messageSender
@ -100,7 +102,8 @@ NS_ASSUME_NONNULL_BEGIN
outgoingReceiptManager:outgoingReceiptManager outgoingReceiptManager:outgoingReceiptManager
reachabilityManager:reachabilityManager reachabilityManager:reachabilityManager
syncManager:syncManager syncManager:syncManager
typingIndicators:typingIndicators]; typingIndicators:typingIndicators
attachmentDownloads:attachmentDownloads];
if (!self) { if (!self) {
return nil; return nil;

Loading…
Cancel
Save