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

693 lines
26 KiB
Mathematica
Raw Normal View History

//
2019-01-25 17:33:09 +01:00
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "OWSQuotedMessageView.h"
#import "ConversationViewItem.h"
#import "Environment.h"
2018-07-07 00:13:25 +02:00
#import "OWSBubbleView.h"
2019-05-02 23:58:48 +02:00
#import "Session-Swift.h"
2020-11-12 00:41:45 +01:00
#import <SignalCoreKit/NSString+OWS.h>
2020-11-16 00:34:47 +01:00
2020-11-11 07:45:50 +01:00
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <SignalUtilitiesKit/UIColor+OWS.h>
2020-11-26 00:37:56 +01:00
#import <SessionUtilitiesKit/UIView+OWS.h>
2020-11-23 00:24:40 +01:00
#import <SessionMessagingKit/TSAttachmentStream.h>
2020-11-26 00:37:56 +01:00
#import <SessionMessagingKit/TSMessage.h>
2020-11-11 07:45:50 +01:00
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
const CGFloat kRemotelySourcedContentGlyphLength = 16;
const CGFloat kRemotelySourcedContentRowMargin = 4;
2019-12-11 00:25:53 +01:00
const CGFloat kRemotelySourcedContentRowSpacing = 4;
@interface OWSQuotedMessageView ()
@property (nonatomic, readonly) OWSQuotedReplyModel *quotedMessage;
@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText;
2018-06-28 19:28:14 +02:00
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
@property (nonatomic, readonly) BOOL isForPreview;
@property (nonatomic, readonly) BOOL isOutgoing;
2018-07-07 00:32:46 +02:00
@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
2018-06-28 19:28:14 +02:00
conversationStyle:(ConversationStyle *)conversationStyle
isOutgoing:(BOOL)isOutgoing
2018-07-07 00:32:46 +02:00
sharpCorners:(OWSDirectionalRectCorner)sharpCorners
{
OWSAssertDebug(quotedMessage);
return [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage
displayableQuotedText:displayableQuotedText
2018-06-28 19:28:14 +02:00
conversationStyle:conversationStyle
isForPreview:NO
2018-06-29 20:57:33 +02:00
isOutgoing:isOutgoing
2018-07-06 22:45:00 +02:00
sharpCorners:sharpCorners];
}
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage
2018-06-28 19:28:14 +02:00
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
2018-06-28 19:28:14 +02:00
conversationStyle:(ConversationStyle *)conversationStyle
isForPreview:(BOOL)isForPreview
isOutgoing:(BOOL)isOutgoing
2018-07-07 00:32:46 +02:00
sharpCorners:(OWSDirectionalRectCorner)sharpCorners
{
self = [super init];
if (!self) {
return self;
}
OWSAssertDebug(quotedMessage);
_quotedMessage = quotedMessage;
_displayableQuotedText = displayableQuotedText;
_isForPreview = isForPreview;
2018-06-28 19:28:14 +02:00
_conversationStyle = conversationStyle;
_isOutgoing = isOutgoing;
2018-07-06 22:45:00 +02:00
_sharpCorners = sharpCorners;
_quotedAuthorLabel = [UILabel new];
_quotedTextLabel = [UILabel new];
_quoteContentSourceLabel = [UILabel new];
2018-04-09 23:15:56 +02:00
return self;
}
- (BOOL)hasQuotedAttachment
{
return (self.quotedMessage.contentType.length > 0
2018-04-05 16:35:57 +02:00
&& ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType]);
}
2018-04-05 16:57:51 +02:00
- (BOOL)hasQuotedAttachmentThumbnailImage
{
return (self.quotedMessage.contentType.length > 0
2018-04-05 16:35:57 +02:00
&& ![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]
2018-07-09 15:58:02 +02:00
: [self.conversationStyle quotingSelfHighlightColor]);
}
#pragma mark -
2018-06-29 17:31:22 +02:00
- (CGFloat)bubbleHMargin
{
2020-09-04 05:35:21 +02:00
return (self.isForPreview ? 0.f : 20.f);
2018-06-29 17:31:22 +02:00
}
2018-06-29 18:00:10 +02:00
- (CGFloat)hSpacing
2018-06-29 17:31:22 +02:00
{
2018-06-29 18:00:10 +02:00
return 8.f;
2018-06-29 17:31:22 +02:00
}
2018-07-02 21:02:36 +02:00
- (CGFloat)vSpacing
{
2019-12-10 05:11:46 +01:00
return 4.f;
2018-07-02 21:02:36 +02:00
}
2018-06-29 18:00:10 +02:00
- (CGFloat)stripeThickness
2018-06-29 17:31:22 +02:00
{
2019-12-10 05:11:46 +01:00
return LKValues.accentLineThickness;
2018-06-29 17:31:22 +02:00
}
- (UIColor *)quoteBubbleBackgroundColor
{
return [self.conversationStyle quotedReplyBubbleColorWithIsIncoming:!self.isOutgoing];
}
- (void)createContents
{
2018-04-13 21:10:16 +02:00
// Ensure only called once.
OWSAssertDebug(self.subviews.count < 1);
self.userInteractionEnabled = YES;
self.layoutMargins = UIEdgeInsetsZero;
self.clipsToBounds = YES;
2019-12-11 00:25:53 +01:00
2018-06-29 17:31:22 +02:00
CAShapeLayer *maskLayer = [CAShapeLayer new];
2018-07-07 00:32:46 +02:00
OWSDirectionalRectCorner sharpCorners = self.sharpCorners;
2018-07-06 22:45:00 +02:00
2018-06-29 17:31:22 +02:00
OWSLayerView *innerBubbleView = [[OWSLayerView alloc]
initWithFrame:CGRectZero
layoutCallback:^(UIView *layerView) {
CGRect layerFrame = layerView.bounds;
2018-07-06 22:45:00 +02:00
const CGFloat bubbleLeft = 0.f;
const CGFloat bubbleRight = layerFrame.size.width;
const CGFloat bubbleTop = 0.f;
const CGFloat bubbleBottom = layerFrame.size.height;
2019-12-11 00:25:53 +01:00
const CGFloat sharpCornerRadius = 2;
2019-12-10 05:11:46 +01:00
const CGFloat wideCornerRadius = self.isForPreview ? 14 : 4;
2018-07-06 22:45:00 +02:00
2018-07-07 00:13:25 +02:00
UIBezierPath *bezierPath = [OWSBubbleView roundedBezierRectWithBubbleTop:bubbleTop
bubbleLeft:bubbleLeft
bubbleBottom:bubbleBottom
bubbleRight:bubbleRight
sharpCornerRadius:sharpCornerRadius
wideCornerRadius:wideCornerRadius
sharpCorners:sharpCorners];
2018-06-29 17:31:22 +02:00
maskLayer.path = bezierPath.CGPath;
}];
innerBubbleView.layer.mask = maskLayer;
if (self.isForPreview) {
2021-01-11 04:37:49 +01:00
NSString *userHexEncodedPublicKey = [SNGeneralUtilities getUserPublicKey];
2019-12-10 05:11:46 +01:00
BOOL wasSentByUser = [self.quotedMessage.authorId isEqual:userHexEncodedPublicKey];
innerBubbleView.backgroundColor = [self.conversationStyle quotedReplyBubbleColorWithIsIncoming:wasSentByUser];
} else {
innerBubbleView.backgroundColor = self.quoteBubbleBackgroundColor;
}
2018-06-29 17:31:22 +02:00
[self addSubview:innerBubbleView];
[innerBubbleView autoPinLeadingToSuperviewMarginWithInset:self.bubbleHMargin];
[innerBubbleView autoPinTrailingToSuperviewMarginWithInset:self.bubbleHMargin];
2018-06-29 18:00:10 +02:00
[innerBubbleView autoPinTopToSuperviewMargin];
2018-06-29 17:31:22 +02:00
[innerBubbleView autoPinBottomToSuperviewMargin];
2019-01-25 17:33:09 +01:00
[innerBubbleView setContentHuggingHorizontalLow];
[innerBubbleView setCompressionResistanceHorizontalLow];
2018-06-29 17:31:22 +02:00
UIStackView *hStackView = [UIStackView new];
hStackView.axis = UILayoutConstraintAxisHorizontal;
hStackView.spacing = self.hSpacing;
UIView *stripeView = [UIView new];
if (self.isForPreview) {
2019-12-10 05:11:46 +01:00
stripeView.backgroundColor = LKColors.accent;
} else {
stripeView.backgroundColor = [self.conversationStyle quotedReplyStripeColorWithIsIncoming:!self.isOutgoing];
}
2018-06-29 18:00:10 +02:00
[stripeView autoSetDimension:ALDimensionWidth toSize:self.stripeThickness];
2018-06-29 17:31:22 +02:00
[stripeView setContentHuggingHigh];
[stripeView setCompressionResistanceHigh];
[hStackView addArrangedSubview:stripeView];
UIStackView *vStackView = [UIStackView new];
vStackView.axis = UILayoutConstraintAxisVertical;
2018-06-29 18:00:10 +02:00
vStackView.layoutMargins = UIEdgeInsetsMake(self.textVMargin, 0, self.textVMargin, 0);
2018-06-29 17:31:22 +02:00
vStackView.layoutMarginsRelativeArrangement = YES;
2018-07-02 21:02:36 +02:00
vStackView.spacing = self.vSpacing;
2019-01-25 23:06:05 +01:00
[vStackView setContentHuggingHorizontalLow];
[vStackView setCompressionResistanceHorizontalLow];
2018-06-29 17:31:22 +02:00
[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) {
2018-06-29 17:31:22 +02:00
UIView *_Nullable quotedAttachmentView = nil;
UIImage *_Nullable thumbnailImage = [self tryToLoadThumbnailImage];
if (thumbnailImage) {
quotedAttachmentView = [self imageViewForImage:thumbnailImage];
quotedAttachmentView.clipsToBounds = YES;
quotedAttachmentView.backgroundColor = [UIColor whiteColor];
2018-04-16 21:36:02 +02:00
if (self.isVideoAttachment) {
UIImage *contentIcon = [UIImage imageNamed:@"Play"];
2018-04-16 21:36:02 +02:00
contentIcon = [contentIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *contentImageView = [self imageViewForImage:contentIcon];
contentImageView.tintColor = LKColors.text;
2018-04-16 21:36:02 +02:00
[quotedAttachmentView addSubview:contentImageView];
[contentImageView autoSetDimension:ALDimensionWidth toSize:16];
[contentImageView autoSetDimension:ALDimensionHeight toSize:16];
2018-04-16 21:36:02 +02:00
[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;
2018-06-29 17:31:22 +02:00
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];
2018-06-29 17:31:22 +02:00
quotedAttachmentView.userInteractionEnabled = YES;
} else {
2018-06-29 17:31:22 +02:00
UIImage *contentIcon = [UIImage imageNamed:@"generic-attachment"];
2018-04-16 21:36:02 +02:00
UIImageView *contentImageView = [self imageViewForImage:contentIcon];
2018-06-29 17:31:22 +02:00
contentImageView.contentMode = UIViewContentModeScaleAspectFit;
UIView *wrapper = [UIView containerView];
[wrapper addSubview:contentImageView];
[contentImageView autoCenterInSuperview];
2018-06-29 18:00:10 +02:00
[contentImageView autoSetDimension:ALDimensionWidth toSize:self.quotedAttachmentSize * 0.5f];
2018-06-29 17:31:22 +02:00
quotedAttachmentView = wrapper;
}
2019-01-25 23:06:05 +01:00
[quotedAttachmentView autoPinToSquareAspectRatio];
[quotedAttachmentView setContentHuggingHigh];
[quotedAttachmentView setCompressionResistanceHigh];
2018-06-29 17:31:22 +02:00
[hStackView addArrangedSubview:quotedAttachmentView];
} else {
2018-06-29 23:09:51 +02:00
// 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.
2018-06-29 17:31:22 +02:00
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];
2020-09-25 06:07:05 +02:00
UIColor *tintColor = [LKAppModeUtilities isLightMode] ? UIColor.blackColor : UIColor.whiteColor;
UIImage *cancelIcon = [[UIImage imageNamed:@"X"] asTintedImageWithColor:tintColor];
2020-03-17 06:18:53 +01:00
[cancelButton setImage:cancelIcon forState:UIControlStateNormal];
2019-12-11 00:25:53 +01:00
cancelButton.contentMode = UIViewContentModeScaleAspectFit;
[cancelButton addTarget:self action:@selector(didTapCancel) forControlEvents:UIControlEventTouchUpInside];
2019-12-11 00:25:53 +01:00
[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;
2019-12-11 00:25:53 +01:00
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];
2019-12-11 00:25:53 +01:00
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;
2019-12-11 00:25:53 +01:00
const CGFloat leftMargin = 4;
sourceRow.layoutMargins = UIEdgeInsetsMake(kRemotelySourcedContentRowMargin,
leftMargin,
kRemotelySourcedContentRowMargin,
kRemotelySourcedContentRowMargin);
2021-01-29 01:46:32 +01:00
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
{
2018-04-05 16:57:51 +02:00
if (!self.hasQuotedAttachmentThumbnailImage) {
return nil;
}
2018-04-06 16:59:23 +02:00
// 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;
2018-07-18 04:20:31 +02:00
SUPPRESS_DEADSTORE_WARNING(textColor);
UIFont *font = self.quotedTextFont;
2018-07-18 04:20:31 +02:00
SUPPRESS_DEADSTORE_WARNING(font);
NSString *text = @"";
NSString *_Nullable fileTypeForSnippet = [self fileTypeForSnippet];
2018-04-06 16:59:23 +02:00
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;
2018-04-05 17:15:00 +02:00
} 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;
}
2018-06-29 19:18:23 +02:00
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);
2019-12-11 00:25:53 +01:00
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;
2018-04-05 17:15:00 +02:00
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.");
2018-04-09 21:13:07 +02:00
} else if ([MIMETypeUtil isImage:contentType]) {
return NSLocalizedString(
@"QUOTED_REPLY_TYPE_IMAGE", @"Indicates this message is a quoted reply to an image file.");
2018-04-09 21:13:07 +02:00
} else if ([MIMETypeUtil isAnimated:contentType]) {
return NSLocalizedString(
@"QUOTED_REPLY_TYPE_GIF", @"Indicates this message is a quoted reply to animated GIF file.");
}
return nil;
}
2018-04-16 21:36:02 +02:00
- (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]) {
2020-11-26 03:23:35 +01:00
quotedAuthorText = NSLocalizedString(@"You", @"");
} else {
__block NSString *quotedAuthor = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.quotedMessage.authorId] ?: self.quotedMessage.authorId;
2019-09-11 08:25:33 +02:00
if (quotedAuthor == self.quotedMessage.authorId) {
SNOpenGroup *openGroup = [LKStorage.shared getOpenGroupForThreadID:self.quotedMessage.threadId];
2019-09-11 08:25:33 +02:00
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
if (openGroup != nil) {
quotedAuthor = [LKUserDisplayNameUtilities getPublicChatDisplayNameFor:self.quotedMessage.authorId in:openGroup.channel on:openGroup.server using:transaction];
}
2019-09-11 08:25:33 +02:00
}];
}
2019-12-10 05:11:46 +01:00
quotedAuthorText = quotedAuthor;
}
self.quotedAuthorLabel.text = quotedAuthorText;
self.quotedAuthorLabel.font = self.quotedAuthorFont;
2018-06-29 17:31:22 +02:00
// TODO:
self.quotedAuthorLabel.textColor = [self quotedAuthorColor];
self.quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.quotedAuthorLabel.numberOfLines = 1;
return self.quotedAuthorLabel;
}
#pragma mark - Measurement
2018-06-29 18:00:10 +02:00
- (CGFloat)textVMargin
{
return 7.f;
}
- (CGSize)sizeForMaxWidth:(CGFloat)maxWidth
{
CGSize result = CGSizeZero;
2018-06-29 18:00:10 +02:00
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
2018-06-29 18:00:10 +02:00
CGFloat textWidth = 0.f;
CGFloat maxTextWidth = maxWidth - result.width;
2018-07-02 21:02:36 +02:00
CGFloat textHeight = self.textVMargin * 2 + self.quotedAuthorHeight + self.vSpacing;
{
UILabel *quotedAuthorLabel = [self configureQuotedAuthorLabel];
2018-06-29 18:00:10 +02:00
CGSize quotedAuthorSize = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
textWidth = quotedAuthorSize.width;
}
{
UILabel *quotedTextLabel = [self configureQuotedTextLabel];
2018-06-29 18:00:10 +02:00
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;
}
2018-06-29 18:00:10 +02:00
textWidth = MIN(textWidth, maxTextWidth);
result.width += textWidth;
2018-06-29 18:00:10 +02:00
result.height += MAX(textHeight, thumbnailHeight);
2018-06-29 18:00:10 +02:00
return CGSizeCeil(result);
}
- (UIFont *)quotedAuthorFont
{
2019-12-10 05:11:46 +01:00
return [UIFont boldSystemFontOfSize:LKValues.smallFontSize];
}
- (UIColor *)quotedAuthorColor
{
2018-07-09 15:58:02 +02:00
return [self.conversationStyle quotedReplyAuthorColor];
}
- (UIColor *)quotedTextColor
{
2018-07-09 15:58:02 +02:00
return [self.conversationStyle quotedReplyTextColor];
}
- (UIFont *)quotedTextFont
{
2020-09-04 05:35:21 +02:00
return [UIFont systemFontOfSize:LKValues.smallFontSize];
}
- (UIColor *)fileTypeTextColor
{
2018-07-09 15:58:02 +02:00
return [self.conversationStyle quotedReplyAttachmentColor];
}
- (UIFont *)fileTypeFont
{
2019-12-11 04:07:27 +01:00
return self.quotedTextFont;
}
- (UIColor *)filenameTextColor
{
2018-07-09 15:58:02 +02:00
return [self.conversationStyle quotedReplyAttachmentColor];
}
- (UIFont *)filenameFont
{
return self.quotedTextFont;
}
- (CGFloat)quotedAuthorHeight
{
return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.f);
}
- (CGFloat)quotedAttachmentSize
{
2018-06-29 18:00:10 +02:00
return 54.f;
}
#pragma mark -
- (CGSize)sizeThatFits:(CGSize)size
{
return [self sizeForMaxWidth:CGFLOAT_MAX];
}
- (void)didTapCancel
{
[self.delegate didCancelQuotedReply];
}
@end
NS_ASSUME_NONNULL_END