From 27b6a5e5bb604dd31228416df29547583a0388e3 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 11 Jun 2018 15:31:54 -0400 Subject: [PATCH 1/4] Open message search results. --- .../ConversationViewController.h | 4 +- .../ConversationViewController.m | 38 +++++++++++++++++-- .../ConversationSearchViewController.swift | 4 +- .../HomeView/HomeViewController.h | 3 ++ .../HomeView/HomeViewController.m | 15 ++++++-- Signal/src/call/OutboundCallInitiator.swift | 5 +-- Signal/src/environment/SignalApp.h | 11 +++++- Signal/src/environment/SignalApp.m | 13 ++++++- .../utils/ConversationSearcher.swift | 12 ++++-- 9 files changed, 84 insertions(+), 21 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.h b/Signal/src/ViewControllers/ConversationView/ConversationViewController.h index 002a95218..31d7841c1 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.h @@ -19,7 +19,9 @@ typedef NS_ENUM(NSUInteger, ConversationViewAction) { @property (nonatomic, readonly) TSThread *thread; -- (void)configureForThread:(TSThread *)thread action:(ConversationViewAction)action; +- (void)configureForThread:(TSThread *)thread + action:(ConversationViewAction)action + focusMessageId:(nullable NSString *)focusMessageId; - (void)popKeyBoard; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 861314ec2..de074d384 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -195,6 +195,8 @@ typedef enum : NSUInteger { @property (nonatomic) NSUInteger lastRangeLength; @property (nonatomic) ConversationViewAction actionOnOpen; +@property (nonatomic, nullable) NSString *focusMessageIdOnOpen; + @property (nonatomic) BOOL peek; @property (nonatomic, readonly) OWSContactsManager *contactsManager; @@ -426,11 +428,16 @@ typedef enum : NSUInteger { [self hideInputIfNeeded]; } -- (void)configureForThread:(TSThread *)thread action:(ConversationViewAction)action +- (void)configureForThread:(TSThread *)thread + action:(ConversationViewAction)action + focusMessageId:(nullable NSString *)focusMessageId { + OWSAssert(thread); + _thread = thread; _isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]]; self.actionOnOpen = action; + self.focusMessageIdOnOpen = focusMessageId; _cellMediaCache = [NSCache new]; // Cache the cell media for ~24 cells. self.cellMediaCache.countLimit = 24; @@ -698,13 +705,35 @@ typedef enum : NSUInteger { return nil; } +- (NSIndexPath *_Nullable)indexPathOfMessageOnOpen +{ + OWSAssert(self.focusMessageIdOnOpen); + + NSInteger row = 0; + for (ConversationViewItem *viewItem in self.viewItems) { + if ([viewItem.interaction.uniqueId isEqualToString:self.focusMessageIdOnOpen]) { + return [NSIndexPath indexPathForRow:row inSection:0]; + } + row++; + } + return nil; +} + - (void)scrollToDefaultPosition { if (self.isUserScrolling) { return; } - NSIndexPath *_Nullable indexPath = [self indexPathOfUnreadMessagesIndicator]; + NSIndexPath *_Nullable indexPath = nil; + if (self.focusMessageIdOnOpen) { + indexPath = [self indexPathOfMessageOnOpen]; + } + + if (!indexPath) { + indexPath = [self indexPathOfUnreadMessagesIndicator]; + } + if (indexPath) { if (indexPath.section == 0 && indexPath.row == 0) { [self.collectionView setContentOffset:CGPointZero animated:NO]; @@ -1081,8 +1110,9 @@ typedef enum : NSUInteger { break; } + // Clear the "on open" state after the view has been presented. self.actionOnOpen = ConversationViewActionNone; - + self.focusMessageIdOnOpen = nil; self.isViewCompletelyAppeared = YES; self.viewHasEverAppeared = YES; @@ -1557,7 +1587,7 @@ typedef enum : NSUInteger { // Don’t auto-scroll after “loading more messages” unless we have “more unseen messages”. // // Otherwise, tapping on "load more messages" autoscrolls you downward which is completely wrong. - if (hasEarlierUnseenMessages) { + if (hasEarlierUnseenMessages && !self.focusMessageIdOnOpen) { [self scrollToUnreadIndicatorAnimated]; } } diff --git a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift index 46f5b99b0..a68001c76 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift @@ -79,7 +79,9 @@ class ConversationSearchViewController: UITableViewController { } let thread = searchResult.thread - SignalApp.shared().presentConversation(for: thread.threadRecord, action: .compose) + SignalApp.shared().presentConversation(for: thread.threadRecord, + action: .compose, + focusMessageId: searchResult.messageId) } } diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.h b/Signal/src/ViewControllers/HomeView/HomeViewController.h index a19c94488..d86824b5b 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.h +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.h @@ -11,6 +11,9 @@ @interface HomeViewController : OWSViewController - (void)presentThread:(TSThread *)thread action:(ConversationViewAction)action; +- (void)presentThread:(TSThread *)thread + action:(ConversationViewAction)action + focusMessageId:(nullable NSString *)focusMessageId; - (void)showNewConversationView; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 5a858829e..fb4d3c048 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -424,7 +424,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations ConversationViewController *vc = [ConversationViewController new]; TSThread *thread = [self threadForIndexPath:indexPath]; self.lastThread = thread; - [vc configureForThread:thread action:ConversationViewActionNone]; + [vc configureForThread:thread action:ConversationViewActionNone focusMessageId:nil]; [vc peekSetup]; return vc; @@ -1000,6 +1000,13 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations } - (void)presentThread:(TSThread *)thread action:(ConversationViewAction)action +{ + [self presentThread:thread action:action focusMessageId:nil]; +} + +- (void)presentThread:(TSThread *)thread + action:(ConversationViewAction)action + focusMessageId:(nullable NSString *)focusMessageId { if (thread == nil) { OWSFail(@"Thread unexpectedly nil"); @@ -1008,11 +1015,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations // We do this synchronously if we're already on the main thread. DispatchMainThreadSafe(^{ - ConversationViewController *mvc = [ConversationViewController new]; - [mvc configureForThread:thread action:action]; + ConversationViewController *viewController = [ConversationViewController new]; + [viewController configureForThread:thread action:action focusMessageId:focusMessageId]; self.lastThread = thread; - [self pushTopLevelViewController:mvc animateDismissal:YES animatePresentation:YES]; + [self pushTopLevelViewController:viewController animateDismissal:YES animatePresentation:YES]; }); } diff --git a/Signal/src/call/OutboundCallInitiator.swift b/Signal/src/call/OutboundCallInitiator.swift index b3900960f..cc2f49626 100644 --- a/Signal/src/call/OutboundCallInitiator.swift +++ b/Signal/src/call/OutboundCallInitiator.swift @@ -45,10 +45,7 @@ import SignalMessaging isVideo: Bool) -> Bool { // Rather than an init-assigned dependency property, we access `callUIAdapter` via Environment // because it can change after app launch due to user settings - guard let callUIAdapter = SignalApp.shared().callUIAdapter else { - owsFail("\(TAG) can't initiate call because callUIAdapter is nil") - return false - } + let callUIAdapter = SignalApp.shared().callUIAdapter guard let frontmostViewController = UIApplication.shared.frontmostViewController else { owsFail("\(TAG) could not identify frontmostViewController in \(#function)") return false diff --git a/Signal/src/environment/SignalApp.h b/Signal/src/environment/SignalApp.h index 05231e32a..f9277ec49 100644 --- a/Signal/src/environment/SignalApp.h +++ b/Signal/src/environment/SignalApp.h @@ -4,6 +4,8 @@ #import "ConversationViewController.h" +NS_ASSUME_NONNULL_BEGIN + @class AccountManager; @class CallService; @class CallUIAdapter; @@ -17,8 +19,8 @@ @interface SignalApp : NSObject -@property (nonatomic, weak) HomeViewController *homeViewController; -@property (nonatomic, weak) OWSNavigationController *signUpFlowNavigationController; +@property (nonatomic, nullable, weak) HomeViewController *homeViewController; +@property (nonatomic, nullable, weak) OWSNavigationController *signUpFlowNavigationController; // TODO: Convert to singletons? @property (nonatomic, readonly) OWSWebRTCCallMessageHandler *callMessageHandler; @@ -40,6 +42,9 @@ - (void)presentConversationForThreadId:(NSString *)threadId; - (void)presentConversationForThread:(TSThread *)thread; - (void)presentConversationForThread:(TSThread *)thread action:(ConversationViewAction)action; +- (void)presentConversationForThread:(TSThread *)thread + action:(ConversationViewAction)action + focusMessageId:(nullable NSString *)focusMessageId; #pragma mark - Methods @@ -48,3 +53,5 @@ + (void)clearAllNotifications; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/environment/SignalApp.m b/Signal/src/environment/SignalApp.m index de9e7a952..4d95b7d8d 100644 --- a/Signal/src/environment/SignalApp.m +++ b/Signal/src/environment/SignalApp.m @@ -13,6 +13,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + @interface SignalApp () @property (nonatomic) OWSWebRTCCallMessageHandler *callMessageHandler; @@ -186,6 +188,13 @@ } - (void)presentConversationForThread:(TSThread *)thread action:(ConversationViewAction)action +{ + [self presentConversationForThread:thread action:action focusMessageId:nil]; +} + +- (void)presentConversationForThread:(TSThread *)thread + action:(ConversationViewAction)action + focusMessageId:(nullable NSString *)focusMessageId { OWSAssertIsOnMainThread(); @@ -207,7 +216,7 @@ } } - [self.homeViewController presentThread:thread action:action]; + [self.homeViewController presentThread:thread action:action focusMessageId:focusMessageId]; }); } @@ -248,3 +257,5 @@ } @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/utils/ConversationSearcher.swift b/SignalMessaging/utils/ConversationSearcher.swift index 32f4bab1d..2c68cc250 100644 --- a/SignalMessaging/utils/ConversationSearcher.swift +++ b/SignalMessaging/utils/ConversationSearcher.swift @@ -7,10 +7,14 @@ import SignalServiceKit public class ConversationSearchResult { public let thread: ThreadViewModel + + public let messageId: String? + public let snippet: String? - init(thread: ThreadViewModel, snippet: String?) { + init(thread: ThreadViewModel, messageId: String?, snippet: String?) { self.thread = thread + self.messageId = messageId self.snippet = snippet } } @@ -71,7 +75,7 @@ public class ConversationSearcher: NSObject { if let thread = match as? TSThread { let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) let snippet: String? = thread.lastMessageText(transaction: transaction) - let searchResult = ConversationSearchResult(thread: threadViewModel, snippet: snippet) + let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: nil, snippet: snippet) if let contactThread = thread as? TSContactThread { let recipientId = contactThread.contactIdentifier() @@ -82,14 +86,14 @@ public class ConversationSearcher: NSObject { let thread = message.thread(with: transaction) let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - let searchResult = ConversationSearchResult(thread: threadViewModel, snippet: snippet) + let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: message.uniqueId, snippet: snippet) messages.append(searchResult) } else if let signalAccount = match as? SignalAccount { let searchResult = ContactSearchResult(signalAccount: signalAccount) contacts.append(searchResult) } else { - Logger.debug("\(self.logTag) in \(#function) unhandled item: \(match)") + owsFail("\(self.logTag) in \(#function) unhandled item: \(match)") } } From 13e9f11b4e73bb85c61855e19be5558e88769105 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 11 Jun 2018 16:20:37 -0400 Subject: [PATCH 2/4] Open message search results. --- .../ConversationViewController.m | 17 ++++++++- SignalMessaging/utils/ThreadUtil.h | 3 ++ SignalMessaging/utils/ThreadUtil.m | 38 +++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index de074d384..70525279d 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1095,6 +1095,7 @@ typedef enum : NSUInteger { [self startReadTimer]; [self updateNavigationBarSubtitleLabel]; [self updateBackButtonUnreadCount]; + [self autoLoadMoreIfNecessary]; switch (self.actionOnOpen) { case ConversationViewActionNone: @@ -1664,9 +1665,20 @@ typedef enum : NSUInteger { if (self.lastRangeLength == 0) { // If this is the first time we're configuring the range length, - // try to take into account the position of the unread indicator. + // try to take into account the position of the unread indicator + // and the "focus message". OWSAssert(self.dynamicInteractions); + if (self.focusMessageIdOnOpen) { + OWSAssert(self.dynamicInteractions.focusMessagePosition); + if (self.dynamicInteractions.focusMessagePosition) { + DDLogVerbose(@"%@ ensuring load of focus message: %@", + self.logTag, + self.dynamicInteractions.focusMessagePosition); + rangeLength = MAX(rangeLength, 1 + self.dynamicInteractions.focusMessagePosition.unsignedIntegerValue); + } + } + if (self.dynamicInteractions.unreadIndicatorPosition) { NSUInteger unreadIndicatorPosition = (NSUInteger)[self.dynamicInteractions.unreadIndicatorPosition longValue]; @@ -1679,7 +1691,7 @@ typedef enum : NSUInteger { // We'd like to include at least N seen messages, // to give the user the context of where they left off the conversation. const NSUInteger kPreferredSeenMessageCount = 1; - rangeLength = unreadIndicatorPosition + kPreferredSeenMessageCount; + rangeLength = MAX(rangeLength, unreadIndicatorPosition + kPreferredSeenMessageCount); } } @@ -2503,6 +2515,7 @@ typedef enum : NSUInteger { dbConnection:self.editingDatabaseConnection hideUnreadMessagesIndicator:self.hasClearedUnreadMessagesIndicator firstUnseenInteractionTimestamp:self.dynamicInteractions.firstUnseenInteractionTimestamp + focusMessageId:self.focusMessageIdOnOpen maxRangeSize:maxRangeSize]; } diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 197cc1c67..9d65f4520 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN // to include the unread indicator. @property (nonatomic, nullable, readonly) NSNumber *unreadIndicatorPosition; +@property (nonatomic, nullable, readonly) NSNumber *focusMessagePosition; + // If there are unseen messages in the thread, this is the timestamp // of the oldest unseen message. // @@ -105,6 +107,7 @@ NS_ASSUME_NONNULL_BEGIN dbConnection:(YapDatabaseConnection *)dbConnection hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator firstUnseenInteractionTimestamp:(nullable NSNumber *)firstUnseenInteractionTimestamp + focusMessageId:(nullable NSString *)focusMessageId maxRangeSize:(int)maxRangeSize; + (BOOL)shouldShowGroupProfileBannerInThread:(TSThread *)thread blockingManager:(OWSBlockingManager *)blockingManager; diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index c5df4a72a..41d7f6889 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -31,6 +31,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) NSNumber *unreadIndicatorPosition; +@property (nonatomic, nullable) NSNumber *focusMessagePosition; + @property (nonatomic, nullable) NSNumber *firstUnseenInteractionTimestamp; @property (nonatomic) BOOL hasMoreUnseenMessages; @@ -221,6 +223,7 @@ NS_ASSUME_NONNULL_BEGIN hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator firstUnseenInteractionTimestamp: (nullable NSNumber *)firstUnseenInteractionTimestampParameter + focusMessageId:(nullable NSString *)focusMessageId maxRangeSize:(int)maxRangeSize { OWSAssert(thread); @@ -615,11 +618,46 @@ NS_ASSUME_NONNULL_BEGIN indicator.timestampForSorting); } } + + // Determine the position of the focus message _after_ performing any mutations + // around dynamic interactions. + if (focusMessageId != nil) { + result.focusMessagePosition = + [self focusMessagePositionForThread:thread transaction:transaction focusMessageId:focusMessageId]; + } }]; return result; } + ++ (nullable NSNumber *)focusMessagePositionForThread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction + focusMessageId:(NSString *)focusMessageId +{ + OWSAssert(thread); + OWSAssert(transaction); + OWSAssert(focusMessageId); + + // Enumerate in reverse to count the number of messages after the "focus message". + __block NSUInteger count = 0; + __block BOOL didMatch = NO; + [[transaction ext:TSMessageDatabaseViewExtensionName] + enumerateKeysInGroup:thread.uniqueId + withOptions:NSEnumerationReverse + usingBlock:^(NSString *collection, NSString *key, NSUInteger index, BOOL *stop) { + if ([key isEqualToString:focusMessageId]) { + didMatch = YES; + *stop = YES; + return; + } + + count++; + }]; + + return didMatch ? @(count) : nil; +} + + (BOOL)shouldShowGroupProfileBannerInThread:(TSThread *)thread blockingManager:(OWSBlockingManager *)blockingManager { OWSAssert(thread); From 999e8c8e31297deb9ef761ad620910921fa11554 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 12 Jun 2018 09:43:59 -0400 Subject: [PATCH 3/4] Respond to CR. --- .../ConversationViewController.m | 22 +++++++---- SignalMessaging/utils/ThreadUtil.h | 4 ++ SignalMessaging/utils/ThreadUtil.m | 38 +++++++++++-------- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 70525279d..55f436262 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -708,15 +708,23 @@ typedef enum : NSUInteger { - (NSIndexPath *_Nullable)indexPathOfMessageOnOpen { OWSAssert(self.focusMessageIdOnOpen); + OWSAssert(self.dynamicInteractions.focusMessagePosition); - NSInteger row = 0; - for (ConversationViewItem *viewItem in self.viewItems) { - if ([viewItem.interaction.uniqueId isEqualToString:self.focusMessageIdOnOpen]) { - return [NSIndexPath indexPathForRow:row inSection:0]; - } - row++; + if (!self.dynamicInteractions.focusMessagePosition) { + // This might happen if the focus message has disappeared + // before this view could appear. + OWSFail(@"%@ focus message has unknown position.", self.logTag); + return nil; } - return nil; + NSUInteger focusMessagePosition = self.dynamicInteractions.focusMessagePosition.unsignedIntegerValue; + if (focusMessagePosition >= self.viewItems.count) { + // This might happen if the focus message is outside the maximum + // valid load window size for this view. + OWSFail(@"%@ focus message has invalid position.", self.logTag); + return nil; + } + NSInteger row = (NSInteger)((self.viewItems.count - 1) - focusMessagePosition); + return [NSIndexPath indexPathForRow:row inSection:0]; } - (void)scrollToDefaultPosition diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 9d65f4520..32833f0ad 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -25,6 +25,10 @@ NS_ASSUME_NONNULL_BEGIN // to include the unread indicator. @property (nonatomic, nullable, readonly) NSNumber *unreadIndicatorPosition; +// Represents the "reverse index" of the focus message, if any. +// The "reverse index" is the distance of this interaction from +// the last interaction in the thread. Therefore the last interaction +// will have a "reverse index" of zero. @property (nonatomic, nullable, readonly) NSNumber *focusMessagePosition; // If there are unseen messages in the thread, this is the timestamp diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 41d7f6889..ad516915a 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -639,23 +639,29 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(transaction); OWSAssert(focusMessageId); - // Enumerate in reverse to count the number of messages after the "focus message". - __block NSUInteger count = 0; - __block BOOL didMatch = NO; - [[transaction ext:TSMessageDatabaseViewExtensionName] - enumerateKeysInGroup:thread.uniqueId - withOptions:NSEnumerationReverse - usingBlock:^(NSString *collection, NSString *key, NSUInteger index, BOOL *stop) { - if ([key isEqualToString:focusMessageId]) { - didMatch = YES; - *stop = YES; - return; - } + YapDatabaseViewTransaction *databaseView = [transaction ext:TSMessageDatabaseViewExtensionName]; - count++; - }]; - - return didMatch ? @(count) : nil; + NSString *_Nullable group = nil; + NSUInteger index; + BOOL success = + [databaseView getGroup:&group index:&index forKey:focusMessageId inCollection:TSInteraction.collection]; + if (!success) { + // This might happen if the focus message has disappeared + // before this view could appear. + OWSFail(@"%@ failed to find focus message index.", self.logTag); + return nil; + } + if (![group isEqualToString:thread.uniqueId]) { + OWSFail(@"%@ focus message has invalid group.", self.logTag); + return nil; + } + NSUInteger count = [databaseView numberOfItemsInGroup:thread.uniqueId]; + if (index >= count) { + OWSFail(@"%@ focus message has invalid index.", self.logTag); + return nil; + } + NSUInteger position = (count - index) - 1; + return @(position); } + (BOOL)shouldShowGroupProfileBannerInThread:(TSThread *)thread blockingManager:(OWSBlockingManager *)blockingManager From 8164b893adec418bbc0430993a969fb444231003 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 12 Jun 2018 12:55:20 -0400 Subject: [PATCH 4/4] Respond to CR. --- SignalMessaging/utils/ThreadUtil.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 32833f0ad..dde4de03b 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -29,6 +29,9 @@ NS_ASSUME_NONNULL_BEGIN // The "reverse index" is the distance of this interaction from // the last interaction in the thread. Therefore the last interaction // will have a "reverse index" of zero. +// +// We use "reverse indices" because (among other uses) we use this to +// determine the initial load window size. @property (nonatomic, nullable, readonly) NSNumber *focusMessagePosition; // If there are unseen messages in the thread, this is the timestamp