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