Sharing attachment shows progress / retry dialog
// FREEBIE
This commit is contained in:
parent
37ee9f0e7f
commit
b87079d4b4
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue