|  |  |  | @ -24,6 +24,8 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @end | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark - | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @implementation BubbleMaskingView | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)setFrame:(CGRect)frame | 
		
	
	
		
			
				
					|  |  |  | @ -65,6 +67,26 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark - | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @interface OWSMessageTextView : UITextView | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @end | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark - | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @implementation OWSMessageTextView | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // Our message text views are never used for editing; | 
		
	
		
			
				|  |  |  |  | // suppress their ability to become first responder | 
		
	
		
			
				|  |  |  |  | // so that tapping on them doesn't hide keyboard. | 
		
	
		
			
				|  |  |  |  | - (BOOL)canBecomeFirstResponder | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     return NO; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @end | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark - | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @interface OWSMessageCell () | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // The nullable properties are created as needed. | 
		
	
	
		
			
				
					|  |  |  | @ -90,6 +112,7 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  | @property (nonatomic, nullable) NSArray<NSLayoutConstraint *> *dateHeaderConstraints; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic, nullable) NSArray<NSLayoutConstraint *> *contentConstraints; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic, nullable) NSArray<NSLayoutConstraint *> *footerConstraints; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) BOOL isPresentingMenuController; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @end | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -132,7 +155,7 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  |     [self.payloadView addSubview:self.bubbleImageView]; | 
		
	
		
			
				|  |  |  |  |     [self.bubbleImageView autoPinToSuperviewEdges]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     self.textView = [UITextView new]; | 
		
	
		
			
				|  |  |  |  |     self.textView = [OWSMessageTextView new]; | 
		
	
		
			
				|  |  |  |  |     self.textView.backgroundColor = [UIColor clearColor]; | 
		
	
		
			
				|  |  |  |  |     self.textView.opaque = NO; | 
		
	
		
			
				|  |  |  |  |     self.textView.editable = NO; | 
		
	
	
		
			
				
					|  |  |  | @ -1038,6 +1061,8 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  |     [self.expirationTimerView clearAnimations]; | 
		
	
		
			
				|  |  |  |  |     [self.expirationTimerView removeFromSuperview]; | 
		
	
		
			
				|  |  |  |  |     self.expirationTimerView = nil; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     self.isPresentingMenuController = NO; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark - Notifications | 
		
	
	
		
			
				
					|  |  |  | @ -1062,6 +1087,16 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |         [self.expirationTimerView clearAnimations]; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     [self debugFirstResponder:self depth:0]; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)debugFirstResponder:(UIView *)view depth:(int)depth | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     DDLogError(@"debugFirstResponder[%@ / %d]: %d", view.class, depth, [view canBecomeFirstResponder]); | 
		
	
		
			
				|  |  |  |  |     for (UIView *subview in view.subviews) { | 
		
	
		
			
				|  |  |  |  |         [self debugFirstResponder:subview depth:depth + 1]; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // case TSInfoMessageAdapter: { | 
		
	
	
		
			
				
					|  |  |  | @ -1159,6 +1194,11 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)showMenuController:(CGPoint)fromLocation | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     // We don't want taps on messages to hide the keyboard, | 
		
	
		
			
				|  |  |  |  |     // so we only let messages become first responder | 
		
	
		
			
				|  |  |  |  |     // while they are trying to present the menu controller. | 
		
	
		
			
				|  |  |  |  |     self.isPresentingMenuController = YES; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     [self becomeFirstResponder]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if ([UIMenuController sharedMenuController].isMenuVisible) { | 
		
	
	
		
			
				
					|  |  |  | @ -1208,7 +1248,35 @@ NS_ASSUME_NONNULL_BEGIN | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (BOOL)canBecomeFirstResponder | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     return YES; | 
		
	
		
			
				|  |  |  |  |     return self.isPresentingMenuController; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)didHideMenuController:(NSNotification *)notification | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     self.isPresentingMenuController = NO; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)setIsPresentingMenuController:(BOOL)isPresentingMenuController | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     if (_isPresentingMenuController == isPresentingMenuController) { | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if (isPresentingMenuController) { | 
		
	
		
			
				|  |  |  |  |         [[NSNotificationCenter defaultCenter] addObserver:self | 
		
	
		
			
				|  |  |  |  |                                                  selector:@selector(didHideMenuController:) | 
		
	
		
			
				|  |  |  |  |                                                      name:UIMenuControllerDidHideMenuNotification | 
		
	
		
			
				|  |  |  |  |                                                    object:nil]; | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |         [[NSNotificationCenter defaultCenter] removeObserver:self | 
		
	
		
			
				|  |  |  |  |                                                         name:UIMenuControllerDidHideMenuNotification | 
		
	
		
			
				|  |  |  |  |                                                       object:nil]; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)dealloc | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     [[NSNotificationCenter defaultCenter] removeObserver:self]; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark - Logging | 
		
	
	
		
			
				
					|  |  |  | 
 |