From c180d20dcd879458445f725b180e161d0c9bb859 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 09:36:07 -0400 Subject: [PATCH 01/11] Store media size from attachment pointer protos. --- .../ViewControllers/DebugUI/DebugUIMessages.m | 6 +++-- .../Attachments/TSAttachmentPointer.h | 7 ++++-- .../Attachments/TSAttachmentPointer.m | 23 +++++++++++++++++-- .../Messages/Attachments/TSAttachmentStream.m | 13 +++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 660c0362e..acf3440cf 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -3802,7 +3802,8 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac sourceFilename:@"test.mp3" caption:nil albumMessageId:nil - attachmentType:TSAttachmentTypeDefault]; + attachmentType:TSAttachmentTypeDefault + mediaSize:CGSizeZero]; pointer.state = TSAttachmentPointerStateFailed; [pointer saveWithTransaction:transaction]; // MJK - should be safe to remove this senderTimestamp @@ -4701,7 +4702,8 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac sourceFilename:fakeAssetLoader.filename caption:nil albumMessageId:nil - attachmentType:TSAttachmentTypeDefault]; + attachmentType:TSAttachmentTypeDefault + mediaSize:CGSizeZero]; attachmentPointer.state = TSAttachmentPointerStateFailed; [attachmentPointer saveWithTransaction:transaction]; return attachmentPointer; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h index a4a19bd28..9090c9cf8 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "TSAttachment.h" @@ -36,6 +36,8 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) { // messages received from other clients @property (nullable, nonatomic, readonly) NSData *digest; +@property (nonatomic, readonly) CGSize mediaSize; + // Non-nil for attachments which need "lazy backup restore." - (nullable OWSBackupFragment *)lazyRestoreFragment; @@ -49,7 +51,8 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) { sourceFilename:(nullable NSString *)sourceFilename caption:(nullable NSString *)caption albumMessageId:(nullable NSString *)albumMessageId - attachmentType:(TSAttachmentType)attachmentType NS_DESIGNATED_INITIALIZER; + attachmentType:(TSAttachmentType)attachmentType + mediaSize:(CGSize)mediaSize NS_DESIGNATED_INITIALIZER; - (instancetype)initForRestoreWithAttachmentStream:(TSAttachmentStream *)attachmentStream NS_DESIGNATED_INITIALIZER; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m index bcf7aef20..e967ed40d 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "TSAttachmentPointer.h" @@ -12,6 +12,15 @@ NS_ASSUME_NONNULL_BEGIN +@interface TSAttachmentStream (TSAttachmentPointer) + +- (CGSize)cachedImageSize; + +@end + +#pragma mark - + + @interface TSAttachmentPointer () // Optional property. Only set for attachments which need "lazy backup restore." @@ -53,6 +62,7 @@ NS_ASSUME_NONNULL_BEGIN caption:(nullable NSString *)caption albumMessageId:(nullable NSString *)albumMessageId attachmentType:(TSAttachmentType)attachmentType + mediaSize:(CGSize)mediaSize { self = [super initWithServerId:serverId encryptionKey:key @@ -69,6 +79,7 @@ NS_ASSUME_NONNULL_BEGIN _state = TSAttachmentPointerStateEnqueued; self.attachmentType = attachmentType; _pointerType = TSAttachmentPointerTypeIncoming; + _mediaSize = mediaSize; return self; } @@ -89,6 +100,7 @@ NS_ASSUME_NONNULL_BEGIN _state = TSAttachmentPointerStateEnqueued; self.attachmentType = attachmentStream.attachmentType; _pointerType = TSAttachmentPointerTypeRestoring; + _mediaSize = (attachmentStream.shouldHaveImageSize ? attachmentStream.cachedImageSize : CGSizeZero); return self; } @@ -129,6 +141,12 @@ NS_ASSUME_NONNULL_BEGIN albumMessageId = albumMessage.uniqueId; } + CGSize mediaSize = CGSizeZero; + if (attachmentProto.hasWidth && attachmentProto.hasHeight && attachmentProto.width > 0 + && attachmentProto.height > 0) { + mediaSize = CGSizeMake(attachmentProto.width, attachmentProto.height); + } + TSAttachmentPointer *pointer = [[TSAttachmentPointer alloc] initWithServerId:attachmentProto.id key:attachmentProto.key digest:digest @@ -137,7 +155,8 @@ NS_ASSUME_NONNULL_BEGIN sourceFilename:attachmentProto.fileName caption:caption albumMessageId:albumMessageId - attachmentType:attachmentType]; + attachmentType:attachmentType + mediaSize:mediaSize]; return pointer; } diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index 9109cd4e3..bb0384d8d 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -526,6 +526,19 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail); } } +- (CGSize)cachedImageSize +{ + OWSAssertDebug(self.shouldHaveImageSize); + + @synchronized(self) { + if (self.cachedImageWidth && self.cachedImageHeight) { + return CGSizeMake(self.cachedImageWidth.floatValue, self.cachedImageHeight.floatValue); + } else { + return CGSizeZero; + } + } +} + #pragma mark - Update With... - (void)applyChangeAsyncToLatestCopyWithChangeBlock:(void (^)(TSAttachmentStream *))changeBlock From 14e7274c3d2eb4d66f6acbbd42130118dfdda6dd Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 09:47:04 -0400 Subject: [PATCH 02/11] Ensure constant bubble sizes for visual media. --- .../ConversationView/Cells/OWSMessageBubbleView.m | 3 +-- .../ConversationView/ConversationViewItem.m | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 0f2f3ef01..db8bf8a08 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -1027,8 +1027,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes if (self.viewItem.mediaAlbumItems.count == 1) { // Honor the content aspect ratio for single media. ConversationMediaAlbumItem *mediaAlbumItem = self.viewItem.mediaAlbumItems.firstObject; - if (mediaAlbumItem.attachmentStream && mediaAlbumItem.mediaSize.width > 0 - && mediaAlbumItem.mediaSize.height > 0) { + if (mediaAlbumItem.mediaSize.width > 0 && mediaAlbumItem.mediaSize.height > 0) { CGSize mediaSize = mediaAlbumItem.mediaSize; CGFloat contentAspectRatio = mediaSize.width / mediaSize.height; // Clamp the aspect ratio so that very thin/wide content is presented diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 8decd7c26..caf782ef5 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -704,10 +704,15 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) : nil); if (![attachment isKindOfClass:[TSAttachmentStream class]]) { + TSAttachmentPointer *attachmentPointer = (TSAttachmentPointer *)attachment; + CGSize mediaSize = CGSizeZero; + if (attachmentPointer.mediaSize.width > 0 && attachmentPointer.mediaSize.height > 0) { + mediaSize = attachmentPointer.mediaSize; + } [mediaAlbumItems addObject:[[ConversationMediaAlbumItem alloc] initWithAttachment:attachment attachmentStream:nil caption:caption - mediaSize:CGSizeZero]]; + mediaSize:mediaSize]]; continue; } TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment; From fed4899c8db6587eab3a7657a2ba3ecce6bc0102 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 10:01:57 -0400 Subject: [PATCH 03/11] Handle incoming attachments with missing MIME type. --- .../Messages/Attachments/TSAttachmentPointer.m | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m index e967ed40d..da2684f01 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m @@ -116,9 +116,17 @@ NS_ASSUME_NONNULL_BEGIN OWSFailDebug(@"Invalid attachment key."); return nil; } - if (attachmentProto.contentType.length < 1) { - OWSFailDebug(@"Invalid attachment content type."); - return nil; + NSString *_Nullable fileName = attachmentProto.fileName; + NSString *_Nullable contentType = attachmentProto.contentType; + if (contentType.length < 1) { + OWSLogError(@"Invalid attachment content type."); + NSString *_Nullable fileExtension = [fileName pathExtension].lowercaseString; + if (fileExtension.length > 0) { + contentType = [MIMETypeUtil mimeTypeForFileExtension:fileExtension]; + } + if (contentType.length < 1) { + contentType = OWSMimeTypeApplicationOctetStream; + } } // digest will be empty for old clients. @@ -151,8 +159,8 @@ NS_ASSUME_NONNULL_BEGIN key:attachmentProto.key digest:digest byteCount:attachmentProto.size - contentType:attachmentProto.contentType - sourceFilename:attachmentProto.fileName + contentType:contentType + sourceFilename:fileName caption:caption albumMessageId:albumMessageId attachmentType:attachmentType From d1447d0730c6b1c7da09c29576cd8c50b1a29ece Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 11:39:21 -0400 Subject: [PATCH 04/11] Ensure constant bubble sizes for audio media. --- .../Cells/OWSAudioMessageView.h | 6 +- .../Cells/OWSAudioMessageView.m | 22 +++--- .../Cells/OWSMessageBubbleView.m | 69 ++++++++----------- .../ConversationView/ConversationViewItem.m | 8 ++- 4 files changed, 50 insertions(+), 55 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h index f7be8be2e..1c6d2d573 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h @@ -1,17 +1,17 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @class ConversationStyle; -@class TSAttachmentStream; +@class TSAttachment; @protocol ConversationViewItem; @interface OWSAudioMessageView : UIStackView -- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream +- (instancetype)initWithAttachment:(TSAttachment *)attachment isIncoming:(BOOL)isIncoming viewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m index f06f9a2ea..358428aa4 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSAudioMessageView.h" @@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSAudioMessageView () -@property (nonatomic) TSAttachmentStream *attachmentStream; +@property (nonatomic) TSAttachment *attachment; +@property (nonatomic, nullable) TSAttachmentStream *attachmentStream; @property (nonatomic) BOOL isIncoming; @property (nonatomic, weak) id viewItem; @property (nonatomic, readonly) ConversationStyle *conversationStyle; @@ -30,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSAudioMessageView -- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream +- (instancetype)initWithAttachment:(TSAttachment *)attachment isIncoming:(BOOL)isIncoming viewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle @@ -38,7 +39,10 @@ NS_ASSUME_NONNULL_BEGIN self = [super init]; if (self) { - _attachmentStream = attachmentStream; + _attachment = attachment; + if ([attachment isKindOfClass:[TSAttachmentStream class]]) { + _attachmentStream = (TSAttachmentStream *)attachment; + } _isIncoming = isIncoming; _viewItem = viewItem; _conversationStyle = conversationStyle; @@ -66,8 +70,6 @@ NS_ASSUME_NONNULL_BEGIN - (CGFloat)audioDurationSeconds { - OWSAssertDebug(self.viewItem.audioDurationSeconds > 0.f); - return self.viewItem.audioDurationSeconds; } @@ -174,7 +176,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isVoiceMessage { - return self.attachmentStream.isVoiceMessage; + return self.attachment.isVoiceMessage; } - (void)createContents @@ -190,13 +192,13 @@ NS_ASSUME_NONNULL_BEGIN [self addArrangedSubview:self.audioPlayPauseButton]; [self.audioPlayPauseButton setContentHuggingHigh]; - NSString *filename = self.attachmentStream.sourceFilename; - if (!filename) { + NSString *_Nullable filename = self.attachment.sourceFilename; + if (filename.length < 1) { filename = [self.attachmentStream.originalFilePath lastPathComponent]; } NSString *topText = [[filename stringByDeletingPathExtension] ows_stripped]; if (topText.length < 1) { - topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].localizedUppercaseString; + topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType].localizedUppercaseString; } if (topText.length < 1) { topText = NSLocalizedString(@"GENERIC_ATTACHMENT_LABEL", @"A label for generic attachments."); diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index db8bf8a08..40d3b762c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -168,22 +168,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes return self.viewItem.displayableBodyText; } -- (nullable TSAttachmentStream *)attachmentStream -{ - // This should always be valid for the appropriate cell types. - OWSAssertDebug(self.viewItem.attachmentStream); - - return self.viewItem.attachmentStream; -} - -- (nullable TSAttachmentPointer *)attachmentPointer -{ - // This should always be valid for the appropriate cell types. - OWSAssertDebug(self.viewItem.attachmentPointer); - - return self.viewItem.attachmentPointer; -} - - (TSMessage *)message { OWSAssertDebug([self.viewItem.interaction isKindOfClass:[TSMessage class]]); @@ -276,7 +260,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_TextOnlyMessage: break; case OWSMessageCellType_Audio: - OWSAssertDebug(self.viewItem.attachmentStream); bodyMediaView = [self loadViewForAudio]; break; case OWSMessageCellType_GenericAttachment: @@ -837,10 +820,11 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes - (UIView *)loadViewForAudio { - OWSAssertDebug(self.attachmentStream); - OWSAssertDebug([self.attachmentStream isAudio]); + TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer); + OWSAssertDebug(attachment); + OWSAssertDebug([attachment isAudio]); - OWSAudioMessageView *audioMessageView = [[OWSAudioMessageView alloc] initWithAttachment:self.attachmentStream + OWSAudioMessageView *audioMessageView = [[OWSAudioMessageView alloc] initWithAttachment:attachment isIncoming:self.isIncoming viewItem:self.viewItem conversationStyle:self.conversationStyle]; @@ -861,8 +845,10 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes - (UIView *)loadViewForGenericAttachment { OWSAssertDebug(self.viewItem.attachmentStream); + + // TODO: OWSGenericAttachmentView *attachmentView = - [[OWSGenericAttachmentView alloc] initWithAttachment:self.attachmentStream isIncoming:self.isIncoming]; + [[OWSGenericAttachmentView alloc] initWithAttachment:self.viewItem.attachmentStream isIncoming:self.isIncoming]; [attachmentView createContentsWithConversationStyle:self.conversationStyle]; [self addAttachmentUploadViewIfNecessary]; @@ -878,12 +864,12 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes - (UIView *)loadViewForDownloadingAttachment { - OWSAssertDebug(self.attachmentPointer); + OWSAssertDebug(self.viewItem.attachmentPointer); // TODO: We probably want to do something different for attachments // being restored from backup. AttachmentPointerView *downloadView = - [[AttachmentPointerView alloc] initWithAttachmentPointer:self.attachmentPointer + [[AttachmentPointerView alloc] initWithAttachmentPointer:self.viewItem.attachmentPointer isIncoming:self.isIncoming conversationStyle:self.conversationStyle]; @@ -929,7 +915,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes - (void)addAttachmentUploadViewIfNecessaryWithAttachmentStateCallback: (nullable AttachmentStateBlock)attachmentStateCallback { - OWSAssertDebug(self.attachmentStream); + if (!self.viewItem.attachmentStream) { + return; + } if (!attachmentStateCallback) { attachmentStateCallback = ^(BOOL isAttachmentReady) { @@ -937,9 +925,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes } if (self.isOutgoing) { - if (!self.attachmentStream.isUploaded) { + if (!self.viewItem.attachmentStream.isUploaded) { AttachmentUploadView *attachmentUploadView = - [[AttachmentUploadView alloc] initWithAttachment:self.attachmentStream + [[AttachmentUploadView alloc] initWithAttachment:self.viewItem.attachmentStream attachmentStateCallback:attachmentStateCallback]; [self.bubbleView addSubview:attachmentUploadView]; [attachmentUploadView ows_autoPinToSuperviewEdges]; @@ -1007,7 +995,8 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_GenericAttachment: { OWSAssertDebug(self.viewItem.attachmentStream); OWSGenericAttachmentView *attachmentView = - [[OWSGenericAttachmentView alloc] initWithAttachment:self.attachmentStream isIncoming:self.isIncoming]; + [[OWSGenericAttachmentView alloc] initWithAttachment:self.viewItem.attachmentStream + isIncoming:self.isIncoming]; [attachmentView createContentsWithConversationStyle:self.conversationStyle]; result = [attachmentView measureSizeWithMaxMessageWidth:maxMessageWidth]; break; @@ -1391,29 +1380,27 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes { OWSAssertDebug(self.delegate); + if (self.viewItem.attachmentPointer && self.viewItem.attachmentPointer.state == TSAttachmentPointerStateFailed) { + [self.delegate didTapFailedIncomingAttachment:self.viewItem]; + return; + } + switch (self.cellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: break; case OWSMessageCellType_Audio: - OWSAssertDebug(self.viewItem.attachmentStream); - - [self.delegate didTapAudioViewItem:self.viewItem attachmentStream:self.viewItem.attachmentStream]; + if (self.viewItem.attachmentStream) { + [self.delegate didTapAudioViewItem:self.viewItem attachmentStream:self.viewItem.attachmentStream]; + } return; case OWSMessageCellType_GenericAttachment: - OWSAssertDebug(self.viewItem.attachmentStream); - - [AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream]; - break; - case OWSMessageCellType_DownloadingAttachment: { - TSAttachmentPointer *_Nullable attachmentPointer = self.viewItem.attachmentPointer; - OWSAssertDebug(attachmentPointer); - - if (attachmentPointer.state == TSAttachmentPointerStateFailed) { - [self.delegate didTapFailedIncomingAttachment:self.viewItem]; + if (self.viewItem.attachmentStream) { + [AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream]; } break; - } + case OWSMessageCellType_DownloadingAttachment: + break; case OWSMessageCellType_ContactShare: [self.delegate didTapContactShareViewItem:self.viewItem]; break; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index caf782ef5..530a69c00 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -632,13 +632,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.audioDurationSeconds = audioDurationSeconds; self.messageCellType = OWSMessageCellType_Audio; } else { + OWSLogVerbose(@"contentType: %@", self.attachmentStream.contentType); self.messageCellType = OWSMessageCellType_GenericAttachment; } } else if (self.messageCellType == OWSMessageCellType_Unknown) { self.messageCellType = OWSMessageCellType_GenericAttachment; } } else if ([mediaAttachment isKindOfClass:[TSAttachmentPointer class]]) { - self.messageCellType = OWSMessageCellType_DownloadingAttachment; + if ([mediaAttachment isAudio]) { + self.audioDurationSeconds = 0; + self.messageCellType = OWSMessageCellType_Audio; + } else { + self.messageCellType = OWSMessageCellType_DownloadingAttachment; + } self.attachmentPointer = (TSAttachmentPointer *)mediaAttachment; } else { OWSFailDebug(@"Unknown attachment type"); From 3702dfa1980914af2218fc3dba560e9215039616 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 12:34:33 -0400 Subject: [PATCH 05/11] Rework "upload progress", "download progress" and "tap to retry" states. --- .../Cells/AttachmentUploadView.h | 7 +- .../Cells/AttachmentUploadView.m | 14 +-- .../Cells/OWSMessageBubbleView.m | 113 +++++++++++++++--- 3 files changed, 97 insertions(+), 37 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.h b/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.h index 312676e89..76c30e613 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.h @@ -1,13 +1,11 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @class TSAttachmentStream; -typedef void (^AttachmentStateBlock)(BOOL isAttachmentReady); - // This entity is used to display upload progress for outgoing // attachments in conversation view cells. // @@ -18,8 +16,7 @@ typedef void (^AttachmentStateBlock)(BOOL isAttachmentReady); // * Disable any media view controls using a callback. @interface AttachmentUploadView : UIView -- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment - attachmentStateCallback:(AttachmentStateBlock _Nullable)attachmentStateCallback; +- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment; @end diff --git a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.m b/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.m index 48fc5a3b9..b3b4a5432 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/AttachmentUploadView.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "AttachmentUploadView.h" @@ -21,8 +21,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UILabel *progressLabel; -@property (nonatomic) AttachmentStateBlock _Nullable attachmentStateCallback; - @property (nonatomic) BOOL isAttachmentReady; @property (nonatomic) CGFloat lastProgress; @@ -34,7 +32,6 @@ NS_ASSUME_NONNULL_BEGIN @implementation AttachmentUploadView - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment - attachmentStateCallback:(AttachmentStateBlock _Nullable)attachmentStateCallback { self = [super init]; @@ -42,7 +39,6 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(attachment); self.attachment = attachment; - self.attachmentStateCallback = attachmentStateCallback; [self createContents]; @@ -54,10 +50,6 @@ NS_ASSUME_NONNULL_BEGIN _isAttachmentReady = self.attachment.isUploaded; [self ensureViewState]; - - if (attachmentStateCallback) { - self.attachmentStateCallback(_isAttachmentReady); - } } return self; } @@ -110,10 +102,6 @@ NS_ASSUME_NONNULL_BEGIN _isAttachmentReady = isAttachmentReady; [self ensureViewState]; - - if (self.attachmentStateCallback) { - self.attachmentStateCallback(isAttachmentReady); - } } - (void)setLastProgress:(CGFloat)lastProgress diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 40d3b762c..fe9e77099 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -60,6 +60,15 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes @implementation OWSMessageBubbleView +#pragma mark - Dependencies + +- (OWSAttachmentDownloads *)attachmentDownloads +{ + return SSKEnvironment.shared.attachmentDownloads; +} + +#pragma mark - + - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; @@ -830,7 +839,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes conversationStyle:self.conversationStyle]; self.viewItem.lastAudioMessageView = audioMessageView; [audioMessageView createContents]; - [self addAttachmentUploadViewIfNecessary]; + [self addProgressViewsIfNecessary:audioMessageView]; self.loadCellContentBlock = ^{ // Do nothing. @@ -850,7 +859,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes OWSGenericAttachmentView *attachmentView = [[OWSGenericAttachmentView alloc] initWithAttachment:self.viewItem.attachmentStream isIncoming:self.isIncoming]; [attachmentView createContentsWithConversationStyle:self.conversationStyle]; - [self addAttachmentUploadViewIfNecessary]; + [self addProgressViewsIfNecessary:attachmentView]; self.loadCellContentBlock = ^{ // Do nothing. @@ -907,32 +916,98 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes return contactShareView; } -- (void)addAttachmentUploadViewIfNecessary +- (void)addProgressViewsIfNecessary:(UIView *)bodyMediaView { - [self addAttachmentUploadViewIfNecessaryWithAttachmentStateCallback:nil]; + if (self.viewItem.attachmentStream) { + [self addUploadViewIfNecessary:bodyMediaView]; + } else if (self.viewItem.attachmentPointer) { + [self addDownloadViewIfNecessary:bodyMediaView]; + } } -- (void)addAttachmentUploadViewIfNecessaryWithAttachmentStateCallback: - (nullable AttachmentStateBlock)attachmentStateCallback +- (void)addUploadViewIfNecessary:(UIView *)bodyMediaView { - if (!self.viewItem.attachmentStream) { + OWSAssertDebug(self.viewItem.attachmentStream); + + if (!self.isOutgoing) { return; } - - if (!attachmentStateCallback) { - attachmentStateCallback = ^(BOOL isAttachmentReady) { - }; + if (self.viewItem.attachmentStream.isUploaded) { + return; } - if (self.isOutgoing) { - if (!self.viewItem.attachmentStream.isUploaded) { - AttachmentUploadView *attachmentUploadView = - [[AttachmentUploadView alloc] initWithAttachment:self.viewItem.attachmentStream - attachmentStateCallback:attachmentStateCallback]; - [self.bubbleView addSubview:attachmentUploadView]; - [attachmentUploadView ows_autoPinToSuperviewEdges]; - } + AttachmentUploadView *uploadView = [[AttachmentUploadView alloc] initWithAttachment:self.viewItem.attachmentStream]; + [self.bubbleView addSubview:uploadView]; + [uploadView autoPinEdgesToSuperviewEdges]; + [uploadView setContentHuggingLow]; + [uploadView setCompressionResistanceLow]; +} + +- (void)addDownloadViewIfNecessary:(UIView *)bodyMediaView +{ + OWSAssertDebug(self.viewItem.attachmentPointer); + + switch (self.viewItem.attachmentPointer.state) { + case TSAttachmentPointerStateFailed: + [self addTapToRetryView:bodyMediaView]; + return; + case TSAttachmentPointerStateEnqueued: + case TSAttachmentPointerStateDownloading: + break; + } + switch (self.viewItem.attachmentPointer.pointerType) { + case TSAttachmentPointerTypeRestoring: + // TODO: Show "restoring" indicator and possibly progress. + return; + case TSAttachmentPointerTypeUnknown: + case TSAttachmentPointerTypeIncoming: + break; + } + NSString *_Nullable uniqueId = self.viewItem.attachmentPointer.uniqueId; + if (uniqueId.length < 1) { + OWSFailDebug(@"Missing uniqueId."); + return; + } + if ([self.attachmentDownloads downloadProgressForAttachmentId:uniqueId] == nil) { + OWSFailDebug(@"Missing download progress."); + return; } + + UIView *overlayView = [UIView new]; + overlayView.backgroundColor = [self.bubbleColor colorWithAlphaComponent:0.5]; + [bodyMediaView addSubview:overlayView]; + [overlayView autoPinEdgesToSuperviewEdges]; + [overlayView setContentHuggingLow]; + [overlayView setCompressionResistanceLow]; + + MediaDownloadView *downloadView = + [[MediaDownloadView alloc] initWithAttachmentId:uniqueId radius:self.conversationStyle.maxMessageWidth * 0.1f]; + bodyMediaView.layer.opacity = 0.5f; + [self.bubbleView addSubview:downloadView]; + [downloadView autoPinEdgesToSuperviewEdges]; + [downloadView setContentHuggingLow]; + [downloadView setCompressionResistanceLow]; +} + +- (void)addTapToRetryView:(UIView *)bodyMediaView +{ + OWSAssertDebug(self.viewItem.attachmentPointer); + + // Hide the body media view, replace with "tap to retry" indicator. + + UILabel *label = [UILabel new]; + label.text = NSLocalizedString( + @"ATTACHMENT_DOWNLOADING_STATUS_FAILED", @"Status label when an attachment download has failed."); + label.font = UIFont.ows_dynamicTypeCaption1Font; + label.textColor = Theme.primaryColor; + label.numberOfLines = 0; + label.lineBreakMode = NSLineBreakByWordWrapping; + label.textAlignment = NSTextAlignmentCenter; + label.backgroundColor = self.bubbleColor; + [bodyMediaView addSubview:label]; + [label autoPinEdgesToSuperviewEdges]; + [label setContentHuggingLow]; + [label setCompressionResistanceLow]; } - (void)showAttachmentErrorViewWithMediaView:(UIView *)mediaView From dc168270c2a94613fdecbf6007db195140c65585 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 12:44:02 -0400 Subject: [PATCH 06/11] Ensure constant bubble sizes for generic attachments. --- .../Cells/OWSGenericAttachmentView.h | 6 +-- .../Cells/OWSGenericAttachmentView.m | 38 ++++++++++++------- .../Cells/OWSMessageBubbleView.m | 13 +++---- .../ConversationView/ConversationViewItem.m | 2 +- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h index f3cf1bb04..350b93465 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h @@ -1,15 +1,15 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @class ConversationStyle; -@class TSAttachmentStream; +@class TSAttachment; @interface OWSGenericAttachmentView : UIStackView -- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming; +- (instancetype)initWithAttachment:(TSAttachment *)attachment isIncoming:(BOOL)isIncoming; - (void)createContentsWithConversationStyle:(ConversationStyle *)conversationStyle; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m index 29c2eea37..b4895613d 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSGenericAttachmentView.h" @@ -18,7 +18,8 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSGenericAttachmentView () -@property (nonatomic) TSAttachmentStream *attachmentStream; +@property (nonatomic) TSAttachment *attachment; +@property (nonatomic, nullable) TSAttachmentStream *attachmentStream; @property (nonatomic) BOOL isIncoming; @property (nonatomic) UILabel *topLabel; @property (nonatomic) UILabel *bottomLabel; @@ -29,12 +30,15 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSGenericAttachmentView -- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming +- (instancetype)initWithAttachment:(TSAttachment *)attachment isIncoming:(BOOL)isIncoming { self = [super init]; if (self) { - _attachmentStream = attachmentStream; + _attachment = attachment; + if ([attachment isKindOfClass:[TSAttachmentStream class]]) { + _attachmentStream = (TSAttachmentStream *)attachment; + } _isIncoming = isIncoming; } @@ -105,13 +109,13 @@ NS_ASSUME_NONNULL_BEGIN [self addArrangedSubview:imageView]; [imageView setContentHuggingHigh]; - NSString *filename = self.attachmentStream.sourceFilename; + NSString *_Nullable filename = self.attachment.sourceFilename; if (!filename) { filename = [[self.attachmentStream originalFilePath] lastPathComponent]; } NSString *fileExtension = filename.pathExtension; if (fileExtension.length < 1) { - fileExtension = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType]; + fileExtension = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType]; } UILabel *fileTypeLabel = [UILabel new]; @@ -132,9 +136,9 @@ NS_ASSUME_NONNULL_BEGIN labelsView.alignment = UIStackViewAlignmentLeading; [self addArrangedSubview:labelsView]; - NSString *topText = [self.attachmentStream.sourceFilename ows_stripped]; + NSString *topText = [self.attachment.sourceFilename ows_stripped]; if (topText.length < 1) { - topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].localizedUppercaseString; + topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType].localizedUppercaseString; } if (topText.length < 1) { topText = NSLocalizedString(@"GENERIC_ATTACHMENT_LABEL", @"A label for generic attachments."); @@ -147,12 +151,18 @@ NS_ASSUME_NONNULL_BEGIN topLabel.font = [OWSGenericAttachmentView topLabelFont]; [labelsView addArrangedSubview:topLabel]; - NSError *error; - unsigned long long fileSize = - [[NSFileManager defaultManager] attributesOfItemAtPath:[self.attachmentStream originalFilePath] error:&error] - .fileSize; - OWSAssertDebug(!error); - NSString *bottomText = [OWSFormat formatFileSize:fileSize]; + unsigned long long fileSize = 0; + if (self.attachmentStream) { + NSError *error; + fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:[self.attachmentStream originalFilePath] + error:&error] + .fileSize; + OWSAssertDebug(!error); + } + NSString *bottomText = @" "; + if (fileSize > 0) { + bottomText = [OWSFormat formatFileSize:fileSize]; + } UILabel *bottomLabel = [UILabel new]; self.bottomLabel = bottomLabel; bottomLabel.text = bottomText; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index fe9e77099..28643adae 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -853,11 +853,10 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes - (UIView *)loadViewForGenericAttachment { - OWSAssertDebug(self.viewItem.attachmentStream); - - // TODO: + TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer); + OWSAssertDebug(attachment); OWSGenericAttachmentView *attachmentView = - [[OWSGenericAttachmentView alloc] initWithAttachment:self.viewItem.attachmentStream isIncoming:self.isIncoming]; + [[OWSGenericAttachmentView alloc] initWithAttachment:attachment isIncoming:self.isIncoming]; [attachmentView createContentsWithConversationStyle:self.conversationStyle]; [self addProgressViewsIfNecessary:attachmentView]; @@ -1068,10 +1067,10 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes result = CGSizeMake(maxMessageWidth, OWSAudioMessageView.bubbleHeight); break; case OWSMessageCellType_GenericAttachment: { - OWSAssertDebug(self.viewItem.attachmentStream); + TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer); + OWSAssertDebug(attachment); OWSGenericAttachmentView *attachmentView = - [[OWSGenericAttachmentView alloc] initWithAttachment:self.viewItem.attachmentStream - isIncoming:self.isIncoming]; + [[OWSGenericAttachmentView alloc] initWithAttachment:attachment isIncoming:self.isIncoming]; [attachmentView createContentsWithConversationStyle:self.conversationStyle]; result = [attachmentView measureSizeWithMaxMessageWidth:maxMessageWidth]; break; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 530a69c00..c486ebb82 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -643,7 +643,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.audioDurationSeconds = 0; self.messageCellType = OWSMessageCellType_Audio; } else { - self.messageCellType = OWSMessageCellType_DownloadingAttachment; + self.messageCellType = OWSMessageCellType_GenericAttachment; } self.attachmentPointer = (TSAttachmentPointer *)mediaAttachment; } else { From 67c89cb4e30924cd24f71851c975404fe1768c95 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 13:21:54 -0400 Subject: [PATCH 07/11] Ensure constant bubble sizes for oversize text. --- Signal.xcodeproj/project.pbxproj | 4 - .../Cells/OWSMessageBubbleView.m | 63 ++++---- .../ConversationView/ConversationViewItem.h | 3 +- .../ConversationView/ConversationViewItem.m | 93 ++++++++--- Signal/src/views/AttachmentPointerView.swift | 145 ------------------ 5 files changed, 103 insertions(+), 205 deletions(-) delete mode 100644 Signal/src/views/AttachmentPointerView.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 5b751d7eb..0dba75b4d 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -369,7 +369,6 @@ 452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; }; 452C7CA72037628B003D51A5 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170D51E315310003FC1F2 /* Weak.swift */; }; 452D1AF12081059C00A67F7F /* StringAdditionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */; }; - 452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */; }; 452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */; }; 452EC6E1205FF5DC000E787C /* Bench.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6E0205FF5DC000E787C /* Bench.swift */; }; 452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; }; @@ -1093,7 +1092,6 @@ 452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactShareToExistingContactViewController.swift; sourceTree = ""; }; 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutboundCallInitiator.swift; sourceTree = ""; }; 452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAdditionsTest.swift; sourceTree = ""; }; - 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentPointerView.swift; sourceTree = ""; }; 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryViewController.swift; sourceTree = ""; }; 452EC6E0205FF5DC000E787C /* Bench.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bench.swift; sourceTree = ""; }; 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageFetcherJob.swift; sourceTree = ""; }; @@ -2419,7 +2417,6 @@ 76EB052B18170B33006006FC /* Views */ = { isa = PBXGroup; children = ( - 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */, 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */, 4C2F454E214C00E1004871FF /* AvatarTableViewCell.swift */, 4CA46F4B219CCC630038ABDE /* CaptionView.swift */, @@ -3602,7 +3599,6 @@ 34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */, 3448E1622213585C004B052E /* OnboardingBaseViewController.swift in Sources */, 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */, - 452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */, 45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */, 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */, 34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 28643adae..246363ea4 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -274,15 +274,16 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_GenericAttachment: bodyMediaView = [self loadViewForGenericAttachment]; break; - case OWSMessageCellType_DownloadingAttachment: - bodyMediaView = [self loadViewForDownloadingAttachment]; - break; case OWSMessageCellType_ContactShare: bodyMediaView = [self loadViewForContactShare]; break; case OWSMessageCellType_MediaMessage: bodyMediaView = [self loadViewForMediaAlbum]; break; + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + bodyMediaView = [self loadViewForOversizeTextDownload]; + break; } if (bodyMediaView) { @@ -564,8 +565,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_TextOnlyMessage: case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_DownloadingAttachment: case OWSMessageCellType_ContactShare: + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: return NO; case OWSMessageCellType_MediaMessage: return YES; @@ -579,9 +581,10 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes return NO; case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_DownloadingAttachment: case OWSMessageCellType_ContactShare: case OWSMessageCellType_MediaMessage: + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: return YES; } } @@ -870,20 +873,15 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes return attachmentView; } -- (UIView *)loadViewForDownloadingAttachment +- (UIView *)loadViewForContactShare { - OWSAssertDebug(self.viewItem.attachmentPointer); - - // TODO: We probably want to do something different for attachments - // being restored from backup. - AttachmentPointerView *downloadView = - [[AttachmentPointerView alloc] initWithAttachmentPointer:self.viewItem.attachmentPointer - isIncoming:self.isIncoming - conversationStyle:self.conversationStyle]; + OWSAssertDebug(self.viewItem.contactShare); - UIView *wrapper = [UIView new]; - [wrapper addSubview:downloadView]; - [downloadView autoPinEdgesToSuperviewEdges]; + OWSContactShareView *contactShareView = [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare + isIncoming:self.isIncoming + conversationStyle:self.conversationStyle]; + [contactShareView createContents]; + // TODO: Should we change appearance if contact avatar is uploading? self.loadCellContentBlock = ^{ // Do nothing. @@ -892,18 +890,18 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes // Do nothing. }; - return wrapper; + return contactShareView; } -- (UIView *)loadViewForContactShare +- (UIView *)loadViewForOversizeTextDownload { - OWSAssertDebug(self.viewItem.contactShare); + BOOL isFailed = self.cellType == OWSMessageCellType_OversizeTextFailed; - OWSContactShareView *contactShareView = [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare - isIncoming:self.isIncoming - conversationStyle:self.conversationStyle]; - [contactShareView createContents]; - // TODO: Should we change appearance if contact avatar is uploading? + // We can use an empty view. The progress views will display download + // progress or tap-to-retry UI. + UIView *attachmentView = [UIView new]; + + [self addProgressViewsIfNecessary:attachmentView]; self.loadCellContentBlock = ^{ // Do nothing. @@ -912,7 +910,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes // Do nothing. }; - return contactShareView; + return attachmentView; } - (void)addProgressViewsIfNecessary:(UIView *)bodyMediaView @@ -1075,9 +1073,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes result = [attachmentView measureSizeWithMaxMessageWidth:maxMessageWidth]; break; } - case OWSMessageCellType_DownloadingAttachment: - result = CGSizeMake(MIN(200, maxMessageWidth), [AttachmentPointerView measureHeight]); - break; case OWSMessageCellType_ContactShare: OWSAssertDebug(self.viewItem.contactShare); @@ -1122,6 +1117,12 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes } } break; + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + // There's no way to predict the size of the oversize text, + // so we just use a square bubble. + result = CGSizeMake(maxMessageWidth, maxMessageWidth); + break; } OWSAssertDebug(result.width <= maxMessageWidth); @@ -1462,6 +1463,8 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes switch (self.cellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: break; case OWSMessageCellType_Audio: if (self.viewItem.attachmentStream) { @@ -1473,8 +1476,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes [AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream]; } break; - case OWSMessageCellType_DownloadingAttachment: - break; case OWSMessageCellType_ContactShare: [self.delegate didTapContactShareViewItem:self.viewItem]; break; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 3ec982aeb..b82272eaa 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -12,9 +12,10 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) { OWSMessageCellType_TextOnlyMessage, OWSMessageCellType_Audio, OWSMessageCellType_GenericAttachment, - OWSMessageCellType_DownloadingAttachment, OWSMessageCellType_ContactShare, OWSMessageCellType_MediaMessage, + OWSMessageCellType_OversizeTextDownloading, + OWSMessageCellType_OversizeTextFailed, }; NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index c486ebb82..180eef15e 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -26,14 +26,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return @"OWSMessageCellType_Audio"; case OWSMessageCellType_GenericAttachment: return @"OWSMessageCellType_GenericAttachment"; - case OWSMessageCellType_DownloadingAttachment: - return @"OWSMessageCellType_DownloadingAttachment"; case OWSMessageCellType_Unknown: return @"OWSMessageCellType_Unknown"; case OWSMessageCellType_ContactShare: return @"OWSMessageCellType_ContactShare"; case OWSMessageCellType_MediaMessage: return @"OWSMessageCellType_MediaMessage"; + case OWSMessageCellType_OversizeTextDownloading: + return @"OWSMessageCellType_OversizeTextDownloading"; + case OWSMessageCellType_OversizeTextFailed: + return @"OWSMessageCellType_OversizeTextFailed"; } } @@ -591,10 +593,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } TSAttachment *_Nullable oversizeTextAttachment = [message oversizeTextAttachmentWithTransaction:transaction]; - if (oversizeTextAttachment != nil && [oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) { + if ([oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) { TSAttachmentStream *oversizeTextAttachmentStream = (TSAttachmentStream *)oversizeTextAttachment; self.displayableBodyText = [self displayableBodyTextForOversizeTextAttachment:oversizeTextAttachmentStream interactionId:message.uniqueId]; + } else if ([oversizeTextAttachment isKindOfClass:[TSAttachmentPointer class]]) { + TSAttachmentPointer *oversizeTextAttachmentPointer = (TSAttachmentPointer *)oversizeTextAttachment; + // TODO: Handle backup restore. + self.messageCellType = (oversizeTextAttachmentPointer.state == TSAttachmentPointerStateFailed + ? OWSMessageCellType_OversizeTextFailed + : OWSMessageCellType_OversizeTextDownloading); + self.attachmentPointer = (TSAttachmentPointer *)oversizeTextAttachmentPointer; + return; } else { NSString *_Nullable bodyText = [message bodyTextWithTransaction:transaction]; if (bodyText) { @@ -860,6 +870,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (void)copyTextAction { + if (self.attachmentPointer != nil) { + OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); + return; + } + switch (self.messageCellType) { case OWSMessageCellType_TextOnlyMessage: case OWSMessageCellType_Audio: @@ -869,10 +884,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [UIPasteboard.generalPasteboard setString:self.displayableBodyText.fullText]; break; } - case OWSMessageCellType_DownloadingAttachment: { - OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); - break; - } case OWSMessageCellType_Unknown: { OWSFailDebug(@"No text to copy"); break; @@ -882,11 +893,20 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSFailDebug(@"Not implemented yet"); break; } + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); + return; } } - (void)copyMediaAction { + if (self.attachmentPointer != nil) { + OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); + return; + } + switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: @@ -899,10 +919,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [self copyAttachmentToPasteboard:self.attachmentStream]; break; } - case OWSMessageCellType_DownloadingAttachment: { - OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); - break; - } case OWSMessageCellType_MediaMessage: { if (self.mediaAlbumItems.count == 1) { ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject; @@ -915,6 +931,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSFailDebug(@"Can't copy media album"); break; } + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); + return; } } @@ -937,6 +957,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (void)shareMediaAction { + if (self.attachmentPointer != nil) { + OWSFailDebug(@"Can't share not-yet-downloaded attachment"); + return; + } + switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: @@ -947,10 +972,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_GenericAttachment: [AttachmentSharing showShareUIForAttachment:self.attachmentStream]; break; - case OWSMessageCellType_DownloadingAttachment: { - OWSFailDebug(@"Can't share not-yet-downloaded attachment"); - break; - } case OWSMessageCellType_MediaMessage: { // TODO: We need a "canShareMediaAction" method. OWSAssertDebug(self.mediaAlbumItems); @@ -967,11 +988,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [AttachmentSharing showShareUIForAttachments:attachmentStreams completion:nil]; break; } + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + OWSFailDebug(@"Can't share not-yet-downloaded attachment"); + return; } } - (BOOL)canCopyMedia { + if (self.attachmentPointer != nil) { + return NO; + } + switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: @@ -980,7 +1009,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_Audio: return NO; case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_DownloadingAttachment: case OWSMessageCellType_MediaMessage: { if (self.mediaAlbumItems.count == 1) { ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject; @@ -990,11 +1018,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } return NO; } + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + return NO; } } - (BOOL)canSaveMedia { + if (self.attachmentPointer != nil) { + return NO; + } + switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: @@ -1003,7 +1038,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_Audio: return NO; case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_DownloadingAttachment: return NO; case OWSMessageCellType_MediaMessage: { for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) { @@ -1025,11 +1059,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } return NO; } + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + return NO; } } - (void)saveMediaAction { + if (self.attachmentPointer != nil) { + OWSFailDebug(@"Can't save not-yet-downloaded attachment"); + return; + } switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: @@ -1042,14 +1083,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_GenericAttachment: OWSFailDebug(@"Cannot save media data."); break; - case OWSMessageCellType_DownloadingAttachment: { - OWSFailDebug(@"Can't save not-yet-downloaded attachment"); - break; - } case OWSMessageCellType_MediaMessage: { [self saveMediaAlbumItems]; break; } + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + OWSFailDebug(@"Can't save not-yet-downloaded attachment"); + return; } } @@ -1112,6 +1153,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (BOOL)hasMediaActionContent { + if (self.attachmentPointer != nil) { + return NO; + } + switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: @@ -1120,11 +1165,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: return self.attachmentStream != nil; - case OWSMessageCellType_DownloadingAttachment: { - return NO; - } case OWSMessageCellType_MediaMessage: return self.firstValidAlbumAttachment != nil; + case OWSMessageCellType_OversizeTextDownloading: + case OWSMessageCellType_OversizeTextFailed: + return NO; } } diff --git a/Signal/src/views/AttachmentPointerView.swift b/Signal/src/views/AttachmentPointerView.swift deleted file mode 100644 index ada2e7c97..000000000 --- a/Signal/src/views/AttachmentPointerView.swift +++ /dev/null @@ -1,145 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalServiceKit -import SignalMessaging - -class AttachmentPointerView: UIStackView { - - let isIncoming: Bool - let attachmentPointer: TSAttachmentPointer - let conversationStyle: ConversationStyle - - let progressView = OWSProgressView() - let nameLabel = UILabel() - let statusLabel = UILabel() - let filename: String - let genericFilename = NSLocalizedString("ATTACHMENT_DEFAULT_FILENAME", comment: "Generic filename for an attachment with no known name") - - var progress: CGFloat = 0 { - didSet { - self.progressView.progress = progress - } - } - - @objc - required init(attachmentPointer: TSAttachmentPointer, isIncoming: Bool, conversationStyle: ConversationStyle) { - self.attachmentPointer = attachmentPointer - self.isIncoming = isIncoming - self.conversationStyle = conversationStyle - - let attachmentPointerFilename = attachmentPointer.sourceFilename - if let filename = attachmentPointerFilename, !filename.isEmpty { - self.filename = filename - } else { - self.filename = genericFilename - } - - super.init(frame: CGRect.zero) - - createSubviews() - updateViews() - - if attachmentPointer.state == .downloading { - NotificationCenter.default.addObserver(self, - selector: #selector(attachmentDownloadProgress(_:)), - name: NSNotification.Name.attachmentDownloadProgress, - object: nil) - } - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - @objc internal func attachmentDownloadProgress(_ notification: Notification) { - guard let attachmentId = attachmentPointer.uniqueId else { - owsFailDebug("Missing attachment id.") - return - } - guard let progress = (notification as NSNotification).userInfo?[kAttachmentDownloadProgressKey] as? NSNumber else { - owsFailDebug("Attachment download notification missing progress.") - return - } - guard let notificationAttachmentId = (notification as NSNotification).userInfo?[kAttachmentDownloadAttachmentIDKey] as? String else { - owsFailDebug("Attachment download notification missing attachment id.") - return - } - guard notificationAttachmentId == attachmentId else { - return - } - self.progress = CGFloat(progress.floatValue) - } - - @available(*, unavailable, message: "use init(call:) constructor instead.") - required init(coder aDecoder: NSCoder) { - notImplemented() - } - - private static var vSpacing: CGFloat = 5 - private class func nameFont() -> UIFont { return UIFont.ows_dynamicTypeBody } - private class func statusFont() -> UIFont { return UIFont.ows_dynamicTypeCaption1 } - private static var progressWidth: CGFloat = 80 - private static var progressHeight: CGFloat = 6 - - func createSubviews() { - progressView.autoSetDimension(.width, toSize: AttachmentPointerView.progressWidth) - progressView.autoSetDimension(.height, toSize: AttachmentPointerView.progressHeight) - - // truncate middle to be sure we include file extension - nameLabel.lineBreakMode = .byTruncatingMiddle - nameLabel.textAlignment = .center - nameLabel.textColor = self.textColor - nameLabel.font = AttachmentPointerView.nameFont() - - statusLabel.textAlignment = .center - statusLabel.adjustsFontSizeToFitWidth = true - statusLabel.numberOfLines = 2 - statusLabel.textColor = self.textColor - statusLabel.font = AttachmentPointerView.statusFont() - - self.axis = .vertical - self.spacing = AttachmentPointerView.vSpacing - addArrangedSubview(nameLabel) - addArrangedSubview(progressView) - addArrangedSubview(statusLabel) - } - - func updateViews() { - let emoji = TSAttachment.emoji(forMimeType: self.attachmentPointer.contentType) - nameLabel.text = "\(emoji) \(self.filename)" - - statusLabel.text = { - switch self.attachmentPointer.state { - case .enqueued: - return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_QUEUED", comment: "Status label when an attachment is enqueued, but hasn't yet started downloading") - case .downloading: - return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_IN_PROGRESS", comment: "Status label when an attachment is currently downloading") - case .failed: - return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_FAILED", comment: "Status label when an attachment download has failed.") - } - }() - - if attachmentPointer.state == .downloading { - progressView.isHidden = false - progressView.autoSetDimension(.height, toSize: 8) - } else { - progressView.isHidden = true - progressView.autoSetDimension(.height, toSize: 0) - } - } - - var textColor: UIColor { - return conversationStyle.bubbleTextColor(isIncoming: isIncoming) - } - - @objc - public class func measureHeight() -> CGFloat { - return ceil(nameFont().lineHeight + - statusFont().lineHeight + - progressHeight + - vSpacing * 2) - } -} From 4bfa2513215982097f9b11b92453f6f7ceb90f99 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 13:22:54 -0400 Subject: [PATCH 08/11] Ensure constant bubble sizes for oversize text. --- .../ConversationView/Cells/OWSMessageBubbleView.m | 7 ------- .../ConversationView/ConversationViewItem.h | 1 - .../ConversationView/ConversationViewItem.m | 13 +------------ 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 246363ea4..fb6dcaa6a 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -281,7 +281,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes bodyMediaView = [self loadViewForMediaAlbum]; break; case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: bodyMediaView = [self loadViewForOversizeTextDownload]; break; } @@ -567,7 +566,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_GenericAttachment: case OWSMessageCellType_ContactShare: case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: return NO; case OWSMessageCellType_MediaMessage: return YES; @@ -584,7 +582,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_ContactShare: case OWSMessageCellType_MediaMessage: case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: return YES; } } @@ -895,8 +892,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes - (UIView *)loadViewForOversizeTextDownload { - BOOL isFailed = self.cellType == OWSMessageCellType_OversizeTextFailed; - // We can use an empty view. The progress views will display download // progress or tap-to-retry UI. UIView *attachmentView = [UIView new]; @@ -1118,7 +1113,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes } break; case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: // There's no way to predict the size of the oversize text, // so we just use a square bubble. result = CGSizeMake(maxMessageWidth, maxMessageWidth); @@ -1464,7 +1458,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: break; case OWSMessageCellType_Audio: if (self.viewItem.attachmentStream) { diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index b82272eaa..22bcf932c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -15,7 +15,6 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) { OWSMessageCellType_ContactShare, OWSMessageCellType_MediaMessage, OWSMessageCellType_OversizeTextDownloading, - OWSMessageCellType_OversizeTextFailed, }; NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 180eef15e..de576aeaa 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -34,8 +34,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return @"OWSMessageCellType_MediaMessage"; case OWSMessageCellType_OversizeTextDownloading: return @"OWSMessageCellType_OversizeTextDownloading"; - case OWSMessageCellType_OversizeTextFailed: - return @"OWSMessageCellType_OversizeTextFailed"; } } @@ -600,9 +598,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } else if ([oversizeTextAttachment isKindOfClass:[TSAttachmentPointer class]]) { TSAttachmentPointer *oversizeTextAttachmentPointer = (TSAttachmentPointer *)oversizeTextAttachment; // TODO: Handle backup restore. - self.messageCellType = (oversizeTextAttachmentPointer.state == TSAttachmentPointerStateFailed - ? OWSMessageCellType_OversizeTextFailed - : OWSMessageCellType_OversizeTextDownloading); + self.messageCellType = OWSMessageCellType_OversizeTextDownloading; self.attachmentPointer = (TSAttachmentPointer *)oversizeTextAttachmentPointer; return; } else { @@ -894,7 +890,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) break; } case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); return; } @@ -932,7 +927,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) break; } case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); return; } @@ -989,7 +983,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) break; } case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: OWSFailDebug(@"Can't share not-yet-downloaded attachment"); return; } @@ -1019,7 +1012,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return NO; } case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: return NO; } } @@ -1060,7 +1052,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return NO; } case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: return NO; } } @@ -1088,7 +1079,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) break; } case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: OWSFailDebug(@"Can't save not-yet-downloaded attachment"); return; } @@ -1168,7 +1158,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_MediaMessage: return self.firstValidAlbumAttachment != nil; case OWSMessageCellType_OversizeTextDownloading: - case OWSMessageCellType_OversizeTextFailed: return NO; } } From 936aa5842d5affeaade4b26fdffc39fe12e082fe Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 13:28:28 -0400 Subject: [PATCH 09/11] Clean up ahead of PR. --- .../ConversationView/Cells/OWSMessageBubbleView.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index fb6dcaa6a..eee75e651 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -990,14 +990,14 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes UILabel *label = [UILabel new]; label.text = NSLocalizedString( @"ATTACHMENT_DOWNLOADING_STATUS_FAILED", @"Status label when an attachment download has failed."); - label.font = UIFont.ows_dynamicTypeCaption1Font; - label.textColor = Theme.primaryColor; + label.font = UIFont.ows_dynamicTypeBodyFont; + label.textColor = Theme.secondaryColor; label.numberOfLines = 0; label.lineBreakMode = NSLineBreakByWordWrapping; label.textAlignment = NSTextAlignmentCenter; label.backgroundColor = self.bubbleColor; [bodyMediaView addSubview:label]; - [label autoPinEdgesToSuperviewEdges]; + [label autoPinEdgesToSuperviewMargins]; [label setContentHuggingLow]; [label setCompressionResistanceLow]; } From b6724ee1813b72ee0d9ed4fd218f0113f5091727 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Mar 2019 13:39:13 -0400 Subject: [PATCH 10/11] Clean up ahead of PR. --- .../src/ViewControllers/ConversationView/ConversationViewItem.m | 1 - 1 file changed, 1 deletion(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index de576aeaa..d7f53ad92 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -638,7 +638,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.audioDurationSeconds = audioDurationSeconds; self.messageCellType = OWSMessageCellType_Audio; } else { - OWSLogVerbose(@"contentType: %@", self.attachmentStream.contentType); self.messageCellType = OWSMessageCellType_GenericAttachment; } } else if (self.messageCellType == OWSMessageCellType_Unknown) { From df4cf5c09e8bf0c45968dc8aceed7a8d104876b7 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 18 Mar 2019 14:24:33 -0400 Subject: [PATCH 11/11] Respond to CR. --- .../ConversationView/Cells/OWSGenericAttachmentView.m | 3 +++ .../ConversationView/ConversationViewItem.m | 3 +++ .../src/Messages/Attachments/TSAttachmentPointer.m | 8 +++++--- .../src/Messages/Attachments/TSAttachmentStream.m | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m index b4895613d..b95fd220d 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m @@ -159,6 +159,9 @@ NS_ASSUME_NONNULL_BEGIN .fileSize; OWSAssertDebug(!error); } + // We don't want to show the file size while the attachment is downloading. + // To avoid layout jitter when the download completes, we reserve space in + // the layout using a whitespace string. NSString *bottomText = @" "; if (fileSize > 0) { bottomText = [OWSFormat formatFileSize:fileSize]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index d7f53ad92..5999f6731 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -990,6 +990,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (BOOL)canCopyMedia { if (self.attachmentPointer != nil) { + // The attachment is still downloading. return NO; } @@ -1018,6 +1019,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (BOOL)canSaveMedia { if (self.attachmentPointer != nil) { + // The attachment is still downloading. return NO; } @@ -1143,6 +1145,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (BOOL)hasMediaActionContent { if (self.attachmentPointer != nil) { + // The attachment is still downloading. return NO; } diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m index da2684f01..0e04b52bd 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @interface TSAttachmentStream (TSAttachmentPointer) -- (CGSize)cachedImageSize; +- (CGSize)cachedMediaSize; @end @@ -100,7 +100,7 @@ NS_ASSUME_NONNULL_BEGIN _state = TSAttachmentPointerStateEnqueued; self.attachmentType = attachmentStream.attachmentType; _pointerType = TSAttachmentPointerTypeRestoring; - _mediaSize = (attachmentStream.shouldHaveImageSize ? attachmentStream.cachedImageSize : CGSizeZero); + _mediaSize = (attachmentStream.shouldHaveImageSize ? attachmentStream.cachedMediaSize : CGSizeZero); return self; } @@ -119,7 +119,9 @@ NS_ASSUME_NONNULL_BEGIN NSString *_Nullable fileName = attachmentProto.fileName; NSString *_Nullable contentType = attachmentProto.contentType; if (contentType.length < 1) { - OWSLogError(@"Invalid attachment content type."); + // Content type might not set if the sending client can't + // infer a MIME type from the file extension. + OWSLogWarn(@"Invalid attachment content type."); NSString *_Nullable fileExtension = [fileName pathExtension].lowercaseString; if (fileExtension.length > 0) { contentType = [MIMETypeUtil mimeTypeForFileExtension:fileExtension]; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index bb0384d8d..8fba29f15 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -526,7 +526,7 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail); } } -- (CGSize)cachedImageSize +- (CGSize)cachedMediaSize { OWSAssertDebug(self.shouldHaveImageSize);