From d42642c079892aaf27f5cc9c31e716c4daf1ee8f Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Sat, 16 Jan 2021 18:16:43 -0800 Subject: [PATCH] Fix inset adjustment --- View Controllers/TableViewController.swift | 11 ++-- .../ViewModels/CollectionItemsViewModel.swift | 53 ++++++++++--------- .../ViewModels/CollectionViewModel.swift | 1 - .../Entities/CollectionUpdate.swift | 1 + .../Sources/ViewModels/ProfileViewModel.swift | 4 -- 5 files changed, 33 insertions(+), 37 deletions(-) diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index 2b74230..0df9acd 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -291,14 +291,11 @@ private extension TableViewController { if let itemId = update.maintainScrollPositionItemId, let indexPath = self.dataSource.indexPath(itemId: itemId) { - if self.viewModel.shouldAdjustContentInset { + if update.shouldAdjustContentInset { self.tableView.contentInset.bottom = max( - Self.bottomInset, - self.tableView.frame.height - - self.tableView.contentSize.height - - self.tableView.safeAreaInsets.top - - self.tableView.safeAreaInsets.bottom) - + self.tableView.rectForRow(at: indexPath).minY + self.tableView.safeAreaLayoutGuide.layoutFrame.height + - self.tableView.rectForRow(at: indexPath).height, + Self.bottomInset) } self.tableView.scrollToRow(at: indexPath, at: .top, animated: false) diff --git a/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift b/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift index 0010348..0efe066 100644 --- a/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift +++ b/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift @@ -9,14 +9,16 @@ public final class CollectionItemsViewModel: ObservableObject { @Published public var alertItem: AlertItem? public private(set) var nextPageMaxId: String? - private let items = CurrentValueSubject<[[CollectionItem]], Never>([]) + @Published private var lastUpdate = CollectionUpdate( + items: [], + maintainScrollPositionItemId: nil, + shouldAdjustContentInset: false) private let collectionService: CollectionService private let identification: Identification private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]() private let eventsSubject = PassthroughSubject() private let loadingSubject = PassthroughSubject() private let expandAllSubject: CurrentValueSubject - private var maintainScrollPositionItemId: CollectionItem.Id? private var topVisibleIndexPath = IndexPath(item: 0, section: 0) private let lastReadId = CurrentValueSubject(nil) private var lastSelectedLoadMore: LoadMore? @@ -57,11 +59,7 @@ public final class CollectionItemsViewModel: ObservableObject { extension CollectionItemsViewModel: CollectionViewModel { public var updates: AnyPublisher { - items.map { [weak self] in - CollectionUpdate(items: $0, - maintainScrollPositionItemId: self?.maintainScrollPositionItemId) - } - .eraseToAnyPublisher() + $lastUpdate.eraseToAnyPublisher() } public var title: AnyPublisher { collectionService.title } @@ -80,8 +78,6 @@ extension CollectionItemsViewModel: CollectionViewModel { public var events: AnyPublisher { eventsSubject.eraseToAnyPublisher() } - public var shouldAdjustContentInset: Bool { collectionService is ContextService } - public var preferLastPresentIdOverNextPageMaxId: Bool { collectionService.preferLastPresentIdOverNextPageMaxId } public var canRefresh: Bool { collectionService.canRefresh } @@ -120,7 +116,7 @@ extension CollectionItemsViewModel: CollectionViewModel { } public func select(indexPath: IndexPath) { - let item = items.value[indexPath.section][indexPath.item] + let item = lastUpdate.items[indexPath.section][indexPath.item] switch item { case let .status(status, _): @@ -162,14 +158,14 @@ extension CollectionItemsViewModel: CollectionViewModel { topVisibleIndexPath = indexPath if !shouldRestorePositionOfLocalLastReadId, - items.value.count > indexPath.section, - items.value[indexPath.section].count > indexPath.item { - lastReadId.send(items.value[indexPath.section][indexPath.item].itemId) + lastUpdate.items.count > indexPath.section, + lastUpdate.items[indexPath.section].count > indexPath.item { + lastReadId.send(lastUpdate.items[indexPath.section][indexPath.item].itemId) } } public func canSelect(indexPath: IndexPath) -> Bool { - switch items.value[indexPath.section][indexPath.item] { + switch lastUpdate.items[indexPath.section][indexPath.item] { case let .status(_, configuration): return !configuration.isContextParent case .loadMore: @@ -181,7 +177,7 @@ extension CollectionItemsViewModel: CollectionViewModel { // swiftlint:disable:next function_body_length cyclomatic_complexity public func viewModel(indexPath: IndexPath) -> CollectionItemViewModel { - let item = items.value[indexPath.section][indexPath.item] + let item = lastUpdate.items[indexPath.section][indexPath.item] let cachedViewModel = viewModelCache[item]?.viewModel switch item { @@ -261,7 +257,7 @@ extension CollectionItemsViewModel: CollectionViewModel { } public func toggleExpandAll() { - let statusIds = Set(items.value.reduce([], +).compactMap { item -> Status.Id? in + let statusIds = Set(lastUpdate.items.reduce([], +).compactMap { item -> Status.Id? in guard case let .status(status, _) = item else { return nil } return status.id @@ -287,6 +283,10 @@ extension CollectionItemsViewModel: CollectionViewModel { } private extension CollectionItemsViewModel { + var lastUpdateWasContextParentOnly: Bool { + collectionService is ContextService && lastUpdate.items.map(\.count) == [0, 1, 0] + } + func cache(viewModel: CollectionItemViewModel, forItem item: CollectionItem) { viewModelCache[item] = (viewModel, viewModel.events .flatMap { [weak self] events -> AnyPublisher in @@ -299,10 +299,13 @@ private extension CollectionItemsViewModel { } func process(items: [[CollectionItem]]) { - maintainScrollPositionItemId = idForScrollPositionMaintenance(newItems: items) - self.items.send(items) + let flatItems = items.reduce([], +) + let itemsSet = Set(flatItems) - let itemsSet = Set(items.reduce([], +)) + self.lastUpdate = .init( + items: items, + maintainScrollPositionItemId: idForScrollPositionMaintenance(newItems: items), + shouldAdjustContentInset: lastUpdateWasContextParentOnly && flatItems.count > 1) viewModelCache = viewModelCache.filter { itemsSet.contains($0.key) } } @@ -312,14 +315,14 @@ private extension CollectionItemsViewModel { guard let markerTimeline = collectionService.markerTimeline, identification.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition, - let lastItemId = items.value.last?.last?.itemId + let lastItemId = lastUpdate.items.last?.last?.itemId else { return maxId } return min(maxId, lastItemId) } func idForScrollPositionMaintenance(newItems: [[CollectionItem]]) -> CollectionItem.Id? { - let flatItems = items.value.reduce([], +) + let flatItems = lastUpdate.items.reduce([], +) let flatNewItems = newItems.reduce([], +) if shouldRestorePositionOfLocalLastReadId, @@ -332,7 +335,7 @@ private extension CollectionItemsViewModel { } if collectionService is ContextService, - items.value.isEmpty || items.value.map(\.count) == [0, 1, 0], + lastUpdate.items.isEmpty || lastUpdate.items.map(\.count) == [0, 1, 0], let contextParent = flatNewItems.first(where: { guard case let .status(_, configuration) = $0 else { return false } @@ -359,9 +362,9 @@ private extension CollectionItemsViewModel { } } - if items.value.count > topVisibleIndexPath.section, - items.value[topVisibleIndexPath.section].count > topVisibleIndexPath.item { - let topVisibleItem = items.value[topVisibleIndexPath.section][topVisibleIndexPath.item] + if lastUpdate.items.count > topVisibleIndexPath.section, + lastUpdate.items[topVisibleIndexPath.section].count > topVisibleIndexPath.item { + let topVisibleItem = lastUpdate.items[topVisibleIndexPath.section][topVisibleIndexPath.item] if newItems.count > topVisibleIndexPath.section, let newIndex = newItems[topVisibleIndexPath.section] diff --git a/ViewModels/Sources/ViewModels/CollectionViewModel.swift b/ViewModels/Sources/ViewModels/CollectionViewModel.swift index 778a7c3..6f321d6 100644 --- a/ViewModels/Sources/ViewModels/CollectionViewModel.swift +++ b/ViewModels/Sources/ViewModels/CollectionViewModel.swift @@ -11,7 +11,6 @@ public protocol CollectionViewModel { var alertItems: AnyPublisher { get } var loading: AnyPublisher { get } var events: AnyPublisher { get } - var shouldAdjustContentInset: Bool { get } var nextPageMaxId: String? { get } var preferLastPresentIdOverNextPageMaxId: Bool { get } var canRefresh: Bool { get } diff --git a/ViewModels/Sources/ViewModels/Entities/CollectionUpdate.swift b/ViewModels/Sources/ViewModels/Entities/CollectionUpdate.swift index 15b9260..36e4999 100644 --- a/ViewModels/Sources/ViewModels/Entities/CollectionUpdate.swift +++ b/ViewModels/Sources/ViewModels/Entities/CollectionUpdate.swift @@ -3,4 +3,5 @@ public struct CollectionUpdate: Hashable { public let items: [[CollectionItem]] public let maintainScrollPositionItemId: String? + public let shouldAdjustContentInset: Bool } diff --git a/ViewModels/Sources/ViewModels/ProfileViewModel.swift b/ViewModels/Sources/ViewModels/ProfileViewModel.swift index 6828c6a..4a963ac 100644 --- a/ViewModels/Sources/ViewModels/ProfileViewModel.swift +++ b/ViewModels/Sources/ViewModels/ProfileViewModel.swift @@ -95,10 +95,6 @@ extension ProfileViewModel: CollectionViewModel { .eraseToAnyPublisher() } - public var shouldAdjustContentInset: Bool { - collectionViewModel.value.shouldAdjustContentInset - } - public var nextPageMaxId: String? { collectionViewModel.value.nextPageMaxId }