From 71c5c3a4b7b84d3eed565f54c0be44bfa3377d65 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 17:33:11 -0400 Subject: [PATCH] Refine appearance of quoted reply message cells. --- .../ConversationView/Cells/OWSMessageCell.m | 21 +- .../Cells/OWSQuotedMessageView.m | 183 +++++++++++------- .../ConversationViewController.m | 8 + .../translations/en.lproj/Localizable.strings | 12 ++ SignalMessaging/categories/UIView+OWS.h | 5 + 5 files changed, 157 insertions(+), 72 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 4b1178416..08745bb37 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -1144,13 +1144,24 @@ NS_ASSUME_NONNULL_BEGIN const CGFloat maxMediaWidth = maxMessageWidth; const CGFloat maxMediaHeight = maxMessageWidth; - CGFloat mediaWidth = (CGFloat)round(maxMediaHeight * contentAspectRatio); - CGFloat mediaHeight = (CGFloat)round(maxMediaHeight); + CGFloat mediaWidth = maxMediaHeight * contentAspectRatio; + CGFloat mediaHeight = maxMediaHeight; if (mediaWidth > maxMediaWidth) { - mediaWidth = (CGFloat)round(maxMediaWidth); - mediaHeight = (CGFloat)round(maxMediaWidth / contentAspectRatio); + mediaWidth = maxMediaWidth; + mediaHeight = maxMediaWidth / contentAspectRatio; } - return CGSizeMake(mediaWidth, mediaHeight); + + // We don't want to blow up small images unnecessarily. + const CGFloat kMinimumSize = 150.f; + CGFloat shortSrcDimension = MIN(self.mediaSize.width, self.mediaSize.height); + CGFloat shortDstDimension = MIN(mediaWidth, mediaHeight); + if (shortDstDimension > kMinimumSize && shortDstDimension > shortSrcDimension) { + CGFloat factor = kMinimumSize / shortDstDimension; + mediaWidth *= factor; + mediaHeight *= factor; + } + + return CGSizeRound(CGSizeMake(mediaWidth, mediaHeight)); } case OWSMessageCellType_Audio: return CGSizeMake(maxMessageWidth, OWSAudioMessageView.bubbleHeight); diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index f3cff4e36..492efd787 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -22,8 +22,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) TSQuotedMessage *quotedMessage; @property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText; -@property (nonatomic, readonly) UIFont *textMessageFont; - @property (nonatomic, nullable) OWSBubbleStrokeView *boundsStrokeView; @end @@ -67,7 +65,6 @@ NS_ASSUME_NONNULL_BEGIN _quotedMessage = quotedMessage; _displayableQuotedText = displayableQuotedText; - _textMessageFont = [UIFont ows_dynamicTypeBodyFont]; return self; } @@ -85,22 +82,6 @@ NS_ASSUME_NONNULL_BEGIN [TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]); } -- (NSString *)quotedSnippet -{ - if (self.displayableQuotedText.displayText.length > 0) { - return self.displayableQuotedText.displayText; - } else { - // TODO: Are we going to use the filename? For all mimetypes? - NSString *mimeType = self.quotedMessage.contentType; - - if (mimeType.length > 0) { - return [TSAttachment emojiForMimeType:mimeType]; - } - } - - return @""; -} - - (UIColor *)highlightColor { BOOL isIncomingQuote @@ -136,6 +117,7 @@ NS_ASSUME_NONNULL_BEGIN quotedAttachmentView.layer.borderColor = [UIColor colorWithWhite:0.f alpha:0.1f].CGColor; quotedAttachmentView.layer.borderWidth = 1.f; quotedAttachmentView.layer.cornerRadius = 2.f; + quotedAttachmentView.clipsToBounds = YES; } else { // TODO: This asset is wrong. // TODO: There's a special asset for audio files. @@ -163,17 +145,8 @@ NS_ASSUME_NONNULL_BEGIN } } - OWSContactsManager *contactsManager = Environment.current.contactsManager; - NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; - - UILabel *quotedAuthorLabel = [UILabel new]; + UILabel *quotedAuthorLabel = [self createQuotedAuthorLabel]; { - quotedAuthorLabel.text = quotedAuthor; - quotedAuthorLabel.font = self.quotedAuthorFont; - // TODO: - quotedAuthorLabel.textColor = [UIColor ows_darkGrayColor]; - quotedAuthorLabel.numberOfLines = 1; - quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; [self addSubview:quotedAuthorLabel]; [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedAuthorTopInset]; [quotedAuthorLabel autoPinLeadingToSuperviewMarginWithInset:self.quotedContentHInset]; @@ -261,6 +234,77 @@ NS_ASSUME_NONNULL_BEGIN return imageView; } +- (UILabel *)createQuotedTextLabel +{ + UIColor *textColor = self.quotedTextColor; + UIFont *font = self.quotedTextFont; + NSString *text = @""; + + NSString *_Nullable fileTypeForSnippet = [self fileTypeForSnippet]; + NSString *_Nullable sourceFilename = [self.quotedMessage.sourceFilename filterStringForDisplay]; + + if (self.displayableQuotedText.displayText.length > 0) { + text = self.displayableQuotedText.displayText; + textColor = self.quotedTextColor; + font = self.quotedTextFont; + } else if (fileTypeForSnippet) { + text = fileTypeForSnippet; + textColor = self.fileTypeTextColor; + font = self.fileTypeFont; + } else if (sourceFilename) { + text = sourceFilename; + textColor = self.filenameTextColor; + font = self.filenameFont; + } + + UILabel *quotedTextLabel = [UILabel new]; + quotedTextLabel.numberOfLines = 3; + quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping; + quotedTextLabel.text = text; + quotedTextLabel.textColor = textColor; + quotedTextLabel.font = font; + + return quotedTextLabel; +} + +- (nullable NSString *)fileTypeForSnippet +{ + // TODO: Are we going to use the filename? For all mimetypes? + NSString *_Nullable contentType = self.quotedMessage.contentType; + if (contentType.length > 0) { + if ([MIMETypeUtil isAudio:contentType]) { + return NSLocalizedString( + @"QUOTED_REPLY_TYPE_AUDIO", @"Indicates this message is a quoted reply to an audio file."); + } else if ([MIMETypeUtil isVideo:contentType]) { + return NSLocalizedString( + @"QUOTED_REPLY_TYPE_VIDEO", @"Indicates this message is a quoted reply to a video file."); + } + } else if ([MIMETypeUtil isImage:contentType] || [MIMETypeUtil isAnimated:contentType]) { + return NSLocalizedString( + @"QUOTED_REPLY_TYPE_IMAGE", @"Indicates this message is a quoted reply to an image file."); + } + return nil; +} + +- (UILabel *)createQuotedAuthorLabel +{ + OWSContactsManager *contactsManager = Environment.current.contactsManager; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; + NSString *quotedAuthorText = + [NSString stringWithFormat: + NSLocalizedString(@"QUOTED_REPLY_AUTHOR_INDICATOR_FORMAT", + @"Indicates the author of a quoted message. Embeds {{the author's name or phone number}}."), + quotedAuthor]; + + UILabel *quotedAuthorLabel = [UILabel new]; + quotedAuthorLabel.text = quotedAuthorText; + quotedAuthorLabel.font = self.quotedAuthorFont; + quotedAuthorLabel.textColor = [self quotedAuthorColor]; + quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; + quotedAuthorLabel.numberOfLines = 1; + return quotedAuthorLabel; +} + #pragma mark - Measurement - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth @@ -286,14 +330,7 @@ NS_ASSUME_NONNULL_BEGIN { CGFloat maxQuotedAuthorWidth = maxWidth - result.width; - OWSContactsManager *contactsManager = Environment.current.contactsManager; - NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; - - UILabel *quotedAuthorLabel = [UILabel new]; - quotedAuthorLabel.text = quotedAuthor; - quotedAuthorLabel.font = self.quotedAuthorFont; - quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; - quotedAuthorLabel.numberOfLines = 1; + UILabel *quotedAuthorLabel = [self createQuotedAuthorLabel]; CGSize quotedAuthorSize = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxQuotedAuthorWidth, CGFLOAT_MAX)]); @@ -327,17 +364,14 @@ NS_ASSUME_NONNULL_BEGIN return result; } -- (UILabel *)createQuotedTextLabel +- (UIFont *)quotedAuthorFont { - UILabel *quotedTextLabel = [UILabel new]; - quotedTextLabel.numberOfLines = 3; - quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping; - quotedTextLabel.text = self.quotedSnippet; - quotedTextLabel.textColor = self.quotedTextColor; + return [UIFont ows_mediumFontWithSize:11.f]; +} - // Honor dynamic type in the message bodies. - quotedTextLabel.font = self.textMessageFont; - return quotedTextLabel; +- (UIColor *)quotedAuthorColor +{ + return [UIColor colorWithRGBHex:0x8E8E93]; } - (UIColor *)quotedTextColor @@ -345,77 +379,92 @@ NS_ASSUME_NONNULL_BEGIN return [UIColor blackColor]; } -// TODO: -- (UIFont *)quotedAuthorFont +- (UIFont *)quotedTextFont { - return [UIFont ows_regularFontWithSize:10.f]; + // Honor dynamic type in the text. + // TODO: ? + return [UIFont ows_dynamicTypeBodyFont]; +} + +- (UIColor *)fileTypeTextColor +{ + return [UIColor colorWithWhite:0.5f alpha:1.f]; +} + +- (UIFont *)fileTypeFont +{ + UIFontDescriptor *fontD = + [self.quotedTextFont.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic]; + UIFont *font = [UIFont fontWithDescriptor:fontD size:0]; + OWSAssert(font); + return font ?: self.quotedTextFont; +} + +- (UIColor *)filenameTextColor +{ + return [UIColor colorWithWhite:0.5f alpha:1.f]; +} + +- (UIFont *)filenameFont +{ + return self.quotedTextFont; } -// TODO: - (CGFloat)quotedAuthorHeight { return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.f); } -// TODO: - (CGFloat)quotedAuthorTopInset { - return 4.f; + return 8.f; } // TODO: - (CGFloat)quotedAuthorBottomSpacing { - return 2.f; + return 3.f; } -// TODO: - (CGFloat)quotedTextBottomInset { - return 5.f; + return 4.f; } -// TODO: - (CGFloat)quotedReplyStripeThickness { return 2.f; } -// TODO: - (CGFloat)quotedReplyStripeVExtension { - return 5.f; + return 8.f; } // The spacing between the vertical "quoted reply stripe" // and the quoted message content. -// TODO: - (CGFloat)quotedReplyStripeHSpacing { - return 8.f; + return 4.f; } // Distance from top edge of "quoted message" bubble to top of message bubble. -// TODO: - (CGFloat)quotedAttachmentMinVInset { - return 10.f; + return 12.f; } -// TODO: - (CGFloat)quotedAttachmentSize { - return 30.f; + return 44.f; } -// TODO: - (CGFloat)quotedAttachmentHSpacing { - return 10.f; + return 8.f; } // Distance from sides of the quoted content to the sides of the message bubble. -// TODO: - (CGFloat)quotedContentHInset { return 8.f; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 8cad36aaa..61b4cf186 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2299,6 +2299,14 @@ typedef enum : NSUInteger { - (void)scrollDownButtonTapped { +#ifdef DEBUG + CGPoint contentOffset = self.collectionView.contentOffset; + contentOffset.y += self.collectionView.height + - (self.collectionView.contentInset.top + self.collectionView.contentInset.bottom); + [self.collectionView setContentOffset:contentOffset animated:NO]; + return; +#endif + NSIndexPath *indexPathOfUnreadMessagesIndicator = [self indexPathOfUnreadMessagesIndicator]; if (indexPathOfUnreadMessagesIndicator != nil) { NSInteger unreadRow = indexPathOfUnreadMessagesIndicator.row; diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 2c5c420c9..26a6e8f43 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1405,6 +1405,18 @@ /* No comment provided by engineer. */ "QUESTIONMARK_PUNCTUATION" = "?"; +/* Indicates the author of a quoted message. Embeds {{the author's name or phone number}}. */ +"QUOTED_REPLY_AUTHOR_INDICATOR_FORMAT" = "Replying to %@"; + +/* Indicates this message is a quoted reply to an audio file. */ +"QUOTED_REPLY_TYPE_AUDIO" = "Audio"; + +/* Indicates this message is a quoted reply to an image file. */ +"QUOTED_REPLY_TYPE_IMAGE" = "Image"; + +/* Indicates this message is a quoted reply to a video file. */ +"QUOTED_REPLY_TYPE_VIDEO" = "Video"; + /* No comment provided by engineer. */ "RATING_MSG" = "If you enjoy using Signal to have private conversations, you can support our project by rating it. It won't take more than a minute, and will help others find some privacy."; diff --git a/SignalMessaging/categories/UIView+OWS.h b/SignalMessaging/categories/UIView+OWS.h index 043a63fc3..d10c0b7fb 100644 --- a/SignalMessaging/categories/UIView+OWS.h +++ b/SignalMessaging/categories/UIView+OWS.h @@ -132,4 +132,9 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return CGSizeMake((CGFloat)ceil(size.width), (CGFloat)ceil(size.height)); } +CG_INLINE CGSize CGSizeRound(CGSize size) +{ + return CGSizeMake((CGFloat)round(size.width), (CGFloat)round(size.height)); +} + NS_ASSUME_NONNULL_END