From f761ffa7e5f93c42e23f4b7fad9bbf2f68bf8361 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 15 Jul 2021 09:50:34 -0300 Subject: [PATCH] Convert ConversationListView to HomeView. --- ...nversationListViewController+Actions.swift | 38 +++++++++ ...nversationListViewController+Helpers.swift | 3 + .../HomeView/ConversationListViewController.m | 54 +------------ .../HomeView/HVRenderState.swift | 77 ++++++++++++++++++- .../HomeView/HVViewState.swift | 4 +- 5 files changed, 119 insertions(+), 57 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/ConversationListViewController+Actions.swift b/Signal/src/ViewControllers/HomeView/ConversationListViewController+Actions.swift index b1ebcd3e3f..e3e2e07094 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationListViewController+Actions.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationListViewController+Actions.swift @@ -125,4 +125,42 @@ extension ConversationListViewController { owsFailDebug("Error: \(error)") } } + + func selectPreviousConversation() { + AssertIsOnMainThread() + + Logger.info("") + + // If we have presented a conversation list (the archive) navigate through that instead. + if let presentedConversationListViewController = self.presentedConversationListViewController { + presentedConversationListViewController.selectPreviousConversation() + return + } + + let currentThread = self.conversationSplitViewController?.selectedThread + if let previousIndexPath = renderState.indexPath(beforeThread: currentThread), + let thread = self.thread(forIndexPath: previousIndexPath) { + self.present(thread, action: .compose, animated: true) + tableView.selectRow(at: previousIndexPath, animated: true, scrollPosition: .none) + } + } + + func selectNextConversation() { + AssertIsOnMainThread() + + Logger.info("") + + // If we have presented a conversation list (the archive) navigate through that instead. + if let presentedConversationListViewController = self.presentedConversationListViewController { + presentedConversationListViewController.selectNextConversation() + return + } + + let currentThread = self.conversationSplitViewController?.selectedThread + if let nextIndexPath = renderState.indexPath(afterThread: currentThread), + let thread = self.thread(forIndexPath: nextIndexPath) { + self.present(thread, action: .compose, animated: true) + tableView.selectRow(at: nextIndexPath, animated: true, scrollPosition: .none) + } + } } diff --git a/Signal/src/ViewControllers/HomeView/ConversationListViewController+Helpers.swift b/Signal/src/ViewControllers/HomeView/ConversationListViewController+Helpers.swift index 1a8ffe450c..88762f383a 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationListViewController+Helpers.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationListViewController+Helpers.swift @@ -22,6 +22,9 @@ public extension ConversationListViewController { renderState.threadViewModel(forIndexPath: indexPath) } + var numberOfInboxThreads: UInt { renderState.inboxCount } + var numberOfArchivedThreads: UInt { renderState.archiveCount } + // MARK: - Accessors with side effects var hasVisibleReminders: Bool { diff --git a/Signal/src/ViewControllers/HomeView/ConversationListViewController.m b/Signal/src/ViewControllers/HomeView/ConversationListViewController.m index ac65c77395..ada0c03c95 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationListViewController.m +++ b/Signal/src/ViewControllers/HomeView/ConversationListViewController.m @@ -729,48 +729,6 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup"; [self.searchBar becomeFirstResponder]; } -- (void)selectPreviousConversation -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - // If we have presented a conversation list (the archive) navigate through that instead. - if (self.presentedConversationListViewController) { - [self.presentedConversationListViewController selectPreviousConversation]; - return; - } - - TSThread *_Nullable currentThread = self.conversationSplitViewController.selectedThread; - NSIndexPath *_Nullable previousIndexPath = [self.threadMapping indexPathBeforeThread:currentThread]; - if (previousIndexPath) { - [self presentThread:[self threadForIndexPath:previousIndexPath] action:ConversationViewActionCompose animated:YES]; - [self.tableView selectRowAtIndexPath:previousIndexPath - animated:YES - scrollPosition:UITableViewScrollPositionNone]; - } -} - -- (void)selectNextConversation -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - // If we have presented a conversation list (the archive) navigate through that instead. - if (self.presentedConversationListViewController) { - [self.presentedConversationListViewController selectNextConversation]; - return; - } - - TSThread *_Nullable currentThread = self.conversationSplitViewController.selectedThread; - NSIndexPath *_Nullable nextIndexPath = [self.threadMapping indexPathAfterThread:currentThread]; - if (nextIndexPath) { - [self presentThread:[self threadForIndexPath:nextIndexPath] action:ConversationViewActionCompose animated:YES]; - [self.tableView selectRowAtIndexPath:nextIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; - } -} - - (void)archiveSelectedConversation { OWSAssertIsOnMainThread(); @@ -873,7 +831,7 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup"; // visible. The threads often change ordering while in conversation view due // to incoming & outgoing messages. NSIndexPath *_Nullable indexPathOfLastThread = - [self.threadMapping indexPathForUniqueId:self.lastViewedThread.uniqueId]; + [self.renderState indexPathForUniqueId:self.lastViewedThread.uniqueId]; if (indexPathOfLastThread) { [self.tableView scrollToRowAtIndexPath:indexPathOfLastThread atScrollPosition:UITableViewScrollPositionNone @@ -1284,16 +1242,6 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup"; #pragma mark - -- (NSUInteger)numberOfInboxThreads -{ - return self.threadMapping.inboxCount; -} - -- (NSUInteger)numberOfArchivedThreads -{ - return self.threadMapping.archiveCount; -} - - (void)updateViewState { if (self.shouldShowEmptyInboxView) { diff --git a/Signal/src/ViewControllers/HomeView/HVRenderState.swift b/Signal/src/ViewControllers/HomeView/HVRenderState.swift index d9b453c6b1..3e27b5573f 100644 --- a/Signal/src/ViewControllers/HomeView/HVRenderState.swift +++ b/Signal/src/ViewControllers/HomeView/HVRenderState.swift @@ -11,18 +11,27 @@ public class HVRenderState: NSObject { let pinnedThreads: OrderedDictionary let unpinnedThreads: [TSThread] + let archiveCount: UInt + let inboxCount: UInt + // We use a new cache for each render state. private let threadViewModelCache = LRUCache(maxSize: 32) public init(pinnedThreads: OrderedDictionary, - unpinnedThreads: [TSThread]) { + unpinnedThreads: [TSThread], + archiveCount: UInt, + inboxCount: UInt) { self.pinnedThreads = pinnedThreads self.unpinnedThreads = unpinnedThreads + self.archiveCount = archiveCount + self.inboxCount = inboxCount } public static var empty: HVRenderState { HVRenderState(pinnedThreads: OrderedDictionary(), - unpinnedThreads: []) + unpinnedThreads: [], + archiveCount: 0, + inboxCount: 0) } public var hasPinnedAndUnpinnedThreads: Bool { @@ -84,4 +93,68 @@ public class HVRenderState: NSObject { return nil } } + + func indexPath(afterThread thread: TSThread?) -> IndexPath? { + let isPinnedThread: Bool + if let thread = thread, pinnedThreads.orderedKeys.contains(thread.uniqueId) { + isPinnedThread = true + } else { + isPinnedThread = false + } + + let section: HomeViewSection = isPinnedThread ? .pinned : .unpinned + let threadsInSection = isPinnedThread ? pinnedThreads.orderedValues : unpinnedThreads + + guard !threadsInSection.isEmpty else { return nil } + + let firstIndexPath = IndexPath(item: 0, section: section.rawValue) + + guard let thread = thread else { return firstIndexPath } + guard let index = threadsInSection.firstIndex(where: { $0.uniqueId == thread.uniqueId}) else { + return firstIndexPath + } + + if index < (threadsInSection.count - 1) { + return IndexPath(item: index + 1, section: section.rawValue) + } else { + return nil + } + } + + func indexPath(beforeThread thread: TSThread?) -> IndexPath? { + let isPinnedThread: Bool + if let thread = thread, pinnedThreads.orderedKeys.contains(thread.uniqueId) { + isPinnedThread = true + } else { + isPinnedThread = false + } + + let section: HomeViewSection = isPinnedThread ? .pinned : .unpinned + let threadsInSection = isPinnedThread ? pinnedThreads.orderedValues : unpinnedThreads + + guard !threadsInSection.isEmpty else { return nil } + + let lastIndexPath = IndexPath(item: threadsInSection.count - 1, section: section.rawValue) + + guard let thread = thread else { return lastIndexPath } + guard let index = threadsInSection.firstIndex(where: { $0.uniqueId == thread.uniqueId}) else { + return lastIndexPath + } + + if index > 0 { + return IndexPath(item: index - 1, section: section.rawValue) + } else { + return nil + } + } + + func indexPath(forUniqueId uniqueId: String) -> IndexPath? { + if let index = (unpinnedThreads.firstIndex { $0.uniqueId == uniqueId}) { + return IndexPath(item: index, section: HomeViewSection.unpinned.rawValue) + } else if let index = (pinnedThreads.orderedKeys.firstIndex { $0 == uniqueId}) { + return IndexPath(item: index, section: HomeViewSection.pinned.rawValue) + } else { + return nil + } + } } diff --git a/Signal/src/ViewControllers/HomeView/HVViewState.swift b/Signal/src/ViewControllers/HomeView/HVViewState.swift index a11a27657a..97cff30b26 100644 --- a/Signal/src/ViewControllers/HomeView/HVViewState.swift +++ b/Signal/src/ViewControllers/HomeView/HVViewState.swift @@ -7,7 +7,7 @@ import Foundation @objc public class HVViewState: NSObject { - public let threadMapping = ThreadMapping() + public let threadMappingOld = ThreadMapping() public let tableDataSource = HVTableDataSource() // TODO: Rework OWSBlockListCache. @@ -51,7 +51,7 @@ public class HVViewState: NSObject { @objc public extension ConversationListViewController { - var threadMapping: ThreadMapping { viewState.threadMapping } + var threadMappingOld: ThreadMapping { viewState.threadMappingOld } var tableDataSource: HVTableDataSource { viewState.tableDataSource } var blocklistCache: BlockListCache { viewState.blocklistCache }