Fix older notifications not loading when initially visible set fits on one screen
Closes #346
This commit is contained in:
parent
53d43b5707
commit
c4bf5d406d
|
@ -539,6 +539,10 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
||||||
let itemsInSection = collectionView.numberOfItems(inSection: indexPath.section)
|
let itemsInSection = collectionView.numberOfItems(inSection: indexPath.section)
|
||||||
if indexPath.row == itemsInSection - 1 {
|
if indexPath.row == itemsInSection - 1 {
|
||||||
Task {
|
Task {
|
||||||
|
// Because of grouping, all cells from the first load may fit on screen,
|
||||||
|
// in which case, we try to load older while still in the loadingInitial state.
|
||||||
|
// So, wait for that to finish before trying to load more.
|
||||||
|
await controller.finishPendingOperation()
|
||||||
await controller.loadOlder()
|
await controller.loadOlder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import Combine
|
||||||
|
|
||||||
protocol TimelineLikeControllerDelegate<TimelineItem>: AnyObject {
|
protocol TimelineLikeControllerDelegate<TimelineItem>: AnyObject {
|
||||||
associatedtype TimelineItem: Sendable
|
associatedtype TimelineItem: Sendable
|
||||||
|
@ -42,7 +43,7 @@ class TimelineLikeController<Item: Sendable> {
|
||||||
private unowned var delegate: any TimelineLikeControllerDelegate<Item>
|
private unowned var delegate: any TimelineLikeControllerDelegate<Item>
|
||||||
private let ownerType: String
|
private let ownerType: String
|
||||||
|
|
||||||
private(set) var state = State.notLoadedInitial {
|
@AsyncObservable private(set) var state = State.notLoadedInitial {
|
||||||
willSet {
|
willSet {
|
||||||
guard state.canTransition(to: newValue) else {
|
guard state.canTransition(to: newValue) else {
|
||||||
logger.error("\(self.ownerType, privacy: .public) State \(self.state.debugDescription, privacy: .public) cannot transition to \(newValue.debugDescription, privacy: .public)")
|
logger.error("\(self.ownerType, privacy: .public) State \(self.state.debugDescription, privacy: .public) cannot transition to \(newValue.debugDescription, privacy: .public)")
|
||||||
|
@ -57,6 +58,19 @@ class TimelineLikeController<Item: Sendable> {
|
||||||
self.ownerType = ownerType
|
self.ownerType = ownerType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Waits for the controller to finish the current operation and arrive at the idle state.
|
||||||
|
///
|
||||||
|
/// If the current state is `notLoadedInitial`, this will wait until the controller
|
||||||
|
/// settles after the initial load.
|
||||||
|
func finishPendingOperation() async {
|
||||||
|
guard state != .idle else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for await state in $state where state == .idle {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadInitial() async {
|
func loadInitial() async {
|
||||||
guard state == .notLoadedInitial || state == .idle else {
|
guard state == .notLoadedInitial || state == .idle else {
|
||||||
return
|
return
|
||||||
|
@ -369,3 +383,17 @@ enum TimelineGapDirection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I would love to be able to do this with @Observable, but it's not clear how to do so.
|
||||||
|
@propertyWrapper
|
||||||
|
private class AsyncObservable<Value>: ObservableObject {
|
||||||
|
@Published var wrappedValue: Value
|
||||||
|
|
||||||
|
var projectedValue: AsyncPublisher<Published<Value>.Publisher> {
|
||||||
|
$wrappedValue.values
|
||||||
|
}
|
||||||
|
|
||||||
|
init(wrappedValue: Value) {
|
||||||
|
self.wrappedValue = wrappedValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue