|  |  | @ -56,6 +56,10 @@ NS_ASSUME_NONNULL_BEGIN | 
			
		
	
		
		
			
				
					
					|  |  |  |         _referenceSystemMessageCell = [OWSSystemMessageCell new]; |  |  |  |         _referenceSystemMessageCell = [OWSSystemMessageCell new]; | 
			
		
	
		
		
			
				
					
					|  |  |  |         _referenceUnreadIndicatorCell = [OWSUnreadIndicatorCell new]; |  |  |  |         _referenceUnreadIndicatorCell = [OWSUnreadIndicatorCell new]; | 
			
		
	
		
		
			
				
					
					|  |  |  |         _referenceContactOffersCell = [OWSContactOffersCell new]; |  |  |  |         _referenceContactOffersCell = [OWSContactOffersCell new]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Calculating message size is relatively expensive, so unbound the size of the cache. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.cache.countLimit = 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.cache.totalCostLimit = 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |     return self; |  |  |  |     return self; | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | @ -74,6 +78,21 @@ NS_ASSUME_NONNULL_BEGIN | 
			
		
	
		
		
			
				
					
					|  |  |  | - (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData |  |  |  | - (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData | 
			
		
	
		
		
			
				
					
					|  |  |  |                               atIndexPath:(NSIndexPath *)indexPath |  |  |  |                               atIndexPath:(NSIndexPath *)indexPath | 
			
		
	
		
		
			
				
					
					|  |  |  |                                withLayout:(JSQMessagesCollectionViewFlowLayout *)layout |  |  |  |                                withLayout:(JSQMessagesCollectionViewFlowLayout *)layout | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     id cacheKey = [self cacheKeyForMessageData:messageData]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     NSValue *cachedSize = [self.cache objectForKey:cacheKey]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if (cachedSize != nil) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return [cachedSize CGSizeValue]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     CGSize result = [self calculateMessageBubbleSizeForMessageData:messageData atIndexPath:indexPath withLayout:layout]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     return result; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | - (CGSize)calculateMessageBubbleSizeForMessageData:(id<JSQMessageData>)messageData | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                        atIndexPath:(NSIndexPath *)indexPath | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                         withLayout:(JSQMessagesCollectionViewFlowLayout *)layout | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     if ([messageData isKindOfClass:[TSMessageAdapter class]]) { |  |  |  |     if ([messageData isKindOfClass:[TSMessageAdapter class]]) { | 
			
		
	
		
		
			
				
					
					|  |  |  |         TSMessageAdapter *message = (TSMessageAdapter *)messageData; |  |  |  |         TSMessageAdapter *message = (TSMessageAdapter *)messageData; | 
			
		
	
	
		
		
			
				
					|  |  | @ -82,21 +101,18 @@ NS_ASSUME_NONNULL_BEGIN | 
			
		
	
		
		
			
				
					
					|  |  |  |             case TSCallAdapter: |  |  |  |             case TSCallAdapter: | 
			
		
	
		
		
			
				
					
					|  |  |  |             case TSInfoMessageAdapter: |  |  |  |             case TSInfoMessageAdapter: | 
			
		
	
		
		
			
				
					
					|  |  |  |             case TSErrorMessageAdapter: { |  |  |  |             case TSErrorMessageAdapter: { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 id cacheKey = [self cacheKeyForMessageData:messageData]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 TSInteraction *interaction = ((TSMessageAdapter *)messageData).interaction; |  |  |  |                 TSInteraction *interaction = ((TSMessageAdapter *)messageData).interaction; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return [self sizeForSystemMessage:interaction cacheKey:cacheKey layout:layout]; |  |  |  |                 return [self sizeForSystemMessage:interaction layout:layout]; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             case TSUnreadIndicatorAdapter: { |  |  |  |             case TSUnreadIndicatorAdapter: { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 id cacheKey = [self cacheKeyForMessageData:messageData]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 TSUnreadIndicatorInteraction *interaction |  |  |  |                 TSUnreadIndicatorInteraction *interaction | 
			
		
	
		
		
			
				
					
					|  |  |  |                     = (TSUnreadIndicatorInteraction *)((TSMessageAdapter *)messageData).interaction; |  |  |  |                     = (TSUnreadIndicatorInteraction *)((TSMessageAdapter *)messageData).interaction; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return [self sizeForUnreadIndicator:interaction cacheKey:cacheKey layout:layout]; |  |  |  |                 return [self sizeForUnreadIndicator:interaction layout:layout]; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             case OWSContactOffersAdapter: { |  |  |  |             case OWSContactOffersAdapter: { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 id cacheKey = [self cacheKeyForMessageData:messageData]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 OWSContactOffersInteraction *interaction |  |  |  |                 OWSContactOffersInteraction *interaction | 
			
		
	
		
		
			
				
					
					|  |  |  |                     = (OWSContactOffersInteraction *)((TSMessageAdapter *)messageData).interaction; |  |  |  |                     = (OWSContactOffersInteraction *)((TSMessageAdapter *)messageData).interaction; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return [self sizeForContactOffers:interaction cacheKey:cacheKey layout:layout]; |  |  |  |                 return [self sizeForContactOffers:interaction layout:layout]; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             case TSIncomingMessageAdapter: |  |  |  |             case TSIncomingMessageAdapter: | 
			
		
	
		
		
			
				
					
					|  |  |  |             case TSOutgoingMessageAdapter: |  |  |  |             case TSOutgoingMessageAdapter: | 
			
		
	
	
		
		
			
				
					|  |  | @ -106,9 +122,8 @@ NS_ASSUME_NONNULL_BEGIN | 
			
		
	
		
		
			
				
					
					|  |  |  |                 break; |  |  |  |                 break; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else if ([messageData isKindOfClass:[OWSCall class]]) { |  |  |  |     } else if ([messageData isKindOfClass:[OWSCall class]]) { | 
			
		
	
		
		
			
				
					
					|  |  |  |         id cacheKey = [self cacheKeyForMessageData:messageData]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         TSInteraction *interaction = ((OWSCall *)messageData).interaction; |  |  |  |         TSInteraction *interaction = ((OWSCall *)messageData).interaction; | 
			
		
	
		
		
			
				
					
					|  |  |  |         return [self sizeForSystemMessage:interaction cacheKey:cacheKey layout:layout]; |  |  |  |         return [self sizeForSystemMessage:interaction layout:layout]; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } else { |  |  |  |     } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |         OWSFail(@"Can't size unknown message data type: %@", [messageData class]); |  |  |  |         OWSFail(@"Can't size unknown message data type: %@", [messageData class]); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
	
		
		
			
				
					|  |  | @ -126,64 +141,31 @@ NS_ASSUME_NONNULL_BEGIN | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | - (CGSize)sizeForSystemMessage:(TSInteraction *)interaction |  |  |  | - (CGSize)sizeForSystemMessage:(TSInteraction *)interaction | 
			
		
	
		
		
			
				
					
					|  |  |  |                       cacheKey:(id)cacheKey |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         layout:(JSQMessagesCollectionViewFlowLayout *)layout |  |  |  |                         layout:(JSQMessagesCollectionViewFlowLayout *)layout | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert([NSThread isMainThread]); |  |  |  |     OWSAssert([NSThread isMainThread]); | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert(interaction); |  |  |  |     OWSAssert(interaction); | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert(cacheKey); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     NSValue *cachedSize = [self.cache objectForKey:cacheKey]; |  |  |  |     return [self.referenceSystemMessageCell bubbleSizeForInteraction:interaction | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if (cachedSize != nil) { |  |  |  |                                                  collectionViewWidth:layout.collectionView.width]; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         return [cachedSize CGSizeValue]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     CGSize result = [self.referenceSystemMessageCell bubbleSizeForInteraction:interaction |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                                           collectionViewWidth:layout.collectionView.width]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return result; |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | - (CGSize)sizeForUnreadIndicator:(TSUnreadIndicatorInteraction *)interaction |  |  |  | - (CGSize)sizeForUnreadIndicator:(TSUnreadIndicatorInteraction *)interaction | 
			
		
	
		
		
			
				
					
					|  |  |  |                         cacheKey:(id)cacheKey |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                           layout:(JSQMessagesCollectionViewFlowLayout *)layout |  |  |  |                           layout:(JSQMessagesCollectionViewFlowLayout *)layout | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert(interaction); |  |  |  |     OWSAssert(interaction); | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert(cacheKey); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     NSValue *cachedSize = [self.cache objectForKey:cacheKey]; |  |  |  |     return [self.referenceUnreadIndicatorCell bubbleSizeForInteraction:interaction | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if (cachedSize != nil) { |  |  |  |                                                    collectionViewWidth:layout.collectionView.width]; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         return [cachedSize CGSizeValue]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     CGSize result = [self.referenceUnreadIndicatorCell bubbleSizeForInteraction:interaction |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                                             collectionViewWidth:layout.collectionView.width]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return result; |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | - (CGSize)sizeForContactOffers:(OWSContactOffersInteraction *)interaction |  |  |  | - (CGSize)sizeForContactOffers:(OWSContactOffersInteraction *)interaction | 
			
		
	
		
		
			
				
					
					|  |  |  |                       cacheKey:(id)cacheKey |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         layout:(JSQMessagesCollectionViewFlowLayout *)layout |  |  |  |                         layout:(JSQMessagesCollectionViewFlowLayout *)layout | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert(interaction); |  |  |  |     OWSAssert(interaction); | 
			
		
	
		
		
			
				
					
					|  |  |  |     OWSAssert(cacheKey); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     NSValue *cachedSize = [self.cache objectForKey:cacheKey]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (cachedSize != nil) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return [cachedSize CGSizeValue]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     CGSize result = [self.referenceContactOffersCell bubbleSizeForInteraction:interaction |  |  |  |     return [self.referenceContactOffersCell bubbleSizeForInteraction:interaction | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                                           collectionViewWidth:layout.collectionView.width]; |  |  |  |                                                  collectionViewWidth:layout.collectionView.width]; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return result; |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | /** |  |  |  | /** | 
			
		
	
	
		
		
			
				
					|  |  | @ -254,6 +236,18 @@ NS_ASSUME_NONNULL_BEGIN | 
			
		
	
		
		
			
				
					
					|  |  |  |     return @([messageData messageHash]); |  |  |  |     return @([messageData messageHash]); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | #pragma mark - Logging | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | + (NSString *)tag | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     return [NSString stringWithFormat:@"[%@]", self.class]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | - (NSString *)tag | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     return self.class.tag; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @end |  |  |  | @end | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | NS_ASSUME_NONNULL_END |  |  |  | NS_ASSUME_NONNULL_END | 
			
		
	
	
		
		
			
				
					|  |  | 
 |