mirror of
https://github.com/mastodon/mastodon-ios
synced 2025-04-11 22:58:02 +02:00
Restore ability to load additional notifications beyond the first fetched batch
This commit is contained in:
parent
f552c3af91
commit
bc918ffdfc
@ -89,7 +89,7 @@ struct NotificationListView: View {
|
||||
|
||||
@ViewBuilder func rowView(_ notificationListItem: NotificationListItem) -> some View {
|
||||
switch notificationListItem {
|
||||
case .bottomLoader, .middleLoader:
|
||||
case .bottomLoader:
|
||||
Text("loader not yet implemented")
|
||||
case .filteredNotificationsInfo:
|
||||
Text("filtered notifications not yet implemented")
|
||||
|
@ -12,19 +12,16 @@ import MastodonSDK
|
||||
enum NotificationListItem: Hashable {
|
||||
case filteredNotificationsInfo(policy: Mastodon.Entity.NotificationPolicy)
|
||||
case notification(MastodonFeedItemIdentifier)
|
||||
case middleLoader(after: MastodonFeedItemIdentifier, before: MastodonFeedItemIdentifier)
|
||||
case bottomLoader
|
||||
|
||||
var nextFetchAnchors: (MastodonFeedItemIdentifier?, MastodonFeedItemIdentifier?) {
|
||||
var fetchAnchor: MastodonFeedItemIdentifier? {
|
||||
switch self {
|
||||
case .filteredNotificationsInfo:
|
||||
return (nil, nil)
|
||||
return nil
|
||||
case .notification(let identifier):
|
||||
return (identifier, nil)
|
||||
case .middleLoader(let after, let before):
|
||||
return (after, before)
|
||||
return identifier
|
||||
case .bottomLoader:
|
||||
return (nil, nil)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -38,8 +35,6 @@ extension NotificationListItem: Identifiable {
|
||||
return "filtered_notifications_info"
|
||||
case .notification(let identifier):
|
||||
return identifier.id
|
||||
case let .middleLoader(afterID, beforeID):
|
||||
return afterID.id+"-"+beforeID.id
|
||||
case .bottomLoader:
|
||||
return "bottom_loader"
|
||||
}
|
||||
|
@ -56,11 +56,6 @@ extension NotificationSection {
|
||||
)
|
||||
return cell
|
||||
}
|
||||
|
||||
case .middleLoader:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
||||
cell.activityIndicatorView.startAnimating()
|
||||
return cell
|
||||
case .bottomLoader:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
||||
cell.activityIndicatorView.startAnimating()
|
||||
|
@ -58,7 +58,7 @@ extension NotificationTimelineViewController: DataSourceProvider {
|
||||
}
|
||||
case .filteredNotificationsInfo(let policy):
|
||||
return DataSourceItem.notificationBanner(policy: policy)
|
||||
case .bottomLoader, .middleLoader:
|
||||
case .bottomLoader:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ extension NotificationTimelineViewController: UITableViewDelegate, AutoGenerateT
|
||||
return
|
||||
}
|
||||
Task {
|
||||
await viewModel.loadMore(item: item)
|
||||
await viewModel.loadMore(olderThan: item, newerThan: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,22 +123,26 @@ extension NotificationTimelineViewModel {
|
||||
func loadLatest() async {
|
||||
isLoadingLatest = true
|
||||
defer { isLoadingLatest = false }
|
||||
feedLoader.loadMore(olderThan: nil, newerThan: nil)
|
||||
let currentFirst = diffableDataSource?.snapshot().itemIdentifiers.first
|
||||
await loadMore(olderThan: nil, newerThan: currentFirst)
|
||||
didLoadLatest.send()
|
||||
}
|
||||
|
||||
// load timeline gap
|
||||
func loadMore(item: NotificationListItem) async {
|
||||
let olderThan: MastodonFeedItemIdentifier?
|
||||
let newerThan: MastodonFeedItemIdentifier?
|
||||
switch item {
|
||||
case .notification, .middleLoader:
|
||||
(olderThan, newerThan) = item.nextFetchAnchors
|
||||
case .bottomLoader:
|
||||
(olderThan, newerThan) = diffableDataSource?.snapshot().itemIdentifiers.last(where: { $0 != .bottomLoader })?.nextFetchAnchors ?? (nil, nil)
|
||||
case .filteredNotificationsInfo:
|
||||
return
|
||||
func loadMore(olderThan: NotificationListItem?, newerThan: NotificationListItem?) async {
|
||||
|
||||
func fetchAnchor(for item: NotificationListItem?) -> MastodonFeedItemIdentifier? {
|
||||
switch item {
|
||||
case .notification:
|
||||
return item?.fetchAnchor
|
||||
case .bottomLoader:
|
||||
return diffableDataSource?.snapshot().itemIdentifiers.last(where: { $0.fetchAnchor != nil })?.fetchAnchor
|
||||
case .filteredNotificationsInfo:
|
||||
return diffableDataSource?.snapshot().itemIdentifiers.first(where: { $0.fetchAnchor != nil })?.fetchAnchor
|
||||
case .none:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
feedLoader.loadMore(olderThan: olderThan, newerThan: newerThan)
|
||||
|
||||
feedLoader.loadMore(olderThan: fetchAnchor(for: olderThan), newerThan: fetchAnchor(for: newerThan))
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,28 @@ import os.log
|
||||
@MainActor
|
||||
final public class MastodonFeedLoader {
|
||||
|
||||
struct FeedLoadRequest: Equatable {
|
||||
let olderThan: MastodonFeedItemIdentifier?
|
||||
let newerThan: MastodonFeedItemIdentifier?
|
||||
|
||||
var maxID: String? { olderThan?.id }
|
||||
|
||||
var resultsInsertionPoint: InsertLocation {
|
||||
if olderThan != nil {
|
||||
return .end
|
||||
} else if newerThan != nil {
|
||||
return .start
|
||||
} else {
|
||||
return .replace
|
||||
}
|
||||
}
|
||||
enum InsertLocation {
|
||||
case start
|
||||
case end
|
||||
case replace
|
||||
}
|
||||
}
|
||||
|
||||
private let logger = Logger(subsystem: "MastodonFeedLoader", category: "Data")
|
||||
private static let entryNotFoundMessage = "Failed to find suitable record. Depending on the context this might result in errors (data not being updated) or can be discarded (e.g. when there are mixed data sources where an entry might or might not exist)."
|
||||
|
||||
@ -37,43 +59,26 @@ final public class MastodonFeedLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private var mostRecentLoad: FeedLoadRequest?
|
||||
|
||||
public func loadMore(olderThan: MastodonFeedItemIdentifier?, newerThan: MastodonFeedItemIdentifier?) {
|
||||
if let olderThan {
|
||||
Task {
|
||||
let unfiltered = try await load(kind: kind, olderThan: olderThan.id)
|
||||
await setRecordsAfterFiltering(unfiltered)
|
||||
}
|
||||
} else {
|
||||
loadInitial(kind: kind)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadInitial(kind: MastodonFeedKind) {
|
||||
let request = FeedLoadRequest(olderThan: olderThan, newerThan: newerThan)
|
||||
Task {
|
||||
let unfilteredRecords = try await load(kind: kind)
|
||||
await setRecordsAfterFiltering(unfilteredRecords)
|
||||
let unfiltered = try await load(request)
|
||||
await insertRecordsAfterFiltering(at: request.resultsInsertionPoint, additionalRecords:unfiltered)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadNext(kind: MastodonFeedKind) {
|
||||
Task {
|
||||
guard let lastId = records.last?.id else {
|
||||
return loadInitial(kind: kind)
|
||||
}
|
||||
|
||||
let unfiltered = try await load(kind: kind, olderThan: lastId)
|
||||
await self.appendRecordsAfterFiltering(unfiltered)
|
||||
}
|
||||
}
|
||||
|
||||
private func load(kind: MastodonFeedKind, olderThan maxID: String? = nil) async throws -> [MastodonFeedItemIdentifier] {
|
||||
private func load(_ request: FeedLoadRequest) async throws -> [MastodonFeedItemIdentifier] {
|
||||
guard request != mostRecentLoad else { throw AppError.badRequest }
|
||||
mostRecentLoad = request
|
||||
switch kind {
|
||||
case .notificationsAll:
|
||||
return try await loadNotifications(withScope: .everything, olderThan: maxID)
|
||||
return try await loadNotifications(withScope: .everything, olderThan: request.maxID)
|
||||
case .notificationsMentionsOnly:
|
||||
return try await loadNotifications(withScope: .mentions, olderThan: maxID)
|
||||
return try await loadNotifications(withScope: .mentions, olderThan: request.maxID)
|
||||
case .notificationsWithAccount(let accountID):
|
||||
return try await loadNotifications(withAccountID: accountID, olderThan: maxID)
|
||||
return try await loadNotifications(withAccountID: accountID, olderThan: request.maxID)
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,10 +222,19 @@ private extension MastodonFeedLoader {
|
||||
self.records = filtered.removingDuplicates()
|
||||
}
|
||||
|
||||
private func appendRecordsAfterFiltering(_ additionalRecords: [MastodonFeedItemIdentifier]) async {
|
||||
private func insertRecordsAfterFiltering(at insertionPoint: FeedLoadRequest.InsertLocation, additionalRecords: [MastodonFeedItemIdentifier]) async {
|
||||
guard let filterBox = StatusFilterService.shared.activeFilterBox else { self.records += additionalRecords; return }
|
||||
let newRecords = await self.filter(additionalRecords, forFeed: kind, with: filterBox)
|
||||
self.records = (self.records + newRecords).removingDuplicates()
|
||||
var combinedRecords = self.records
|
||||
switch insertionPoint {
|
||||
case .start:
|
||||
combinedRecords = newRecords + combinedRecords
|
||||
case .end:
|
||||
combinedRecords.append(contentsOf: newRecords)
|
||||
case .replace:
|
||||
combinedRecords = newRecords
|
||||
}
|
||||
self.records = combinedRecords.removingDuplicates()
|
||||
}
|
||||
|
||||
private func filter(_ records: [MastodonFeedItemIdentifier], forFeed feedKind: MastodonFeedKind, with filterBox: Mastodon.Entity.FilterBox) async -> [MastodonFeedItemIdentifier] {
|
||||
|
Loading…
x
Reference in New Issue
Block a user