From 3343b4ec58360d1a0f6b8a807e0b5511e6ca2c3e Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 3 Apr 2018 17:27:14 -0400 Subject: [PATCH 01/10] Refine appearance of quoted reply message cells. --- .../ConversationView/Cells/OWSMessageCell.m | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 58f5b30c7..24a572396 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -406,9 +406,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) lastSubview = quotedMessageView; bottomMargin = 0; - [self.bubbleView logFrameLaterWithLabel:@"bubbleView"]; - [quotedMessageView logFrameLaterWithLabel:@"quotedMessageView"]; - // TODO: Consider stroking the quoted thumbnail. } @@ -882,6 +879,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [quoteStripView autoPinHeightToSuperview]; [quoteStripView autoPinLeadingToSuperviewMargin]; [quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness]; + [quoteStripView setContentHuggingHigh]; + [quoteStripView setCompressionResistanceHigh]; UIView *_Nullable quotedThumbnailView = nil; if (self.hasQuotedAttachmentThumbnail) { @@ -894,6 +893,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [quotedThumbnailView autoPinTrailingToSuperviewMargin]; [quotedThumbnailView autoSetDimension:ALDimensionWidth toSize:self.quotedThumbnailSize]; [quotedThumbnailView autoSetDimension:ALDimensionHeight toSize:self.quotedThumbnailSize]; + [quotedThumbnailView setContentHuggingHigh]; + [quotedThumbnailView setCompressionResistanceHigh]; } OWSContactsManager *contactsManager = Environment.current.contactsManager; @@ -910,10 +911,13 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedContentTopInset]; [quotedAuthorLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; if (quotedThumbnailView) { - [quotedAuthorLabel autoPinTrailingToEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; + [quotedAuthorLabel autoPinTrailingToLeadingEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; } else { [quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; } + [quotedAuthorLabel autoSetDimension:ALDimensionHeight toSize:self.quotedAuthorHeight]; + [quotedAuthorLabel setContentHuggingLow]; + [quotedAuthorLabel setCompressionResistanceLow]; if (self.hasQuotedText) { UILabel *quotedTextLabel = [self createQuotedTextLabel]; @@ -925,12 +929,14 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) withOffset:self.quotedAuthorBottomSpacing]; [quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; if (quotedThumbnailView) { - [quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quotedThumbnailView + [quotedTextLabel autoPinTrailingToLeadingEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; } else { [quotedTextLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; } [quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset]; + [quotedTextLabel setContentHuggingLow]; + [quotedTextLabel setCompressionResistanceLow]; } return quotedMessageView; From 10b4ade55acee654bb54fcece0dca198236948cd Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 11:31:33 -0400 Subject: [PATCH 02/10] Refine appearance of quoted reply message cells. --- Signal.xcodeproj/project.pbxproj | 6 + .../ConversationView/Cells/OWSMessageCell.m | 309 +--------------- .../Cells/OWSQuotedMessageView.h | 26 ++ .../Cells/OWSQuotedMessageView.m | 347 ++++++++++++++++++ SignalMessaging/categories/UIView+OWS.h | 7 + 5 files changed, 402 insertions(+), 293 deletions(-) create mode 100644 Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h create mode 100644 Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 77e277bf9..ca30b3827 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CC20518C76007AEB0F /* OWSBackupJob.m */; }; 340FC8D0205BF2FA007AEB0F /* OWSBackupIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CE205BF2FA007AEB0F /* OWSBackupIO.m */; }; 341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */; }; + 34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */; }; 3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430FE171F7751D4000EC51B /* GiphyAPI.swift */; }; 34330A5A1E7875FB00DF2FB9 /* fontawesome-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */; }; 34330A5C1E787A9800DF2FB9 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; }; @@ -602,6 +603,8 @@ 341458471FBE11C4005ABCF9 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = translations/fa.lproj/Localizable.strings; sourceTree = ""; }; 341F2C0D1F2B8AE700D07D6B /* DebugUIMisc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMisc.h; sourceTree = ""; }; 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMisc.m; sourceTree = ""; }; + 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQuotedMessageView.m; sourceTree = ""; }; + 34277A5D20751BDC006049F2 /* OWSQuotedMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQuotedMessageView.h; sourceTree = ""; }; 3430FE171F7751D4000EC51B /* GiphyAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPI.swift; sourceTree = ""; }; 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fontawesome-webfont.ttf"; sourceTree = ""; }; 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dripicons-v2.ttf"; sourceTree = ""; }; @@ -1665,6 +1668,8 @@ 34D1F0A21F867BFC0066283D /* OWSMessageCell.m */, 34DBF000206BD5A400025978 /* OWSMessageTextView.h */, 34DBEFFF206BD5A400025978 /* OWSMessageTextView.m */, + 34277A5D20751BDC006049F2 /* OWSQuotedMessageView.h */, + 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */, 34D1F0A51F867BFC0066283D /* OWSSystemMessageCell.h */, 34D1F0A61F867BFC0066283D /* OWSSystemMessageCell.m */, 34D1F0A71F867BFC0066283D /* OWSUnreadIndicatorCell.h */, @@ -3251,6 +3256,7 @@ 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */, 45F659731E1BD99C00444429 /* CallKitCallUIAdaptee.swift in Sources */, 45BB93381E688E14001E3939 /* UIDevice+featureSupport.swift in Sources */, + 34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */, 458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */, 45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */, 34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 24a572396..8e31d7194 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -13,6 +13,7 @@ #import "OWSExpirationTimerView.h" #import "OWSGenericAttachmentView.h" #import "OWSMessageTextView.h" +#import "OWSQuotedMessageView.h" #import "Signal-Swift.h" #import "UIColor+OWS.h" #import @@ -21,11 +22,6 @@ NS_ASSUME_NONNULL_BEGIN -CG_INLINE CGSize CGSizeCeil(CGSize size) -{ - return CGSizeMake((CGFloat)ceil(size.width), (CGFloat)ceil(size.height)); -} - @interface OWSMessageCell () // The nullable properties are created as needed. @@ -266,23 +262,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) 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]]); @@ -382,31 +361,26 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) if (self.isQuotedReply) { OWSAssert(!lastSubview); - UIView *quotedMessageView = [self createQuotedMessageView]; - + OWSQuotedMessageView *quotedMessageView = + [[OWSQuotedMessageView alloc] initWithViewItem:self.viewItem textMessageFont:self.textMessageFont]; + [quotedMessageView createContents]; [self.bubbleView addSubview:quotedMessageView]; - CGFloat leadingMargin = self.quotedBubbleLeadingMargin; - CGFloat trailingMargin = self.quotedBubbleTrailingMargin; - + CGFloat bubbleLeadingMargin = (self.isIncoming ? kBubbleThornSideInset : 0.f); + CGFloat bubbleTrailingMargin = (self.isIncoming ? 0.f : kBubbleThornSideInset); [self.viewConstraints addObjectsFromArray:@[ - [quotedMessageView autoPinLeadingToSuperviewMarginWithInset:leadingMargin], - [quotedMessageView autoPinTrailingToSuperviewMarginWithInset:trailingMargin], + [quotedMessageView autoPinLeadingToSuperviewMarginWithInset:bubbleLeadingMargin], + [quotedMessageView autoPinTrailingToSuperviewMarginWithInset:bubbleTrailingMargin], ]]; if (lastSubview) { - [self.viewConstraints addObject:[quotedMessageView autoPinEdge:ALEdgeTop - toEdge:ALEdgeBottom - ofView:lastSubview - withOffset:self.quotedMessageTopInset]]; + [self.viewConstraints + addObject:[quotedMessageView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:lastSubview]]; } else { - [self.viewConstraints addObject:[quotedMessageView autoPinEdgeToSuperviewEdge:ALEdgeTop - withInset:self.quotedMessageTopInset]]; + [self.viewConstraints addObject:[quotedMessageView autoPinEdgeToSuperviewEdge:ALEdgeTop]]; } lastSubview = quotedMessageView; bottomMargin = 0; - - // TODO: Consider stroking the quoted thumbnail. } UIView *_Nullable bodyMediaView = nil; @@ -783,19 +757,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return [UIFont systemFontOfSize:12.0f]; } -- (UILabel *)createQuotedTextLabel -{ - 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. - quotedTextLabel.font = self.textMessageFont; - return quotedTextLabel; -} - - (OWSMessageTextView *)configureBodyTextView { OWSAssert(self.hasBodyText); @@ -861,87 +822,6 @@ 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 autoPinLeadingToSuperviewMargin]; - [quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness]; - [quoteStripView setContentHuggingHigh]; - [quoteStripView setCompressionResistanceHigh]; - - UIView *_Nullable quotedThumbnailView = nil; - if (self.hasQuotedAttachmentThumbnail) { - // TODO: - quotedThumbnailView = [UIView containerView]; - quotedThumbnailView.userInteractionEnabled = NO; - quotedThumbnailView.backgroundColor = [UIColor redColor]; - [quotedMessageView addSubview:quotedThumbnailView]; - [quotedThumbnailView autoPinTopToSuperviewMargin]; - [quotedThumbnailView autoPinTrailingToSuperviewMargin]; - [quotedThumbnailView autoSetDimension:ALDimensionWidth toSize:self.quotedThumbnailSize]; - [quotedThumbnailView autoSetDimension:ALDimensionHeight toSize:self.quotedThumbnailSize]; - [quotedThumbnailView setContentHuggingHigh]; - [quotedThumbnailView setCompressionResistanceHigh]; - } - - 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.quotedContentTopInset]; - [quotedAuthorLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; - if (quotedThumbnailView) { - [quotedAuthorLabel autoPinTrailingToLeadingEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing]; - } else { - [quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; - } - [quotedAuthorLabel autoSetDimension:ALDimensionHeight toSize:self.quotedAuthorHeight]; - [quotedAuthorLabel setContentHuggingLow]; - [quotedAuthorLabel setCompressionResistanceLow]; - - if (self.hasQuotedText) { - UILabel *quotedTextLabel = [self createQuotedTextLabel]; - - [quotedMessageView addSubview:quotedTextLabel]; - [quotedTextLabel autoPinEdge:ALEdgeTop - toEdge:ALEdgeBottom - ofView:quotedAuthorLabel - withOffset:self.quotedAuthorBottomSpacing]; - [quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; - if (quotedThumbnailView) { - [quotedTextLabel autoPinTrailingToLeadingEdgeOfView:quotedThumbnailView - offset:self.quotedThumbnailHSpacing]; - } else { - [quotedTextLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin]; - } - [quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset]; - [quotedTextLabel setContentHuggingLow]; - [quotedTextLabel setCompressionResistanceLow]; - } - - return quotedMessageView; -} - - (UIView *)loadViewForStillImage { OWSAssert(self.attachmentStream); @@ -1290,73 +1170,15 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - CGSize result = CGSizeZero; - if (!self.isQuotedReply) { - return result; - } - - result.width += self.quotedMessageHInset; - result.width += self.quotedReplyStripeThickness; - result.width += self.quotedReplyStripeHSpacing; - - result.height += self.quotedMessageTopInset; - - CGFloat thumbnailHeight = 0.f; - if (self.hasQuotedAttachmentThumbnail) { - result.width += self.quotedThumbnailHSpacing; - result.width += self.quotedThumbnailSize; - - thumbnailHeight = self.quotedThumbnailSize; - } else { - result.width += self.quotedContentTrailingMargin; + return CGSizeZero; } - 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. + OWSQuotedMessageView *quotedMessageView = + [[OWSQuotedMessageView alloc] initWithViewItem:self.viewItem textMessageFont:self.textMessageFont]; const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; - CGFloat maxTextWidth = (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 = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); - - textWidth = MAX(textWidth, quotedAuthorSize.width); - result.height += self.quotedContentTopInset; - result.height += self.quotedAuthorHeight; - } - - if (self.hasQuotedText) { - UILabel *quotedTextLabel = [self createQuotedTextLabel]; - - CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxTextWidth, 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); - - if (includeMargins) { - result.width += kBubbleThornSideInset; - } + CGSize result = [quotedMessageView sizeForMaxWidth:maxMessageWidth - kBubbleThornSideInset]; + result.width += kBubbleThornSideInset; return result; } @@ -1418,82 +1240,6 @@ 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); -} - -// TODO: -- (CGFloat)quotedAuthorBottomSpacing -{ - return 2.f; -} - -// TODO: -- (CGFloat)quotedContentTopInset -{ - return 3.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 side of "quoted message" bubble to side of message bubble. -// TODO: -- (CGFloat)quotedMessageHInset -{ - return 3.f; -} - -// TODO: -- (CGFloat)quotedReplyStripeThickness -{ - return 3.f; -} - -// 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; -} - #pragma mark - - (BOOL)isIncoming @@ -1524,24 +1270,6 @@ 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; @@ -1557,11 +1285,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size) return self.isIncoming ? [UIColor blackColor] : [UIColor whiteColor]; } -- (UIColor *)quotedTextColor -{ - return [UIColor blackColor]; -} - - (BOOL)isMediaBeingSent { if (self.isIncoming) { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h new file mode 100644 index 000000000..45cc7ce14 --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@class ConversationViewItem; +@class TSQuotedMessage; + +@interface OWSQuotedMessageView : UIView + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithViewItem:(ConversationViewItem *)viewItem + // quotedMessage:(TSQuotedMessage *)quotedMessage + textMessageFont:(UIFont *)textMessageFont; + +// Only needs to be called if we're going to render this instance. +- (void)createContents; + +// Measurement +- (CGSize)sizeForMaxWidth:(CGFloat)maxWidth; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m new file mode 100644 index 000000000..39d5a0b67 --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -0,0 +1,347 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import "OWSQuotedMessageView.h" +#import "ConversationViewItem.h" +#import "Environment.h" +#import "Signal-Swift.h" +#import +#import +#import +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSQuotedMessageView () + +@property (nonatomic, readonly) ConversationViewItem *viewItem; +@property (nonatomic, readonly) UIFont *textMessageFont; + +@end + +@implementation OWSQuotedMessageView + +- (instancetype)initWithViewItem:(ConversationViewItem *)viewItem + // quotedMessage:(TSQuotedMessage *)quotedMessage + textMessageFont:(UIFont *)textMessageFont +{ + self = [super init]; + + if (!self) { + return self; + } + + OWSAssert(viewItem); + // OWSAssert(quotedMessage); + OWSAssert(textMessageFont); + + _viewItem = viewItem; + // _quotedMessage = quotedMessage; + _textMessageFont = textMessageFont; + + return self; +} + +- (BOOL)isIncoming +{ + return self.viewItem.interaction.interactionType == OWSInteractionType_IncomingMessage; +} + +- (BOOL)hasQuotedAttachmentThumbnail +{ + // This should always be valid for the appropriate cell types. + OWSAssert(self.viewItem); + + return (self.viewItem.hasQuotedAttachment && + [TSAttachmentStream hasThumbnailForMimeType:self.viewItem.quotedAttachmentMimetype]); +} + +#pragma mark - + +- (void)createContents +{ + OWSAssert(self.viewItem.isQuotedReply); + + self.backgroundColor = [UIColor whiteColor]; + self.userInteractionEnabled = NO; + self.layoutMargins = UIEdgeInsetsZero; + self.clipsToBounds = YES; + + UIView *_Nullable quotedAttachmentView = nil; + // TODO: + // if (self.hasQuotedAttachmentThumbnail) + { + // TODO: + quotedAttachmentView = [UIView containerView]; + quotedAttachmentView.userInteractionEnabled = NO; + quotedAttachmentView.backgroundColor = [UIColor redColor]; + [self addSubview:quotedAttachmentView]; + [quotedAttachmentView autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset]; + [quotedAttachmentView autoVCenterInSuperview]; + [quotedAttachmentView autoSetDimension:ALDimensionWidth toSize:self.quotedAttachmentSize]; + [quotedAttachmentView autoSetDimension:ALDimensionHeight toSize:self.quotedAttachmentSize]; + [quotedAttachmentView setContentHuggingHigh]; + [quotedAttachmentView setCompressionResistanceHigh]; + + // TODO: Consider stroking the quoted thumbnail. + } + + 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; + [self addSubview:quotedAuthorLabel]; + [quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedAuthorTopInset]; + [quotedAuthorLabel autoPinLeadingToSuperviewMarginWithInset:self.quotedContentHInset]; + if (quotedAttachmentView) { + [quotedAuthorLabel autoPinTrailingToLeadingEdgeOfView:quotedAttachmentView + offset:self.quotedAttachmentHSpacing]; + } else { + [quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset]; + } + [quotedAuthorLabel autoSetDimension:ALDimensionHeight toSize:self.quotedAuthorHeight]; + [quotedAuthorLabel setContentHuggingLow]; + [quotedAuthorLabel setCompressionResistanceLow]; + } + + { + // Stripe and text container. + UIView *stripeAndTextContainer = [UIView containerView]; + [self addSubview:stripeAndTextContainer]; + [stripeAndTextContainer autoPinEdge:ALEdgeTop + toEdge:ALEdgeBottom + ofView:quotedAuthorLabel + withOffset:self.quotedAuthorBottomSpacing]; + [stripeAndTextContainer autoPinLeadingToSuperviewMarginWithInset:self.quotedContentHInset]; + if (quotedAttachmentView) { + [stripeAndTextContainer autoPinTrailingToLeadingEdgeOfView:quotedAttachmentView + offset:self.quotedAttachmentHSpacing]; + } else { + [stripeAndTextContainer autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset]; + } + [stripeAndTextContainer autoPinBottomToSuperviewMarginWithInset:self.quotedContentHInset]; + [stripeAndTextContainer setContentHuggingLow]; + [stripeAndTextContainer setCompressionResistanceLow]; + + // Stripe. + UIView *quoteStripView = [UIView containerView]; + quoteStripView.backgroundColor = (self.isIncoming ? [UIColor whiteColor] : [UIColor colorWithRGBHex:0x007884]); + quoteStripView.userInteractionEnabled = NO; + [stripeAndTextContainer addSubview:quoteStripView]; + [quoteStripView autoPinHeightToSuperview]; + [quoteStripView autoPinLeadingToSuperviewMargin]; + [quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness]; + [quoteStripView setContentHuggingVerticalLow]; + [quoteStripView setContentHuggingHorizontalHigh]; + [quoteStripView setCompressionResistanceHigh]; + + // Text. + UILabel *quotedTextLabel = [self createQuotedTextLabel]; + [stripeAndTextContainer addSubview:quotedTextLabel]; + [quotedTextLabel autoPinTopToSuperviewMarginWithInset:self.quotedReplyStripeVExtension]; + [quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedReplyStripeVExtension]; + [quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing]; + [quotedTextLabel autoPinTrailingToSuperviewMargin]; + [quotedTextLabel setContentHuggingLow]; + [quotedTextLabel setCompressionResistanceLow]; + } +} + +#pragma mark - Measurement + +// TODO: Class method? +- (CGSize)sizeForMaxWidth:(CGFloat)maxWidth +{ + OWSAssert(self.viewItem); + OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); + + CGSize result = CGSizeZero; + + if (!self.viewItem.isQuotedReply) { + return result; + } + + result.width += self.quotedContentHInset; + + CGFloat thumbnailHeight = 0.f; + if (self.hasQuotedAttachmentThumbnail) { + result.width += self.quotedAttachmentHSpacing; + result.width += self.quotedAttachmentSize; + + thumbnailHeight += self.quotedAttachmentMinVInset; + thumbnailHeight += self.quotedAttachmentSize; + thumbnailHeight += self.quotedAttachmentMinVInset; + } + + result.width += self.quotedContentHInset; + + // Quoted Author + CGFloat quotedAuthorWidth = 0.f; + { + CGFloat maxQuotedAuthorWidth = maxWidth - result.width; + + 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 + = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxQuotedAuthorWidth, CGFLOAT_MAX)]); + + quotedAuthorWidth = quotedAuthorSize.width; + + result.height += self.quotedAuthorTopInset; + result.height += self.quotedAuthorHeight; + result.height += self.quotedAuthorBottomSpacing; + } + + CGFloat quotedTextWidth = 0.f; + { + CGFloat maxQuotedTextWidth + = (maxWidth - (result.width + self.quotedReplyStripeThickness + self.quotedReplyStripeHSpacing)); + + UILabel *quotedTextLabel = [self createQuotedTextLabel]; + + CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxQuotedTextWidth, CGFLOAT_MAX)]); + + quotedTextWidth = textSize.width + self.quotedReplyStripeThickness + self.quotedReplyStripeHSpacing; + result.height += self.quotedAuthorBottomSpacing; + result.height += textSize.height + self.quotedReplyStripeVExtension * 2; + } + + CGFloat textWidth = MAX(quotedAuthorWidth, quotedTextWidth); + result.width += textWidth; + + result.height += self.quotedTextBottomInset; + result.height = MAX(result.height, thumbnailHeight); + + return result; +} + +- (UILabel *)createQuotedTextLabel +{ + UILabel *quotedTextLabel = [UILabel new]; + quotedTextLabel.numberOfLines = 3; + quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping; + quotedTextLabel.text = self.quotedSnippet; + quotedTextLabel.textColor = self.quotedTextColor; + + // Honor dynamic type in the message bodies. + quotedTextLabel.font = self.textMessageFont; + return quotedTextLabel; +} + +- (UIColor *)quotedTextColor +{ + return [UIColor blackColor]; +} + +- (NSString *)quotedSnippet +{ + if (self.viewItem.hasQuotedText && self.viewItem.displayableQuotedText.displayText.length > 0) { + return self.viewItem.displayableQuotedText.displayText; + } else { + NSString *mimeType = self.viewItem.quotedAttachmentMimetype; + + if (mimeType.length > 0) { + return [TSAttachment emojiForMimeType:mimeType]; + } + } + + return @""; +} + +// TODO: +- (UIFont *)quotedAuthorFont +{ + return [UIFont ows_regularFontWithSize:10.f]; +} + +// TODO: +- (CGFloat)quotedAuthorHeight +{ + return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.f); +} + +// TODO: +- (CGFloat)quotedAuthorTopInset +{ + return 4.f; +} + +// TODO: +- (CGFloat)quotedAuthorBottomSpacing +{ + return 2.f; +} + +// TODO: +- (CGFloat)quotedTextBottomInset +{ + return 5.f; +} + +// TODO: +- (CGFloat)quotedReplyStripeThickness +{ + return 3.f; +} + +// TODO: +- (CGFloat)quotedReplyStripeVExtension +{ + return 5.f; +} + +// The spacing between the vertical "quoted reply stripe" +// and the quoted message content. +// TODO: +- (CGFloat)quotedReplyStripeHSpacing +{ + return 8.f; +} + +// Distance from top edge of "quoted message" bubble to top of message bubble. +// TODO: +- (CGFloat)quotedAttachmentMinVInset +{ + return 10.f; +} + +// TODO: +- (CGFloat)quotedAttachmentSize +{ + return 30.f; +} + +// TODO: +- (CGFloat)quotedAttachmentHSpacing +{ + return 10.f; +} + +// Distance from sides of the quoted content to the sides of the message bubble. +// TODO: +- (CGFloat)quotedContentHInset +{ + return 8.f; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/categories/UIView+OWS.h b/SignalMessaging/categories/UIView+OWS.h index 2b4b114c8..043a63fc3 100644 --- a/SignalMessaging/categories/UIView+OWS.h +++ b/SignalMessaging/categories/UIView+OWS.h @@ -125,4 +125,11 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value); @end +#pragma mark - Macros + +CG_INLINE CGSize CGSizeCeil(CGSize size) +{ + return CGSizeMake((CGFloat)ceil(size.width), (CGFloat)ceil(size.height)); +} + NS_ASSUME_NONNULL_END From 6171505657a6a616aee6cbf3277cfcfcc9e94055 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 14:42:39 -0400 Subject: [PATCH 03/10] Refine appearance of quoted reply message cells. --- .../Cells/OWSBubbleStrokeView.h | 8 +- .../Cells/OWSBubbleStrokeView.m | 8 +- .../ConversationView/Cells/OWSBubbleView.h | 24 ++- .../ConversationView/Cells/OWSBubbleView.m | 48 ++++- .../ConversationView/Cells/OWSMessageCell.h | 2 + .../ConversationView/Cells/OWSMessageCell.m | 23 ++- .../Cells/OWSQuotedMessageView.h | 15 +- .../Cells/OWSQuotedMessageView.m | 195 ++++++++++++++---- .../ConversationViewController.m | 8 + .../utils/OWSMessagesBubbleImageFactory.swift | 7 +- 10 files changed, 265 insertions(+), 73 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h index c2ddcc92b..c02a4b70e 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h @@ -2,19 +2,17 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +#import "OWSBubbleView.h" + NS_ASSUME_NONNULL_BEGIN @class OWSBubbleView; -@interface OWSBubbleStrokeView : UIView - -@property (nonatomic, weak) OWSBubbleView *bubbleView; +@interface OWSBubbleStrokeView : UIView @property (nonatomic) UIColor *strokeColor; @property (nonatomic) CGFloat strokeThickness; -- (void)updateLayers; - @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m index 368f44fef..425bed494 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m @@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) CAShapeLayer *shapeLayer; +@property (nonatomic, weak) OWSBubbleView *bubbleView; + @end #pragma mark - @@ -31,8 +33,6 @@ NS_ASSUME_NONNULL_BEGIN self.shapeLayer = [CAShapeLayer new]; [self.layer addSublayer:self.shapeLayer]; - [self updateLayers]; - return self; } @@ -56,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN [super setFrame:frame]; - if (didChange || !self.shapeLayer) { + if (didChange) { [self updateLayers]; } } @@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN [super setBounds:bounds]; - if (didChange || !self.shapeLayer) { + if (didChange) { [self updateLayers]; } } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h index b7ea6b871..4ef469937 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h @@ -2,6 +2,8 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +#import "OWSBubbleView.h" + NS_ASSUME_NONNULL_BEGIN extern const CGFloat kOWSMessageCellCornerRadius; @@ -13,11 +15,19 @@ extern const CGFloat kBubbleThornVInset; extern const CGFloat kBubbleTextHInset; extern const CGFloat kBubbleTextVInset; -@class OWSBubbleStrokeView; +@class OWSBubbleView; -@interface OWSBubbleView : UIView +@protocol OWSBubbleViewPartner + +- (void)updateLayers; + +- (void)setBubbleView:(OWSBubbleView *)bubbleView; + +@end -@property (nonatomic, weak, nullable) OWSBubbleStrokeView *bubbleStrokeView; +#pragma mark - + +@interface OWSBubbleView : UIView @property (nonatomic) BOOL isOutgoing; @property (nonatomic) BOOL hideTail; @@ -27,6 +37,14 @@ extern const CGFloat kBubbleTextVInset; - (UIBezierPath *)maskPath; +#pragma mark - Coordination + +- (void)addPartnerView:(id)view; + +- (void)clearPartnerViews; + +- (void)updatePartnerViews; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m index c8f3acf69..4cb83dd3f 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m @@ -3,7 +3,6 @@ // #import "OWSBubbleView.h" -#import "OWSBubbleStrokeView.h" #import NS_ASSUME_NONNULL_BEGIN @@ -22,6 +21,8 @@ const CGFloat kBubbleTextVInset = 10.f; @property (nonatomic) CAShapeLayer *maskLayer; @property (nonatomic) CAShapeLayer *shapeLayer; +@property (nonatomic, readonly) NSMutableArray> *partnerViews; + @end #pragma mark - @@ -41,7 +42,7 @@ const CGFloat kBubbleTextVInset = 10.f; self.maskLayer = [CAShapeLayer new]; self.layer.mask = self.maskLayer; - [self updateLayers]; + _partnerViews = [NSMutableArray new]; return self; } @@ -52,7 +53,7 @@ const CGFloat kBubbleTextVInset = 10.f; _isOutgoing = isOutgoing; - if (didChange || !self.shapeLayer) { + if (didChange) { [self updateLayers]; } } @@ -63,7 +64,7 @@ const CGFloat kBubbleTextVInset = 10.f; _hideTail = hideTail; - if (didChange || !self.shapeLayer) { + if (didChange) { [self updateLayers]; } } @@ -74,7 +75,7 @@ const CGFloat kBubbleTextVInset = 10.f; _isTruncated = isTruncated; - if (didChange || !self.shapeLayer) { + if (didChange) { [self updateLayers]; } } @@ -87,13 +88,13 @@ const CGFloat kBubbleTextVInset = 10.f; [super setFrame:frame]; - if (didChangeSize || !self.shapeLayer) { + if (didChangeSize) { [self updateLayers]; } // We always need to inform the "bubble stroke view" (if any) if our // frame/bounds/center changes. Its contents are not in local coordinates. - [self.bubbleStrokeView updateLayers]; + [self updatePartnerViews]; } - (void)setBounds:(CGRect)bounds @@ -104,13 +105,13 @@ const CGFloat kBubbleTextVInset = 10.f; [super setBounds:bounds]; - if (didChangeSize || !self.shapeLayer) { + if (didChangeSize) { [self updateLayers]; } // We always need to inform the "bubble stroke view" (if any) if our // frame/bounds/center changes. Its contents are not in local coordinates. - [self.bubbleStrokeView updateLayers]; + [self updatePartnerViews]; } - (void)setCenter:(CGPoint)center @@ -119,7 +120,7 @@ const CGFloat kBubbleTextVInset = 10.f; // We always need to inform the "bubble stroke view" (if any) if our // frame/bounds/center changes. Its contents are not in local coordinates. - [self.bubbleStrokeView updateLayers]; + [self updatePartnerViews]; } - (void)setBubbleColor:(nullable UIColor *)bubbleColor @@ -211,6 +212,33 @@ const CGFloat kBubbleTextVInset = 10.f; return bezierPath; } +#pragma mark - Coordination + +- (void)addPartnerView:(id)partnerView +{ + OWSAssert(self.partnerViews); + + [partnerView setBubbleView:self]; + + [self.partnerViews addObject:partnerView]; +} + +- (void)clearPartnerViews +{ + OWSAssert(self.partnerViews); + + [self.partnerViews removeAllObjects]; +} + +- (void)updatePartnerViews +{ + [self layoutIfNeeded]; + + for (id partnerView in self.partnerViews) { + [partnerView updateLayers]; + } +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h index 789b94fbb..1a52f7555 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h @@ -10,6 +10,8 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)cellReuseIdentifier; ++ (UIFont *)defaultTextMessageFont; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 8e31d7194..f81e766ba 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -147,11 +147,16 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass([self class]); } ++ (UIFont *)defaultTextMessageFont +{ + return [UIFont ows_dynamicTypeBodyFont]; +} + - (UIFont *)textMessageFont { OWSAssert(DisplayableText.kMaxJumbomojiCount == 5); - CGFloat basePointSize = [UIFont ows_dynamicTypeBodyFont].pointSize; + CGFloat basePointSize = self.class.defaultTextMessageFont.pointSize; switch (self.displayableBodyText.jumbomojiCount) { case 0: break; @@ -361,8 +366,10 @@ NS_ASSUME_NONNULL_BEGIN if (self.isQuotedReply) { OWSAssert(!lastSubview); + TSMessage *message = (TSMessage *)self.viewItem.interaction; OWSQuotedMessageView *quotedMessageView = - [[OWSQuotedMessageView alloc] initWithViewItem:self.viewItem textMessageFont:self.textMessageFont]; + [OWSQuotedMessageView quotedMessageViewForConversation:message.quotedMessage + displayableQuotedText:self.viewItem.displayableQuotedText]; [quotedMessageView createContents]; [self.bubbleView addSubview:quotedMessageView]; @@ -381,6 +388,8 @@ NS_ASSUME_NONNULL_BEGIN } lastSubview = quotedMessageView; bottomMargin = 0; + + [self.bubbleView addPartnerView:quotedMessageView]; } UIView *_Nullable bodyMediaView = nil; @@ -471,8 +480,8 @@ NS_ASSUME_NONNULL_BEGIN [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); + + [self.bubbleView addPartnerView:bubbleStrokeView]; } } @@ -1174,8 +1183,10 @@ NS_ASSUME_NONNULL_BEGIN return CGSizeZero; } + TSMessage *message = (TSMessage *)self.viewItem.interaction; OWSQuotedMessageView *quotedMessageView = - [[OWSQuotedMessageView alloc] initWithViewItem:self.viewItem textMessageFont:self.textMessageFont]; + [OWSQuotedMessageView quotedMessageViewForConversation:message.quotedMessage + displayableQuotedText:self.viewItem.displayableQuotedText]; const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; CGSize result = [quotedMessageView sizeForMaxWidth:maxMessageWidth - kBubbleThornSideInset]; result.width += kBubbleThornSideInset; @@ -1324,7 +1335,7 @@ NS_ASSUME_NONNULL_BEGIN self.bubbleView.hidden = YES; self.bubbleView.bubbleColor = nil; - self.bubbleView.bubbleStrokeView = nil; + [self.bubbleView clearPartnerViews]; for (UIView *subview in self.bubbleView.subviews) { [subview removeFromSuperview]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h index 45cc7ce14..f65109e8c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h @@ -2,25 +2,28 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +#import "OWSBubbleView.h" + NS_ASSUME_NONNULL_BEGIN -@class ConversationViewItem; +@class DisplayableText; @class TSQuotedMessage; -@interface OWSQuotedMessageView : UIView +@interface OWSQuotedMessageView : UIView - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithViewItem:(ConversationViewItem *)viewItem - // quotedMessage:(TSQuotedMessage *)quotedMessage - textMessageFont:(UIFont *)textMessageFont; - // Only needs to be called if we're going to render this instance. - (void)createContents; // Measurement - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth; ++ (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage + displayableQuotedText:(nullable DisplayableText *)displayableQuotedText; + ++ (OWSQuotedMessageView *)quotedMessageViewForPreview:(TSQuotedMessage *)quotedMessage; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 39d5a0b67..e8a956b16 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -5,6 +5,7 @@ #import "OWSQuotedMessageView.h" #import "ConversationViewItem.h" #import "Environment.h" +#import "OWSMessageCell.h" #import "Signal-Swift.h" #import #import @@ -18,16 +19,47 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSQuotedMessageView () -@property (nonatomic, readonly) ConversationViewItem *viewItem; +@property (nonatomic, readonly) TSQuotedMessage *quotedMessage; +@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText; + @property (nonatomic, readonly) UIFont *textMessageFont; +@property (nonatomic, readonly) UIColor *strokeColor; +@property (nonatomic, readonly) CGFloat strokeThickness; + +// TODO: Replace with a bubble stroke view. +@property (nonatomic) CAShapeLayer *shapeLayer; + +@property (nonatomic, weak) OWSBubbleView *bubbleView; + @end @implementation OWSQuotedMessageView -- (instancetype)initWithViewItem:(ConversationViewItem *)viewItem - // quotedMessage:(TSQuotedMessage *)quotedMessage - textMessageFont:(UIFont *)textMessageFont ++ (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage + displayableQuotedText:(nullable DisplayableText *)displayableQuotedText +{ + OWSAssert(quotedMessage); + + return + [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage displayableQuotedText:displayableQuotedText]; +} + ++ (OWSQuotedMessageView *)quotedMessageViewForPreview:(TSQuotedMessage *)quotedMessage +{ + OWSAssert(quotedMessage); + + DisplayableText *_Nullable displayableQuotedText = nil; + if (quotedMessage.body.length > 0) { + displayableQuotedText = [DisplayableText displayableText:quotedMessage.body]; + } + + return + [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage displayableQuotedText:displayableQuotedText]; +} + +- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage + displayableQuotedText:(nullable DisplayableText *)displayableQuotedText { self = [super init]; @@ -35,37 +67,31 @@ NS_ASSUME_NONNULL_BEGIN return self; } - OWSAssert(viewItem); - // OWSAssert(quotedMessage); - OWSAssert(textMessageFont); + OWSAssert(quotedMessage); + OWSAssert(displayableQuotedText); - _viewItem = viewItem; - // _quotedMessage = quotedMessage; - _textMessageFont = textMessageFont; + _quotedMessage = quotedMessage; + _displayableQuotedText = displayableQuotedText; + _textMessageFont = OWSMessageCell.defaultTextMessageFont; + _strokeColor = OWSMessagesBubbleImageFactory.bubbleColorIncoming; + _strokeThickness = 1.f; - return self; -} + self.shapeLayer = [CAShapeLayer new]; + [self.layer addSublayer:self.shapeLayer]; -- (BOOL)isIncoming -{ - return self.viewItem.interaction.interactionType == OWSInteractionType_IncomingMessage; + return self; } - (BOOL)hasQuotedAttachmentThumbnail { - // This should always be valid for the appropriate cell types. - OWSAssert(self.viewItem); - - return (self.viewItem.hasQuotedAttachment && - [TSAttachmentStream hasThumbnailForMimeType:self.viewItem.quotedAttachmentMimetype]); + return (self.quotedMessage.contentType.length > 0 && + [TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]); } #pragma mark - - (void)createContents { - OWSAssert(self.viewItem.isQuotedReply); - self.backgroundColor = [UIColor whiteColor]; self.userInteractionEnabled = NO; self.layoutMargins = UIEdgeInsetsZero; @@ -91,14 +117,15 @@ NS_ASSUME_NONNULL_BEGIN } OWSContactsManager *contactsManager = Environment.current.contactsManager; - NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId]; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; UILabel *quotedAuthorLabel = [UILabel new]; { quotedAuthorLabel.text = quotedAuthor; quotedAuthorLabel.font = self.quotedAuthorFont; - quotedAuthorLabel.textColor - = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); + // TODO: + quotedAuthorLabel.textColor = [UIColor ows_darkGrayColor]; + // = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); quotedAuthorLabel.numberOfLines = 1; quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; [self addSubview:quotedAuthorLabel]; @@ -135,8 +162,12 @@ NS_ASSUME_NONNULL_BEGIN [stripeAndTextContainer setCompressionResistanceLow]; // Stripe. + BOOL isIncomingQuote + = ![NSObject isNullableObject:self.quotedMessage.authorId equalTo:TSAccountManager.localNumber]; + UIColor *stripeColor = (isIncomingQuote ? OWSMessagesBubbleImageFactory.bubbleColorIncoming + : OWSMessagesBubbleImageFactory.bubbleColorOutgoingSent); UIView *quoteStripView = [UIView containerView]; - quoteStripView.backgroundColor = (self.isIncoming ? [UIColor whiteColor] : [UIColor colorWithRGBHex:0x007884]); + quoteStripView.backgroundColor = stripeColor; quoteStripView.userInteractionEnabled = NO; [stripeAndTextContainer addSubview:quoteStripView]; [quoteStripView autoPinHeightToSuperview]; @@ -163,15 +194,8 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Class method? - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth { - OWSAssert(self.viewItem); - OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - CGSize result = CGSizeZero; - if (!self.viewItem.isQuotedReply) { - return result; - } - result.width += self.quotedContentHInset; CGFloat thumbnailHeight = 0.f; @@ -192,7 +216,7 @@ NS_ASSUME_NONNULL_BEGIN CGFloat maxQuotedAuthorWidth = maxWidth - result.width; OWSContactsManager *contactsManager = Environment.current.contactsManager; - NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId]; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; UILabel *quotedAuthorLabel = [UILabel new]; quotedAuthorLabel.text = quotedAuthor; @@ -253,10 +277,10 @@ NS_ASSUME_NONNULL_BEGIN - (NSString *)quotedSnippet { - if (self.viewItem.hasQuotedText && self.viewItem.displayableQuotedText.displayText.length > 0) { - return self.viewItem.displayableQuotedText.displayText; + if (self.displayableQuotedText.displayText.length > 0) { + return self.displayableQuotedText.displayText; } else { - NSString *mimeType = self.viewItem.quotedAttachmentMimetype; + NSString *mimeType = self.quotedMessage.contentType; if (mimeType.length > 0) { return [TSAttachment emojiForMimeType:mimeType]; @@ -299,7 +323,7 @@ NS_ASSUME_NONNULL_BEGIN // TODO: - (CGFloat)quotedReplyStripeThickness { - return 3.f; + return 2.f; } // TODO: @@ -342,6 +366,101 @@ NS_ASSUME_NONNULL_BEGIN return 8.f; } +#pragma mark - Stroke + +//- (instancetype)init +//{ +// self = [super init]; +// if (!self) { +// return self; +// } +// +// self.opaque = NO; +// self.backgroundColor = [UIColor clearColor]; +// +// +// [self updateLayers]; +// +// return self; +//} + +- (void)setStrokeColor:(UIColor *)strokeColor +{ + _strokeColor = strokeColor; + + [self updateLayers]; +} + +- (void)setStrokeThickness:(CGFloat)strokeThickness +{ + _strokeThickness = strokeThickness; + + [self updateLayers]; +} + +- (void)setFrame:(CGRect)frame +{ + BOOL didChange = !CGRectEqualToRect(self.frame, frame); + + [super setFrame:frame]; + + if (didChange) { + [self updateLayers]; + } +} + +- (void)setBounds:(CGRect)bounds +{ + BOOL didChange = !CGRectEqualToRect(self.bounds, bounds); + + [super setBounds:bounds]; + + if (didChange) { + [self updateLayers]; + } +} + +- (void)setCenter:(CGPoint)center +{ + [super setCenter:center]; + + [self updateLayers]; +} + +- (void)updateLayers +{ + if (!self.shapeLayer) { + return; + } + + // Don't fill the shape layer; we just want a stroke around the border. + self.shapeLayer.fillColor = [UIColor clearColor].CGColor; + + self.clipsToBounds = YES; + + if (!self.bubbleView) { + return; + } + + self.shapeLayer.strokeColor = self.strokeColor.CGColor; + self.shapeLayer.lineWidth = self.strokeThickness; + self.shapeLayer.zPosition = 100.f; + + UIBezierPath *bezierPath = [UIBezierPath new]; + + UIBezierPath *boundsBezierPath = [UIBezierPath bezierPathWithRect:self.bounds]; + [bezierPath appendPath:boundsBezierPath]; + + UIBezierPath *bubbleBezierPath = [self.bubbleView maskPath]; + // We need to convert between coordinate systems using layers, not views. + CGPoint bubbleOffset = [self.layer convertPoint:CGPointZero fromLayer:self.bubbleView.layer]; + CGAffineTransform transform = CGAffineTransformMakeTranslation(bubbleOffset.x, bubbleOffset.y); + [bubbleBezierPath applyTransform:transform]; + [bezierPath appendPath:bubbleBezierPath]; + + self.shapeLayer.path = bezierPath.CGPath; +} + @end NS_ASSUME_NONNULL_END 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/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift b/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift index 7f9c65d38..83582cf4b 100644 --- a/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift +++ b/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift @@ -9,7 +9,8 @@ import SignalServiceKit @objc public class OWSMessagesBubbleImageFactory: NSObject { - static let shared = OWSMessagesBubbleImageFactory() + @objc + public static let shared = OWSMessagesBubbleImageFactory() private let jsqFactory = JSQMessagesBubbleImageFactory()! @@ -57,12 +58,16 @@ public class OWSMessagesBubbleImageFactory: NSObject { } } + @objc public static let bubbleColorIncoming = UIColor.jsq_messageBubbleLightGray()! + @objc public static let bubbleColorOutgoingUnsent = UIColor.gray + @objc public static let bubbleColorOutgoingSending = UIColor.ows_fadedBlue + @objc public static let bubbleColorOutgoingSent = UIColor.ows_materialBlue public func bubbleColor(message: TSMessage) -> UIColor { From c70f911f6fcc79ddba6d247dd04c9c7a777e940c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 14:56:48 -0400 Subject: [PATCH 04/10] Refine appearance of quoted reply message cells. --- .../ConversationView/Cells/OWSMessageCell.m | 3 +- .../Cells/OWSQuotedMessageView.h | 7 +- .../Cells/OWSQuotedMessageView.m | 124 +++--------------- 3 files changed, 22 insertions(+), 112 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index f81e766ba..161e5d906 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -389,7 +389,7 @@ NS_ASSUME_NONNULL_BEGIN lastSubview = quotedMessageView; bottomMargin = 0; - [self.bubbleView addPartnerView:quotedMessageView]; + [self.bubbleView addPartnerView:quotedMessageView.boundsStrokeView]; } UIView *_Nullable bodyMediaView = nil; @@ -473,7 +473,6 @@ NS_ASSUME_NONNULL_BEGIN 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]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h index f65109e8c..d4f455157 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h @@ -2,14 +2,15 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // -#import "OWSBubbleView.h" - NS_ASSUME_NONNULL_BEGIN @class DisplayableText; +@class OWSBubbleStrokeView; @class TSQuotedMessage; -@interface OWSQuotedMessageView : UIView +@interface OWSQuotedMessageView : UIView + +@property (nonatomic, nullable, readonly) OWSBubbleStrokeView *boundsStrokeView; - (instancetype)init NS_UNAVAILABLE; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index e8a956b16..8a69d2922 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -5,6 +5,7 @@ #import "OWSQuotedMessageView.h" #import "ConversationViewItem.h" #import "Environment.h" +#import "OWSBubbleStrokeView.h" #import "OWSMessageCell.h" #import "Signal-Swift.h" #import @@ -24,16 +25,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) UIFont *textMessageFont; -@property (nonatomic, readonly) UIColor *strokeColor; -@property (nonatomic, readonly) CGFloat strokeThickness; - -// TODO: Replace with a bubble stroke view. -@property (nonatomic) CAShapeLayer *shapeLayer; - -@property (nonatomic, weak) OWSBubbleView *bubbleView; +@property (nonatomic, nullable) OWSBubbleStrokeView *boundsStrokeView; @end +#pragma mark - + @implementation OWSQuotedMessageView + (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage @@ -73,11 +70,6 @@ NS_ASSUME_NONNULL_BEGIN _quotedMessage = quotedMessage; _displayableQuotedText = displayableQuotedText; _textMessageFont = OWSMessageCell.defaultTextMessageFont; - _strokeColor = OWSMessagesBubbleImageFactory.bubbleColorIncoming; - _strokeThickness = 1.f; - - self.shapeLayer = [CAShapeLayer new]; - [self.layer addSublayer:self.shapeLayer]; return self; } @@ -97,6 +89,14 @@ NS_ASSUME_NONNULL_BEGIN self.layoutMargins = UIEdgeInsetsZero; self.clipsToBounds = YES; + self.boundsStrokeView = [OWSBubbleStrokeView new]; + self.boundsStrokeView.strokeColor = OWSMessagesBubbleImageFactory.bubbleColorIncoming; + self.boundsStrokeView.strokeThickness = 1.f; + [self addSubview:self.boundsStrokeView]; + [self.boundsStrokeView autoPinToSuperviewEdges]; + [self.boundsStrokeView setContentHuggingLow]; + [self.boundsStrokeView setCompressionResistanceLow]; + UIView *_Nullable quotedAttachmentView = nil; // TODO: // if (self.hasQuotedAttachmentThumbnail) @@ -114,6 +114,11 @@ NS_ASSUME_NONNULL_BEGIN [quotedAttachmentView setCompressionResistanceHigh]; // TODO: Consider stroking the quoted thumbnail. + if (quotedAttachmentView) { + quotedAttachmentView.layer.borderColor = [UIColor colorWithWhite:0.f alpha:0.1f].CGColor; + quotedAttachmentView.layer.borderWidth = 1.f; + quotedAttachmentView.layer.cornerRadius = 2.f; + } } OWSContactsManager *contactsManager = Environment.current.contactsManager; @@ -366,101 +371,6 @@ NS_ASSUME_NONNULL_BEGIN return 8.f; } -#pragma mark - Stroke - -//- (instancetype)init -//{ -// self = [super init]; -// if (!self) { -// return self; -// } -// -// self.opaque = NO; -// self.backgroundColor = [UIColor clearColor]; -// -// -// [self updateLayers]; -// -// return self; -//} - -- (void)setStrokeColor:(UIColor *)strokeColor -{ - _strokeColor = strokeColor; - - [self updateLayers]; -} - -- (void)setStrokeThickness:(CGFloat)strokeThickness -{ - _strokeThickness = strokeThickness; - - [self updateLayers]; -} - -- (void)setFrame:(CGRect)frame -{ - BOOL didChange = !CGRectEqualToRect(self.frame, frame); - - [super setFrame:frame]; - - if (didChange) { - [self updateLayers]; - } -} - -- (void)setBounds:(CGRect)bounds -{ - BOOL didChange = !CGRectEqualToRect(self.bounds, bounds); - - [super setBounds:bounds]; - - if (didChange) { - [self updateLayers]; - } -} - -- (void)setCenter:(CGPoint)center -{ - [super setCenter:center]; - - [self updateLayers]; -} - -- (void)updateLayers -{ - if (!self.shapeLayer) { - return; - } - - // Don't fill the shape layer; we just want a stroke around the border. - self.shapeLayer.fillColor = [UIColor clearColor].CGColor; - - self.clipsToBounds = YES; - - if (!self.bubbleView) { - return; - } - - self.shapeLayer.strokeColor = self.strokeColor.CGColor; - self.shapeLayer.lineWidth = self.strokeThickness; - self.shapeLayer.zPosition = 100.f; - - UIBezierPath *bezierPath = [UIBezierPath new]; - - UIBezierPath *boundsBezierPath = [UIBezierPath bezierPathWithRect:self.bounds]; - [bezierPath appendPath:boundsBezierPath]; - - UIBezierPath *bubbleBezierPath = [self.bubbleView maskPath]; - // We need to convert between coordinate systems using layers, not views. - CGPoint bubbleOffset = [self.layer convertPoint:CGPointZero fromLayer:self.bubbleView.layer]; - CGAffineTransform transform = CGAffineTransformMakeTranslation(bubbleOffset.x, bubbleOffset.y); - [bubbleBezierPath applyTransform:transform]; - [bezierPath appendPath:bubbleBezierPath]; - - self.shapeLayer.path = bezierPath.CGPath; -} - @end NS_ASSUME_NONNULL_END From d6f3df82aeffa58fc4044ac38325d2d76d035778 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 14:59:16 -0400 Subject: [PATCH 05/10] Refine appearance of quoted reply message cells. --- .../ConversationView/ConversationViewController.m | 8 -------- Signal/src/ViewControllers/HomeViewController.m | 9 --------- 2 files changed, 17 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 61b4cf186..8cad36aaa 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2299,14 +2299,6 @@ 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/src/ViewControllers/HomeViewController.m b/Signal/src/ViewControllers/HomeViewController.m index 0c4af11c7..344c90819 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 5235f6eee92ceb5399af2ff47186bedb53fb4221 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 15:45:35 -0400 Subject: [PATCH 06/10] Refine appearance of quoted reply message cells. --- .../ConversationView/Cells/OWSMessageCell.m | 8 +++---- .../Cells/OWSQuotedMessageView.m | 21 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 161e5d906..4c83320dc 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -367,9 +367,9 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(!lastSubview); TSMessage *message = (TSMessage *)self.viewItem.interaction; - OWSQuotedMessageView *quotedMessageView = - [OWSQuotedMessageView quotedMessageViewForConversation:message.quotedMessage - displayableQuotedText:self.viewItem.displayableQuotedText]; + OWSQuotedMessageView *quotedMessageView = [OWSQuotedMessageView + quotedMessageViewForConversation:message.quotedMessage + displayableQuotedText:(self.viewItem.hasQuotedText ? self.viewItem.displayableQuotedText : nil)]; [quotedMessageView createContents]; [self.bubbleView addSubview:quotedMessageView]; @@ -1178,7 +1178,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - if (!self.isQuotedReply) { + if (!self.isQuotedReply || !self.hasQuotedText) { return CGSizeZero; } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 8a69d2922..0fa2932a7 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -65,7 +65,6 @@ NS_ASSUME_NONNULL_BEGIN } OWSAssert(quotedMessage); - OWSAssert(displayableQuotedText); _quotedMessage = quotedMessage; _displayableQuotedText = displayableQuotedText; @@ -74,6 +73,11 @@ NS_ASSUME_NONNULL_BEGIN return self; } +- (BOOL)hasQuotedAttachment +{ + return self.quotedMessage.contentType.length > 0; +} + - (BOOL)hasQuotedAttachmentThumbnail { return (self.quotedMessage.contentType.length > 0 && @@ -99,8 +103,7 @@ NS_ASSUME_NONNULL_BEGIN UIView *_Nullable quotedAttachmentView = nil; // TODO: - // if (self.hasQuotedAttachmentThumbnail) - { + if (self.hasQuotedAttachment) { // TODO: quotedAttachmentView = [UIView containerView]; quotedAttachmentView.userInteractionEnabled = NO; @@ -162,7 +165,7 @@ NS_ASSUME_NONNULL_BEGIN } else { [stripeAndTextContainer autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset]; } - [stripeAndTextContainer autoPinBottomToSuperviewMarginWithInset:self.quotedContentHInset]; + [stripeAndTextContainer autoPinBottomToSuperviewMarginWithInset:self.quotedTextBottomInset]; [stripeAndTextContainer setContentHuggingLow]; [stripeAndTextContainer setCompressionResistanceLow]; @@ -204,7 +207,7 @@ NS_ASSUME_NONNULL_BEGIN result.width += self.quotedContentHInset; CGFloat thumbnailHeight = 0.f; - if (self.hasQuotedAttachmentThumbnail) { + if (self.hasQuotedAttachment) { result.width += self.quotedAttachmentHSpacing; result.width += self.quotedAttachmentSize; @@ -249,7 +252,6 @@ NS_ASSUME_NONNULL_BEGIN CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxQuotedTextWidth, CGFLOAT_MAX)]); quotedTextWidth = textSize.width + self.quotedReplyStripeThickness + self.quotedReplyStripeHSpacing; - result.height += self.quotedAuthorBottomSpacing; result.height += textSize.height + self.quotedReplyStripeVExtension * 2; } @@ -371,6 +373,13 @@ NS_ASSUME_NONNULL_BEGIN return 8.f; } +#pragma mark - + +- (CGSize)sizeThatFits:(CGSize)size +{ + return [self sizeForMaxWidth:CGFLOAT_MAX]; +} + @end NS_ASSUME_NONNULL_END From 08ba3852cdb3dca53fcdbb1cffde11b7bf4482be Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 15:55:19 -0400 Subject: [PATCH 07/10] Refine appearance of quoted reply message cells. --- .../Cells/OWSQuotedMessageView.m | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 0fa2932a7..349bde007 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -75,15 +75,32 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasQuotedAttachment { - return self.quotedMessage.contentType.length > 0; + return (self.quotedMessage.contentType.length > 0 + && ![NSObject isNullableObject:self.quotedMessage.contentType equalTo:OWSMimeTypeOversizeTextMessage]); } - (BOOL)hasQuotedAttachmentThumbnail { - return (self.quotedMessage.contentType.length > 0 && + return (self.quotedMessage.contentType.length > 0 + && ![NSObject isNullableObject:self.quotedMessage.contentType equalTo:OWSMimeTypeOversizeTextMessage] && [TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]); } +- (NSString *)quotedSnippet +{ + if (self.displayableQuotedText.displayText.length > 0) { + return self.displayableQuotedText.displayText; + } else { + NSString *mimeType = self.quotedMessage.contentType; + + if (mimeType.length > 0) { + return [TSAttachment emojiForMimeType:mimeType]; + } + } + + return @""; +} + #pragma mark - - (void)createContents @@ -282,21 +299,6 @@ NS_ASSUME_NONNULL_BEGIN return [UIColor blackColor]; } -- (NSString *)quotedSnippet -{ - if (self.displayableQuotedText.displayText.length > 0) { - return self.displayableQuotedText.displayText; - } else { - NSString *mimeType = self.quotedMessage.contentType; - - if (mimeType.length > 0) { - return [TSAttachment emojiForMimeType:mimeType]; - } - } - - return @""; -} - // TODO: - (UIFont *)quotedAuthorFont { From 7e921b7935924a44521d52f750cf0814e63c5ea7 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 15:58:08 -0400 Subject: [PATCH 08/10] Refine appearance of quoted reply message cells. --- .../ConversationView/Cells/OWSMessageCell.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 4c83320dc..b8ceb122c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -1178,14 +1178,14 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - if (!self.isQuotedReply || !self.hasQuotedText) { + if (!self.isQuotedReply) { return CGSizeZero; } TSMessage *message = (TSMessage *)self.viewItem.interaction; - OWSQuotedMessageView *quotedMessageView = - [OWSQuotedMessageView quotedMessageViewForConversation:message.quotedMessage - displayableQuotedText:self.viewItem.displayableQuotedText]; + OWSQuotedMessageView *quotedMessageView = [OWSQuotedMessageView + quotedMessageViewForConversation:message.quotedMessage + displayableQuotedText:(self.hasQuotedText ? self.viewItem.displayableQuotedText : nil)]; const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; CGSize result = [quotedMessageView sizeForMaxWidth:maxMessageWidth - kBubbleThornSideInset]; result.width += kBubbleThornSideInset; From cac85508ccb6c8c9d39c7b7a72220457bf8a0541 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 4 Apr 2018 16:05:13 -0400 Subject: [PATCH 09/10] Refine appearance of quoted reply message cells. --- .../ConversationView/Cells/OWSQuotedMessageView.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 349bde007..996aba485 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -133,7 +133,6 @@ NS_ASSUME_NONNULL_BEGIN [quotedAttachmentView setContentHuggingHigh]; [quotedAttachmentView setCompressionResistanceHigh]; - // TODO: Consider stroking the quoted thumbnail. if (quotedAttachmentView) { quotedAttachmentView.layer.borderColor = [UIColor colorWithWhite:0.f alpha:0.1f].CGColor; quotedAttachmentView.layer.borderWidth = 1.f; @@ -150,7 +149,6 @@ NS_ASSUME_NONNULL_BEGIN quotedAuthorLabel.font = self.quotedAuthorFont; // TODO: quotedAuthorLabel.textColor = [UIColor ows_darkGrayColor]; - // = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); quotedAuthorLabel.numberOfLines = 1; quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; [self addSubview:quotedAuthorLabel]; @@ -216,7 +214,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Measurement -// TODO: Class method? - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth { CGSize result = CGSizeZero; From 822aa64b147d571b2e779f0e28578b5f5e55f527 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 5 Apr 2018 10:35:57 -0400 Subject: [PATCH 10/10] Respond to CR. --- .../ConversationView/Cells/OWSMessageCell.h | 2 -- .../ConversationView/Cells/OWSMessageCell.m | 7 +------ .../ConversationView/Cells/OWSQuotedMessageView.h | 2 ++ .../ConversationView/Cells/OWSQuotedMessageView.m | 7 +++---- SignalMessaging/categories/UIFont+OWS.h | 2 ++ SignalMessaging/categories/UIFont+OWS.m | 5 +++++ 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h index 1a52f7555..789b94fbb 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h @@ -10,8 +10,6 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)cellReuseIdentifier; -+ (UIFont *)defaultTextMessageFont; - @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index b8ceb122c..4b1178416 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -147,16 +147,11 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass([self class]); } -+ (UIFont *)defaultTextMessageFont -{ - return [UIFont ows_dynamicTypeBodyFont]; -} - - (UIFont *)textMessageFont { OWSAssert(DisplayableText.kMaxJumbomojiCount == 5); - CGFloat basePointSize = self.class.defaultTextMessageFont.pointSize; + CGFloat basePointSize = [UIFont ows_dynamicTypeBodyFont].pointSize; switch (self.displayableBodyText.jumbomojiCount) { case 0: break; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h index d4f455157..09cfcd274 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h @@ -20,9 +20,11 @@ NS_ASSUME_NONNULL_BEGIN // Measurement - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth; +// Factory method for "message bubble" views. + (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage displayableQuotedText:(nullable DisplayableText *)displayableQuotedText; +// Factory method for "message compose" views. + (OWSQuotedMessageView *)quotedMessageViewForPreview:(TSQuotedMessage *)quotedMessage; @end diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 996aba485..835091e36 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -6,7 +6,6 @@ #import "ConversationViewItem.h" #import "Environment.h" #import "OWSBubbleStrokeView.h" -#import "OWSMessageCell.h" #import "Signal-Swift.h" #import #import @@ -68,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN _quotedMessage = quotedMessage; _displayableQuotedText = displayableQuotedText; - _textMessageFont = OWSMessageCell.defaultTextMessageFont; + _textMessageFont = [UIFont ows_dynamicTypeBodyFont]; return self; } @@ -76,13 +75,13 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasQuotedAttachment { return (self.quotedMessage.contentType.length > 0 - && ![NSObject isNullableObject:self.quotedMessage.contentType equalTo:OWSMimeTypeOversizeTextMessage]); + && ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType]); } - (BOOL)hasQuotedAttachmentThumbnail { return (self.quotedMessage.contentType.length > 0 - && ![NSObject isNullableObject:self.quotedMessage.contentType equalTo:OWSMimeTypeOversizeTextMessage] && + && ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType] && [TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]); } diff --git a/SignalMessaging/categories/UIFont+OWS.h b/SignalMessaging/categories/UIFont+OWS.h index 2bb7cb392..715f5bf52 100644 --- a/SignalMessaging/categories/UIFont+OWS.h +++ b/SignalMessaging/categories/UIFont+OWS.h @@ -18,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN + (UIFont *)ows_boldFontWithSize:(CGFloat)size; ++ (UIFont *)ows_dynamicTypeBodyFont:(CGFloat)size; + #pragma mark - Icon Fonts + (UIFont *)ows_fontAwesomeFont:(CGFloat)size; diff --git a/SignalMessaging/categories/UIFont+OWS.m b/SignalMessaging/categories/UIFont+OWS.m index 1275111f8..87532213b 100644 --- a/SignalMessaging/categories/UIFont+OWS.m +++ b/SignalMessaging/categories/UIFont+OWS.m @@ -33,6 +33,11 @@ NS_ASSUME_NONNULL_BEGIN return [UIFont boldSystemFontOfSize:size]; } ++ (UIFont *)ows_dynamicTypeBodyFont:(CGFloat)size +{ + return [UIFont ows_dynamicTypeBodyFont]; +} + #pragma mark - Icon Fonts + (UIFont *)ows_fontAwesomeFont:(CGFloat)size