Merge branch 'charlesmchen/quotedReplies2'

This commit is contained in:
Matthew Chen 2018-04-04 10:31:03 -04:00
commit 609e68e8bc
7 changed files with 496 additions and 53 deletions

View File

@ -96,6 +96,9 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
[self.contentView addSubview:self.dateHeaderLabel];
self.bodyTextView = [self newTextView];
// Setting dataDetectorTypes is expensive. Do it just once.
self.bodyTextView.dataDetectorTypes
= (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent);
self.footerLabel = [UILabel new];
self.footerLabel.font = [UIFont ows_regularFontWithSize:12.f];
@ -140,8 +143,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
textView.contentInset = UIEdgeInsetsZero;
textView.textContainer.lineFragmentPadding = 0;
textView.scrollEnabled = NO;
// Setting dataDetectorTypes is expensive. Do it just once.
textView.dataDetectorTypes = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent);
return textView;
}
@ -174,16 +175,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return [UIFont ows_dynamicTypeBodyFont];
}
- (UIFont *)tapForMoreFont
{
return [UIFont ows_regularFontWithSize:12.f];
}
- (CGFloat)tapForMoreHeight
{
return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25);
}
- (BOOL)shouldHaveFailedSendBadge
{
if (![self.viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) {
@ -251,6 +242,47 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return self.viewItem.mediaSize;
}
- (BOOL)isQuotedReply
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem);
return self.viewItem.isQuotedReply;
}
- (BOOL)hasQuotedText
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem);
return self.viewItem.hasQuotedText;
}
- (BOOL)hasQuotedAttachment
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem);
return self.viewItem.hasQuotedAttachment;
}
- (BOOL)hasQuotedAttachmentThumbnail
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem);
return (self.viewItem.hasQuotedAttachment &&
[TSAttachmentStream hasThumbnailForMimeType:self.viewItem.quotedAttachmentMimetype]);
}
- (nullable DisplayableText *)displayableQuotedText
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem.displayableQuotedText);
return self.viewItem.displayableQuotedText;
}
- (TSMessage *)message
{
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
@ -346,6 +378,40 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
UIView *_Nullable lastSubview = nil;
CGFloat bottomMargin = 0;
if (self.isQuotedReply) {
OWSAssert(!lastSubview);
UIView *quotedMessageView = [self createQuotedMessageView];
[self.bubbleView addSubview:quotedMessageView];
CGFloat leadingMargin = self.quotedBubbleLeadingMargin;
CGFloat trailingMargin = self.quotedBubbleTrailingMargin;
[self.viewConstraints addObjectsFromArray:@[
[quotedMessageView autoPinLeadingToSuperviewMarginWithInset:leadingMargin],
[quotedMessageView autoPinTrailingToSuperviewMarginWithInset:trailingMargin],
]];
if (lastSubview) {
[self.viewConstraints addObject:[quotedMessageView autoPinEdge:ALEdgeTop
toEdge:ALEdgeBottom
ofView:lastSubview
withOffset:self.quotedMessageTopInset]];
} else {
[self.viewConstraints addObject:[quotedMessageView autoPinEdgeToSuperviewEdge:ALEdgeTop
withInset:self.quotedMessageTopInset]];
}
lastSubview = quotedMessageView;
bottomMargin = 0;
[self.bubbleView logFrameLaterWithLabel:@"bubbleView"];
[quotedMessageView logFrameLaterWithLabel:@"quotedMessageView"];
// TODO: Consider stroking the quoted thumbnail.
}
UIView *_Nullable bodyMediaView = nil;
BOOL bodyMediaViewHasGreedyWidth = NO;
switch (self.cellType) {
@ -443,7 +509,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
// We render malformed messages as "empty text" messages,
// so create a text view if there is no body media view.
if (self.hasBodyText || !bodyMediaView) {
bodyTextView = [self createBodyTextViewIfNecessary];
bodyTextView = [self configureBodyTextView];
}
if (bodyTextView) {
[self.bubbleView addSubview:bodyTextView];
@ -720,8 +786,23 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return [UIFont systemFontOfSize:12.0f];
}
- (OWSMessageTextView *)createBodyTextViewIfNecessary
- (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);
BOOL shouldIgnoreEvents = NO;
if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
// Ignore taps on links in outgoing messages that haven't been sent yet, as
@ -731,7 +812,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
}
[self.class loadForTextDisplay:self.bodyTextView
text:self.displayableBodyText.displayText
textColor:self.textColor
textColor:self.bodyTextColor
font:self.textMessageFont
shouldIgnoreEvents:shouldIgnoreEvents];
return self.bodyTextView;
@ -777,12 +858,84 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
tapForMoreLabel.text = NSLocalizedString(@"CONVERSATION_VIEW_OVERSIZE_TEXT_TAP_FOR_MORE",
@"Indicator on truncated text messages that they can be tapped to see the entire text message.");
tapForMoreLabel.font = [self tapForMoreFont];
tapForMoreLabel.textColor = [self.textColor colorWithAlphaComponent:0.85];
tapForMoreLabel.textColor = [self.bodyTextColor colorWithAlphaComponent:0.85];
tapForMoreLabel.textAlignment = [tapForMoreLabel textAlignmentUnnatural];
return tapForMoreLabel;
}
- (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];
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];
}
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 autoPinTrailingToEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing];
} else {
[quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin];
}
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 autoPinLeadingToTrailingEdgeOfView:quotedThumbnailView
offset:self.quotedThumbnailHSpacing];
} else {
[quotedTextLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin];
}
[quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset];
}
return quotedMessageView;
}
- (UIView *)loadViewForStillImage
{
OWSAssert(self.attachmentStream);
@ -1063,10 +1216,8 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth];
const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin));
self.bodyTextView.text = self.displayableBodyText.displayText;
// Honor dynamic type in the message bodies.
self.bodyTextView.font = [self textMessageFont];
CGSize textSize = CGSizeCeil([self.bodyTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
OWSMessageTextView *bodyTextView = [self configureBodyTextView];
CGSize textSize = CGSizeCeil([bodyTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
CGSize textViewSize = textSize;
if (includeMargins) {
@ -1126,17 +1277,103 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return (int)floor(contentWidth * 0.8f);
}
- (CGSize)quotedMessageSizeForViewWidth:(int)viewWidth
contentWidth:(int)contentWidth
includeMargins:(BOOL)includeMargins
{
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;
}
result.width += self.quotedMessageHInset;
// Once we've determined everything _except_ the size of the text
// content (i.e. the quoted author and the quoted text (if any)),
// we can determine the size of the text content.
const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth];
CGFloat 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;
}
return result;
}
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
{
OWSAssert(self.viewItem);
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth];
CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES];
CGSize cellSize = CGSizeZero;
CGFloat cellContentWidth = fmax(mediaContentSize.width, textContentSize.width);
CGFloat cellContentHeight = mediaContentSize.height + textContentSize.height;
CGSize cellSize = CGSizeMake(cellContentWidth, cellContentHeight);
CGSize quotedMessageSize =
[self quotedMessageSizeForViewWidth:viewWidth contentWidth:contentWidth includeMargins:YES];
cellSize.width = MAX(cellSize.width, quotedMessageSize.width);
cellSize.height += quotedMessageSize.height;
CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth];
cellSize.width = MAX(cellSize.width, mediaContentSize.width);
cellSize.height += mediaContentSize.height;
CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES];
cellSize.width = MAX(cellSize.width, textContentSize.width);
cellSize.height += textContentSize.height;
OWSAssert(cellSize.width > 0 && cellSize.height > 0);
@ -1165,6 +1402,92 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
}
}
- (UIFont *)tapForMoreFont
{
return [UIFont ows_regularFontWithSize:12.f];
}
- (CGFloat)tapForMoreHeight
{
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
@ -1195,6 +1518,24 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return result;
}
- (CGFloat)quotedBubbleLeadingMargin
{
CGFloat result = self.quotedMessageHInset;
if (self.isIncoming) {
result += kBubbleThornSideInset;
}
return result;
}
- (CGFloat)quotedBubbleTrailingMargin
{
CGFloat result = self.quotedMessageHInset;
if (!self.isIncoming) {
result += kBubbleThornSideInset;
}
return result;
}
- (CGFloat)textTopMargin
{
return kBubbleTextVInset;
@ -1205,11 +1546,16 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return kBubbleTextVInset + kBubbleThornVInset;
}
- (UIColor *)textColor
- (UIColor *)bodyTextColor
{
return self.isIncoming ? [UIColor blackColor] : [UIColor whiteColor];
}
- (UIColor *)quotedTextColor
{
return [UIColor blackColor];
}
- (BOOL)isMediaBeingSent
{
if (self.isIncoming) {

View File

@ -43,7 +43,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
@property (nonatomic, readonly) TSInteraction *interaction;
@property (nonatomic, readonly) BOOL isGroupThread;
@property (nonatomic, readonly) BOOL hasBodyText;
@property (nonatomic, readonly) BOOL isQuotedReply;
@property (nonatomic, readonly) BOOL hasQuotedAttachment;
@property (nonatomic, readonly) BOOL hasQuotedText;
@property (nonatomic) BOOL shouldShowDate;
@property (nonatomic) BOOL shouldHideRecipientStatus;
@property (nonatomic) BOOL shouldHideBubbleTail;
@ -85,6 +91,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
- (nullable TSAttachmentPointer *)attachmentPointer;
- (CGSize)mediaSize;
- (nullable DisplayableText *)displayableQuotedText;
- (nullable NSString *)quotedAttachmentMimetype;
- (nullable NSString *)quotedRecipientId;
// We don't want to try to load the media for this item (if any)
// if a load has previously failed.
@property (nonatomic) BOOL didCellMediaFailToLoad;

View File

@ -56,6 +56,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
@property (nonatomic) BOOL hasViewState;
@property (nonatomic) OWSMessageCellType messageCellType;
@property (nonatomic, nullable) DisplayableText *displayableBodyText;
@property (nonatomic, nullable) DisplayableText *displayableQuotedText;
@property (nonatomic, nullable) NSString *quotedAttachmentMimetype;
@property (nonatomic, nullable) NSString *quotedRecipientId;
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
@property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer;
@property (nonatomic) CGSize mediaSize;
@ -99,6 +102,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.attachmentPointer = nil;
self.mediaSize = CGSizeZero;
self.displayableQuotedText = nil;
self.quotedRecipientId = nil;
self.quotedAttachmentMimetype = nil;
[self clearCachedLayoutState];
[self ensureViewState:transaction];
@ -109,6 +116,21 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return _displayableBodyText != nil;
}
- (BOOL)hasQuotedText
{
return _displayableQuotedText != nil;
}
- (BOOL)hasQuotedAttachment
{
return self.quotedAttachmentMimetype.length > 0;
}
- (BOOL)isQuotedReply
{
return self.hasQuotedAttachment || self.hasQuotedText;
}
- (void)setShouldShowDate:(BOOL)shouldShowDate
{
if (_shouldShowDate == shouldShowDate) {
@ -275,11 +297,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[self.lastAudioMessageView updateContents];
}
#pragma mark - View State
#pragma mark - Displayable Text
// TODO: Now that we're caching the displayable text on the view items,
// I don't think we need this cache any more.
- (NSCache *)displayableBodyTextCache
- (NSCache *)displayableTextCache
{
static NSCache *cache = nil;
static dispatch_once_t onceToken;
@ -296,10 +318,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSAssert(text);
OWSAssert(interactionId.length > 0);
return [self displayableBodyTextForInteractionId:interactionId
textBlock:^{
return text;
}];
NSString *displayableTextCacheKey = [@"body-" stringByAppendingString:interactionId];
return [self displayableTextForCacheKey:displayableTextCacheKey
textBlock:^{
return text;
}];
}
- (DisplayableText *)displayableBodyTextForOversizeTextAttachment:(TSAttachmentStream *)attachmentStream
@ -308,30 +332,46 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSAssert(attachmentStream);
OWSAssert(interactionId.length > 0);
return
[self displayableBodyTextForInteractionId:interactionId
textBlock:^{
NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL];
NSString *text =
[[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
return text;
}];
NSString *displayableTextCacheKey = [@"oversize-body-" stringByAppendingString:interactionId];
return [self displayableTextForCacheKey:displayableTextCacheKey
textBlock:^{
NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL];
NSString *text =
[[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
return text;
}];
}
- (DisplayableText *)displayableBodyTextForInteractionId:(NSString *)interactionId
textBlock:(NSString * (^_Nonnull)(void))textBlock
- (DisplayableText *)displayableQuotedTextForText:(NSString *)text interactionId:(NSString *)interactionId
{
OWSAssert(text);
OWSAssert(interactionId.length > 0);
DisplayableText *_Nullable displayableBodyText = [[self displayableBodyTextCache] objectForKey:interactionId];
if (!displayableBodyText) {
NSString *text = textBlock();
displayableBodyText = [DisplayableText displayableText:text];
[[self displayableBodyTextCache] setObject:displayableBodyText forKey:interactionId];
}
return displayableBodyText;
NSString *displayableTextCacheKey = [@"quoted-" stringByAppendingString:interactionId];
return [self displayableTextForCacheKey:displayableTextCacheKey
textBlock:^{
return text;
}];
}
- (DisplayableText *)displayableTextForCacheKey:(NSString *)displayableTextCacheKey
textBlock:(NSString * (^_Nonnull)(void))textBlock
{
OWSAssert(displayableTextCacheKey.length > 0);
DisplayableText *_Nullable displayableText = [[self displayableTextCache] objectForKey:displayableTextCacheKey];
if (!displayableText) {
NSString *text = textBlock();
displayableText = [DisplayableText displayableText:text];
[[self displayableTextCache] setObject:displayableText forKey:displayableTextCacheKey];
}
return displayableText;
}
#pragma mark - View State
- (nullable TSAttachment *)firstAttachmentIfAnyOfMessage:(TSMessage *)message
transaction:(YapDatabaseReadTransaction *)transaction
{
@ -430,6 +470,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.messageCellType = OWSMessageCellType_TextMessage;
self.displayableBodyText = [[DisplayableText alloc] initWithFullText:@"" displayText:@"" isTextTruncated:NO];
}
if (message.quotedMessage && message.quotedMessage.authorId.length > 0
&& (message.quotedMessage.body.length > 0 || message.quotedMessage.contentType.length > 0)) {
if (message.quotedMessage.body.length > 0) {
self.displayableQuotedText =
[self displayableQuotedTextForText:message.quotedMessage.body interactionId:message.uniqueId];
}
self.quotedAttachmentMimetype = message.quotedMessage.contentType;
self.quotedRecipientId = message.quotedMessage.authorId;
}
}
- (OWSMessageCellType)messageCellType
@ -475,6 +525,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return _mediaSize;
}
- (nullable DisplayableText *)displayableQuotedText
{
OWSAssertIsOnMainThread();
OWSAssert(self.hasViewState);
OWSAssert(_displayableQuotedText);
OWSAssert(_displayableQuotedText.displayText);
OWSAssert(_displayableQuotedText.fullText);
return _displayableQuotedText;
}
#pragma mark - UIMenuController
- (NSArray<UIMenuItem *> *)textMenuControllerItems
@ -569,6 +631,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
}
// TODO: Update for quoted text.
- (void)copyTextAction
{
switch (self.messageCellType) {
@ -628,6 +691,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
}
// TODO: Update for quoted text.
- (void)shareTextAction
{
switch (self.messageCellType) {

View File

@ -87,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
[DebugUIMessages performActionNTimes:action];
}]];
}
[items addObjectsFromArray:@[
#pragma mark - Actions
@ -103,6 +104,10 @@ NS_ASSUME_NONNULL_BEGIN
actionBlock:^{
[DebugUIMessages selectSendMediaAction:thread];
}],
[OWSTableItem itemWithTitle:@"Select Quoted Reply"
actionBlock:^{
[DebugUIMessages selectQuotedReplyAction:thread];
}],
#pragma mark - Misc.
@ -2464,6 +2469,14 @@ isQuotedMessageAttachmentDownloaded:(BOOL)isQuotedMessageAttachmentDownloaded
subactions:[self allFakeQuotedReplyActions:thread includeLabels:YES]];
}
+ (void)selectQuotedReplyAction:(TSThread *)thread
{
OWSAssertIsOnMainThread();
OWSAssert(thread);
[self selectActionUI:[self allFakeQuotedReplyActions:thread includeLabels:NO] label:@"Select QuotedReply"];
}
+ (DebugUIMessagesAction *)randomQuotedReplyAction:(TSThread *)thread
{
OWSAssert(thread);
@ -2483,6 +2496,7 @@ isQuotedMessageAttachmentDownloaded:(BOOL)isQuotedMessageAttachmentDownloaded
[actions addObjectsFromArray:[self allFakeMediaActions:thread includeLabels:includeLabels]];
[actions addObjectsFromArray:[self allFakeTextActions:thread includeLabels:includeLabels]];
[actions addObjectsFromArray:[self allFakeSequenceActions:thread includeLabels:includeLabels]];
[actions addObjectsFromArray:[self allFakeQuotedReplyActions:thread includeLabels:includeLabels]];
return actions;
}

View File

@ -46,6 +46,8 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isAudio;
- (nullable NSURL *)mediaURL;
+ (BOOL)hasThumbnailForMimeType:(NSString *)contentType;
- (nullable NSString *)filePath;
- (nullable NSString *)thumbnailPath;

View File

@ -331,6 +331,12 @@ NS_ASSUME_NONNULL_BEGIN
}
}
+ (BOOL)hasThumbnailForMimeType:(NSString *)contentType
{
return ([MIMETypeUtil isVideo:contentType] || [MIMETypeUtil isImage:contentType] ||
[MIMETypeUtil isAnimated:contentType]);
}
- (nullable UIImage *)thumbnailImage
{
NSString *thumbnailPath = self.thumbnailPath;

View File

@ -11,16 +11,17 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, readonly) NSString *authorId;
// This property should be set IFF we are quoting a text message.
// This property should be set IFF we are quoting a text message
// or attachment with caption.
@property (nullable, nonatomic, readonly) NSString *body;
// This property should be set IFF we are quoting a attachment message.
// This property should be set IFF we are quoting an attachment message.
@property (nullable, nonatomic, readonly) NSString *sourceFilename;
// This property can be set IFF we are quoting a attachment message, but it is optional.
// This property can be set IFF we are quoting an attachment message, but it is optional.
@property (nullable, nonatomic, readonly) NSData *thumbnailData;
// This is a MIME type.
//
// This property should be set IFF we are quoting a attachment message.
// This property should be set IFF we are quoting an attachment message.
@property (nullable, nonatomic, readonly) NSString *contentType;
- (instancetype)init NS_UNAVAILABLE;