|
|
@ -175,8 +175,8 @@ typedef enum : NSUInteger {
|
|
|
|
@property (nonatomic, readonly) ConversationViewLayout *layout;
|
|
|
|
@property (nonatomic, readonly) ConversationViewLayout *layout;
|
|
|
|
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
|
|
|
|
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
|
|
|
|
|
|
|
|
|
|
|
|
@property (nonatomic) NSArray<ConversationViewItem *> *viewItems;
|
|
|
|
@property (nonatomic) NSArray<id<ConversationViewItem>> *viewItems;
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, ConversationViewItem *> *viewItemCache;
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, id<ConversationViewItem>> *viewItemCache;
|
|
|
|
|
|
|
|
|
|
|
|
@property (nonatomic, nullable) AVAudioRecorder *audioRecorder;
|
|
|
|
@property (nonatomic, nullable) AVAudioRecorder *audioRecorder;
|
|
|
|
@property (nonatomic, nullable) OWSAudioPlayer *audioAttachmentPlayer;
|
|
|
|
@property (nonatomic, nullable) OWSAudioPlayer *audioAttachmentPlayer;
|
|
|
@ -717,7 +717,7 @@ typedef enum : NSUInteger {
|
|
|
|
- (NSIndexPath *_Nullable)indexPathOfUnreadMessagesIndicator
|
|
|
|
- (NSIndexPath *_Nullable)indexPathOfUnreadMessagesIndicator
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSInteger row = 0;
|
|
|
|
NSInteger row = 0;
|
|
|
|
for (ConversationViewItem *viewItem in self.viewItems) {
|
|
|
|
for (id<ConversationViewItem> viewItem in self.viewItems) {
|
|
|
|
if (viewItem.unreadIndicator) {
|
|
|
|
if (viewItem.unreadIndicator) {
|
|
|
|
return [NSIndexPath indexPathForRow:row inSection:0];
|
|
|
|
return [NSIndexPath indexPathForRow:row inSection:0];
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1553,7 +1553,7 @@ typedef enum : NSUInteger {
|
|
|
|
OWSLogInfo(@"didChangePreferredContentSize");
|
|
|
|
OWSLogInfo(@"didChangePreferredContentSize");
|
|
|
|
|
|
|
|
|
|
|
|
// Evacuate cached cell sizes.
|
|
|
|
// Evacuate cached cell sizes.
|
|
|
|
for (ConversationViewItem *viewItem in self.viewItems) {
|
|
|
|
for (id<ConversationViewItem> viewItem in self.viewItems) {
|
|
|
|
[viewItem clearCachedLayoutState];
|
|
|
|
[viewItem clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[self resetContentAndLayout];
|
|
|
|
[self resetContentAndLayout];
|
|
|
@ -1933,12 +1933,12 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - MessageActionsDelegate
|
|
|
|
#pragma mark - MessageActionsDelegate
|
|
|
|
|
|
|
|
|
|
|
|
- (void)messageActionsShowDetailsForItem:(ConversationViewItem *)conversationViewItem
|
|
|
|
- (void)messageActionsShowDetailsForItem:(id<ConversationViewItem>)conversationViewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
[self showDetailViewForViewItem:conversationViewItem];
|
|
|
|
[self showDetailViewForViewItem:conversationViewItem];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)messageActionsReplyToItem:(ConversationViewItem *)conversationViewItem
|
|
|
|
- (void)messageActionsReplyToItem:(id<ConversationViewItem>)conversationViewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
[self populateReplyForViewItem:conversationViewItem];
|
|
|
|
[self populateReplyForViewItem:conversationViewItem];
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -2006,27 +2006,32 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - ConversationViewCellDelegate
|
|
|
|
#pragma mark - ConversationViewCellDelegate
|
|
|
|
|
|
|
|
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSArray<MenuAction *> *messageActions = [viewItem mediaActionsWithDelegate:self];
|
|
|
|
NSArray<MenuAction *> *messageActions =
|
|
|
|
|
|
|
|
[ConversationViewItemActions mediaActionsWithConversationViewItem:viewItem delegate:self];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSArray<MenuAction *> *messageActions = [viewItem textActionsWithDelegate:self];
|
|
|
|
NSArray<MenuAction *> *messageActions =
|
|
|
|
|
|
|
|
[ConversationViewItemActions textActionsWithConversationViewItem:viewItem delegate:self];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSArray<MenuAction *> *messageActions = [viewItem quotedMessageActionsWithDelegate:self];
|
|
|
|
NSArray<MenuAction *> *messageActions =
|
|
|
|
|
|
|
|
[ConversationViewItemActions quotedMessageActionsWithConversationViewItem:viewItem delegate:self];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell didLongpressSystemMessageViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)conversationCell:(ConversationViewCell *)cell
|
|
|
|
|
|
|
|
didLongpressSystemMessageViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSArray<MenuAction *> *messageActions = [viewItem infoMessageActionsWithDelegate:self];
|
|
|
|
NSArray<MenuAction *> *messageActions =
|
|
|
|
|
|
|
|
[ConversationViewItemActions infoMessageActionsWithConversationViewItem:viewItem delegate:self];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
[self presentMessageActions:messageActions withFocusedCell:cell];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -2141,7 +2146,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - OWSMessageBubbleViewDelegate
|
|
|
|
#pragma mark - OWSMessageBubbleViewDelegate
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapImageViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)didTapImageViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
attachmentStream:(TSAttachmentStream *)attachmentStream
|
|
|
|
attachmentStream:(TSAttachmentStream *)attachmentStream
|
|
|
|
imageView:(UIView *)imageView
|
|
|
|
imageView:(UIView *)imageView
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2172,7 +2177,7 @@ typedef enum : NSUInteger {
|
|
|
|
[vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView];
|
|
|
|
[vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapVideoViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)didTapVideoViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
attachmentStream:(TSAttachmentStream *)attachmentStream
|
|
|
|
attachmentStream:(TSAttachmentStream *)attachmentStream
|
|
|
|
imageView:(UIImageView *)imageView
|
|
|
|
imageView:(UIImageView *)imageView
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2201,7 +2206,7 @@ typedef enum : NSUInteger {
|
|
|
|
[vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView];
|
|
|
|
[vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream
|
|
|
|
- (void)didTapAudioViewItem:(id<ConversationViewItem>)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertDebug(viewItem);
|
|
|
|
OWSAssertDebug(viewItem);
|
|
|
@ -2231,7 +2236,7 @@ typedef enum : NSUInteger {
|
|
|
|
[self.audioAttachmentPlayer playWithPlaybackAudioCategory];
|
|
|
|
[self.audioAttachmentPlayer playWithPlaybackAudioCategory];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem
|
|
|
|
- (void)didTapTruncatedTextMessage:(id<ConversationViewItem>)conversationItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertDebug(conversationItem);
|
|
|
|
OWSAssertDebug(conversationItem);
|
|
|
@ -2241,7 +2246,7 @@ typedef enum : NSUInteger {
|
|
|
|
[self.navigationController pushViewController:view animated:YES];
|
|
|
|
[self.navigationController pushViewController:view animated:YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapContactShareViewItem:(ConversationViewItem *)conversationItem
|
|
|
|
- (void)didTapContactShareViewItem:(id<ConversationViewItem>)conversationItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertDebug(conversationItem);
|
|
|
|
OWSAssertDebug(conversationItem);
|
|
|
@ -2276,7 +2281,7 @@ typedef enum : NSUInteger {
|
|
|
|
[self.contactShareViewHelper showAddToContactsWithContactShare:contactShare fromViewController:self];
|
|
|
|
[self.contactShareViewHelper showAddToContactsWithContactShare:contactShare fromViewController:self];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem
|
|
|
|
- (void)didTapFailedIncomingAttachment:(id<ConversationViewItem>)viewItem
|
|
|
|
attachmentPointer:(TSAttachmentPointer *)attachmentPointer
|
|
|
|
attachmentPointer:(TSAttachmentPointer *)attachmentPointer
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
@ -2296,7 +2301,7 @@ typedef enum : NSUInteger {
|
|
|
|
[self handleUnsentMessageTap:message];
|
|
|
|
[self handleUnsentMessageTap:message];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapConversationItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)didTapConversationItem:(id<ConversationViewItem>)viewItem
|
|
|
|
quotedReply:(OWSQuotedReplyModel *)quotedReply
|
|
|
|
quotedReply:(OWSQuotedReplyModel *)quotedReply
|
|
|
|
failedThumbnailDownloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer
|
|
|
|
failedThumbnailDownloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2334,7 +2339,7 @@ typedef enum : NSUInteger {
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didTapConversationItem:(ConversationViewItem *)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply
|
|
|
|
- (void)didTapConversationItem:(id<ConversationViewItem>)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertDebug(viewItem);
|
|
|
|
OWSAssertDebug(viewItem);
|
|
|
@ -2469,7 +2474,7 @@ typedef enum : NSUInteger {
|
|
|
|
return @(groupIndex);
|
|
|
|
return @(groupIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)showDetailViewForViewItem:(ConversationViewItem *)conversationItem
|
|
|
|
- (void)showDetailViewForViewItem:(id<ConversationViewItem>)conversationItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertDebug(conversationItem);
|
|
|
|
OWSAssertDebug(conversationItem);
|
|
|
@ -2484,7 +2489,7 @@ typedef enum : NSUInteger {
|
|
|
|
[self.navigationController pushViewController:view animated:YES];
|
|
|
|
[self.navigationController pushViewController:view animated:YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)populateReplyForViewItem:(ConversationViewItem *)conversationItem
|
|
|
|
- (void)populateReplyForViewItem:(id<ConversationViewItem>)conversationItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSLogDebug(@"user did tap reply");
|
|
|
|
OWSLogDebug(@"user did tap reply");
|
|
|
|
|
|
|
|
|
|
|
@ -2559,7 +2564,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
|
|
NSIndexPath *_Nullable indexPathOfUnreadIndicator = [self indexPathOfUnreadMessagesIndicator];
|
|
|
|
NSIndexPath *_Nullable indexPathOfUnreadIndicator = [self indexPathOfUnreadMessagesIndicator];
|
|
|
|
if (indexPathOfUnreadIndicator) {
|
|
|
|
if (indexPathOfUnreadIndicator) {
|
|
|
|
ConversationViewItem *oldIndicatorItem = [self viewItemForIndex:indexPathOfUnreadIndicator.row];
|
|
|
|
id<ConversationViewItem> oldIndicatorItem = [self viewItemForIndex:indexPathOfUnreadIndicator.row];
|
|
|
|
OWSAssertDebug(oldIndicatorItem);
|
|
|
|
OWSAssertDebug(oldIndicatorItem);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO ideally this would be happening within the *same* transaction that caused the unreadMessageIndicator
|
|
|
|
// TODO ideally this would be happening within the *same* transaction that caused the unreadMessageIndicator
|
|
|
@ -2673,7 +2678,7 @@ typedef enum : NSUInteger {
|
|
|
|
BOOL isScrolledUp = scrollSpaceToBottom > pageHeight * 1.f;
|
|
|
|
BOOL isScrolledUp = scrollSpaceToBottom > pageHeight * 1.f;
|
|
|
|
|
|
|
|
|
|
|
|
if (self.viewItems.count > 0) {
|
|
|
|
if (self.viewItems.count > 0) {
|
|
|
|
ConversationViewItem *lastViewItem = [self.viewItems lastObject];
|
|
|
|
id<ConversationViewItem> lastViewItem = [self.viewItems lastObject];
|
|
|
|
OWSAssertDebug(lastViewItem);
|
|
|
|
OWSAssertDebug(lastViewItem);
|
|
|
|
|
|
|
|
|
|
|
|
if (lastViewItem.interaction.timestampForSorting > self.lastVisibleTimestamp) {
|
|
|
|
if (lastViewItem.interaction.timestampForSorting > self.lastVisibleTimestamp) {
|
|
|
@ -3289,7 +3294,7 @@ typedef enum : NSUInteger {
|
|
|
|
case YapDatabaseViewChangeUpdate: {
|
|
|
|
case YapDatabaseViewChangeUpdate: {
|
|
|
|
YapCollectionKey *collectionKey = rowChange.collectionKey;
|
|
|
|
YapCollectionKey *collectionKey = rowChange.collectionKey;
|
|
|
|
if (collectionKey.key) {
|
|
|
|
if (collectionKey.key) {
|
|
|
|
ConversationViewItem *_Nullable viewItem = self.viewItemCache[collectionKey.key];
|
|
|
|
id<ConversationViewItem> _Nullable viewItem = self.viewItemCache[collectionKey.key];
|
|
|
|
if (viewItem) {
|
|
|
|
if (viewItem) {
|
|
|
|
[self reloadInteractionForViewItem:viewItem];
|
|
|
|
[self reloadInteractionForViewItem:viewItem];
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -3373,7 +3378,8 @@ typedef enum : NSUInteger {
|
|
|
|
(unsigned long)rowChange.finalIndex);
|
|
|
|
(unsigned long)rowChange.finalIndex);
|
|
|
|
[self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]];
|
|
|
|
[self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]];
|
|
|
|
|
|
|
|
|
|
|
|
ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex];
|
|
|
|
id<ConversationViewItem> _Nullable viewItem =
|
|
|
|
|
|
|
|
[self viewItemForIndex:(NSInteger)rowChange.finalIndex];
|
|
|
|
if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) {
|
|
|
|
if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) {
|
|
|
|
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction;
|
|
|
|
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction;
|
|
|
|
if (!outgoingMessage.isFromLinkedDevice) {
|
|
|
|
if (!outgoingMessage.isFromLinkedDevice) {
|
|
|
@ -3508,7 +3514,7 @@ typedef enum : NSUInteger {
|
|
|
|
isOnlyModifyingLastMessage = NO;
|
|
|
|
isOnlyModifyingLastMessage = NO;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case YapDatabaseViewChangeInsert: {
|
|
|
|
case YapDatabaseViewChangeInsert: {
|
|
|
|
ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex];
|
|
|
|
id<ConversationViewItem> _Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex];
|
|
|
|
if (([viewItem.interaction isKindOfClass:[TSIncomingMessage class]] ||
|
|
|
|
if (([viewItem.interaction isKindOfClass:[TSIncomingMessage class]] ||
|
|
|
|
[viewItem.interaction isKindOfClass:[TSOutgoingMessage class]])
|
|
|
|
[viewItem.interaction isKindOfClass:[TSOutgoingMessage class]])
|
|
|
|
&& rowChange.finalIndex >= oldViewItemCount) {
|
|
|
|
&& rowChange.finalIndex >= oldViewItemCount) {
|
|
|
@ -3524,7 +3530,7 @@ typedef enum : NSUInteger {
|
|
|
|
if (rowChange.changes == YapDatabaseViewChangedDependency) {
|
|
|
|
if (rowChange.changes == YapDatabaseViewChangedDependency) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex];
|
|
|
|
id<ConversationViewItem> _Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex];
|
|
|
|
if (([viewItem.interaction isKindOfClass:[TSIncomingMessage class]] ||
|
|
|
|
if (([viewItem.interaction isKindOfClass:[TSIncomingMessage class]] ||
|
|
|
|
[viewItem.interaction isKindOfClass:[TSOutgoingMessage class]])
|
|
|
|
[viewItem.interaction isKindOfClass:[TSOutgoingMessage class]])
|
|
|
|
&& rowChange.finalIndex >= oldViewItemCount) {
|
|
|
|
&& rowChange.finalIndex >= oldViewItemCount) {
|
|
|
@ -3855,7 +3861,7 @@ typedef enum : NSUInteger {
|
|
|
|
return lastVisibleIndexPath;
|
|
|
|
return lastVisibleIndexPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (nullable ConversationViewItem *)lastVisibleViewItem
|
|
|
|
- (nullable id<ConversationViewItem>)lastVisibleViewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSIndexPath *_Nullable lastVisibleIndexPath = [self lastVisibleIndexPath];
|
|
|
|
NSIndexPath *_Nullable lastVisibleIndexPath = [self lastVisibleIndexPath];
|
|
|
|
if (!lastVisibleIndexPath) {
|
|
|
|
if (!lastVisibleIndexPath) {
|
|
|
@ -3870,7 +3876,7 @@ typedef enum : NSUInteger {
|
|
|
|
- (void)didScrollToBottom
|
|
|
|
- (void)didScrollToBottom
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
ConversationViewItem *_Nullable lastVisibleViewItem = [self.viewItems lastObject];
|
|
|
|
id<ConversationViewItem> _Nullable lastVisibleViewItem = [self.viewItems lastObject];
|
|
|
|
if (lastVisibleViewItem) {
|
|
|
|
if (lastVisibleViewItem) {
|
|
|
|
uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting;
|
|
|
|
uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting;
|
|
|
|
self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp);
|
|
|
|
self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp);
|
|
|
@ -3883,7 +3889,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
|
|
- (void)updateLastVisibleTimestamp
|
|
|
|
- (void)updateLastVisibleTimestamp
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ConversationViewItem *_Nullable lastVisibleViewItem = [self lastVisibleViewItem];
|
|
|
|
id<ConversationViewItem> _Nullable lastVisibleViewItem = [self lastVisibleViewItem];
|
|
|
|
if (lastVisibleViewItem) {
|
|
|
|
if (lastVisibleViewItem) {
|
|
|
|
uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting;
|
|
|
|
uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting;
|
|
|
|
self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp);
|
|
|
|
self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp);
|
|
|
@ -4640,7 +4646,7 @@ typedef enum : NSUInteger {
|
|
|
|
// any new items inserted while we were not observing. We therefore find the
|
|
|
|
// any new items inserted while we were not observing. We therefore find the
|
|
|
|
// first item at or after the "view horizon". See the comments below which explain
|
|
|
|
// first item at or after the "view horizon". See the comments below which explain
|
|
|
|
// the "view horizon".
|
|
|
|
// the "view horizon".
|
|
|
|
ConversationViewItem *_Nullable lastViewItem = self.viewItems.lastObject;
|
|
|
|
id<ConversationViewItem> _Nullable lastViewItem = self.viewItems.lastObject;
|
|
|
|
BOOL hasAddedNewItems = (lastViewItem && previousLastTimestamp
|
|
|
|
BOOL hasAddedNewItems = (lastViewItem && previousLastTimestamp
|
|
|
|
&& lastViewItem.interaction.timestamp > previousLastTimestamp.unsignedLongLongValue);
|
|
|
|
&& lastViewItem.interaction.timestamp > previousLastTimestamp.unsignedLongLongValue);
|
|
|
|
|
|
|
|
|
|
|
@ -4671,7 +4677,7 @@ typedef enum : NSUInteger {
|
|
|
|
// We'll use this later to update the view to reflect any changes made while
|
|
|
|
// We'll use this later to update the view to reflect any changes made while
|
|
|
|
// we were not observing the database. See extendRangeToIncludeUnobservedItems
|
|
|
|
// we were not observing the database. See extendRangeToIncludeUnobservedItems
|
|
|
|
// and the logic above.
|
|
|
|
// and the logic above.
|
|
|
|
ConversationViewItem *_Nullable lastViewItem = self.viewItems.lastObject;
|
|
|
|
id<ConversationViewItem> _Nullable lastViewItem = self.viewItems.lastObject;
|
|
|
|
if (lastViewItem) {
|
|
|
|
if (lastViewItem) {
|
|
|
|
self.previousLastTimestamp = @(lastViewItem.interaction.timestamp);
|
|
|
|
self.previousLastTimestamp = @(lastViewItem.interaction.timestamp);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -4717,7 +4723,7 @@ typedef enum : NSUInteger {
|
|
|
|
NSUInteger mid = (left + right) / 2;
|
|
|
|
NSUInteger mid = (left + right) / 2;
|
|
|
|
OWSAssertDebug(left <= mid);
|
|
|
|
OWSAssertDebug(left <= mid);
|
|
|
|
OWSAssertDebug(mid < right);
|
|
|
|
OWSAssertDebug(mid < right);
|
|
|
|
ConversationViewItem *viewItem = self.viewItems[mid];
|
|
|
|
id<ConversationViewItem> viewItem = self.viewItems[mid];
|
|
|
|
if (viewItem.interaction.timestamp >= viewHorizonTimestamp) {
|
|
|
|
if (viewItem.interaction.timestamp >= viewHorizonTimestamp) {
|
|
|
|
right = mid;
|
|
|
|
right = mid;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -4726,7 +4732,7 @@ typedef enum : NSUInteger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OWSAssertDebug(left == right);
|
|
|
|
OWSAssertDebug(left == right);
|
|
|
|
ConversationViewItem *viewItem = self.viewItems[left];
|
|
|
|
id<ConversationViewItem> viewItem = self.viewItems[left];
|
|
|
|
if (viewItem.interaction.timestamp >= viewHorizonTimestamp) {
|
|
|
|
if (viewItem.interaction.timestamp >= viewHorizonTimestamp) {
|
|
|
|
OWSLogInfo(@"firstIndexPathAtViewHorizonTimestamp: %zd / %zd", left, self.viewItems.count);
|
|
|
|
OWSLogInfo(@"firstIndexPathAtViewHorizonTimestamp: %zd / %zd", left, self.viewItems.count);
|
|
|
|
return [NSIndexPath indexPathForRow:(NSInteger) left inSection:0];
|
|
|
|
return [NSIndexPath indexPathForRow:(NSInteger) left inSection:0];
|
|
|
@ -4860,8 +4866,8 @@ typedef enum : NSUInteger {
|
|
|
|
// Returns NO on error.
|
|
|
|
// Returns NO on error.
|
|
|
|
- (BOOL)reloadViewItems
|
|
|
|
- (BOOL)reloadViewItems
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSMutableArray<ConversationViewItem *> *viewItems = [NSMutableArray new];
|
|
|
|
NSMutableArray<id<ConversationViewItem>> *viewItems = [NSMutableArray new];
|
|
|
|
NSMutableDictionary<NSString *, ConversationViewItem *> *viewItemCache = [NSMutableDictionary new];
|
|
|
|
NSMutableDictionary<NSString *, id<ConversationViewItem>> *viewItemCache = [NSMutableDictionary new];
|
|
|
|
|
|
|
|
|
|
|
|
NSUInteger count = [self.messageMappings numberOfItemsInSection:0];
|
|
|
|
NSUInteger count = [self.messageMappings numberOfItemsInSection:0];
|
|
|
|
BOOL isGroupThread = self.isGroupConversation;
|
|
|
|
BOOL isGroupThread = self.isGroupConversation;
|
|
|
@ -4890,12 +4896,12 @@ typedef enum : NSUInteger {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ConversationViewItem *_Nullable viewItem = self.viewItemCache[interaction.uniqueId];
|
|
|
|
id<ConversationViewItem> _Nullable viewItem = self.viewItemCache[interaction.uniqueId];
|
|
|
|
if (!viewItem) {
|
|
|
|
if (!viewItem) {
|
|
|
|
viewItem = [[ConversationViewItem alloc] initWithInteraction:interaction
|
|
|
|
viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:interaction
|
|
|
|
isGroupThread:isGroupThread
|
|
|
|
isGroupThread:isGroupThread
|
|
|
|
transaction:transaction
|
|
|
|
transaction:transaction
|
|
|
|
conversationStyle:self.conversationStyle];
|
|
|
|
conversationStyle:self.conversationStyle];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[viewItems addObject:viewItem];
|
|
|
|
[viewItems addObject:viewItem];
|
|
|
|
OWSAssertDebug(!viewItemCache[interaction.uniqueId]);
|
|
|
|
OWSAssertDebug(!viewItemCache[interaction.uniqueId]);
|
|
|
@ -4916,7 +4922,7 @@ typedef enum : NSUInteger {
|
|
|
|
uint64_t collapseCutoffTimestamp = [NSDate ows_millisecondsSince1970ForDate:self.collapseCutoffDate];
|
|
|
|
uint64_t collapseCutoffTimestamp = [NSDate ows_millisecondsSince1970ForDate:self.collapseCutoffDate];
|
|
|
|
|
|
|
|
|
|
|
|
BOOL hasPlacedUnreadIndicator = NO;
|
|
|
|
BOOL hasPlacedUnreadIndicator = NO;
|
|
|
|
for (ConversationViewItem *viewItem in viewItems) {
|
|
|
|
for (id<ConversationViewItem> viewItem in viewItems) {
|
|
|
|
BOOL canShowDate = NO;
|
|
|
|
BOOL canShowDate = NO;
|
|
|
|
switch (viewItem.interaction.interactionType) {
|
|
|
|
switch (viewItem.interaction.interactionType) {
|
|
|
|
case OWSInteractionType_Unknown:
|
|
|
|
case OWSInteractionType_Unknown:
|
|
|
@ -4992,9 +4998,9 @@ typedef enum : NSUInteger {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// NOTE: This logic uses the break properties which are set in the previous pass.
|
|
|
|
// NOTE: This logic uses the break properties which are set in the previous pass.
|
|
|
|
for (NSUInteger i = 0; i < viewItems.count; i++) {
|
|
|
|
for (NSUInteger i = 0; i < viewItems.count; i++) {
|
|
|
|
ConversationViewItem *viewItem = viewItems[i];
|
|
|
|
id<ConversationViewItem> viewItem = viewItems[i];
|
|
|
|
ConversationViewItem *_Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil);
|
|
|
|
id<ConversationViewItem> _Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil);
|
|
|
|
ConversationViewItem *_Nullable nextViewItem = (i + 1 < viewItems.count ? viewItems[i + 1] : nil);
|
|
|
|
id<ConversationViewItem> _Nullable nextViewItem = (i + 1 < viewItems.count ? viewItems[i + 1] : nil);
|
|
|
|
BOOL shouldShowSenderAvatar = NO;
|
|
|
|
BOOL shouldShowSenderAvatar = NO;
|
|
|
|
BOOL shouldHideFooter = NO;
|
|
|
|
BOOL shouldHideFooter = NO;
|
|
|
|
BOOL isFirstInCluster = YES;
|
|
|
|
BOOL isFirstInCluster = YES;
|
|
|
@ -5143,7 +5149,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
|
|
// Whenever an interaction is modified, we need to reload it from the DB
|
|
|
|
// Whenever an interaction is modified, we need to reload it from the DB
|
|
|
|
// and update the corresponding view item.
|
|
|
|
// and update the corresponding view item.
|
|
|
|
- (void)reloadInteractionForViewItem:(ConversationViewItem *)viewItem
|
|
|
|
- (void)reloadInteractionForViewItem:(id<ConversationViewItem>)viewItem
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
OWSAssertDebug(viewItem);
|
|
|
|
OWSAssertDebug(viewItem);
|
|
|
@ -5164,7 +5170,7 @@ typedef enum : NSUInteger {
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (nullable ConversationViewItem *)viewItemForIndex:(NSInteger)index
|
|
|
|
- (nullable id<ConversationViewItem>)viewItemForIndex:(NSInteger)index
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (index < 0 || index >= (NSInteger)self.viewItems.count) {
|
|
|
|
if (index < 0 || index >= (NSInteger)self.viewItems.count) {
|
|
|
|
OWSFailDebug(@"Invalid view item index: %lu", (unsigned long)index);
|
|
|
|
OWSFailDebug(@"Invalid view item index: %lu", (unsigned long)index);
|
|
|
@ -5183,7 +5189,7 @@ typedef enum : NSUInteger {
|
|
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
|
|
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
|
|
|
|
cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:indexPath.row];
|
|
|
|
id<ConversationViewItem> _Nullable viewItem = [self viewItemForIndex:indexPath.row];
|
|
|
|
ConversationViewCell *cell = [viewItem dequeueCellForCollectionView:self.collectionView indexPath:indexPath];
|
|
|
|
ConversationViewCell *cell = [viewItem dequeueCellForCollectionView:self.collectionView indexPath:indexPath];
|
|
|
|
if (!cell) {
|
|
|
|
if (!cell) {
|
|
|
|
OWSFailDebug(@"Could not dequeue cell.");
|
|
|
|
OWSFailDebug(@"Could not dequeue cell.");
|
|
|
|