mirror of
https://github.com/metabolist/metatext.git
synced 2025-01-08 21:15:24 +00:00
Fix inset adjustment
This commit is contained in:
parent
73fc2cd599
commit
d42642c079
5 changed files with 33 additions and 37 deletions
|
@ -291,14 +291,11 @@ private extension TableViewController {
|
||||||
if
|
if
|
||||||
let itemId = update.maintainScrollPositionItemId,
|
let itemId = update.maintainScrollPositionItemId,
|
||||||
let indexPath = self.dataSource.indexPath(itemId: itemId) {
|
let indexPath = self.dataSource.indexPath(itemId: itemId) {
|
||||||
if self.viewModel.shouldAdjustContentInset {
|
if update.shouldAdjustContentInset {
|
||||||
self.tableView.contentInset.bottom = max(
|
self.tableView.contentInset.bottom = max(
|
||||||
Self.bottomInset,
|
self.tableView.safeAreaLayoutGuide.layoutFrame.height
|
||||||
self.tableView.frame.height
|
- self.tableView.rectForRow(at: indexPath).height,
|
||||||
- self.tableView.contentSize.height
|
Self.bottomInset)
|
||||||
- self.tableView.safeAreaInsets.top
|
|
||||||
- self.tableView.safeAreaInsets.bottom)
|
|
||||||
+ self.tableView.rectForRow(at: indexPath).minY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
|
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
|
||||||
|
|
|
@ -9,14 +9,16 @@ public final class CollectionItemsViewModel: ObservableObject {
|
||||||
@Published public var alertItem: AlertItem?
|
@Published public var alertItem: AlertItem?
|
||||||
public private(set) var nextPageMaxId: String?
|
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 collectionService: CollectionService
|
||||||
private let identification: Identification
|
private let identification: Identification
|
||||||
private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]()
|
private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]()
|
||||||
private let eventsSubject = PassthroughSubject<CollectionItemEvent, Never>()
|
private let eventsSubject = PassthroughSubject<CollectionItemEvent, Never>()
|
||||||
private let loadingSubject = PassthroughSubject<Bool, Never>()
|
private let loadingSubject = PassthroughSubject<Bool, Never>()
|
||||||
private let expandAllSubject: CurrentValueSubject<ExpandAllState, Never>
|
private let expandAllSubject: CurrentValueSubject<ExpandAllState, Never>
|
||||||
private var maintainScrollPositionItemId: CollectionItem.Id?
|
|
||||||
private var topVisibleIndexPath = IndexPath(item: 0, section: 0)
|
private var topVisibleIndexPath = IndexPath(item: 0, section: 0)
|
||||||
private let lastReadId = CurrentValueSubject<String?, Never>(nil)
|
private let lastReadId = CurrentValueSubject<String?, Never>(nil)
|
||||||
private var lastSelectedLoadMore: LoadMore?
|
private var lastSelectedLoadMore: LoadMore?
|
||||||
|
@ -57,11 +59,7 @@ public final class CollectionItemsViewModel: ObservableObject {
|
||||||
|
|
||||||
extension CollectionItemsViewModel: CollectionViewModel {
|
extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
public var updates: AnyPublisher<CollectionUpdate, Never> {
|
public var updates: AnyPublisher<CollectionUpdate, Never> {
|
||||||
items.map { [weak self] in
|
$lastUpdate.eraseToAnyPublisher()
|
||||||
CollectionUpdate(items: $0,
|
|
||||||
maintainScrollPositionItemId: self?.maintainScrollPositionItemId)
|
|
||||||
}
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var title: AnyPublisher<String, Never> { collectionService.title }
|
public var title: AnyPublisher<String, Never> { collectionService.title }
|
||||||
|
@ -80,8 +78,6 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
|
|
||||||
public var events: AnyPublisher<CollectionItemEvent, Never> { eventsSubject.eraseToAnyPublisher() }
|
public var events: AnyPublisher<CollectionItemEvent, Never> { eventsSubject.eraseToAnyPublisher() }
|
||||||
|
|
||||||
public var shouldAdjustContentInset: Bool { collectionService is ContextService }
|
|
||||||
|
|
||||||
public var preferLastPresentIdOverNextPageMaxId: Bool { collectionService.preferLastPresentIdOverNextPageMaxId }
|
public var preferLastPresentIdOverNextPageMaxId: Bool { collectionService.preferLastPresentIdOverNextPageMaxId }
|
||||||
|
|
||||||
public var canRefresh: Bool { collectionService.canRefresh }
|
public var canRefresh: Bool { collectionService.canRefresh }
|
||||||
|
@ -120,7 +116,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func select(indexPath: IndexPath) {
|
public func select(indexPath: IndexPath) {
|
||||||
let item = items.value[indexPath.section][indexPath.item]
|
let item = lastUpdate.items[indexPath.section][indexPath.item]
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
case let .status(status, _):
|
case let .status(status, _):
|
||||||
|
@ -162,14 +158,14 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
topVisibleIndexPath = indexPath
|
topVisibleIndexPath = indexPath
|
||||||
|
|
||||||
if !shouldRestorePositionOfLocalLastReadId,
|
if !shouldRestorePositionOfLocalLastReadId,
|
||||||
items.value.count > indexPath.section,
|
lastUpdate.items.count > indexPath.section,
|
||||||
items.value[indexPath.section].count > indexPath.item {
|
lastUpdate.items[indexPath.section].count > indexPath.item {
|
||||||
lastReadId.send(items.value[indexPath.section][indexPath.item].itemId)
|
lastReadId.send(lastUpdate.items[indexPath.section][indexPath.item].itemId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func canSelect(indexPath: IndexPath) -> Bool {
|
public func canSelect(indexPath: IndexPath) -> Bool {
|
||||||
switch items.value[indexPath.section][indexPath.item] {
|
switch lastUpdate.items[indexPath.section][indexPath.item] {
|
||||||
case let .status(_, configuration):
|
case let .status(_, configuration):
|
||||||
return !configuration.isContextParent
|
return !configuration.isContextParent
|
||||||
case .loadMore:
|
case .loadMore:
|
||||||
|
@ -181,7 +177,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
|
|
||||||
// swiftlint:disable:next function_body_length cyclomatic_complexity
|
// swiftlint:disable:next function_body_length cyclomatic_complexity
|
||||||
public func viewModel(indexPath: IndexPath) -> CollectionItemViewModel {
|
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
|
let cachedViewModel = viewModelCache[item]?.viewModel
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
|
@ -261,7 +257,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toggleExpandAll() {
|
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 }
|
guard case let .status(status, _) = item else { return nil }
|
||||||
|
|
||||||
return status.id
|
return status.id
|
||||||
|
@ -287,6 +283,10 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension CollectionItemsViewModel {
|
private extension CollectionItemsViewModel {
|
||||||
|
var lastUpdateWasContextParentOnly: Bool {
|
||||||
|
collectionService is ContextService && lastUpdate.items.map(\.count) == [0, 1, 0]
|
||||||
|
}
|
||||||
|
|
||||||
func cache(viewModel: CollectionItemViewModel, forItem item: CollectionItem) {
|
func cache(viewModel: CollectionItemViewModel, forItem item: CollectionItem) {
|
||||||
viewModelCache[item] = (viewModel, viewModel.events
|
viewModelCache[item] = (viewModel, viewModel.events
|
||||||
.flatMap { [weak self] events -> AnyPublisher<CollectionItemEvent, Never> in
|
.flatMap { [weak self] events -> AnyPublisher<CollectionItemEvent, Never> in
|
||||||
|
@ -299,10 +299,13 @@ private extension CollectionItemsViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func process(items: [[CollectionItem]]) {
|
func process(items: [[CollectionItem]]) {
|
||||||
maintainScrollPositionItemId = idForScrollPositionMaintenance(newItems: items)
|
let flatItems = items.reduce([], +)
|
||||||
self.items.send(items)
|
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) }
|
viewModelCache = viewModelCache.filter { itemsSet.contains($0.key) }
|
||||||
}
|
}
|
||||||
|
@ -312,14 +315,14 @@ private extension CollectionItemsViewModel {
|
||||||
|
|
||||||
guard let markerTimeline = collectionService.markerTimeline,
|
guard let markerTimeline = collectionService.markerTimeline,
|
||||||
identification.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition,
|
identification.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition,
|
||||||
let lastItemId = items.value.last?.last?.itemId
|
let lastItemId = lastUpdate.items.last?.last?.itemId
|
||||||
else { return maxId }
|
else { return maxId }
|
||||||
|
|
||||||
return min(maxId, lastItemId)
|
return min(maxId, lastItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func idForScrollPositionMaintenance(newItems: [[CollectionItem]]) -> CollectionItem.Id? {
|
func idForScrollPositionMaintenance(newItems: [[CollectionItem]]) -> CollectionItem.Id? {
|
||||||
let flatItems = items.value.reduce([], +)
|
let flatItems = lastUpdate.items.reduce([], +)
|
||||||
let flatNewItems = newItems.reduce([], +)
|
let flatNewItems = newItems.reduce([], +)
|
||||||
|
|
||||||
if shouldRestorePositionOfLocalLastReadId,
|
if shouldRestorePositionOfLocalLastReadId,
|
||||||
|
@ -332,7 +335,7 @@ private extension CollectionItemsViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
if collectionService is ContextService,
|
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: {
|
let contextParent = flatNewItems.first(where: {
|
||||||
guard case let .status(_, configuration) = $0 else { return false }
|
guard case let .status(_, configuration) = $0 else { return false }
|
||||||
|
|
||||||
|
@ -359,9 +362,9 @@ private extension CollectionItemsViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if items.value.count > topVisibleIndexPath.section,
|
if lastUpdate.items.count > topVisibleIndexPath.section,
|
||||||
items.value[topVisibleIndexPath.section].count > topVisibleIndexPath.item {
|
lastUpdate.items[topVisibleIndexPath.section].count > topVisibleIndexPath.item {
|
||||||
let topVisibleItem = items.value[topVisibleIndexPath.section][topVisibleIndexPath.item]
|
let topVisibleItem = lastUpdate.items[topVisibleIndexPath.section][topVisibleIndexPath.item]
|
||||||
|
|
||||||
if newItems.count > topVisibleIndexPath.section,
|
if newItems.count > topVisibleIndexPath.section,
|
||||||
let newIndex = newItems[topVisibleIndexPath.section]
|
let newIndex = newItems[topVisibleIndexPath.section]
|
||||||
|
|
|
@ -11,7 +11,6 @@ public protocol CollectionViewModel {
|
||||||
var alertItems: AnyPublisher<AlertItem, Never> { get }
|
var alertItems: AnyPublisher<AlertItem, Never> { get }
|
||||||
var loading: AnyPublisher<Bool, Never> { get }
|
var loading: AnyPublisher<Bool, Never> { get }
|
||||||
var events: AnyPublisher<CollectionItemEvent, Never> { get }
|
var events: AnyPublisher<CollectionItemEvent, Never> { get }
|
||||||
var shouldAdjustContentInset: Bool { get }
|
|
||||||
var nextPageMaxId: String? { get }
|
var nextPageMaxId: String? { get }
|
||||||
var preferLastPresentIdOverNextPageMaxId: Bool { get }
|
var preferLastPresentIdOverNextPageMaxId: Bool { get }
|
||||||
var canRefresh: Bool { get }
|
var canRefresh: Bool { get }
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
public struct CollectionUpdate: Hashable {
|
public struct CollectionUpdate: Hashable {
|
||||||
public let items: [[CollectionItem]]
|
public let items: [[CollectionItem]]
|
||||||
public let maintainScrollPositionItemId: String?
|
public let maintainScrollPositionItemId: String?
|
||||||
|
public let shouldAdjustContentInset: Bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,10 +95,6 @@ extension ProfileViewModel: CollectionViewModel {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var shouldAdjustContentInset: Bool {
|
|
||||||
collectionViewModel.value.shouldAdjustContentInset
|
|
||||||
}
|
|
||||||
|
|
||||||
public var nextPageMaxId: String? {
|
public var nextPageMaxId: String? {
|
||||||
collectionViewModel.value.nextPageMaxId
|
collectionViewModel.value.nextPageMaxId
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue