session-ios/Session/Conversations/Views & Cells/OWSQuotedMessageView.m

693 lines
26 KiB
Objective-C

//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "OWSQuotedMessageView.h"
#import "ConversationViewItem.h"
#import "Environment.h"
#import "OWSBubbleView.h"
#import "Session-Swift.h"
#import <SignalCoreKit/NSString+OWS.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <SignalUtilitiesKit/UIColor+OWS.h>
#import <SessionUtilitiesKit/UIView+OWS.h>
#import <SessionMessagingKit/TSAttachmentStream.h>
#import <SessionMessagingKit/TSMessage.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
const CGFloat kRemotelySourcedContentGlyphLength = 16;
const CGFloat kRemotelySourcedContentRowMargin = 4;
const CGFloat kRemotelySourcedContentRowSpacing = 4;
@interface OWSQuotedMessageView ()
@property (nonatomic, readonly) OWSQuotedReplyModel *quotedMessage;
@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText;
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
@property (nonatomic, readonly) BOOL isForPreview;
@property (nonatomic, readonly) BOOL isOutgoing;
@property (nonatomic, readonly) OWSDirectionalRectCorner sharpCorners;
@property (nonatomic, readonly) UILabel *quotedAuthorLabel;
@property (nonatomic, readonly) UILabel *quotedTextLabel;
@property (nonatomic, readonly) UILabel *quoteContentSourceLabel;
@end
#pragma mark -
@implementation OWSQuotedMessageView
+ (OWSQuotedMessageView *)quotedMessageViewForConversation:(OWSQuotedReplyModel *)quotedMessage
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
conversationStyle:(ConversationStyle *)conversationStyle
isOutgoing:(BOOL)isOutgoing
sharpCorners:(OWSDirectionalRectCorner)sharpCorners
{
OWSAssertDebug(quotedMessage);
return [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage
displayableQuotedText:displayableQuotedText
conversationStyle:conversationStyle
isForPreview:NO
isOutgoing:isOutgoing
sharpCorners:sharpCorners];
}
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage
conversationStyle:(ConversationStyle *)conversationStyle
{
OWSAssertDebug(quotedMessage);
DisplayableText *_Nullable displayableQuotedText = nil;
if (quotedMessage.body.length > 0) {
displayableQuotedText = [DisplayableText displayableText:quotedMessage.body];
}
OWSQuotedMessageView *instance = [[OWSQuotedMessageView alloc]
initWithQuotedMessage:quotedMessage
displayableQuotedText:displayableQuotedText
conversationStyle:conversationStyle
isForPreview:YES
isOutgoing:YES
sharpCorners:(OWSDirectionalRectCornerBottomLeading | OWSDirectionalRectCornerBottomTrailing)];
[instance createContents];
return instance;
}
- (instancetype)initWithQuotedMessage:(OWSQuotedReplyModel *)quotedMessage
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
conversationStyle:(ConversationStyle *)conversationStyle
isForPreview:(BOOL)isForPreview
isOutgoing:(BOOL)isOutgoing
sharpCorners:(OWSDirectionalRectCorner)sharpCorners
{
self = [super init];
if (!self) {
return self;
}
OWSAssertDebug(quotedMessage);
_quotedMessage = quotedMessage;
_displayableQuotedText = displayableQuotedText;
_isForPreview = isForPreview;
_conversationStyle = conversationStyle;
_isOutgoing = isOutgoing;
_sharpCorners = sharpCorners;
_quotedAuthorLabel = [UILabel new];
_quotedTextLabel = [UILabel new];
_quoteContentSourceLabel = [UILabel new];
return self;
}
- (BOOL)hasQuotedAttachment
{
return (self.quotedMessage.contentType.length > 0
&& ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType]);
}
- (BOOL)hasQuotedAttachmentThumbnailImage
{
return (self.quotedMessage.contentType.length > 0
&& ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType] &&
[TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]);
}
- (UIColor *)highlightColor
{
BOOL isQuotingSelf = [NSObject isNullableObject:self.quotedMessage.authorId equalTo:TSAccountManager.localNumber];
return (isQuotingSelf ? [self.conversationStyle bubbleColorWithIsIncoming:NO]
: [self.conversationStyle quotingSelfHighlightColor]);
}
#pragma mark -
- (CGFloat)bubbleHMargin
{
return (self.isForPreview ? 0.f : 20.f);
}
- (CGFloat)hSpacing
{
return 8.f;
}
- (CGFloat)vSpacing
{
return 4.f;
}
- (CGFloat)stripeThickness
{
return LKValues.accentLineThickness;
}
- (UIColor *)quoteBubbleBackgroundColor
{
return [self.conversationStyle quotedReplyBubbleColorWithIsIncoming:!self.isOutgoing];
}
- (void)createContents
{
// Ensure only called once.
OWSAssertDebug(self.subviews.count < 1);
self.userInteractionEnabled = YES;
self.layoutMargins = UIEdgeInsetsZero;
self.clipsToBounds = YES;
CAShapeLayer *maskLayer = [CAShapeLayer new];
OWSDirectionalRectCorner sharpCorners = self.sharpCorners;
OWSLayerView *innerBubbleView = [[OWSLayerView alloc]
initWithFrame:CGRectZero
layoutCallback:^(UIView *layerView) {
CGRect layerFrame = layerView.bounds;
const CGFloat bubbleLeft = 0.f;
const CGFloat bubbleRight = layerFrame.size.width;
const CGFloat bubbleTop = 0.f;
const CGFloat bubbleBottom = layerFrame.size.height;
const CGFloat sharpCornerRadius = 2;
const CGFloat wideCornerRadius = self.isForPreview ? 14 : 4;
UIBezierPath *bezierPath = [OWSBubbleView roundedBezierRectWithBubbleTop:bubbleTop
bubbleLeft:bubbleLeft
bubbleBottom:bubbleBottom
bubbleRight:bubbleRight
sharpCornerRadius:sharpCornerRadius
wideCornerRadius:wideCornerRadius
sharpCorners:sharpCorners];
maskLayer.path = bezierPath.CGPath;
}];
innerBubbleView.layer.mask = maskLayer;
if (self.isForPreview) {
NSString *userHexEncodedPublicKey = [SNGeneralUtilities getUserPublicKey];
BOOL wasSentByUser = [self.quotedMessage.authorId isEqual:userHexEncodedPublicKey];
innerBubbleView.backgroundColor = [self.conversationStyle quotedReplyBubbleColorWithIsIncoming:wasSentByUser];
} else {
innerBubbleView.backgroundColor = self.quoteBubbleBackgroundColor;
}
[self addSubview:innerBubbleView];
[innerBubbleView autoPinLeadingToSuperviewMarginWithInset:self.bubbleHMargin];
[innerBubbleView autoPinTrailingToSuperviewMarginWithInset:self.bubbleHMargin];
[innerBubbleView autoPinTopToSuperviewMargin];
[innerBubbleView autoPinBottomToSuperviewMargin];
[innerBubbleView setContentHuggingHorizontalLow];
[innerBubbleView setCompressionResistanceHorizontalLow];
UIStackView *hStackView = [UIStackView new];
hStackView.axis = UILayoutConstraintAxisHorizontal;
hStackView.spacing = self.hSpacing;
UIView *stripeView = [UIView new];
if (self.isForPreview) {
stripeView.backgroundColor = LKColors.accent;
} else {
stripeView.backgroundColor = [self.conversationStyle quotedReplyStripeColorWithIsIncoming:!self.isOutgoing];
}
[stripeView autoSetDimension:ALDimensionWidth toSize:self.stripeThickness];
[stripeView setContentHuggingHigh];
[stripeView setCompressionResistanceHigh];
[hStackView addArrangedSubview:stripeView];
UIStackView *vStackView = [UIStackView new];
vStackView.axis = UILayoutConstraintAxisVertical;
vStackView.layoutMargins = UIEdgeInsetsMake(self.textVMargin, 0, self.textVMargin, 0);
vStackView.layoutMarginsRelativeArrangement = YES;
vStackView.spacing = self.vSpacing;
[vStackView setContentHuggingHorizontalLow];
[vStackView setCompressionResistanceHorizontalLow];
[hStackView addArrangedSubview:vStackView];
UILabel *quotedAuthorLabel = [self configureQuotedAuthorLabel];
[vStackView addArrangedSubview:quotedAuthorLabel];
[quotedAuthorLabel autoSetDimension:ALDimensionHeight toSize:self.quotedAuthorHeight];
[quotedAuthorLabel setContentHuggingVerticalHigh];
[quotedAuthorLabel setContentHuggingHorizontalLow];
[quotedAuthorLabel setCompressionResistanceHorizontalLow];
UILabel *quotedTextLabel = [self configureQuotedTextLabel];
[vStackView addArrangedSubview:quotedTextLabel];
[quotedTextLabel setContentHuggingHorizontalLow];
[quotedTextLabel setCompressionResistanceHorizontalLow];
[quotedTextLabel setCompressionResistanceVerticalHigh];
if (self.hasQuotedAttachment) {
UIView *_Nullable quotedAttachmentView = nil;
UIImage *_Nullable thumbnailImage = [self tryToLoadThumbnailImage];
if (thumbnailImage) {
quotedAttachmentView = [self imageViewForImage:thumbnailImage];
quotedAttachmentView.clipsToBounds = YES;
quotedAttachmentView.backgroundColor = [UIColor whiteColor];
if (self.isVideoAttachment) {
UIImage *contentIcon = [UIImage imageNamed:@"Play"];
contentIcon = [contentIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *contentImageView = [self imageViewForImage:contentIcon];
contentImageView.tintColor = LKColors.text;
[quotedAttachmentView addSubview:contentImageView];
[contentImageView autoSetDimension:ALDimensionWidth toSize:16];
[contentImageView autoSetDimension:ALDimensionHeight toSize:16];
[contentImageView autoCenterInSuperview];
}
} else if (self.quotedMessage.thumbnailDownloadFailed) {
// TODO design review icon and color
UIImage *contentIcon =
[[UIImage imageNamed:@"btnRefresh--white"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *contentImageView = [self imageViewForImage:contentIcon];
contentImageView.contentMode = UIViewContentModeScaleAspectFit;
contentImageView.tintColor = UIColor.whiteColor;
quotedAttachmentView = [UIView containerView];
[quotedAttachmentView addSubview:contentImageView];
quotedAttachmentView.backgroundColor = self.highlightColor;
[contentImageView autoCenterInSuperview];
[contentImageView
autoSetDimensionsToSize:CGSizeMake(self.quotedAttachmentSize * 0.5f, self.quotedAttachmentSize * 0.5f)];
UITapGestureRecognizer *tapGesture =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapFailedThumbnailDownload:)];
[quotedAttachmentView addGestureRecognizer:tapGesture];
quotedAttachmentView.userInteractionEnabled = YES;
} else {
UIImage *contentIcon = [UIImage imageNamed:@"generic-attachment"];
UIImageView *contentImageView = [self imageViewForImage:contentIcon];
contentImageView.contentMode = UIViewContentModeScaleAspectFit;
UIView *wrapper = [UIView containerView];
[wrapper addSubview:contentImageView];
[contentImageView autoCenterInSuperview];
[contentImageView autoSetDimension:ALDimensionWidth toSize:self.quotedAttachmentSize * 0.5f];
quotedAttachmentView = wrapper;
}
[quotedAttachmentView autoPinToSquareAspectRatio];
[quotedAttachmentView setContentHuggingHigh];
[quotedAttachmentView setCompressionResistanceHigh];
[hStackView addArrangedSubview:quotedAttachmentView];
} else {
// If there's no attachment, add an empty view so that
// the stack view's spacing serves as a margin between
// the text views and the trailing edge.
UIView *emptyView = [UIView containerView];
[hStackView addArrangedSubview:emptyView];
[emptyView setContentHuggingHigh];
[emptyView autoSetDimension:ALDimensionWidth toSize:0.f];
}
UIView *contentView = hStackView;
[contentView setContentHuggingHorizontalLow];
[contentView setCompressionResistanceHorizontalLow];
if (self.quotedMessage.isRemotelySourced) {
UIStackView *quoteSourceWrapper = [[UIStackView alloc] initWithArrangedSubviews:@[
contentView,
[self buildRemoteContentSourceView],
]];
quoteSourceWrapper.axis = UILayoutConstraintAxisVertical;
contentView = quoteSourceWrapper;
[contentView setContentHuggingHorizontalLow];
[contentView setCompressionResistanceHorizontalLow];
}
if (self.isForPreview) {
UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIColor *tintColor = [LKAppModeUtilities isLightMode] ? UIColor.blackColor : UIColor.whiteColor;
UIImage *cancelIcon = [[UIImage imageNamed:@"X"] asTintedImageWithColor:tintColor];
[cancelButton setImage:cancelIcon forState:UIControlStateNormal];
cancelButton.contentMode = UIViewContentModeScaleAspectFit;
[cancelButton addTarget:self action:@selector(didTapCancel) forControlEvents:UIControlEventTouchUpInside];
[cancelButton autoSetDimension:ALDimensionWidth toSize:14.f];
[cancelButton autoSetDimension:ALDimensionHeight toSize:14.f];
UIStackView *cancelStack = [[UIStackView alloc] initWithArrangedSubviews:@[ cancelButton ]];
cancelStack.axis = UILayoutConstraintAxisHorizontal;
cancelStack.alignment = UIStackViewAlignmentTop;
cancelStack.layoutMarginsRelativeArrangement = YES;
CGFloat hMarginLeading = 8;
CGFloat hMarginTrailing = 8;
cancelStack.layoutMargins = UIEdgeInsetsMake(8,
CurrentAppContext().isRTL ? hMarginTrailing : hMarginLeading,
0,
CurrentAppContext().isRTL ? hMarginLeading : hMarginTrailing);
UIStackView *cancelWrapper = [[UIStackView alloc] initWithArrangedSubviews:@[
contentView,
cancelStack,
]];
cancelWrapper.axis = UILayoutConstraintAxisHorizontal;
contentView = cancelWrapper;
[contentView setContentHuggingHorizontalLow];
[contentView setCompressionResistanceHorizontalLow];
}
[innerBubbleView addSubview:contentView];
[contentView ows_autoPinToSuperviewEdges];
}
- (UIView *)buildRemoteContentSourceView
{
UIImage *glyphImage =
[[UIImage imageNamed:@"ic_broken_link"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
OWSAssertDebug(glyphImage);
OWSAssertDebug(CGSizeEqualToSize(
CGSizeMake(kRemotelySourcedContentGlyphLength, kRemotelySourcedContentGlyphLength), glyphImage.size));
UIImageView *glyphView = [[UIImageView alloc] initWithImage:glyphImage];
glyphView.tintColor = LKColors.text;
[glyphView
autoSetDimensionsToSize:CGSizeMake(kRemotelySourcedContentGlyphLength, kRemotelySourcedContentGlyphLength)];
UILabel *label = [self configureQuoteContentSourceLabel];
UIStackView *sourceRow = [[UIStackView alloc] initWithArrangedSubviews:@[ glyphView, label ]];
sourceRow.axis = UILayoutConstraintAxisHorizontal;
sourceRow.alignment = UIStackViewAlignmentCenter;
// TODO verify spacing w/ design
sourceRow.spacing = kRemotelySourcedContentRowSpacing;
sourceRow.layoutMarginsRelativeArrangement = YES;
const CGFloat leftMargin = 4;
sourceRow.layoutMargins = UIEdgeInsetsMake(kRemotelySourcedContentRowMargin,
leftMargin,
kRemotelySourcedContentRowMargin,
kRemotelySourcedContentRowMargin);
UIColor *backgroundColor = LKAppModeUtilities.isLightMode ? [UIColor.whiteColor colorWithAlphaComponent:LKValues.mediumOpacity] : [LKColors.text colorWithAlphaComponent:LKValues.mediumOpacity];
[sourceRow addBackgroundViewWithBackgroundColor:backgroundColor];
return sourceRow;
}
- (void)didTapFailedThumbnailDownload:(UITapGestureRecognizer *)gestureRecognizer
{
OWSLogDebug(@"in didTapFailedThumbnailDownload");
if (!self.quotedMessage.thumbnailDownloadFailed) {
OWSFailDebug(@"thumbnailDownloadFailed was unexpectedly false");
return;
}
if (!self.quotedMessage.thumbnailAttachmentPointer) {
OWSFailDebug(@"thumbnailAttachmentPointer was unexpectedly nil");
return;
}
[self.delegate didTapQuotedReply:self.quotedMessage
failedThumbnailDownloadAttachmentPointer:self.quotedMessage.thumbnailAttachmentPointer];
}
- (nullable UIImage *)tryToLoadThumbnailImage
{
if (!self.hasQuotedAttachmentThumbnailImage) {
return nil;
}
// TODO: Possibly ignore data that is too large.
UIImage *_Nullable image = self.quotedMessage.thumbnailImage;
// TODO: Possibly ignore images that are too large.
return image;
}
- (UIImageView *)imageViewForImage:(UIImage *)image
{
OWSAssertDebug(image);
UIImageView *imageView = [UIImageView new];
imageView.image = image;
// We need to specify a contentMode since the size of the image
// might not match the aspect ratio of the view.
imageView.contentMode = UIViewContentModeScaleAspectFill;
// Use trilinear filters for better scaling quality at
// some performance cost.
imageView.layer.minificationFilter = kCAFilterTrilinear;
imageView.layer.magnificationFilter = kCAFilterTrilinear;
return imageView;
}
- (UILabel *)configureQuotedTextLabel
{
OWSAssertDebug(self.quotedTextLabel);
UIColor *textColor = self.quotedTextColor;
SUPPRESS_DEADSTORE_WARNING(textColor);
UIFont *font = self.quotedTextFont;
SUPPRESS_DEADSTORE_WARNING(font);
NSString *text = @"";
NSString *_Nullable fileTypeForSnippet = [self fileTypeForSnippet];
NSString *_Nullable sourceFilename = [self.quotedMessage.sourceFilename filterStringForDisplay];
if (self.displayableQuotedText.displayText.length > 0) {
text = self.displayableQuotedText.displayText;
textColor = self.quotedTextColor;
font = self.quotedTextFont;
} else if (fileTypeForSnippet) {
text = fileTypeForSnippet;
textColor = self.fileTypeTextColor;
font = self.fileTypeFont;
} else if (sourceFilename) {
text = sourceFilename;
textColor = self.filenameTextColor;
font = self.filenameFont;
} else {
text = NSLocalizedString(
@"QUOTED_REPLY_TYPE_ATTACHMENT", @"Indicates this message is a quoted reply to an attachment of unknown type.");
textColor = self.fileTypeTextColor;
font = self.fileTypeFont;
}
self.quotedTextLabel.numberOfLines = self.isForPreview ? 1 : 2;
self.quotedTextLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.quotedTextLabel.text = text;
self.quotedTextLabel.textColor = textColor;
self.quotedTextLabel.font = font;
return self.quotedTextLabel;
}
- (UILabel *)configureQuoteContentSourceLabel
{
OWSAssertDebug(self.quoteContentSourceLabel);
self.quoteContentSourceLabel.font = [UIFont systemFontOfSize:LKValues.smallFontSize];
self.quoteContentSourceLabel.textColor = LKColors.text;
self.quoteContentSourceLabel.text = NSLocalizedString(@"QUOTED_REPLY_CONTENT_FROM_REMOTE_SOURCE",
@"Footer label that appears below quoted messages when the quoted content was not derived locally. When the "
@"local user doesn't have a copy of the message being quoted, e.g. if it had since been deleted, we instead "
@"show the content specified by the sender.");
return self.quoteContentSourceLabel;
}
- (nullable NSString *)fileTypeForSnippet
{
// TODO: Are we going to use the filename? For all mimetypes?
NSString *_Nullable contentType = self.quotedMessage.contentType;
if (contentType.length < 1) {
return nil;
}
if ([MIMETypeUtil isAudio:contentType]) {
return NSLocalizedString(
@"QUOTED_REPLY_TYPE_AUDIO", @"Indicates this message is a quoted reply to an audio file.");
} else if ([MIMETypeUtil isVideo:contentType]) {
return NSLocalizedString(
@"QUOTED_REPLY_TYPE_VIDEO", @"Indicates this message is a quoted reply to a video file.");
} else if ([MIMETypeUtil isImage:contentType]) {
return NSLocalizedString(
@"QUOTED_REPLY_TYPE_IMAGE", @"Indicates this message is a quoted reply to an image file.");
} else if ([MIMETypeUtil isAnimated:contentType]) {
return NSLocalizedString(
@"QUOTED_REPLY_TYPE_GIF", @"Indicates this message is a quoted reply to animated GIF file.");
}
return nil;
}
- (BOOL)isAudioAttachment
{
// TODO: Are we going to use the filename? For all mimetypes?
NSString *_Nullable contentType = self.quotedMessage.contentType;
if (contentType.length < 1) {
return NO;
}
return [MIMETypeUtil isAudio:contentType];
}
- (BOOL)isVideoAttachment
{
// TODO: Are we going to use the filename? For all mimetypes?
NSString *_Nullable contentType = self.quotedMessage.contentType;
if (contentType.length < 1) {
return NO;
}
return [MIMETypeUtil isVideo:contentType];
}
- (UILabel *)configureQuotedAuthorLabel
{
OWSAssertDebug(self.quotedAuthorLabel);
NSString *_Nullable localNumber = [TSAccountManager localNumber];
NSString *quotedAuthorText;
if ([localNumber isEqualToString:self.quotedMessage.authorId]) {
quotedAuthorText = NSLocalizedString(@"You", @"");
} else {
__block NSString *quotedAuthor = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.quotedMessage.authorId] ?: self.quotedMessage.authorId;
if (quotedAuthor == self.quotedMessage.authorId) {
SNOpenGroup *openGroup = [LKStorage.shared getOpenGroupForThreadID:self.quotedMessage.threadId];
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
if (openGroup != nil) {
quotedAuthor = [LKUserDisplayNameUtilities getPublicChatDisplayNameFor:self.quotedMessage.authorId in:openGroup.channel on:openGroup.server using:transaction];
}
}];
}
quotedAuthorText = quotedAuthor;
}
self.quotedAuthorLabel.text = quotedAuthorText;
self.quotedAuthorLabel.font = self.quotedAuthorFont;
// TODO:
self.quotedAuthorLabel.textColor = [self quotedAuthorColor];
self.quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.quotedAuthorLabel.numberOfLines = 1;
return self.quotedAuthorLabel;
}
#pragma mark - Measurement
- (CGFloat)textVMargin
{
return 7.f;
}
- (CGSize)sizeForMaxWidth:(CGFloat)maxWidth
{
CGSize result = CGSizeZero;
result.width += self.bubbleHMargin * 2 + self.stripeThickness + self.hSpacing * 2;
CGFloat thumbnailHeight = 0.f;
if (self.hasQuotedAttachment) {
result.width += self.quotedAttachmentSize;
thumbnailHeight += self.quotedAttachmentSize;
}
// Quoted Author
CGFloat textWidth = 0.f;
CGFloat maxTextWidth = maxWidth - result.width;
CGFloat textHeight = self.textVMargin * 2 + self.quotedAuthorHeight + self.vSpacing;
{
UILabel *quotedAuthorLabel = [self configureQuotedAuthorLabel];
CGSize quotedAuthorSize = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
textWidth = quotedAuthorSize.width;
}
{
UILabel *quotedTextLabel = [self configureQuotedTextLabel];
CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
textWidth = MAX(textWidth, textSize.width);
textHeight += textSize.height;
}
if (self.quotedMessage.isRemotelySourced) {
UILabel *quoteContentSourceLabel = [self configureQuoteContentSourceLabel];
CGSize textSize = CGSizeCeil([quoteContentSourceLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
CGFloat sourceStackViewHeight = MAX(kRemotelySourcedContentGlyphLength, textSize.height);
textWidth
= MAX(textWidth, textSize.width + kRemotelySourcedContentGlyphLength + kRemotelySourcedContentRowSpacing);
result.height += kRemotelySourcedContentRowMargin * 2 + sourceStackViewHeight;
}
textWidth = MIN(textWidth, maxTextWidth);
result.width += textWidth;
result.height += MAX(textHeight, thumbnailHeight);
return CGSizeCeil(result);
}
- (UIFont *)quotedAuthorFont
{
return [UIFont boldSystemFontOfSize:LKValues.smallFontSize];
}
- (UIColor *)quotedAuthorColor
{
return [self.conversationStyle quotedReplyAuthorColor];
}
- (UIColor *)quotedTextColor
{
return [self.conversationStyle quotedReplyTextColor];
}
- (UIFont *)quotedTextFont
{
return [UIFont systemFontOfSize:LKValues.smallFontSize];
}
- (UIColor *)fileTypeTextColor
{
return [self.conversationStyle quotedReplyAttachmentColor];
}
- (UIFont *)fileTypeFont
{
return self.quotedTextFont;
}
- (UIColor *)filenameTextColor
{
return [self.conversationStyle quotedReplyAttachmentColor];
}
- (UIFont *)filenameFont
{
return self.quotedTextFont;
}
- (CGFloat)quotedAuthorHeight
{
return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.f);
}
- (CGFloat)quotedAttachmentSize
{
return 54.f;
}
#pragma mark -
- (CGSize)sizeThatFits:(CGSize)size
{
return [self sizeForMaxWidth:CGFLOAT_MAX];
}
- (void)didTapCancel
{
[self.delegate didCancelQuotedReply];
}
@end
NS_ASSUME_NONNULL_END