Sharing attachment shows progress / retry dialog

// FREEBIE
This commit is contained in:
sdkjfhsdkjhfsdlkjhfsdf 2017-12-20 10:10:37 -06:00
parent 37ee9f0e7f
commit b87079d4b4
7 changed files with 201 additions and 35 deletions

View File

@ -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];
}

View File

@ -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
{

View File

@ -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";

View File

@ -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.

View File

@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic) TSThread *thread;
@property (nonatomic, readonly, weak) id<ShareViewDelegate> 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,24 +163,144 @@ 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];
// 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(), ^{
[self.shareViewDelegate shareViewWasCompleted];
});
[self tryToSendAttachment:attachment fromViewController:attachmentApproval];
}
- (void)didCancelAttachmentWithAttachment:(SignalAttachment *)attachment
- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval
didCancelAttachment:(SignalAttachment *)attachment
{
[self cancelShareExperience];
}
#pragma mark - Helpers
- (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)attachmentUploadProgress:(NSNotification *)notification
{
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
NS_ASSUME_NONNULL_END

View File

@ -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:

View File

@ -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;