From b87079d4b4550b807779777f001e554c93cdbc6f Mon Sep 17 00:00:00 2001 From: sdkjfhsdkjhfsdlkjhfsdf Date: Wed, 20 Dec 2017 10:10:37 -0600 Subject: [PATCH 1/2] Sharing attachment shows progress / retry dialog // FREEBIE --- .../ConversationViewController.m | 12 +- .../ViewControllers/DebugUI/DebugUIMessages.m | 10 +- .../translations/en.lproj/Localizable.strings | 12 ++ .../AttachmentApprovalViewController.swift | 25 ++- .../SharingThreadPickerViewController.m | 151 ++++++++++++++++-- SignalMessaging/utils/ThreadUtil.h | 6 +- SignalMessaging/utils/ThreadUtil.m | 18 ++- 7 files changed, 200 insertions(+), 34 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 75b3096f3..1d13ae902 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2705,8 +2705,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { (unsigned long)[attachment dataLength], [attachment mimeType]); BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - TSOutgoingMessage *message = - [ThreadUtil sendMessageWithAttachment:attachment inThread:self.thread messageSender:self.messageSender]; + TSOutgoingMessage *message = [ThreadUtil sendMessageWithAttachment:attachment + inThread:self.thread + messageSender:self.messageSender + completion:nil]; [self messageWasSent:message]; @@ -3864,8 +3866,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:text]; SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI]; - message = - [ThreadUtil sendMessageWithAttachment:attachment inThread:self.thread messageSender:self.messageSender]; + message = [ThreadUtil sendMessageWithAttachment:attachment + inThread:self.thread + messageSender:self.messageSender + completion:nil]; } else { message = [ThreadUtil sendMessageWithText:text inThread:self.thread messageSender:self.messageSender]; } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index a7b5f7e42..99657f5c6 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -390,7 +390,7 @@ NS_ASSUME_NONNULL_BEGIN [DDLog flushLog]; } OWSAssert(![attachment hasError]); - [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender]; + [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender completion:nil]; success(); } @@ -648,7 +648,7 @@ NS_ASSUME_NONNULL_BEGIN DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:message]; SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI]; - [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender]; + [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender completion:nil]; } + (NSData *)createRandomNSDataOfSize:(size_t)size @@ -686,7 +686,11 @@ NS_ASSUME_NONNULL_BEGIN // style them indistinguishably from a separate text message. attachment.captionText = [self randomCaptionText]; } - [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender ignoreErrors:YES]; + [ThreadUtil sendMessageWithAttachment:attachment + inThread:thread + messageSender:messageSender + ignoreErrors:YES + completion:nil]; } + (OWSSignalServiceProtosEnvelope *)createEnvelopeForThread:(TSThread *)thread { diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index aa6251756..be3ebffb1 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -106,6 +106,9 @@ /* Attachment error message for image attachments which could not be converted to JPEG */ "ATTACHMENT_ERROR_COULD_NOT_CONVERT_TO_JPEG" = "Image attachment could not be resized."; +/* Attachment error message for video attachments which could not be converted to MP4 */ +"ATTACHMENT_ERROR_COULD_NOT_CONVERT_TO_MP4" = "Unable to process video."; + /* Attachment error message for image attachments which cannot be parsed */ "ATTACHMENT_ERROR_COULD_NOT_PARSE_IMAGE" = "Image attachment could not be parsed."; @@ -1144,6 +1147,9 @@ /* Label for 'Work FAX' phone numbers. */ "PHONE_NUMBER_TYPE_WORK_FAX" = "Work Fax"; +/* accessability label for button to start media playback */ +"PLAY_BUTTON_ACCESSABILITY_LABEL" = "Play Media"; + /* Label indicating that the user is not verified. Embeds {{the user's name or phone number}}. */ "PRIVACY_IDENTITY_IS_NOT_VERIFIED_FORMAT" = "You have not marked %@ as verified."; @@ -1549,6 +1555,12 @@ /* Title indicating that the share extension cannot be used until the main app has been launched at least once. */ "SHARE_EXTENSION_NOT_YET_MIGRATED_TITLE" = "Not Ready"; +/* Alert title */ +"SHARE_EXTENSION_SENDING_FAILURE_TITLE" = "Unable to Send Attachment"; + +/* Alert title */ +"SHARE_EXTENSION_SENDING_IN_PROGRESS_TITLE" = "Uploading…"; + /* Shown when trying to share content to a Signal user for the share extension. Followed by failure details. */ "SHARE_EXTENSION_UNABLE_TO_BUILD_ATTACHMENT_ALERT_TITLE" = "Unable to Prepare Attachment"; diff --git a/SignalMessaging/attachments/AttachmentApprovalViewController.swift b/SignalMessaging/attachments/AttachmentApprovalViewController.swift index 2e8286e18..06c2d2dcd 100644 --- a/SignalMessaging/attachments/AttachmentApprovalViewController.swift +++ b/SignalMessaging/attachments/AttachmentApprovalViewController.swift @@ -7,8 +7,8 @@ import MediaPlayer @objc public protocol AttachmentApprovalViewControllerDelegate: class { - func didApproveAttachment(attachment: SignalAttachment) - func didCancelAttachment(attachment: SignalAttachment) + func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didApproveAttachment attachment: SignalAttachment) + func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didCancelAttachment attachment: SignalAttachment) } @objc @@ -200,7 +200,7 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool } func cancelPressed(sender: UIButton) { - self.delegate?.didCancelAttachment(attachment: attachment) + self.delegate?.attachmentApproval(self, didCancelAttachment: attachment) } // MARK: CaptioningToolbarDelegate @@ -214,7 +214,7 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool } func captioningToolbarDidTapSend(_ captioningToolbar: CaptioningToolbar, captionText: String?) { - self.sendAttachment(captionText: captionText) + self.approveAttachment(captionText: captionText) } func captioningToolbar(_ captioningToolbar: CaptioningToolbar, didChangeTextViewHeight newHeight: CGFloat) { @@ -227,20 +227,15 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool return attachment.isImage || attachment.isVideo } - private func sendAttachment(captionText: String?) { - // disable controls after send was tapped. + private func approveAttachment(captionText: String?) { + // Toolbar flickers in and out if there are errors + // and remains visible momentarily after share extension is dismissed. + // It's easiest to just hide it at this point since we're done with it. self.bottomToolbar.isUserInteractionEnabled = false - - // FIXME - // this is just a temporary hack to provide some UI - // until we have a proper progress indicator - let activityIndicatorView = UIActivityIndicatorView() - view.addSubview(activityIndicatorView) - activityIndicatorView.autoCenterInSuperview() - activityIndicatorView.startAnimating() + self.bottomToolbar.isHidden = true attachment.captionText = captionText - self.delegate?.didApproveAttachment(attachment: attachment) + self.delegate?.attachmentApproval(self, didApproveAttachment: attachment) } // When the keyboard is popped, it can obscure the attachment view. diff --git a/SignalMessaging/attachments/SharingThreadPickerViewController.m b/SignalMessaging/attachments/SharingThreadPickerViewController.m index 4cb006518..83990859c 100644 --- a/SignalMessaging/attachments/SharingThreadPickerViewController.m +++ b/SignalMessaging/attachments/SharingThreadPickerViewController.m @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic) TSThread *thread; @property (nonatomic, readonly, weak) id shareViewDelegate; +@property (nonatomic, readonly) UIProgressView *progressView; +@property (nullable, atomic) TSOutgoingMessage *outgoingMessage; @end @@ -50,9 +52,20 @@ NS_ASSUME_NONNULL_BEGIN _contactsManager = [Environment current].contactsManager; _messageSender = [Environment current].messageSender; + _progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; self.title = NSLocalizedString(@"SEND_EXTERNAL_FILE_VIEW_TITLE", @"Title for the 'send external file' view."); } +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(attachmentUploadProgress:) + name:kAttachmentUploadProgressNotification + object:nil]; +} + - (BOOL)canSelectBlockedContact { return NO; @@ -150,22 +163,142 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - AttachmentApprovalViewControllerDelegate -- (void)didApproveAttachmentWithAttachment:(SignalAttachment *)attachment +- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval + didApproveAttachment:(SignalAttachment *)attachment { [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - [ThreadUtil sendMessageWithAttachment:self.attachment inThread:self.thread messageSender:self.messageSender]; + [self tryToSendAttachment:attachment fromViewController:attachmentApproval]; +} + +- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval + didCancelAttachment:(SignalAttachment *)attachment +{ + [self cancelShareExperience]; +} + +#pragma mark - Helpers - // This is just a temporary hack while testing to hopefully not dismiss too early. - // FIXME Show progress dialog - // FIXME don't dismiss until sending is complete - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ +- (void)tryToSendAttachment:(SignalAttachment *)attachment fromViewController:(UIViewController *)fromViewController +{ + // Reset progress in case we're retrying + self.progressView.progress = 0; + + self.attachment = attachment; + + NSString *progressTitle = NSLocalizedString(@"SHARE_EXTENSION_SENDING_IN_PROGRESS_TITLE", @"Alert title"); + UIAlertController *progressAlert = [UIAlertController alertControllerWithTitle:progressTitle + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *progressCancelAction = [UIAlertAction actionWithTitle:[CommonStrings cancelButton] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + [self.shareViewDelegate shareViewWasCancelled]; + }]; + [progressAlert addAction:progressCancelAction]; + + + // Adding a subview to the alert controller like this is a total hack. + // ...but it looks good, and given how short a progress view is and how + // little the alert controller changes, I'm not super worried about it. +#ifdef DEBUG + if (@available(iOS 12, *)) { + // Congratulations! You survived to see another iOS release. + OWSFail(@"Make sure progress view still looks good increment this version canary."); + } +#endif + [progressAlert.view addSubview:self.progressView]; + [self.progressView autoPinWidthToSuperviewWithMargin:24]; + [self.progressView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:progressAlert.view withOffset:4]; + + void (^presentRetryDialog)(NSError *error) = ^(NSError *error) { + [fromViewController + dismissViewControllerAnimated:YES + completion:^(void) { + AssertIsOnMainThread(); + NSString *failureTitle + = NSLocalizedString(@"SHARE_EXTENSION_SENDING_FAILURE_TITLE", @"Alert title"); + + UIAlertController *failureAlert = + [UIAlertController alertControllerWithTitle:failureTitle + message:error.localizedDescription + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *failureCancelAction = + [UIAlertAction actionWithTitle:[CommonStrings cancelButton] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + [self.shareViewDelegate shareViewWasCancelled]; + }]; + [failureAlert addAction:failureCancelAction]; + + UIAlertAction *retryAction = + [UIAlertAction actionWithTitle:[CommonStrings retryButton] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [self tryToSendAttachment:attachment + fromViewController:fromViewController]; + }]; + [failureAlert addAction:retryAction]; + + [fromViewController presentViewController:failureAlert animated:YES completion:nil]; + }]; + }; + + void (^sendCompletion)(NSError *_Nullable) = ^(NSError *_Nullable error) { + AssertIsOnMainThread(); + + if (error) { + DDLogInfo(@"%@ Sending attachment failed with error: %@", self.logTag, error); + presentRetryDialog(error); + return; + } + + DDLogInfo(@"%@ Sending attachment succeeded.", self.logTag); [self.shareViewDelegate shareViewWasCompleted]; - }); + }; + + [fromViewController presentViewController:progressAlert + animated:YES + completion:^(void) { + TSOutgoingMessage *outgoingMessage = + [ThreadUtil sendMessageWithAttachment:self.attachment + inThread:self.thread + messageSender:self.messageSender + completion:sendCompletion]; + + self.outgoingMessage = outgoingMessage; + }]; } -- (void)didCancelAttachmentWithAttachment:(SignalAttachment *)attachment +- (void)attachmentUploadProgress:(NSNotification *)notification { - [self cancelShareExperience]; + DDLogDebug(@"%@ upload progress.", self.logTag); + AssertIsOnMainThread(); + OWSAssert(self.progressView); + + if (!self.outgoingMessage) { + DDLogDebug(@"%@ Ignoring upload progress until there is an outgoing message.", self.logTag); + return; + } + + NSString *attachmentRecordId = self.outgoingMessage.attachmentIds.firstObject; + if (!attachmentRecordId) { + DDLogDebug(@"%@ Ignoring upload progress until outgoing message has an attachment record id", self.logTag); + return; + } + + NSDictionary *userinfo = [notification userInfo]; + float progress = [[userinfo objectForKey:kAttachmentUploadProgressKey] floatValue]; + NSString *attachmentID = [userinfo objectForKey:kAttachmentUploadAttachmentIDKey]; + + if ([attachmentRecordId isEqual:attachmentID]) { + if (!isnan(progress)) { + [self.progressView setProgress:progress animated:YES]; + } else { + OWSFail(@"%@ Invalid attachment progress.", self.logTag); + } + } } @end diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 7eba155f7..0cca7b2b1 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -59,13 +59,15 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)sendMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread - messageSender:(OWSMessageSender *)messageSender; + messageSender:(OWSMessageSender *)messageSender + completion:(void (^_Nullable)(NSError *_Nullable error))completion; // We only should set ignoreErrors in debug or test code. + (TSOutgoingMessage *)sendMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread messageSender:(OWSMessageSender *)messageSender - ignoreErrors:(BOOL)ignoreErrors; + ignoreErrors:(BOOL)ignoreErrors + completion:(void (^_Nullable)(NSError *_Nullable error))completion; // This method will create and/or remove any offers and indicators // necessary for this thread. This includes: diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index acd384bc6..26c127ffc 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -97,14 +97,20 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)sendMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread messageSender:(OWSMessageSender *)messageSender + completion:(void (^_Nullable)(NSError *_Nullable error))completion { - return [self sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender ignoreErrors:NO]; + return [self sendMessageWithAttachment:attachment + inThread:thread + messageSender:messageSender + ignoreErrors:NO + completion:completion]; } + (TSOutgoingMessage *)sendMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread messageSender:(OWSMessageSender *)messageSender ignoreErrors:(BOOL)ignoreErrors + completion:(void (^_Nullable)(NSError *_Nullable error))completion { OWSAssertIsOnMainThread(); OWSAssert(attachment); @@ -128,9 +134,19 @@ NS_ASSUME_NONNULL_BEGIN inMessage:message success:^{ DDLogDebug(@"%@ Successfully sent message attachment.", self.logTag); + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + completion(nil); + }); + } } failure:^(NSError *error) { DDLogError(@"%@ Failed to send message attachment with error: %@", self.logTag, error); + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + completion(error); + }); + } }]; return message; From 01fa3c89cc587b3e002d702ac0cf2c0b09045ef4 Mon Sep 17 00:00:00 2001 From: sdkjfhsdkjhfsdlkjhfsdf Date: Thu, 21 Dec 2017 10:36:42 -0600 Subject: [PATCH 2/2] CR: cleanup comments, extract callback into method // FREEBIE --- .../SharingThreadPickerViewController.m | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/SignalMessaging/attachments/SharingThreadPickerViewController.m b/SignalMessaging/attachments/SharingThreadPickerViewController.m index 83990859c..60dc64f78 100644 --- a/SignalMessaging/attachments/SharingThreadPickerViewController.m +++ b/SignalMessaging/attachments/SharingThreadPickerViewController.m @@ -198,59 +198,31 @@ NS_ASSUME_NONNULL_BEGIN [progressAlert addAction:progressCancelAction]; - // Adding a subview to the alert controller like this is a total hack. + // We add a progress subview to an AlertController, which is a total hack. // ...but it looks good, and given how short a progress view is and how // little the alert controller changes, I'm not super worried about it. + [progressAlert.view addSubview:self.progressView]; + [self.progressView autoPinWidthToSuperviewWithMargin:24]; + [self.progressView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:progressAlert.view withOffset:4]; #ifdef DEBUG if (@available(iOS 12, *)) { - // Congratulations! You survived to see another iOS release. - OWSFail(@"Make sure progress view still looks good increment this version canary."); + // TODO: Congratulations! You survived to see another iOS release. + OWSFail(@"Make sure the progress view still looks good, and increment the version canary."); } #endif - [progressAlert.view addSubview:self.progressView]; - [self.progressView autoPinWidthToSuperviewWithMargin:24]; - [self.progressView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:progressAlert.view withOffset:4]; - - void (^presentRetryDialog)(NSError *error) = ^(NSError *error) { - [fromViewController - dismissViewControllerAnimated:YES - completion:^(void) { - AssertIsOnMainThread(); - NSString *failureTitle - = NSLocalizedString(@"SHARE_EXTENSION_SENDING_FAILURE_TITLE", @"Alert title"); - - UIAlertController *failureAlert = - [UIAlertController alertControllerWithTitle:failureTitle - message:error.localizedDescription - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *failureCancelAction = - [UIAlertAction actionWithTitle:[CommonStrings cancelButton] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *_Nonnull action) { - [self.shareViewDelegate shareViewWasCancelled]; - }]; - [failureAlert addAction:failureCancelAction]; - - UIAlertAction *retryAction = - [UIAlertAction actionWithTitle:[CommonStrings retryButton] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [self tryToSendAttachment:attachment - fromViewController:fromViewController]; - }]; - [failureAlert addAction:retryAction]; - - [fromViewController presentViewController:failureAlert animated:YES completion:nil]; - }]; - }; void (^sendCompletion)(NSError *_Nullable) = ^(NSError *_Nullable error) { AssertIsOnMainThread(); if (error) { - DDLogInfo(@"%@ Sending attachment failed with error: %@", self.logTag, error); - presentRetryDialog(error); + [fromViewController + dismissViewControllerAnimated:YES + completion:^(void) { + DDLogInfo(@"%@ Sending attachment failed with error: %@", self.logTag, error); + [self showSendFailureAlertWithError:error + attachment:attachment + fromViewController:fromViewController]; + }]; return; } @@ -271,6 +243,36 @@ NS_ASSUME_NONNULL_BEGIN }]; } +- (void)showSendFailureAlertWithError:(NSError *)error + attachment:(SignalAttachment *)attachment + fromViewController:(UIViewController *)fromViewController +{ + AssertIsOnMainThread(); + + NSString *failureTitle = NSLocalizedString(@"SHARE_EXTENSION_SENDING_FAILURE_TITLE", @"Alert title"); + + UIAlertController *failureAlert = [UIAlertController alertControllerWithTitle:failureTitle + message:error.localizedDescription + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *failureCancelAction = [UIAlertAction actionWithTitle:[CommonStrings cancelButton] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + [self.shareViewDelegate shareViewWasCancelled]; + }]; + [failureAlert addAction:failureCancelAction]; + + UIAlertAction *retryAction = + [UIAlertAction actionWithTitle:[CommonStrings retryButton] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [self tryToSendAttachment:attachment fromViewController:fromViewController]; + }]; + [failureAlert addAction:retryAction]; + + [fromViewController presentViewController:failureAlert animated:YES completion:nil]; +} + - (void)attachmentUploadProgress:(NSNotification *)notification { DDLogDebug(@"%@ upload progress.", self.logTag); @@ -282,7 +284,7 @@ NS_ASSUME_NONNULL_BEGIN return; } - NSString *attachmentRecordId = self.outgoingMessage.attachmentIds.firstObject; + NSString *_Nullable attachmentRecordId = self.outgoingMessage.attachmentIds.firstObject; if (!attachmentRecordId) { DDLogDebug(@"%@ Ignoring upload progress until outgoing message has an attachment record id", self.logTag); return;