From 7cf169012c4300b23ea086d63de4588f4422e76f Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 10:32:15 -0400 Subject: [PATCH 1/9] Elaborate conversation view items around quoted replies. --- .../ConversationView/ConversationViewItem.h | 9 ++ .../ConversationView/ConversationViewItem.m | 110 ++++++++++++++---- .../Messages/Interactions/TSQuotedMessage.h | 9 +- 3 files changed, 101 insertions(+), 27 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 0ecf54be2f..8dd152759e 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -43,7 +43,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly) TSInteraction *interaction; @property (nonatomic, readonly) BOOL isGroupThread; + @property (nonatomic, readonly) BOOL hasBodyText; + +@property (nonatomic, readonly) BOOL isQuotedReply; +@property (nonatomic, readonly) BOOL hasQuotedAttachment; +@property (nonatomic, readonly) BOOL hasQuotedText; + @property (nonatomic) BOOL shouldShowDate; @property (nonatomic) BOOL shouldHideRecipientStatus; @property (nonatomic) BOOL shouldHideBubbleTail; @@ -85,6 +91,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); - (nullable TSAttachmentPointer *)attachmentPointer; - (CGSize)mediaSize; +- (nullable DisplayableText *)displayableQuotedText; +- (nullable NSString *)quotedAttachmentMimetype; + // We don't want to try to load the media for this item (if any) // if a load has previously failed. @property (nonatomic) BOOL didCellMediaFailToLoad; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 3581ac47d0..a32338cdca 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -56,6 +56,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @property (nonatomic) BOOL hasViewState; @property (nonatomic) OWSMessageCellType messageCellType; @property (nonatomic, nullable) DisplayableText *displayableBodyText; +@property (nonatomic, nullable) DisplayableText *displayableQuotedText; +@property (nonatomic, nullable) NSString *quotedAttachmentMimetype; +@property (nonatomic, nullable) NSString *quotedRecipientId; @property (nonatomic, nullable) TSAttachmentStream *attachmentStream; @property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer; @property (nonatomic) CGSize mediaSize; @@ -99,6 +102,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.attachmentPointer = nil; self.mediaSize = CGSizeZero; + self.displayableQuotedText = nil; + self.quotedRecipientId = nil; + self.quotedAttachmentMimetype = nil; + [self clearCachedLayoutState]; [self ensureViewState:transaction]; @@ -109,6 +116,21 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return _displayableBodyText != nil; } +- (BOOL)hasQuotedText +{ + return _displayableQuotedText != nil; +} + +- (BOOL)hasQuotedAttachment +{ + return self.quotedAttachmentMimetype.length > 0; +} + +- (BOOL)isQuotedReply +{ + return self.hasQuotedAttachment || self.hasQuotedText; +} + - (void)setShouldShowDate:(BOOL)shouldShowDate { if (_shouldShowDate == shouldShowDate) { @@ -275,11 +297,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [self.lastAudioMessageView updateContents]; } -#pragma mark - View State +#pragma mark - Displayable Text // TODO: Now that we're caching the displayable text on the view items, // I don't think we need this cache any more. -- (NSCache *)displayableBodyTextCache +- (NSCache *)displayableTextCache { static NSCache *cache = nil; static dispatch_once_t onceToken; @@ -296,10 +318,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSAssert(text); OWSAssert(interactionId.length > 0); - return [self displayableBodyTextForInteractionId:interactionId - textBlock:^{ - return text; - }]; + NSString *displayableTextCacheKey = [@"body-" stringByAppendingString:interactionId]; + + return [self displayableTextForCacheKey:displayableTextCacheKey + textBlock:^{ + return text; + }]; } - (DisplayableText *)displayableBodyTextForOversizeTextAttachment:(TSAttachmentStream *)attachmentStream @@ -308,30 +332,46 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSAssert(attachmentStream); OWSAssert(interactionId.length > 0); - return - [self displayableBodyTextForInteractionId:interactionId - textBlock:^{ - NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL]; - NSString *text = - [[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding]; - return text; - }]; + NSString *displayableTextCacheKey = [@"oversize-body-" stringByAppendingString:interactionId]; + + return [self displayableTextForCacheKey:displayableTextCacheKey + textBlock:^{ + NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL]; + NSString *text = + [[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding]; + return text; + }]; } -- (DisplayableText *)displayableBodyTextForInteractionId:(NSString *)interactionId - textBlock:(NSString * (^_Nonnull)(void))textBlock +- (DisplayableText *)displayableQuotedTextForText:(NSString *)text interactionId:(NSString *)interactionId { + OWSAssert(text); OWSAssert(interactionId.length > 0); - DisplayableText *_Nullable displayableBodyText = [[self displayableBodyTextCache] objectForKey:interactionId]; - if (!displayableBodyText) { - NSString *text = textBlock(); - displayableBodyText = [DisplayableText displayableText:text]; - [[self displayableBodyTextCache] setObject:displayableBodyText forKey:interactionId]; - } - return displayableBodyText; + NSString *displayableTextCacheKey = [@"quoted-" stringByAppendingString:interactionId]; + + return [self displayableTextForCacheKey:displayableTextCacheKey + textBlock:^{ + return text; + }]; } +- (DisplayableText *)displayableTextForCacheKey:(NSString *)displayableTextCacheKey + textBlock:(NSString * (^_Nonnull)(void))textBlock +{ + OWSAssert(displayableTextCacheKey.length > 0); + + DisplayableText *_Nullable displayableText = [[self displayableTextCache] objectForKey:displayableTextCacheKey]; + if (!displayableText) { + NSString *text = textBlock(); + displayableText = [DisplayableText displayableText:text]; + [[self displayableTextCache] setObject:displayableText forKey:displayableTextCacheKey]; + } + return displayableText; +} + +#pragma mark - View State + - (nullable TSAttachment *)firstAttachmentIfAnyOfMessage:(TSMessage *)message transaction:(YapDatabaseReadTransaction *)transaction { @@ -430,6 +470,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.messageCellType = OWSMessageCellType_TextMessage; self.displayableBodyText = [[DisplayableText alloc] initWithFullText:@"" displayText:@"" isTextTruncated:NO]; } + + if (message.quotedMessage && message.quotedMessage.authorId.length > 0 + && (message.quotedMessage.body.length > 0 || message.quotedMessage.contentType.length > 0)) { + if (message.quotedMessage.body.length > 0) { + self.displayableQuotedText = + [self displayableQuotedTextForText:message.quotedMessage.body interactionId:message.uniqueId]; + } + self.quotedAttachmentMimetype = message.quotedMessage.contentType; + self.quotedRecipientId = message.quotedMessage.authorId; + } } - (OWSMessageCellType)messageCellType @@ -475,6 +525,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return _mediaSize; } +- (nullable DisplayableText *)displayableQuotedText +{ + OWSAssertIsOnMainThread(); + OWSAssert(self.hasViewState); + + OWSAssert(_displayableQuotedText); + OWSAssert(_displayableQuotedText.displayText); + OWSAssert(_displayableQuotedText.fullText); + + return _displayableQuotedText; +} + #pragma mark - UIMenuController - (NSArray *)textMenuControllerItems @@ -569,6 +631,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } } +// TODO: Update for quoted text. - (void)copyTextAction { switch (self.messageCellType) { @@ -628,6 +691,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } } +// TODO: Update for quoted text. - (void)shareTextAction { switch (self.messageCellType) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h index 0d9ae2d53f..51acde44f9 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h @@ -11,16 +11,17 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) uint64_t timestamp; @property (nonatomic, readonly) NSString *authorId; -// This property should be set IFF we are quoting a text message. +// This property should be set IFF we are quoting a text message +// or attachment with caption. @property (nullable, nonatomic, readonly) NSString *body; -// This property should be set IFF we are quoting a attachment message. +// This property should be set IFF we are quoting an attachment message. @property (nullable, nonatomic, readonly) NSString *sourceFilename; -// This property can be set IFF we are quoting a attachment message, but it is optional. +// This property can be set IFF we are quoting an attachment message, but it is optional. @property (nullable, nonatomic, readonly) NSData *thumbnailData; // This is a MIME type. // -// This property should be set IFF we are quoting a attachment message. +// This property should be set IFF we are quoting an attachment message. @property (nullable, nonatomic, readonly) NSString *contentType; - (instancetype)init NS_UNAVAILABLE; From 2278cdd5894a83808070e994a8a6a2a85f130129 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 12:35:43 -0400 Subject: [PATCH 2/9] Modify cells to show quoted messages. --- .../ConversationView/Cells/OWSMessageCell.m | 381 +++++++++++++++++- .../src/ViewControllers/HomeViewController.m | 9 + .../Messages/Attachments/TSAttachmentStream.h | 2 + .../Messages/Attachments/TSAttachmentStream.m | 6 + 4 files changed, 381 insertions(+), 17 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index ee0a5196a1..ad4d61411c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -43,6 +43,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) @property (nonatomic) UILabel *dateHeaderLabel; @property (nonatomic) OWSMessageTextView *bodyTextView; +@property (nonatomic) OWSMessageTextView *quotedTextView; @property (nonatomic, nullable) UIImageView *failedSendBadgeView; @property (nonatomic) UIView *footerView; @property (nonatomic) UILabel *footerLabel; @@ -96,6 +97,11 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [self.contentView addSubview:self.dateHeaderLabel]; self.bodyTextView = [self newTextView]; + // Setting dataDetectorTypes is expensive. Do it just once. + self.bodyTextView.dataDetectorTypes + = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); + + self.quotedTextView = [self newTextView]; self.footerLabel = [UILabel new]; self.footerLabel.font = [UIFont ows_regularFontWithSize:12.f]; @@ -104,6 +110,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) // Hide these views by default. self.bodyTextView.hidden = YES; + self.quotedTextView.hidden = YES; self.dateHeaderLabel.hidden = YES; self.footerLabel.hidden = YES; @@ -140,8 +147,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) textView.contentInset = UIEdgeInsetsZero; textView.textContainer.lineFragmentPadding = 0; textView.scrollEnabled = NO; - // Setting dataDetectorTypes is expensive. Do it just once. - textView.dataDetectorTypes = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); return textView; } @@ -174,16 +179,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return [UIFont ows_dynamicTypeBodyFont]; } -- (UIFont *)tapForMoreFont -{ - return [UIFont ows_regularFontWithSize:12.f]; -} - -- (CGFloat)tapForMoreHeight -{ - return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25); -} - - (BOOL)shouldHaveFailedSendBadge { if (![self.viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) { @@ -251,6 +246,47 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return self.viewItem.mediaSize; } +- (BOOL)isQuotedReply +{ + // This should always be valid for the appropriate cell types. + OWSAssert(self.viewItem); + + return self.viewItem.isQuotedReply; +} + +- (BOOL)hasQuotedText +{ + // This should always be valid for the appropriate cell types. + OWSAssert(self.viewItem); + + return self.viewItem.hasQuotedText; +} + +- (BOOL)hasQuotedAttachment +{ + // This should always be valid for the appropriate cell types. + OWSAssert(self.viewItem); + + return self.viewItem.hasQuotedAttachment; +} + +- (BOOL)hasQuotedAttachmentThumbnail +{ + // This should always be valid for the appropriate cell types. + OWSAssert(self.viewItem); + + return (self.viewItem.hasQuotedAttachment && + [TSAttachmentStream hasThumbnailForMimeType:self.viewItem.quotedAttachmentMimetype]); +} + +- (nullable DisplayableText *)displayableQuotedText +{ + // This should always be valid for the appropriate cell types. + OWSAssert(self.viewItem.displayableQuotedText); + + return self.viewItem.displayableQuotedText; +} + - (TSMessage *)message { OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); @@ -346,6 +382,60 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) UIView *_Nullable lastSubview = nil; CGFloat bottomMargin = 0; + + if (self.isQuotedReply) { + OWSAssert(!lastSubview); + + UIView *quotedMessageView = [self createQuotedMessageView]; + + [self.bubbleView addSubview:quotedMessageView]; + + CGFloat leadingMargin = self.quotedMessageHInset; + CGFloat trailingMargin = self.quotedMessageHInset; + if (self.isIncoming) { + leadingMargin += kBubbleThornSideInset; + } else { + trailingMargin += kBubbleThornSideInset; + } + + [self.viewConstraints addObjectsFromArray:@[ + [quotedMessageView autoPinLeadingToSuperviewWithMargin:leadingMargin], + [quotedMessageView autoPinTrailingToSuperviewWithMargin:trailingMargin], + ]]; + + if (lastSubview) { + [self.viewConstraints addObject:[quotedMessageView autoPinEdge:ALEdgeTop + toEdge:ALEdgeBottom + ofView:lastSubview + withOffset:self.quotedMessageTopInset]]; + } else { + [self.viewConstraints addObject:[quotedMessageView autoPinEdgeToSuperviewEdge:ALEdgeTop + withInset:self.quotedMessageTopInset]]; + } + lastSubview = quotedMessageView; + bottomMargin = 0; + + [self.bubbleView logFrameLaterWithLabel:@"bubbleView"]; + [quotedMessageView logFrameLaterWithLabel:@"quotedMessageView"]; + + + // BOOL shouldStrokeMediaView = [bodyMediaView isKindOfClass:[UIImageView class]]; + // if (shouldStrokeMediaView) { + // OWSBubbleStrokeView *bubbleStrokeView = [OWSBubbleStrokeView new]; + // bubbleStrokeView.strokeThickness = 1.f; + // bubbleStrokeView.strokeColor = [UIColor colorWithWhite:0.f alpha:0.1f]; + // bubbleStrokeView.bubbleView = self.bubbleView; + // + // [self.bubbleView addSubview:bubbleStrokeView]; + // [bubbleStrokeView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:bodyMediaView]; + // [bubbleStrokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:bodyMediaView]; + // [bubbleStrokeView autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:bodyMediaView]; + // [bubbleStrokeView autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:bodyMediaView]; + // self.bubbleView.bubbleStrokeView = bubbleStrokeView; + // OWSAssert(self.bubbleView.bubbleStrokeView); + // } + } + UIView *_Nullable bodyMediaView = nil; BOOL bodyMediaViewHasGreedyWidth = NO; switch (self.cellType) { @@ -720,6 +810,16 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return [UIFont systemFontOfSize:12.0f]; } +- (OWSMessageTextView *)createQuotedTextViewIfNecessary +{ + [self.class loadForTextDisplay:self.quotedTextView + text:self.displayableQuotedText.displayText + textColor:self.textColor + font:self.textMessageFont + shouldIgnoreEvents:YES]; + return self.quotedTextView; +} + - (OWSMessageTextView *)createBodyTextViewIfNecessary { BOOL shouldIgnoreEvents = NO; @@ -783,6 +883,63 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return tapForMoreLabel; } +- (UIView *)createQuotedMessageView +{ + OWSAssert(self.isQuotedReply); + + UIView *quotedMessageView = [UIView containerView]; + quotedMessageView.userInteractionEnabled = NO; + quotedMessageView.clipsToBounds = YES; + // TODO: + quotedMessageView.layer.cornerRadius = 3.f; + quotedMessageView.backgroundColor = [UIColor colorWithRGBHex:0xe2f7fa]; + + UIView *quoteStripView = [UIView containerView]; + quoteStripView.backgroundColor = (self.isIncoming ? [UIColor whiteColor] : [UIColor colorWithRGBHex:0x007884]); + quoteStripView.userInteractionEnabled = NO; + [quotedMessageView addSubview:quoteStripView]; + [quoteStripView autoPinHeightToSuperview]; + [quoteStripView autoPinLeadingToSuperview]; + [quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness]; + + UIView *_Nullable quotedThumbnailView = nil; + if (self.hasQuotedAttachmentThumbnail) { + // TODO: + quotedThumbnailView = [UIView containerView]; + quotedThumbnailView.userInteractionEnabled = NO; + quotedThumbnailView.backgroundColor = [UIColor redColor]; + [quotedMessageView addSubview:quotedThumbnailView]; + [quotedThumbnailView autoPinTopToSuperview]; + [quotedThumbnailView autoPinTrailingToSuperview]; + [quotedThumbnailView autoSetDimension:ALDimensionWidth toSize:self.quotedThumbnailSize]; + [quotedThumbnailView autoSetDimension:ALDimensionHeight toSize:self.quotedThumbnailSize]; + } + + UILabel *quotedAuthorLabel = [UILabel new]; + quotedAuthorLabel.font = self.quotedAuthorFont; + quotedAuthorLabel.textColor + = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); + quotedAuthorLabel.numberOfLines = 1; + quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; + [quotedMessageView addSubview:quotedAuthorLabel]; + [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedAuthorTopInset]; + [quotedAuthorLabel autoPinLeadingToView:quoteStripView margin:self.quotedReplyStripeHSpacing]; + if (quotedThumbnailView) { + [quotedAuthorLabel autoPinTrailingToView:quotedThumbnailView margin:self.quotedThumbnailHSpacing]; + } else { + [quotedAuthorLabel autoPinTrailingToSuperviewWithMargin:self.quotedContentTrailingMargin]; + } + + // - (CGFloat)quotedAuthorTopInset + // { + // return 2.f; + // } + // + // - (CGFloat)quotedAuthorBottomSpacing + + return quotedMessageView; +} + - (UIView *)loadViewForStillImage { OWSAssert(self.attachmentStream); @@ -1077,6 +1234,37 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return CGSizeCeil(textViewSize); } +// Size of "quoted reply" text. +- (CGSize)quotedTextSizeForContentWidth:(int)contentWidth includeMargins:(BOOL)includeMargins +{ + if (!self.hasBodyText) { + return CGSizeZero; + } + + CGFloat hMarginTotal = (self.textTrailingMargin + self.textLeadingMargin + self.quotedReplyStripeThickness + + self.quotedReplyStripeHSpacing); + // BOOL isRTL = self.isRTL; + // CGFloat leftMargin = isRTL ? self.textTrailingMargin : self.textLeadingMargin; + // CGFloat rightMargin = isRTL ? self.textLeadingMargin : self.textTrailingMargin; + + const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; + const int maxTextWidth = (int)floor(maxMessageWidth - hMarginTotal); + + self.quotedTextView.text = self.displayableBodyText.displayText; + // Honor dynamic type in the message bodies. + self.quotedTextView.font = [self textMessageFont]; + CGSize textSize = CGSizeCeil([self.quotedTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + CGSize textViewSize = textSize; + + if (includeMargins) { + // TODO: + textViewSize.width += hMarginTotal; + textViewSize.height += self.textTopMargin + self.textBottomMargin; + } + + return CGSizeCeil(textViewSize); +} + - (CGSize)bodyMediaSizeForContentWidth:(int)contentWidth { const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; @@ -1126,17 +1314,68 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return (int)floor(contentWidth * 0.8f); } +- (CGSize)quotedMessageSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth +{ + OWSAssert(self.viewItem); + OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); + + // CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth]; + // CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES]; + // + // CGFloat cellContentWidth = fmax(mediaContentSize.width, textContentSize.width); + // CGFloat cellContentHeight = mediaContentSize.height + textContentSize.height; + // CGSize cellSize = CGSizeMake(cellContentWidth, cellContentHeight); + // + // OWSAssert(cellSize.width > 0 && cellSize.height > 0); + + CGSize result = CGSizeZero; + + if (!self.isQuotedReply) { + return result; + } + + result.width += self.quotedMessageHInset; + result.width += self.quotedReplyStripeThickness; + result.width += self.quotedReplyStripeHSpacing; + // TODO: Content width. + + result.height += self.quotedMessageTopInset; + result.height += self.quotedAuthorHeight; + result.height += self.quotedAuthorBottomSpacing; + // TODO: Content height. + + if (self.hasQuotedAttachmentThumbnail) { + result.width = MAX(result.width, self.quotedThumbnailHSpacing); + result.width = MAX(result.width, self.quotedThumbnailSize); + + result.height = MAX(result.height, self.quotedThumbnailSize); + } else { + result.width = MAX(result.width, self.quotedContentTrailingMargin); + } + + result.width += self.quotedMessageHInset; + + return result; +} + - (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth { OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth]; - CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES]; + CGSize cellSize = CGSizeZero; - CGFloat cellContentWidth = fmax(mediaContentSize.width, textContentSize.width); - CGFloat cellContentHeight = mediaContentSize.height + textContentSize.height; - CGSize cellSize = CGSizeMake(cellContentWidth, cellContentHeight); + CGSize quotedMessageSize = [self quotedMessageSizeForViewWidth:viewWidth contentWidth:contentWidth]; + cellSize.width = MAX(cellSize.width, quotedMessageSize.width); + cellSize.height += quotedMessageSize.height; + + CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth]; + cellSize.width = MAX(cellSize.width, mediaContentSize.width); + cellSize.height += mediaContentSize.height; + + CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES]; + cellSize.width = MAX(cellSize.width, textContentSize.width); + cellSize.height += textContentSize.height; OWSAssert(cellSize.width > 0 && cellSize.height > 0); @@ -1165,6 +1404,111 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) } } +- (UIFont *)tapForMoreFont +{ + return [UIFont ows_regularFontWithSize:12.f]; +} + +- (CGFloat)tapForMoreHeight +{ + return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25); +} + +- (UIFont *)quotedAuthorFont +{ + return [UIFont ows_regularFontWithSize:10.f]; +} + +- (CGFloat)quotedAuthorHeight +{ + return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.25); +} + +- (CGFloat)quotedAuthorTopInset +{ + return 2.f; +} + +- (CGFloat)quotedAuthorBottomSpacing +{ + return 2.f; +} + +//- (UIFont *)quoteIndicatorFont +//{ +// return [UIFont ows_regularFontWithSize:10.f]; +//} +// +//- (CGFloat)quoteIndicatorHeight +//{ +// return (CGFloat)ceil([self quoteIndicatorFont].lineHeight * 1.25); +//} + +//- (CGFloat)quotedMessageHMargin +//{ +// return 10.f; +//} + +//- (CGFloat)quotedMessageTopMargin +//{ +// return 10.f; +//} +// +//- (CGFloat)quotedMessageBottomMargin +//{ +// return 10.f; +//} +// +//- (CGFloat)quotedMessageHMargin +//{ +// return 10.f; +//} + +// Distance from top edge of "quoted message" bubble to top of message bubble. +- (CGFloat)quotedMessageTopInset +{ + return 3.f; +} + +//// Distance from bottom edge of "quoted message" bubble to other bubble content. +//- (CGFloat)quotedMessageBottomInset +//{ +// return 3.f; +//} + +// Distance from side of "quoted message" bubble to side of message bubble. +- (CGFloat)quotedMessageHInset +{ + return 3.f; +} + +- (CGFloat)quotedReplyStripeThickness +{ + return 3.f; +} + +// The spacing between the vertical "quoted reply stripe" +// and the quoted message content. +- (CGFloat)quotedReplyStripeHSpacing +{ + return 10.f; +} + +- (CGFloat)quotedThumbnailSize +{ + return 30.f; +} + +- (CGFloat)quotedThumbnailHSpacing +{ + return 10.f; +} + +- (CGFloat)quotedContentTrailingMargin +{ + return 10.f; +} + #pragma mark - - (BOOL)isIncoming @@ -1240,6 +1584,9 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) self.dateHeaderLabel.text = nil; self.dateHeaderLabel.hidden = YES; [self.bodyTextView removeFromSuperview]; + self.quotedTextView.text = nil; + self.quotedTextView.hidden = YES; + [self.quotedTextView removeFromSuperview]; self.bodyTextView.text = nil; self.bodyTextView.hidden = YES; [self.failedSendBadgeView removeFromSuperview]; diff --git a/Signal/src/ViewControllers/HomeViewController.m b/Signal/src/ViewControllers/HomeViewController.m index 1f95eb20aa..3a6bc2ea10 100644 --- a/Signal/src/ViewControllers/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeViewController.m @@ -284,6 +284,15 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; } [self updateBarButtonItems]; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; + TSThread *thread = [self threadForIndexPath:indexPath]; + if (!thread) { + return; + } + [self presentThread:thread keyboardOnViewAppearing:NO callOnViewAppearing:NO]; + }); } - (void)viewDidAppear:(BOOL)animated diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h index 97c73e47c7..22e6fe54b4 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h @@ -46,6 +46,8 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isAudio; - (nullable NSURL *)mediaURL; ++ (BOOL)hasThumbnailForMimeType:(NSString *)contentType; + - (nullable NSString *)filePath; - (nullable NSString *)thumbnailPath; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index 15ca2fbd9f..51fadbaa46 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -331,6 +331,12 @@ NS_ASSUME_NONNULL_BEGIN } } ++ (BOOL)hasThumbnailForMimeType:(NSString *)contentType +{ + return ([MIMETypeUtil isVideo:contentType] || [MIMETypeUtil isImage:contentType] || + [MIMETypeUtil isAnimated:contentType]); +} + - (nullable UIImage *)thumbnailImage { NSString *thumbnailPath = self.thumbnailPath; From 22dc9042834311b833566431e99df409635c411a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 13:15:19 -0400 Subject: [PATCH 3/9] Modify cells to show quoted messages. --- .../ConversationView/Cells/OWSMessageCell.m | 223 ++++++++---------- .../ConversationView/ConversationViewItem.h | 1 + 2 files changed, 103 insertions(+), 121 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index ad4d61411c..1ebab0d745 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -43,7 +43,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) @property (nonatomic) UILabel *dateHeaderLabel; @property (nonatomic) OWSMessageTextView *bodyTextView; -@property (nonatomic) OWSMessageTextView *quotedTextView; +@property (nonatomic) UILabel *quotedTextView; @property (nonatomic, nullable) UIImageView *failedSendBadgeView; @property (nonatomic) UIView *footerView; @property (nonatomic) UILabel *footerLabel; @@ -101,7 +101,9 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) self.bodyTextView.dataDetectorTypes = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); - self.quotedTextView = [self newTextView]; + self.quotedTextView = [UILabel new]; + self.quotedTextView.numberOfLines = 3; + self.quotedTextView.lineBreakMode = NSLineBreakByWordWrapping; self.footerLabel = [UILabel new]; self.footerLabel.font = [UIFont ows_regularFontWithSize:12.f]; @@ -418,22 +420,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [self.bubbleView logFrameLaterWithLabel:@"bubbleView"]; [quotedMessageView logFrameLaterWithLabel:@"quotedMessageView"]; - - // BOOL shouldStrokeMediaView = [bodyMediaView isKindOfClass:[UIImageView class]]; - // if (shouldStrokeMediaView) { - // OWSBubbleStrokeView *bubbleStrokeView = [OWSBubbleStrokeView new]; - // bubbleStrokeView.strokeThickness = 1.f; - // bubbleStrokeView.strokeColor = [UIColor colorWithWhite:0.f alpha:0.1f]; - // bubbleStrokeView.bubbleView = self.bubbleView; - // - // [self.bubbleView addSubview:bubbleStrokeView]; - // [bubbleStrokeView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:bodyMediaView]; - // [bubbleStrokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:bodyMediaView]; - // [bubbleStrokeView autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:bodyMediaView]; - // [bubbleStrokeView autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:bodyMediaView]; - // self.bubbleView.bubbleStrokeView = bubbleStrokeView; - // OWSAssert(self.bubbleView.bubbleStrokeView); - // } + // TODO: Consider stroking the quoted thumbnail. } UIView *_Nullable bodyMediaView = nil; @@ -810,13 +797,14 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return [UIFont systemFontOfSize:12.0f]; } -- (OWSMessageTextView *)createQuotedTextViewIfNecessary +- (UILabel *)createQuotedTextView { - [self.class loadForTextDisplay:self.quotedTextView - text:self.displayableQuotedText.displayText - textColor:self.textColor - font:self.textMessageFont - shouldIgnoreEvents:YES]; + self.quotedTextView.hidden = NO; + self.quotedTextView.text = self.displayableQuotedText.displayText; + self.quotedTextView.textColor = self.quotedTextColor; + + // Honor dynamic type in the message bodies. + self.quotedTextView.font = self.textMessageFont; return self.quotedTextView; } @@ -831,7 +819,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) } [self.class loadForTextDisplay:self.bodyTextView text:self.displayableBodyText.displayText - textColor:self.textColor + textColor:self.bodyTextColor font:self.textMessageFont shouldIgnoreEvents:shouldIgnoreEvents]; return self.bodyTextView; @@ -877,7 +865,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) tapForMoreLabel.text = NSLocalizedString(@"CONVERSATION_VIEW_OVERSIZE_TEXT_TAP_FOR_MORE", @"Indicator on truncated text messages that they can be tapped to see the entire text message."); tapForMoreLabel.font = [self tapForMoreFont]; - tapForMoreLabel.textColor = [self.textColor colorWithAlphaComponent:0.85]; + tapForMoreLabel.textColor = [self.bodyTextColor colorWithAlphaComponent:0.85]; tapForMoreLabel.textAlignment = [tapForMoreLabel textAlignmentUnnatural]; return tapForMoreLabel; @@ -915,14 +903,18 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [quotedThumbnailView autoSetDimension:ALDimensionHeight toSize:self.quotedThumbnailSize]; } + OWSContactsManager *contactsManager = Environment.current.contactsManager; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId]; + UILabel *quotedAuthorLabel = [UILabel new]; + quotedAuthorLabel.text = quotedAuthor; quotedAuthorLabel.font = self.quotedAuthorFont; quotedAuthorLabel.textColor = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); quotedAuthorLabel.numberOfLines = 1; quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; [quotedMessageView addSubview:quotedAuthorLabel]; - [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedAuthorTopInset]; + [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedContentTopInset]; [quotedAuthorLabel autoPinLeadingToView:quoteStripView margin:self.quotedReplyStripeHSpacing]; if (quotedThumbnailView) { [quotedAuthorLabel autoPinTrailingToView:quotedThumbnailView margin:self.quotedThumbnailHSpacing]; @@ -930,12 +922,22 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [quotedAuthorLabel autoPinTrailingToSuperviewWithMargin:self.quotedContentTrailingMargin]; } - // - (CGFloat)quotedAuthorTopInset - // { - // return 2.f; - // } - // - // - (CGFloat)quotedAuthorBottomSpacing + if (self.hasQuotedText) { + UILabel *quotedTextView = [self createQuotedTextView]; + + [quotedMessageView addSubview:quotedTextView]; + [quotedTextView autoPinEdge:ALEdgeTop + toEdge:ALEdgeBottom + ofView:quotedAuthorLabel + withOffset:self.quotedAuthorBottomSpacing]; + [quotedTextView autoPinLeadingToView:quoteStripView margin:self.quotedReplyStripeHSpacing]; + if (quotedThumbnailView) { + [quotedTextView autoPinTrailingToView:quotedThumbnailView margin:self.quotedThumbnailHSpacing]; + } else { + [quotedTextView autoPinTrailingToSuperviewWithMargin:self.quotedContentTrailingMargin]; + } + [quotedTextView autoPinBottomToSuperviewWithMargin:self.quotedContentBottomInset]; + } return quotedMessageView; } @@ -1234,37 +1236,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return CGSizeCeil(textViewSize); } -// Size of "quoted reply" text. -- (CGSize)quotedTextSizeForContentWidth:(int)contentWidth includeMargins:(BOOL)includeMargins -{ - if (!self.hasBodyText) { - return CGSizeZero; - } - - CGFloat hMarginTotal = (self.textTrailingMargin + self.textLeadingMargin + self.quotedReplyStripeThickness - + self.quotedReplyStripeHSpacing); - // BOOL isRTL = self.isRTL; - // CGFloat leftMargin = isRTL ? self.textTrailingMargin : self.textLeadingMargin; - // CGFloat rightMargin = isRTL ? self.textLeadingMargin : self.textTrailingMargin; - - const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; - const int maxTextWidth = (int)floor(maxMessageWidth - hMarginTotal); - - self.quotedTextView.text = self.displayableBodyText.displayText; - // Honor dynamic type in the message bodies. - self.quotedTextView.font = [self textMessageFont]; - CGSize textSize = CGSizeCeil([self.quotedTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); - CGSize textViewSize = textSize; - - if (includeMargins) { - // TODO: - textViewSize.width += hMarginTotal; - textViewSize.height += self.textTopMargin + self.textBottomMargin; - } - - return CGSizeCeil(textViewSize); -} - - (CGSize)bodyMediaSizeForContentWidth:(int)contentWidth { const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; @@ -1319,15 +1290,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - // CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth]; - // CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES]; - // - // CGFloat cellContentWidth = fmax(mediaContentSize.width, textContentSize.width); - // CGFloat cellContentHeight = mediaContentSize.height + textContentSize.height; - // CGSize cellSize = CGSizeMake(cellContentWidth, cellContentHeight); - // - // OWSAssert(cellSize.width > 0 && cellSize.height > 0); - CGSize result = CGSizeZero; if (!self.isQuotedReply) { @@ -1337,24 +1299,57 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) result.width += self.quotedMessageHInset; result.width += self.quotedReplyStripeThickness; result.width += self.quotedReplyStripeHSpacing; - // TODO: Content width. result.height += self.quotedMessageTopInset; - result.height += self.quotedAuthorHeight; - result.height += self.quotedAuthorBottomSpacing; - // TODO: Content height. + CGFloat thumbnailHeight = 0.f; if (self.hasQuotedAttachmentThumbnail) { - result.width = MAX(result.width, self.quotedThumbnailHSpacing); - result.width = MAX(result.width, self.quotedThumbnailSize); + result.width += self.quotedThumbnailHSpacing; + result.width += self.quotedThumbnailSize; - result.height = MAX(result.height, self.quotedThumbnailSize); + thumbnailHeight = self.quotedThumbnailSize; } else { - result.width = MAX(result.width, self.quotedContentTrailingMargin); + result.width += self.quotedContentTrailingMargin; } result.width += self.quotedMessageHInset; + const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; + CGFloat maxContentWidth = (maxMessageWidth - (self.textTrailingMargin + self.textLeadingMargin + result.width)); + CGFloat textWidth = 0.f; + + // Author + { + OWSContactsManager *contactsManager = Environment.current.contactsManager; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId]; + + UILabel *quotedAuthorLabel = [UILabel new]; + quotedAuthorLabel.text = quotedAuthor; + quotedAuthorLabel.font = self.quotedAuthorFont; + quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; + quotedAuthorLabel.numberOfLines = 1; + + CGSize quotedAuthorSize = [quotedAuthorLabel sizeThatFits:CGSizeMake(maxContentWidth, CGFLOAT_MAX)]; + textWidth = MAX(textWidth, quotedAuthorSize.width); + result.height += self.quotedContentTopInset; + result.height += self.quotedAuthorHeight; + } + + if (self.hasQuotedText) { + UILabel *quotedTextView = [self createQuotedTextView]; + + CGSize textSize = CGSizeCeil([quotedTextView sizeThatFits:CGSizeMake(maxContentWidth, CGFLOAT_MAX)]); + + textWidth = MAX(textWidth, textSize.width); + result.height += self.quotedAuthorBottomSpacing; + result.height += textSize.height; + } + + result.width += textWidth; + result.height += self.quotedContentBottomInset; + + result.height = MAX(result.height, thumbnailHeight); + return result; } @@ -1414,74 +1409,51 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25); } +// TODO: - (UIFont *)quotedAuthorFont { return [UIFont ows_regularFontWithSize:10.f]; } +// TODO: - (CGFloat)quotedAuthorHeight { return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.25); } -- (CGFloat)quotedAuthorTopInset -{ - return 2.f; -} - +// TODO: - (CGFloat)quotedAuthorBottomSpacing { return 2.f; } -//- (UIFont *)quoteIndicatorFont -//{ -// return [UIFont ows_regularFontWithSize:10.f]; -//} -// -//- (CGFloat)quoteIndicatorHeight -//{ -// return (CGFloat)ceil([self quoteIndicatorFont].lineHeight * 1.25); -//} +// TODO: +- (CGFloat)quotedContentTopInset +{ + return 3.f; +} -//- (CGFloat)quotedMessageHMargin -//{ -// return 10.f; -//} - -//- (CGFloat)quotedMessageTopMargin -//{ -// return 10.f; -//} -// -//- (CGFloat)quotedMessageBottomMargin -//{ -// return 10.f; -//} -// -//- (CGFloat)quotedMessageHMargin -//{ -// return 10.f; -//} +// TODO: +- (CGFloat)quotedContentBottomInset +{ + return 3.f; +} // Distance from top edge of "quoted message" bubble to top of message bubble. +// TODO: - (CGFloat)quotedMessageTopInset { return 3.f; } -//// Distance from bottom edge of "quoted message" bubble to other bubble content. -//- (CGFloat)quotedMessageBottomInset -//{ -// return 3.f; -//} - // Distance from side of "quoted message" bubble to side of message bubble. +// TODO: - (CGFloat)quotedMessageHInset { return 3.f; } +// TODO: - (CGFloat)quotedReplyStripeThickness { return 3.f; @@ -1489,21 +1461,25 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) // The spacing between the vertical "quoted reply stripe" // and the quoted message content. +// TODO: - (CGFloat)quotedReplyStripeHSpacing { return 10.f; } +// TODO: - (CGFloat)quotedThumbnailSize { return 30.f; } +// TODO: - (CGFloat)quotedThumbnailHSpacing { return 10.f; } +// TODO: - (CGFloat)quotedContentTrailingMargin { return 10.f; @@ -1549,11 +1525,16 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return kBubbleTextVInset + kBubbleThornVInset; } -- (UIColor *)textColor +- (UIColor *)bodyTextColor { return self.isIncoming ? [UIColor blackColor] : [UIColor whiteColor]; } +- (UIColor *)quotedTextColor +{ + return [UIColor blackColor]; +} + - (BOOL)isMediaBeingSent { if (self.isIncoming) { diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 8dd152759e..0732492310 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -93,6 +93,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); - (nullable DisplayableText *)displayableQuotedText; - (nullable NSString *)quotedAttachmentMimetype; +- (nullable NSString *)quotedRecipientId; // We don't want to try to load the media for this item (if any) // if a load has previously failed. From f6f98369a6e125a6bd024d55b0ee604c4c2d7c9d Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 13:37:54 -0400 Subject: [PATCH 4/9] Modify cells to show quoted messages. --- .../ConversationView/Cells/OWSMessageCell.m | 76 ++++++++++++++++--- .../ViewControllers/DebugUI/DebugUIMessages.m | 13 ++++ 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 1ebab0d745..27634288a1 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -392,13 +392,18 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [self.bubbleView addSubview:quotedMessageView]; - CGFloat leadingMargin = self.quotedMessageHInset; - CGFloat trailingMargin = self.quotedMessageHInset; - if (self.isIncoming) { - leadingMargin += kBubbleThornSideInset; - } else { - trailingMargin += kBubbleThornSideInset; - } + CGFloat leadingMargin = self.quotedBubbleLeadingMargin; + CGFloat trailingMargin = self.quotedBubbleTrailingMargin; + + // CGFloat leadingMargin = self.quotedMessageHInset + self.textLeadingMargin; + // CGFloat trailingMargin = self.quotedMessageHInset + self.textTrailingMargin; + // CGFloat leadingMargin = self.quotedMessageHInset; + // CGFloat trailingMargin = self.quotedMessageHInset; + // if (self.isIncoming) { + // leadingMargin += kBubbleThornSideInset; + // } else { + // trailingMargin += kBubbleThornSideInset; + // } [self.viewConstraints addObjectsFromArray:@[ [quotedMessageView autoPinLeadingToSuperviewWithMargin:leadingMargin], @@ -1285,7 +1290,9 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return (int)floor(contentWidth * 0.8f); } -- (CGSize)quotedMessageSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth +- (CGSize)quotedMessageSizeForViewWidth:(int)viewWidth + contentWidth:(int)contentWidth + includeMargins:(BOOL)includeMargins { OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); @@ -1314,10 +1321,15 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) result.width += self.quotedMessageHInset; + // Once we've determined everything _except_ the size of the text + // content (i.e. the quoted author and the quoted text (if any)), + // we can determine the size of the text content. const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; - CGFloat maxContentWidth = (maxMessageWidth - (self.textTrailingMargin + self.textLeadingMargin + result.width)); + CGFloat maxTextWidth = (maxMessageWidth - (self.textTrailingMargin + self.textLeadingMargin + result.width)); CGFloat textWidth = 0.f; + DDLogInfo(@"%@ --- maxTextWidth: %f", self.logTag, maxTextWidth); + // Author { OWSContactsManager *contactsManager = Environment.current.contactsManager; @@ -1329,7 +1341,10 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; quotedAuthorLabel.numberOfLines = 1; - CGSize quotedAuthorSize = [quotedAuthorLabel sizeThatFits:CGSizeMake(maxContentWidth, CGFLOAT_MAX)]; + CGSize quotedAuthorSize = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + + DDLogInfo(@"%@ --- quotedAuthorSize.width: %f", self.logTag, quotedAuthorSize.width); + textWidth = MAX(textWidth, quotedAuthorSize.width); result.height += self.quotedContentTopInset; result.height += self.quotedAuthorHeight; @@ -1338,18 +1353,31 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) if (self.hasQuotedText) { UILabel *quotedTextView = [self createQuotedTextView]; - CGSize textSize = CGSizeCeil([quotedTextView sizeThatFits:CGSizeMake(maxContentWidth, CGFLOAT_MAX)]); + CGSize textSize = CGSizeCeil([quotedTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + + DDLogInfo(@"%@ --- textSize.width: %f", self.logTag, textSize.width); textWidth = MAX(textWidth, textSize.width); result.height += self.quotedAuthorBottomSpacing; result.height += textSize.height; } + DDLogInfo(@"%@ --- textWidth: %f", self.logTag, textWidth); + DDLogInfo(@"%@ --- quotedMessageSizeForViewWidth 3: %@", self.logTag, NSStringFromCGSize(result)); + result.width += textWidth; result.height += self.quotedContentBottomInset; result.height = MAX(result.height, thumbnailHeight); + DDLogInfo(@"%@ --- quotedMessageSizeForViewWidth 4: %@", self.logTag, NSStringFromCGSize(result)); + + if (includeMargins) { + result.width += kBubbleThornSideInset; + } + + DDLogInfo(@"%@ --- quotedMessageSizeForViewWidth 5: %@", self.logTag, NSStringFromCGSize(result)); + return result; } @@ -1360,15 +1388,19 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGSize cellSize = CGSizeZero; - CGSize quotedMessageSize = [self quotedMessageSizeForViewWidth:viewWidth contentWidth:contentWidth]; + CGSize quotedMessageSize = + [self quotedMessageSizeForViewWidth:viewWidth contentWidth:contentWidth includeMargins:YES]; + DDLogInfo(@"%@ --- quotedMessageSize: %@", self.logTag, NSStringFromCGSize(quotedMessageSize)); cellSize.width = MAX(cellSize.width, quotedMessageSize.width); cellSize.height += quotedMessageSize.height; CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth]; + DDLogInfo(@"%@ --- mediaContentSize: %@", self.logTag, NSStringFromCGSize(mediaContentSize)); cellSize.width = MAX(cellSize.width, mediaContentSize.width); cellSize.height += mediaContentSize.height; CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES]; + DDLogInfo(@"%@ --- textContentSize: %@", self.logTag, NSStringFromCGSize(textContentSize)); cellSize.width = MAX(cellSize.width, textContentSize.width); cellSize.height += textContentSize.height; @@ -1386,6 +1418,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) cellSize = CGSizeCeil(cellSize); + DDLogInfo(@"%@ --- cellSize: %@", self.logTag, NSStringFromCGSize(cellSize)); + return cellSize; } @@ -1515,6 +1549,24 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return result; } +- (CGFloat)quotedBubbleLeadingMargin +{ + CGFloat result = self.quotedMessageHInset; + if (self.isIncoming) { + result += kBubbleThornSideInset; + } + return result; +} + +- (CGFloat)quotedBubbleTrailingMargin +{ + CGFloat result = self.quotedMessageHInset; + if (!self.isIncoming) { + result += kBubbleThornSideInset; + } + return result; +} + - (CGFloat)textTopMargin { return kBubbleTextVInset; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index cdeb528bf8..1ecb573958 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -87,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN [DebugUIMessages performActionNTimes:action]; }]]; } + [items addObjectsFromArray:@[ #pragma mark - Actions @@ -103,6 +104,10 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMessages selectSendMediaAction:thread]; }], + [OWSTableItem itemWithTitle:@"Select Quoted Reply" + actionBlock:^{ + [DebugUIMessages selectQuotedReplyAction:thread]; + }], #pragma mark - Misc. @@ -2464,6 +2469,14 @@ isQuotedMessageAttachmentDownloaded:(BOOL)isQuotedMessageAttachmentDownloaded subactions:[self allFakeQuotedReplyActions:thread includeLabels:YES]]; } ++ (void)selectQuotedReplyAction:(TSThread *)thread +{ + OWSAssertIsOnMainThread(); + OWSAssert(thread); + + [self selectActionUI:[self allFakeQuotedReplyActions:thread includeLabels:NO] label:@"Select QuotedReply"]; +} + + (DebugUIMessagesAction *)randomQuotedReplyAction:(TSThread *)thread { OWSAssert(thread); From 988b6ffae796285724c4aadd4c620eb711d7987b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 13:38:46 -0400 Subject: [PATCH 5/9] Modify cells to show quoted messages. --- .../ConversationView/Cells/OWSMessageCell.m | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 27634288a1..03981cd69a 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -395,16 +395,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGFloat leadingMargin = self.quotedBubbleLeadingMargin; CGFloat trailingMargin = self.quotedBubbleTrailingMargin; - // CGFloat leadingMargin = self.quotedMessageHInset + self.textLeadingMargin; - // CGFloat trailingMargin = self.quotedMessageHInset + self.textTrailingMargin; - // CGFloat leadingMargin = self.quotedMessageHInset; - // CGFloat trailingMargin = self.quotedMessageHInset; - // if (self.isIncoming) { - // leadingMargin += kBubbleThornSideInset; - // } else { - // trailingMargin += kBubbleThornSideInset; - // } - [self.viewConstraints addObjectsFromArray:@[ [quotedMessageView autoPinLeadingToSuperviewWithMargin:leadingMargin], [quotedMessageView autoPinTrailingToSuperviewWithMargin:trailingMargin], @@ -1343,8 +1333,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGSize quotedAuthorSize = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); - DDLogInfo(@"%@ --- quotedAuthorSize.width: %f", self.logTag, quotedAuthorSize.width); - textWidth = MAX(textWidth, quotedAuthorSize.width); result.height += self.quotedContentTopInset; result.height += self.quotedAuthorHeight; @@ -1355,29 +1343,20 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGSize textSize = CGSizeCeil([quotedTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); - DDLogInfo(@"%@ --- textSize.width: %f", self.logTag, textSize.width); - textWidth = MAX(textWidth, textSize.width); result.height += self.quotedAuthorBottomSpacing; result.height += textSize.height; } - DDLogInfo(@"%@ --- textWidth: %f", self.logTag, textWidth); - DDLogInfo(@"%@ --- quotedMessageSizeForViewWidth 3: %@", self.logTag, NSStringFromCGSize(result)); - result.width += textWidth; result.height += self.quotedContentBottomInset; result.height = MAX(result.height, thumbnailHeight); - DDLogInfo(@"%@ --- quotedMessageSizeForViewWidth 4: %@", self.logTag, NSStringFromCGSize(result)); - if (includeMargins) { result.width += kBubbleThornSideInset; } - DDLogInfo(@"%@ --- quotedMessageSizeForViewWidth 5: %@", self.logTag, NSStringFromCGSize(result)); - return result; } @@ -1390,17 +1369,14 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGSize quotedMessageSize = [self quotedMessageSizeForViewWidth:viewWidth contentWidth:contentWidth includeMargins:YES]; - DDLogInfo(@"%@ --- quotedMessageSize: %@", self.logTag, NSStringFromCGSize(quotedMessageSize)); cellSize.width = MAX(cellSize.width, quotedMessageSize.width); cellSize.height += quotedMessageSize.height; CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth]; - DDLogInfo(@"%@ --- mediaContentSize: %@", self.logTag, NSStringFromCGSize(mediaContentSize)); cellSize.width = MAX(cellSize.width, mediaContentSize.width); cellSize.height += mediaContentSize.height; CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES]; - DDLogInfo(@"%@ --- textContentSize: %@", self.logTag, NSStringFromCGSize(textContentSize)); cellSize.width = MAX(cellSize.width, textContentSize.width); cellSize.height += textContentSize.height; @@ -1418,8 +1394,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) cellSize = CGSizeCeil(cellSize); - DDLogInfo(@"%@ --- cellSize: %@", self.logTag, NSStringFromCGSize(cellSize)); - return cellSize; } From 5824cbd2a8a3acf9328a04be008dcda0b5afe08d Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 13:39:09 -0400 Subject: [PATCH 6/9] Modify cells to show quoted messages. --- .../src/ViewControllers/ConversationView/Cells/OWSMessageCell.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 03981cd69a..a27a8a5916 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -1318,8 +1318,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGFloat maxTextWidth = (maxMessageWidth - (self.textTrailingMargin + self.textLeadingMargin + result.width)); CGFloat textWidth = 0.f; - DDLogInfo(@"%@ --- maxTextWidth: %f", self.logTag, maxTextWidth); - // Author { OWSContactsManager *contactsManager = Environment.current.contactsManager; From 324afb11524126c4c9bc7356316b540e18aeaebb Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 14:39:40 -0400 Subject: [PATCH 7/9] Modify cells to show quoted messages. --- .../ConversationView/Cells/OWSMessageCell.m | 24 +++++++++---------- .../ViewControllers/DebugUI/DebugUIMessages.m | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index a27a8a5916..e6a11c1dc9 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -396,8 +396,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) CGFloat trailingMargin = self.quotedBubbleTrailingMargin; [self.viewConstraints addObjectsFromArray:@[ - [quotedMessageView autoPinLeadingToSuperviewWithMargin:leadingMargin], - [quotedMessageView autoPinTrailingToSuperviewWithMargin:trailingMargin], + [quotedMessageView autoPinLeadingToSuperviewMarginWithInset:leadingMargin], + [quotedMessageView autoPinTrailingToSuperviewMarginWithInset:trailingMargin], ]]; if (lastSubview) { @@ -882,7 +882,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) quoteStripView.userInteractionEnabled = NO; [quotedMessageView addSubview:quoteStripView]; [quoteStripView autoPinHeightToSuperview]; - [quoteStripView autoPinLeadingToSuperview]; + [quoteStripView autoPinLeadingToSuperviewMargin]; [quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness]; UIView *_Nullable quotedThumbnailView = nil; @@ -892,8 +892,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) quotedThumbnailView.userInteractionEnabled = NO; quotedThumbnailView.backgroundColor = [UIColor redColor]; [quotedMessageView addSubview:quotedThumbnailView]; - [quotedThumbnailView autoPinTopToSuperview]; - [quotedThumbnailView autoPinTrailingToSuperview]; + [quotedThumbnailView autoPinTopToSuperviewMargin]; + [quotedThumbnailView autoPinTrailingToSuperviewMargin]; [quotedThumbnailView autoSetDimension:ALDimensionWidth toSize:self.quotedThumbnailSize]; [quotedThumbnailView autoSetDimension:ALDimensionHeight toSize:self.quotedThumbnailSize]; } @@ -910,11 +910,11 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; [quotedMessageView addSubview:quotedAuthorLabel]; [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedContentTopInset]; - [quotedAuthorLabel autoPinLeadingToView:quoteStripView margin:self.quotedReplyStripeHSpacing]; + [quotedAuthorLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; if (quotedThumbnailView) { - [quotedAuthorLabel autoPinTrailingToView:quotedThumbnailView margin:self.quotedThumbnailHSpacing]; + [quotedAuthorLabel autoPinTrailingToEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; } else { - [quotedAuthorLabel autoPinTrailingToSuperviewWithMargin:self.quotedContentTrailingMargin]; + [quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; } if (self.hasQuotedText) { @@ -925,13 +925,13 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) toEdge:ALEdgeBottom ofView:quotedAuthorLabel withOffset:self.quotedAuthorBottomSpacing]; - [quotedTextView autoPinLeadingToView:quoteStripView margin:self.quotedReplyStripeHSpacing]; + [quotedTextView autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; if (quotedThumbnailView) { - [quotedTextView autoPinTrailingToView:quotedThumbnailView margin:self.quotedThumbnailHSpacing]; + [quotedTextView autoPinLeadingToTrailingEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; } else { - [quotedTextView autoPinTrailingToSuperviewWithMargin:self.quotedContentTrailingMargin]; + [quotedTextView autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; } - [quotedTextView autoPinBottomToSuperviewWithMargin:self.quotedContentBottomInset]; + [quotedTextView autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset]; } return quotedMessageView; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 1ecb573958..846d4b9dd4 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -2496,6 +2496,7 @@ isQuotedMessageAttachmentDownloaded:(BOOL)isQuotedMessageAttachmentDownloaded [actions addObjectsFromArray:[self allFakeMediaActions:thread includeLabels:includeLabels]]; [actions addObjectsFromArray:[self allFakeTextActions:thread includeLabels:includeLabels]]; [actions addObjectsFromArray:[self allFakeSequenceActions:thread includeLabels:includeLabels]]; + [actions addObjectsFromArray:[self allFakeQuotedReplyActions:thread includeLabels:includeLabels]]; return actions; } From 445d38f72ad265ae0a57542122e58e0b9c5d4210 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 14:49:45 -0400 Subject: [PATCH 8/9] Modify cells to show quoted messages. --- Signal/src/ViewControllers/HomeViewController.m | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Signal/src/ViewControllers/HomeViewController.m b/Signal/src/ViewControllers/HomeViewController.m index 3a6bc2ea10..1f95eb20aa 100644 --- a/Signal/src/ViewControllers/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeViewController.m @@ -284,15 +284,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; } [self updateBarButtonItems]; - - dispatch_async(dispatch_get_main_queue(), ^{ - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; - TSThread *thread = [self threadForIndexPath:indexPath]; - if (!thread) { - return; - } - [self presentThread:thread keyboardOnViewAppearing:NO callOnViewAppearing:NO]; - }); } - (void)viewDidAppear:(BOOL)animated From 00a81355d6f642ba05e4b8f2317e572eabc49edf Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 10:27:30 -0400 Subject: [PATCH 9/9] Respond to CR. --- .../ConversationView/Cells/OWSMessageCell.m | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index e6a11c1dc9..844610a445 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -43,7 +43,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) @property (nonatomic) UILabel *dateHeaderLabel; @property (nonatomic) OWSMessageTextView *bodyTextView; -@property (nonatomic) UILabel *quotedTextView; @property (nonatomic, nullable) UIImageView *failedSendBadgeView; @property (nonatomic) UIView *footerView; @property (nonatomic) UILabel *footerLabel; @@ -101,10 +100,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) self.bodyTextView.dataDetectorTypes = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); - self.quotedTextView = [UILabel new]; - self.quotedTextView.numberOfLines = 3; - self.quotedTextView.lineBreakMode = NSLineBreakByWordWrapping; - self.footerLabel = [UILabel new]; self.footerLabel.font = [UIFont ows_regularFontWithSize:12.f]; self.footerLabel.textColor = [UIColor lightGrayColor]; @@ -112,7 +107,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) // Hide these views by default. self.bodyTextView.hidden = YES; - self.quotedTextView.hidden = YES; self.dateHeaderLabel.hidden = YES; self.footerLabel.hidden = YES; @@ -515,7 +509,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) // We render malformed messages as "empty text" messages, // so create a text view if there is no body media view. if (self.hasBodyText || !bodyMediaView) { - bodyTextView = [self createBodyTextViewIfNecessary]; + bodyTextView = [self configureBodyTextView]; } if (bodyTextView) { [self.bubbleView addSubview:bodyTextView]; @@ -792,19 +786,23 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return [UIFont systemFontOfSize:12.0f]; } -- (UILabel *)createQuotedTextView +- (UILabel *)createQuotedTextLabel { - self.quotedTextView.hidden = NO; - self.quotedTextView.text = self.displayableQuotedText.displayText; - self.quotedTextView.textColor = self.quotedTextColor; + UILabel *quotedTextLabel = [UILabel new]; + quotedTextLabel.numberOfLines = 3; + quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping; + quotedTextLabel.text = self.displayableQuotedText.displayText; + quotedTextLabel.textColor = self.quotedTextColor; // Honor dynamic type in the message bodies. - self.quotedTextView.font = self.textMessageFont; - return self.quotedTextView; + quotedTextLabel.font = self.textMessageFont; + return quotedTextLabel; } -- (OWSMessageTextView *)createBodyTextViewIfNecessary +- (OWSMessageTextView *)configureBodyTextView { + OWSAssert(self.hasBodyText); + BOOL shouldIgnoreEvents = NO; if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { // Ignore taps on links in outgoing messages that haven't been sent yet, as @@ -918,20 +916,21 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) } if (self.hasQuotedText) { - UILabel *quotedTextView = [self createQuotedTextView]; + UILabel *quotedTextLabel = [self createQuotedTextLabel]; - [quotedMessageView addSubview:quotedTextView]; - [quotedTextView autoPinEdge:ALEdgeTop - toEdge:ALEdgeBottom - ofView:quotedAuthorLabel - withOffset:self.quotedAuthorBottomSpacing]; - [quotedTextView autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; + [quotedMessageView addSubview:quotedTextLabel]; + [quotedTextLabel autoPinEdge:ALEdgeTop + toEdge:ALEdgeBottom + ofView:quotedAuthorLabel + withOffset:self.quotedAuthorBottomSpacing]; + [quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; if (quotedThumbnailView) { - [quotedTextView autoPinLeadingToTrailingEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; + [quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quotedThumbnailView + offset:self.quotedThumbnailHSpacing]; } else { - [quotedTextView autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; + [quotedTextLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; } - [quotedTextView autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset]; + [quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset]; } return quotedMessageView; @@ -1217,10 +1216,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin)); - self.bodyTextView.text = self.displayableBodyText.displayText; - // Honor dynamic type in the message bodies. - self.bodyTextView.font = [self textMessageFont]; - CGSize textSize = CGSizeCeil([self.bodyTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + OWSMessageTextView *bodyTextView = [self configureBodyTextView]; + CGSize textSize = CGSizeCeil([bodyTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); CGSize textViewSize = textSize; if (includeMargins) { @@ -1337,9 +1334,9 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) } if (self.hasQuotedText) { - UILabel *quotedTextView = [self createQuotedTextView]; + UILabel *quotedTextLabel = [self createQuotedTextLabel]; - CGSize textSize = CGSizeCeil([quotedTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); textWidth = MAX(textWidth, textSize.width); result.height += self.quotedAuthorBottomSpacing; @@ -1589,9 +1586,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) self.dateHeaderLabel.text = nil; self.dateHeaderLabel.hidden = YES; [self.bodyTextView removeFromSuperview]; - self.quotedTextView.text = nil; - self.quotedTextView.hidden = YES; - [self.quotedTextView removeFromSuperview]; self.bodyTextView.text = nil; self.bodyTextView.hidden = YES; [self.failedSendBadgeView removeFromSuperview];