Fix the crash once and for all by using Introspect

This commit is contained in:
Thomas Ricouard 2023-02-03 16:24:09 +01:00
parent 2a1d1fc697
commit 4104fdf4f5
4 changed files with 26 additions and 13 deletions

View file

@ -63,6 +63,15 @@
"version" : "2.5.3" "version" : "2.5.3"
} }
}, },
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
"version" : "0.1.4"
}
},
{ {
"identity" : "swiftui-shimmer", "identity" : "swiftui-shimmer",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",

View file

@ -21,6 +21,7 @@ let package = Package(
.package(name: "Env", path: "../Env"), .package(name: "Env", path: "../Env"),
.package(name: "Status", path: "../Status"), .package(name: "Status", path: "../Status"),
.package(name: "DesignSystem", path: "../DesignSystem"), .package(name: "DesignSystem", path: "../DesignSystem"),
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.1.4"),
], ],
targets: [ targets: [
.target( .target(
@ -31,6 +32,7 @@ let package = Package(
.product(name: "Env", package: "Env"), .product(name: "Env", package: "Env"),
.product(name: "Status", package: "Status"), .product(name: "Status", package: "Status"),
.product(name: "DesignSystem", package: "DesignSystem"), .product(name: "DesignSystem", package: "DesignSystem"),
.product(name: "Introspect", package: "SwiftUI-Introspect"),
] ]
), ),
.testTarget( .testTarget(

View file

@ -5,6 +5,7 @@ import Network
import Shimmer import Shimmer
import Status import Status
import SwiftUI import SwiftUI
import Introspect
public struct TimelineView: View { public struct TimelineView: View {
private enum Constants { private enum Constants {
@ -21,7 +22,7 @@ public struct TimelineView: View {
@StateObject private var viewModel = TimelineViewModel() @StateObject private var viewModel = TimelineViewModel()
@State private var wasBackgrounded: Bool = false @State private var wasBackgrounded: Bool = false
@State private var listOpacity: CGFloat = 1 @State private var collectionView: UICollectionView?
@Binding var timeline: TimelineFilter @Binding var timeline: TimelineFilter
@Binding var scrollToTopSignal: Int @Binding var scrollToTopSignal: Int
@ -47,24 +48,25 @@ public struct TimelineView: View {
StatusesListView(fetcher: viewModel) StatusesListView(fetcher: viewModel)
} }
} }
.id(client.id + (viewModel.scrollToStatus ?? "")) .id(client.id)
.opacity(listOpacity)
.environment(\.defaultMinListRowHeight, 1) .environment(\.defaultMinListRowHeight, 1)
.listStyle(.plain) .listStyle(.plain)
.scrollContentBackground(.hidden) .scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor) .background(theme.primaryBackgroundColor)
.introspect(selector: TargetViewSelector.ancestorOrSiblingContaining,
customize: { (collectionView: UICollectionView) in
self.collectionView = collectionView
})
if viewModel.pendingStatusesEnabled { if viewModel.pendingStatusesEnabled {
PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver) PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver)
} }
} }
.onChange(of: viewModel.scrollToStatus) { statusId in .onChange(of: viewModel.scrollToIndex) { index in
if let statusId { if let index {
viewModel.scrollToStatus = nil viewModel.scrollToIndex = nil
listOpacity = 0 collectionView?.scrollToItem(at: .init(row: index, section: 0),
DispatchQueue.main.async { at: .top,
proxy.scrollTo(statusId, anchor: .top) animated: false)
listOpacity = 1
}
} }
} }
.onChange(of: scrollToTopSignal, perform: { _ in .onChange(of: scrollToTopSignal, perform: { _ in

View file

@ -35,7 +35,7 @@ class TimelineViewModel: ObservableObject {
private let cache: TimelineCache = .shared private let cache: TimelineCache = .shared
@Published var scrollToStatus: String? @Published var scrollToIndex: Int?
@Published var statusesState: StatusesState = .loading @Published var statusesState: StatusesState = .loading
@Published var timeline: TimelineFilter = .federated { @Published var timeline: TimelineFilter = .federated {
@ -222,8 +222,8 @@ extension TimelineViewModel: StatusesFetcher {
// We need to update the statuses state, and then scroll to the previous top most status. // We need to update the statuses state, and then scroll to the previous top most status.
if let topStatusId, visibileStatusesIds.contains(topStatusId), scrollToTopVisible { if let topStatusId, visibileStatusesIds.contains(topStatusId), scrollToTopVisible {
pendingStatusesObserver.disableUpdate = true pendingStatusesObserver.disableUpdate = true
scrollToStatus = topStatusId
statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage) statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
scrollToIndex = newStatuses.count + 1
DispatchQueue.main.async { DispatchQueue.main.async {
self.pendingStatusesObserver.disableUpdate = false self.pendingStatusesObserver.disableUpdate = false
self.canStreamEvents = true self.canStreamEvents = true