Fixes #641 - Room previews failing to load because of incorrect sliding sync view ranges

This commit is contained in:
Stefan Ceriu
2023-04-20 18:15:06 +03:00
committed by Stefan Ceriu
parent 0ed69d5649
commit b564036b68
3 changed files with 47 additions and 46 deletions
+19 -10
View File
@@ -24,30 +24,39 @@ class ScrollViewAdapter: NSObject, UIScrollViewDelegate {
scrollView?.delegate = self scrollView?.delegate = self
} }
} }
var isScrolling = CurrentValueSubject<Bool, Never>(false) let didScroll = PassthroughSubject<Void, Never>()
let isScrolling = CurrentValueSubject<Bool, Never>(false)
private func update(_ scrollView: UIScrollView) {
isScrolling.send(scrollView.isDragging || scrollView.isDecelerating) // MARK: - UIScrollViewDelegate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
didScroll.send(())
} }
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
update(scrollView) updateDidScroll(scrollView)
} }
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
update(scrollView) updateDidScroll(scrollView)
} }
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
update(scrollView) updateDidScroll(scrollView)
} }
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
update(scrollView) updateDidScroll(scrollView)
} }
func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
update(scrollView) updateDidScroll(scrollView)
}
// MARK: - Private
private func updateDidScroll(_ scrollView: UIScrollView) {
isScrolling.send(scrollView.isDragging || scrollView.isDecelerating)
} }
} }
@@ -23,17 +23,8 @@ struct HomeScreen: View {
@ObservedObject var context: HomeScreenViewModel.Context @ObservedObject var context: HomeScreenViewModel.Context
@State private var isViewVisible = false
@State private var scrollViewAdapter = ScrollViewAdapter() @State private var scrollViewAdapter = ScrollViewAdapter()
@State private var showingLogoutConfirmation = false @State private var showingLogoutConfirmation = false
@State private var visibleItemIdentifiers = Set<String>() {
didSet {
if isViewVisible {
updateVisibleRange()
}
}
}
var body: some View { var body: some View {
ScrollView { ScrollView {
@@ -69,16 +60,6 @@ struct HomeScreen: View {
HomeScreenRoomCell(room: room, context: context) HomeScreenRoomCell(room: room, context: context)
} }
} }
.onAppear {
// Ignore while filtering rooms
guard context.searchQuery.isEmpty else { return }
visibleItemIdentifiers.insert(room.id)
}
.onDisappear {
// Ignore while filtering rooms
guard context.searchQuery.isEmpty else { return }
visibleItemIdentifiers.remove(room.id)
}
} }
} }
.searchable(text: $context.searchQuery) .searchable(text: $context.searchQuery)
@@ -86,18 +67,27 @@ struct HomeScreen: View {
.disableAutocorrection(true) .disableAutocorrection(true)
} }
} }
.onAppear {
isViewVisible = true
}
.onDisappear {
isViewVisible = false
}
.introspectScrollView { scrollView in .introspectScrollView { scrollView in
guard scrollView != scrollViewAdapter.scrollView else { return } guard scrollView != scrollViewAdapter.scrollView else { return }
scrollViewAdapter.scrollView = scrollView scrollViewAdapter.scrollView = scrollView
} }
.onReceive(scrollViewAdapter.isScrolling) { isScrolling in .onReceive(scrollViewAdapter.didScroll) { _ in
if !isScrolling { updateVisibleRange()
}
.onReceive(scrollViewAdapter.isScrolling) { _ in
updateVisibleRange()
}
.onChange(of: context.searchQuery) { searchQuery in
if searchQuery.isEmpty {
// Allow the view to update after changing the query
DispatchQueue.main.async {
updateVisibleRange()
}
}
}
.onChange(of: context.viewState.visibleRooms) { _ in
// Give the view a chance to update
DispatchQueue.main.async {
updateVisibleRange() updateVisibleRange()
} }
} }
@@ -225,21 +215,22 @@ struct HomeScreen: View {
} }
private func updateVisibleRange() { private func updateVisibleRange() {
let result = visibleItemIdentifiers.compactMap { itemIdentifier in guard let scrollView = scrollViewAdapter.scrollView,
context.viewState.rooms.firstIndex { $0.id == itemIdentifier } context.viewState.rooms.count > 0 else {
}.sorted()
guard !result.isEmpty else {
return return
} }
guard let firstIndex = result.first, let lastIndex = result.last else { let adjustedContentSize = max(scrollView.contentSize.height - scrollView.contentInset.top - scrollView.contentInset.bottom, scrollView.bounds.height)
return let cellHeight = adjustedContentSize / Double(context.viewState.rooms.count)
}
let firstIndex = Int(max(0.0, scrollView.contentOffset.y + scrollView.contentInset.top) / cellHeight)
let lastIndex = Int(max(0.0, scrollView.contentOffset.y + scrollView.bounds.height) / cellHeight)
// Add some extra padding just to be on the safe side
let lowerBound = max(0, firstIndex - Constants.slidingWindowBoundsPadding) let lowerBound = max(0, firstIndex - Constants.slidingWindowBoundsPadding)
let upperBound = min(Int(context.viewState.rooms.count), lastIndex + Constants.slidingWindowBoundsPadding) let upperBound = min(Int(context.viewState.rooms.count), lastIndex + Constants.slidingWindowBoundsPadding)
// This will be deduped and throttled on the view model layer
context.send(viewAction: .updateVisibleItemRange(range: lowerBound..<upperBound, isScrolling: scrollViewAdapter.isScrolling.value)) context.send(viewAction: .updateVisibleItemRange(range: lowerBound..<upperBound, isScrolling: scrollViewAdapter.isScrolling.value))
} }
} }
+1
View File
@@ -0,0 +1 @@
Fixed room previews failing to load because of incorrect sliding sync view ranges