diff --git a/Mastodon/Diffiable/Notification/NotificationSection.swift b/Mastodon/Diffiable/Notification/NotificationSection.swift index 210d7ea10..7cca0c0eb 100644 --- a/Mastodon/Diffiable/Notification/NotificationSection.swift +++ b/Mastodon/Diffiable/Notification/NotificationSection.swift @@ -49,8 +49,10 @@ extension NotificationSection { ) } return cell - case .feedLoader(let record): - return UITableViewCell() + case .feedLoader: + 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() diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift index 25e17ef51..b587ff742 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift @@ -135,6 +135,18 @@ extension NotificationTimelineViewController: UITableViewDelegate, AutoGenerateT } // sourcery:end + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { + return + } + + // check item type inside `loadMore` + Task { + await viewModel.loadMore(item: item) + } + } + } // MARK: - NotificationTableViewCellDelegate diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift index e62ec4743..41e4cef07 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift @@ -156,4 +156,35 @@ extension NotificationTimelineViewModel { } } + // load timeline gap + func loadMore(item: NotificationItem) async { + guard case let .feedLoader(record) = item else { return } + guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return } + + let managedObjectContext = context.managedObjectContext + let key = "LoadMore@\(record.objectID)" + + // return when already loading state + guard managedObjectContext.cache(froKey: key) == nil else { return } + + guard let feed = record.object(in: managedObjectContext) else { return } + guard let maxID = feed.notification?.id else { return } + // keep transient property live + managedObjectContext.cache(feed, key: key) + defer { + managedObjectContext.cache(nil, key: key) + } + + // fetch data + do { + _ = try await context.apiService.notifications( + maxID: maxID, + scope: scope, + authenticationBox: authenticationBox + ) + } catch { + logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch more failure: \(error.localizedDescription)") + } + } + }