| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -69,6 +69,8 @@ NS_ASSUME_NONNULL_BEGIN
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@interface OWSMessageTextView : UITextView
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic) BOOL shouldIgnoreEvents;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#pragma mark -
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -83,6 +85,50 @@ NS_ASSUME_NONNULL_BEGIN
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return NO;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Ignore interactions with the text view _except_ taps on links.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				//
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// We want to disable "partial" selection of text in the message
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// and we want to enable "tap to resend" by tapping on a message.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *_Nullable)event
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (self.shouldIgnoreEvents) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // We ignore all events for failed messages so that users
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // can tap-to-resend even "all link" messages.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return NO;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Find the nearest text position to the event.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    UITextPosition *_Nullable position = [self closestPositionToPoint:point];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!position) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return NO;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Find the range of the character in the text which contains the event.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Try every layout direction (this might not be necessary).
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    UITextRange *_Nullable range = nil;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (NSNumber *textLayoutDirection in @[
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				             @(UITextLayoutDirectionLeft),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				             @(UITextLayoutDirectionRight),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				             @(UITextLayoutDirectionUp),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				             @(UITextLayoutDirectionDown),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         ]) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        range = [self.tokenizer rangeEnclosingPosition:position
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                                       withGranularity:UITextGranularityCharacter
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                                           inDirection:(UITextDirection)textLayoutDirection.intValue];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (range) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!range) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return NO;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Ignore the event unless it occurred inside a link.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    NSInteger startIndex = [self offsetFromPosition:self.beginningOfDocument toPosition:range.start];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    BOOL result =
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        [self.attributedText attribute:NSLinkAttributeName atIndex:(NSUInteger)startIndex effectiveRange:nil] != nil;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return result;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#pragma mark -
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -94,7 +140,7 @@ NS_ASSUME_NONNULL_BEGIN
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// to always keep one around.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic) BubbleMaskingView *payloadView;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic) UILabel *dateHeaderLabel;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic) UITextView *textView;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic) OWSMessageTextView *textView;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic, nullable) UIImageView *failedSendBadgeView;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic, nullable) UILabel *tapForMoreLabel;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@property (nonatomic, nullable) UIImageView *bubbleImageView;
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -669,21 +715,20 @@ NS_ASSUME_NONNULL_BEGIN
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self.textView.textColor = textColor;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Honor dynamic type in the message bodies.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self.textView.font = [self textMessageFont];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self.textView.linkTextAttributes = @{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        NSForegroundColorAttributeName : textColor,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self.textView.dataDetectorTypes
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // 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) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Ignore taps on links in outgoing messages that haven't been sent yet, as
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // this interferes with "tap to retry".
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        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.textView.shouldIgnoreEvents = outgoingMessage.messageState != TSOutgoingMessageStateSentToService;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        self.textView.shouldIgnoreEvents = NO;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (self.displayableText.isTextTruncated) {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |