From 087e320036269d707a9334057a7880b3263b57f6 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 13 Dec 2018 14:30:50 -0700 Subject: [PATCH] Track 'persisted' viewItems separately --- .../ConversationViewController.m | 2 +- .../ConversationView/ConversationViewModel.h | 2 +- .../ConversationView/ConversationViewModel.m | 120 +++++++++++------- 3 files changed, 79 insertions(+), 45 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 32e8d56ca..6b70c1241 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -702,7 +702,7 @@ typedef enum : NSUInteger { - (NSArray> *)viewItems { - return self.conversationViewModel.viewItems; + return self.conversationViewModel.allViewItems; } - (ThreadDynamicInteractions *)dynamicInteractions diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h index bdbf8f530..8143eddd6 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h @@ -80,7 +80,7 @@ typedef NS_ENUM(NSUInteger, ConversationUpdateItemType) { @interface ConversationViewModel : NSObject -@property (nonatomic, readonly) NSArray> *viewItems; +@property (nonatomic, readonly) NSArray> *allViewItems; @property (nonatomic, nullable) NSString *focusMessageIdOnOpen; @property (nonatomic, readonly, nullable) ThreadDynamicInteractions *dynamicInteractions; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m index c84f226f8..89e04d010 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m @@ -154,7 +154,7 @@ static const int kYapDatabaseRangeMinLength = 0; // * If we do the third step, we must call resetContentAndLayout afterward. @property (nonatomic) YapDatabaseViewMappings *messageMappings; -@property (nonatomic) NSArray> *viewItems; +@property (nonatomic) NSArray> *allViewItems; @property (nonatomic) NSMutableDictionary> *viewItemCache; @property (nonatomic) NSUInteger lastRangeLength; @@ -165,6 +165,7 @@ static const int kYapDatabaseRangeMinLength = 0; @property (nonatomic, nullable) ConversationProfileState *conversationProfileState; @property (nonatomic) BOOL hasTooManyOutgoingMessagesToBlockCached; +@property (nonatomic) NSArray> *persistedViewItems; @end @@ -489,7 +490,7 @@ static const int kYapDatabaseRangeMinLength = 0; - (nullable id)viewItemForUnreadMessagesIndicator { - for (id viewItem in self.viewItems) { + for (id viewItem in self.allViewItems) { if (viewItem.unreadIndicator) { return viewItem; } @@ -596,7 +597,7 @@ static const int kYapDatabaseRangeMinLength = 0; } NSMutableArray *oldItemIdList = [NSMutableArray new]; - for (id viewItem in self.viewItems) { + for (id viewItem in self.allViewItems) { [oldItemIdList addObject:viewItem.itemId]; } @@ -659,7 +660,7 @@ static const int kYapDatabaseRangeMinLength = 0; return; } - OWSLogVerbose(@"self.viewItems.count: %zd -> %zd", oldItemIdList.count, self.viewItems.count); + OWSLogVerbose(@"self.viewItems.count: %zd -> %zd", oldItemIdList.count, self.allViewItems.count); [self updateViewWithOldItemIdList:oldItemIdList updatedItemSet:updatedItemSet]; } @@ -673,7 +674,7 @@ static const int kYapDatabaseRangeMinLength = 0; OWSLogVerbose(@""); NSMutableArray *oldItemIdList = [NSMutableArray new]; - for (id viewItem in self.viewItems) { + for (id viewItem in self.allViewItems) { [oldItemIdList addObject:viewItem.itemId]; } @@ -685,7 +686,7 @@ static const int kYapDatabaseRangeMinLength = 0; return; } - OWSLogVerbose(@"self.viewItems.count: %zd -> %zd", oldItemIdList.count, self.viewItems.count); + OWSLogVerbose(@"self.viewItems.count: %zd -> %zd", oldItemIdList.count, self.allViewItems.count); [self updateViewWithOldItemIdList:oldItemIdList updatedItemSet:[NSSet set]]; } @@ -710,7 +711,7 @@ static const int kYapDatabaseRangeMinLength = 0; NSMutableArray *newItemIdList = [NSMutableArray new]; NSMutableDictionary> *newViewItemMap = [NSMutableDictionary new]; - for (id viewItem in self.viewItems) { + for (id viewItem in self.allViewItems) { [newItemIdList addObject:viewItem.itemId]; newViewItemMap[viewItem.itemId] = viewItem; } @@ -1150,6 +1151,32 @@ static const int kYapDatabaseRangeMinLength = 0; return offersMessage; } + +- (id)addViewItemWithInteraction:(TSInteraction *)interaction + conversationStyle:(ConversationStyle *)conversationStyle + isGroupThread:(BOOL)isGroupThread + transaction:(YapDatabaseReadTransaction *)transaction + viewItemCache: + (NSMutableDictionary> *)viewItemCache + toViewItems:(NSMutableArray> *)viewItems +{ + OWSAssertDebug(interaction.uniqueId.length > 0); + + id _Nullable viewItem = self.viewItemCache[interaction.uniqueId]; + if (!viewItem) { + viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:interaction + isGroupThread:isGroupThread + transaction:transaction + conversationStyle:conversationStyle]; + } + [viewItems addObject:viewItem]; + OWSAssertDebug( + !viewItemCache[interaction.uniqueId] || [interaction isKindOfClass:[OWSTypingIndicatorInteraction class]]); + viewItemCache[interaction.uniqueId] = viewItem; + + return viewItem; +} + // This is a key method. It builds or rebuilds the list of // cell view models. // @@ -1166,21 +1193,15 @@ static const int kYapDatabaseRangeMinLength = 0; [self ensureConversationProfileState]; __block BOOL hasError = NO; - id (^tryToAddViewItem)(TSInteraction *, YapDatabaseReadTransaction *) = ^(TSInteraction *interaction, YapDatabaseReadTransaction *transaction) { - OWSAssertDebug(interaction.uniqueId.length > 0); - - id _Nullable viewItem = self.viewItemCache[interaction.uniqueId]; - if (!viewItem) { - viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:interaction - isGroupThread:isGroupThread - transaction:transaction - conversationStyle:conversationStyle]; - } - [viewItems addObject:viewItem]; - OWSAssertDebug(!viewItemCache[interaction.uniqueId]); - viewItemCache[interaction.uniqueId] = viewItem; - return viewItem; - }; + id (^tryToAddViewItem)(TSInteraction *, YapDatabaseReadTransaction *) + = ^(TSInteraction *interaction, YapDatabaseReadTransaction *transaction) { + return [self addViewItemWithInteraction:interaction + conversationStyle:conversationStyle + isGroupThread:isGroupThread + transaction:transaction + viewItemCache:viewItemCache + toViewItems:viewItems]; + }; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { NSMutableArray *interactions = [NSMutableArray new]; @@ -1242,17 +1263,6 @@ static const int kYapDatabaseRangeMinLength = 0; return [left.interaction compareForSorting:right.interaction]; }]; - if (self.typingIndicatorsSender) { - [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - id _Nullable lastViewItem = viewItems.lastObject; - uint64_t typingIndicatorTimestamp = (lastViewItem ? lastViewItem.interaction.timestamp + 1 : 1); - TSInteraction *interaction = - [[OWSTypingIndicatorInteraction alloc] initWithThread:self.thread - timestamp:typingIndicatorTimestamp - recipientId:self.typingIndicatorsSender]; - tryToAddViewItem(interaction, transaction); - }]; - } // Flag to ensure that we only increment once per launch. if (hasError) { OWSLogWarn(@"incrementing version of: %@", TSMessageDatabaseViewExtensionName); @@ -1260,13 +1270,36 @@ static const int kYapDatabaseRangeMinLength = 0; } self.viewItemCache = viewItemCache; - self.viewItems = [self prepareViewItemsForDisplay:viewItems]; + self.persistedViewItems = [viewItems copy]; + self.allViewItems = [self prepareViewItemsForDisplay:viewItems]; return !hasError; } -- (NSArray> *)prepareViewItemsForDisplay:(NSArray> *)viewItems +- (NSArray> *)prepareViewItemsForDisplay:(NSArray> *)viewItemsArg { + NSArray> *viewItems = viewItemsArg; + if (self.typingIndicatorsSender) { + NSMutableArray> *mutableViewItems = [viewItems mutableCopy]; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + id _Nullable lastViewItem = viewItems.lastObject; + uint64_t typingIndicatorTimestamp = (lastViewItem ? lastViewItem.interaction.timestamp + 1 : 1); + TSInteraction *interaction = + [[OWSTypingIndicatorInteraction alloc] initWithThread:self.thread + timestamp:typingIndicatorTimestamp + recipientId:self.typingIndicatorsSender]; + + [self addViewItemWithInteraction:interaction + conversationStyle:self.delegate.conversationStyle + isGroupThread:self.thread.isGroupThread + transaction:transaction + viewItemCache:self.viewItemCache + toViewItems:mutableViewItems]; + + }]; + viewItems = [mutableViewItems copy]; + } + // Update the "break" properties (shouldShowDate and unreadIndicator) of the view items. BOOL shouldShowDateOnNextViewItem = YES; uint64_t previousViewItemTimestamp = 0; @@ -1509,14 +1542,15 @@ static const int kYapDatabaseRangeMinLength = 0; conversationStyle:self.delegate.conversationStyle]; }]; - ConversationUpdateItem *updateItem = [[ConversationUpdateItem alloc] initWithUpdateItemType:ConversationUpdateItemType_Insert - oldIndex:0 - newIndex:self.viewItems.count - viewItem:viewItem]; + ConversationUpdateItem *updateItem = + [[ConversationUpdateItem alloc] initWithUpdateItemType:ConversationUpdateItemType_Insert + oldIndex:0 + newIndex:self.persistedViewItems.count + viewItem:viewItem]; - NSMutableArray> *viewItems = [self.viewItems mutableCopy]; + NSMutableArray> *viewItems = [self.persistedViewItems mutableCopy]; [viewItems addObject:viewItem]; - self.viewItems = [self prepareViewItemsForDisplay:viewItems]; + self.allViewItems = [self prepareViewItemsForDisplay:viewItems]; ConversationUpdate *conversationUpdate = [[ConversationUpdate alloc] initWithConversationUpdateType:ConversationUpdateType_Diff updateItems:@[updateItem] shouldAnimateUpdates:NO]; @@ -1666,7 +1700,7 @@ static const int kYapDatabaseRangeMinLength = 0; OWSFailDebug(@"Could not locate view item for quoted reply."); return nil; } - NSUInteger viewItemIndex = [self.viewItems indexOfObject:viewItem]; + NSUInteger viewItemIndex = [self.allViewItems indexOfObject:viewItem]; if (viewItemIndex == NSNotFound) { OWSFailDebug(@"Could not locate view item index for quoted reply."); return nil; @@ -1718,7 +1752,7 @@ static const int kYapDatabaseRangeMinLength = 0; // Update the view items if necessary. // We don't have to do this if they haven't been configured yet. - if (didChange && self.viewItems != nil) { + if (didChange && self.allViewItems != nil) { [self updateForTransientItems]; } }