From bd5639baaf4a6cb504e435809cc464364d4701ee Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 18 Oct 2017 21:33:17 -0700 Subject: [PATCH] Linkify text messages. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 6 - .../Cells/JSQMessagesCollectionViewCell+OWS.h | 15 --- .../Cells/JSQMessagesCollectionViewCell+OWS.m | 31 ----- .../ConversationView/Cells/OWSMessageCell.m | 107 +++++++----------- 4 files changed, 43 insertions(+), 116 deletions(-) delete mode 100644 Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.h delete mode 100644 Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index c5a0669ed..a284ef5b5 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -89,7 +89,6 @@ 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0701F8678AA0066283D /* ConversationViewItem.m */; }; 34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0721F8678AA0066283D /* ConversationViewLayout.m */; }; 34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0971F867BFC0066283D /* ConversationViewCell.m */; }; - 34D1F0AA1F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0991F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.m */; }; 34D1F0AB1F867BFC0066283D /* OWSContactOffersCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */; }; 34D1F0AC1F867BFC0066283D /* OWSExpirationTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F09E1F867BFC0066283D /* OWSExpirationTimerView.m */; }; 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0A21F867BFC0066283D /* OWSMessageCell.m */; }; @@ -541,8 +540,6 @@ 34D1F0721F8678AA0066283D /* ConversationViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationViewLayout.m; sourceTree = ""; }; 34D1F0961F867BFC0066283D /* ConversationViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversationViewCell.h; sourceTree = ""; }; 34D1F0971F867BFC0066283D /* ConversationViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationViewCell.m; sourceTree = ""; }; - 34D1F0981F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSQMessagesCollectionViewCell+OWS.h"; sourceTree = ""; }; - 34D1F0991F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMessagesCollectionViewCell+OWS.m"; sourceTree = ""; }; 34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactOffersCell.h; sourceTree = ""; }; 34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactOffersCell.m; sourceTree = ""; }; 34D1F09C1F867BFC0066283D /* OWSExpirableMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSExpirableMessageView.h; sourceTree = ""; }; @@ -1145,8 +1142,6 @@ 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */, 34D1F0961F867BFC0066283D /* ConversationViewCell.h */, 34D1F0971F867BFC0066283D /* ConversationViewCell.m */, - 34D1F0981F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.h */, - 34D1F0991F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.m */, 34D1F0B81F8800D90066283D /* OWSAudioMessageView.h */, 34D1F0B91F8800D90066283D /* OWSAudioMessageView.m */, 34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */, @@ -2357,7 +2352,6 @@ 45F170D61E315310003FC1F2 /* Weak.swift in Sources */, 4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */, 34B3F8891E8DF1700035BE1A /* OWSConversationSettingsViewController.m in Sources */, - 34D1F0AA1F867BFC0066283D /* JSQMessagesCollectionViewCell+OWS.m in Sources */, 34C42D671F4734ED0072EC04 /* TSUnreadIndicatorInteraction.m in Sources */, 34B3F87E1E8DF1700035BE1A /* InboxTableViewCell.m in Sources */, 34B3F8731E8DF1700035BE1A /* AttachmentApprovalViewController.swift in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.h b/Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.h deleted file mode 100644 index fdbc05595..000000000 --- a/Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface JSQMessagesCollectionViewCell (OWS) - -- (void)ows_didLoad; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.m b/Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.m deleted file mode 100644 index abbbd1375..000000000 --- a/Signal/src/ViewControllers/ConversationView/Cells/JSQMessagesCollectionViewCell+OWS.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import "JSQMessagesCollectionViewCell+OWS.h" -#import "UIColor+OWS.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation JSQMessagesCollectionViewCell (OWS) - -- (UIColor *)ows_textColor -{ - return [UIColor ows_blackColor]; -} - -- (void)ows_didLoad -{ - self.textView.textColor = self.ows_textColor; - self.textView.linkTextAttributes = @{ - NSForegroundColorAttributeName : self.ows_textColor, - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) - }; - - self.textView.dataDetectorTypes - = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 671737d66..714e6fb4c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -69,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN // to always keep one around. @property (nonatomic) BubbleMaskingView *payloadView; @property (nonatomic) UILabel *dateHeaderLabel; -@property (nonatomic) UILabel *textLabel; +@property (nonatomic) UITextView *textView; @property (nonatomic, nullable) UIImageView *bubbleImageView; @property (nonatomic, nullable) AttachmentUploadView *attachmentUploadView; @property (nonatomic, nullable) UIImageView *stillImageView; @@ -101,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)commontInit { - OWSAssert(!self.textLabel); + OWSAssert(!self.textView); self.layoutMargins = UIEdgeInsetsZero; @@ -124,13 +124,17 @@ NS_ASSUME_NONNULL_BEGIN [self.payloadView addSubview:self.bubbleImageView]; [self.bubbleImageView autoPinToSuperviewEdges]; - self.textLabel = [UILabel new]; - self.textLabel.font = [UIFont ows_regularFontWithSize:16.f]; - self.textLabel.numberOfLines = 0; - self.textLabel.lineBreakMode = NSLineBreakByWordWrapping; - self.textLabel.textAlignment = NSTextAlignmentLeft; - [self.bubbleImageView addSubview:self.textLabel]; - OWSAssert(self.textLabel.superview); + self.textView = [UITextView new]; + self.textView.font = [UIFont ows_regularFontWithSize:16.f]; + self.textView.backgroundColor = [UIColor clearColor]; + self.textView.opaque = NO; + self.textView.editable = NO; + self.textView.selectable = YES; + self.textView.textContainerInset = UIEdgeInsetsZero; + self.textView.contentInset = UIEdgeInsetsZero; + self.textView.scrollEnabled = NO; + [self.bubbleImageView addSubview:self.textView]; + OWSAssert(self.textView.superview); self.footerLabel = [UILabel new]; self.footerLabel.font = [UIFont ows_regularFontWithSize:12.f]; @@ -139,7 +143,7 @@ NS_ASSUME_NONNULL_BEGIN // Hide these views by default. self.bubbleImageView.hidden = YES; - self.textLabel.hidden = YES; + self.textView.hidden = YES; self.dateHeaderLabel.hidden = YES; self.footerLabel.hidden = YES; @@ -255,14 +259,6 @@ NS_ASSUME_NONNULL_BEGIN } [self ensureViewMediaState]; - - // dispatch_async(dispatch_get_main_queue(), ^{ - // NSLog(@"---- %@", self.viewItem.interaction.debugDescription); - // NSLog(@"cell: %@", NSStringFromCGRect(self.frame)); - // NSLog(@"contentView: %@", NSStringFromCGRect(self.contentView.frame)); - // NSLog(@"textLabel: %@", NSStringFromCGRect(self.textLabel.frame)); - // NSLog(@"bubbleImageView: %@", NSStringFromCGRect(self.bubbleImageView.frame)); - // }); } // We now eagerly create out view hierarchy (to do this exactly once per cell usage) @@ -543,15 +539,32 @@ NS_ASSUME_NONNULL_BEGIN - (void)loadForTextDisplay { self.bubbleImageView.hidden = NO; - self.textLabel.hidden = NO; - self.textLabel.text = self.textMessage; - self.textLabel.textColor = [self textColor]; + self.textView.hidden = NO; + self.textView.text = self.textMessage; + UIColor *textColor = [self textColor]; + self.textView.textColor = textColor; + + // Don't link outgoing messages that haven't been sent yet, as + // this interferes with "tap to retry". + BOOL canLinkify = YES; + if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; + canLinkify = outgoingMessage.messageState == TSOutgoingMessageStateSentToService; + } + if (canLinkify) { + self.textView.linkTextAttributes = @{ + NSForegroundColorAttributeName : textColor, + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) + }; + self.textView.dataDetectorTypes + = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); + } self.contentConstraints = @[ - [self.textLabel autoPinLeadingToSuperviewWithMargin:self.textLeadingMargin], - [self.textLabel autoPinTrailingToSuperviewWithMargin:self.textTrailingMargin], - [self.textLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.textVMargin], - [self.textLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.textVMargin], + [self.textView autoPinLeadingToSuperviewWithMargin:self.textLeadingMargin], + [self.textView autoPinTrailingToSuperviewWithMargin:self.textTrailingMargin], + [self.textView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.textVMargin], + [self.textView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.textVMargin], ]; } @@ -724,8 +737,8 @@ NS_ASSUME_NONNULL_BEGIN CGFloat textVMargin = self.textVMargin; const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin)); - self.textLabel.text = self.textMessage; - CGSize textSize = [self.textLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]; + self.textView.text = self.textMessage; + CGSize textSize = [self.textView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]; cellSize = CGSizeMake((CGFloat)ceil(textSize.width + leftMargin + rightMargin), (CGFloat)ceil(textSize.height + textVMargin * 2)); break; @@ -854,8 +867,9 @@ NS_ASSUME_NONNULL_BEGIN self.dateHeaderLabel.text = nil; self.dateHeaderLabel.hidden = YES; - self.textLabel.text = nil; - self.textLabel.hidden = YES; + self.textView.text = nil; + self.textView.hidden = YES; + self.textView.dataDetectorTypes = UIDataDetectorTypeNone; self.footerLabel.text = nil; self.footerLabel.hidden = YES; self.bubbleImageView.image = nil; @@ -880,41 +894,6 @@ NS_ASSUME_NONNULL_BEGIN self.expirationTimerView = nil; } -//- (void)prepareForReuse -//{ -// [super prepareForReuse]; -// self.mediaView.alpha = 1.0; -// self.expirationTimerViewWidthConstraint.constant = 0.0f; -// -// [self.mediaAdapter setCellVisible:NO]; -// -// // Clear this adapter's views IFF this was the last cell to use this adapter. -// [self.mediaAdapter clearCachedMediaViewsIfLastPresentingCell:self]; -// [_mediaAdapter setLastPresentingCell:nil]; -// -// self.mediaAdapter = nil; -//} -// -//- (void)setMediaAdapter:(nullable id)mediaAdapter -//{ -// _mediaAdapter = mediaAdapter; -// -// // Mark this as the last cell to use this adapter. -// [_mediaAdapter setLastPresentingCell:self]; -//} -// -//// pragma mark - OWSMessageCollectionViewCell -// -//- (void)setCellVisible:(BOOL)isVisible -//{ -// [self.mediaAdapter setCellVisible:isVisible]; -//} -// -//- (UIColor *)ows_textColor -//{ -// return [UIColor whiteColor]; -//} - #pragma mark - Notifications - (void)setIsCellVisible:(BOOL)isCellVisible {