Add a new loading-state for timeline-switches (IOS-235)

Gosh, this took longer than expected. It didn't work for quite some time but the trick is to check for the states in several places, like in HomeTimelineViewModel+Diffable or when the timeline reached the end. Those triggered a reload and we don't want that in case the state is `contextSwitch`.
This commit is contained in:
Nathan Mattes 2024-04-01 20:10:41 +02:00
parent 86ab9101a3
commit 1583ce0d9a
5 changed files with 41 additions and 10 deletions

View File

@ -12,7 +12,7 @@ extension HomeTimelineViewController: DataSourceProvider {
func item(from source: DataSourceItem.Source) async -> DataSourceItem? {
var _indexPath = source.indexPath
if _indexPath == nil, let cell = source.tableViewCell {
_indexPath = await self.indexPath(for: cell)
_indexPath = self.indexPath(for: cell)
}
guard let indexPath = _indexPath else { return nil }
@ -37,8 +37,7 @@ extension HomeTimelineViewController: DataSourceProvider {
viewModel?.dataController.update(status: status, intent: intent)
}
@MainActor
private func indexPath(for cell: UITableViewCell) async -> IndexPath? {
private func indexPath(for cell: UITableViewCell) -> IndexPath? {
return tableView.indexPath(for: cell)
}
}

View File

@ -109,7 +109,7 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency, Media
viewModel.timelineContext = .following
viewModel.dataController.records = []
viewModel.loadLatestStateMachine.enter(HomeTimelineViewModel.LoadLatestState.LoadingManually.self)
viewModel.loadLatestStateMachine.enter(HomeTimelineViewModel.LoadLatestState.ContextSwitch.self)
timelineSelectorButton.setAttributedTitle(
.init(string: "Following", attributes: [
.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 20, weight: .semibold))
@ -124,9 +124,7 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency, Media
guard let self, let viewModel = self.viewModel else { return }
viewModel.timelineContext = .localCommunity
viewModel.dataController.records = []
viewModel.loadLatestStateMachine.enter(HomeTimelineViewModel.LoadLatestState.LoadingManually.self)
viewModel.loadLatestStateMachine.enter(HomeTimelineViewModel.LoadLatestState.ContextSwitch.self)
timelineSelectorButton.setAttributedTitle(
.init(string: "Local", attributes: [
.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 20, weight: .semibold))
@ -591,8 +589,12 @@ extension HomeTimelineViewController: UITableViewDelegate, AutoGenerateTableView
// sourcery:end
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let viewModel,
let currentState = viewModel.loadLatestStateMachine.currentState as? HomeTimelineViewModel.LoadLatestState,
(currentState.self is HomeTimelineViewModel.LoadLatestState.ContextSwitch) == false else { return }
if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
viewModel?.timelineDidReachEnd()
viewModel.timelineDidReachEnd()
}
}
}

View File

@ -40,6 +40,9 @@ extension HomeTimelineViewModel {
guard let self = self else { return }
guard let diffableDataSource = self.diffableDataSource else { return }
guard let currentState = loadLatestStateMachine.currentState as? HomeTimelineViewModel.LoadLatestState,
(currentState.self is HomeTimelineViewModel.LoadLatestState.ContextSwitch) == false else { return }
Task { @MainActor in
let oldSnapshot = diffableDataSource.snapshot()
var newSnapshot: NSDiffableDataSourceSnapshot<StatusSection, StatusItem> = {

View File

@ -70,7 +70,33 @@ extension HomeTimelineViewModel.LoadLatestState {
class Idle: HomeTimelineViewModel.LoadLatestState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == Loading.self || stateClass == LoadingManually.self
return stateClass == Loading.self || stateClass == LoadingManually.self || stateClass == ContextSwitch.self
}
}
class ContextSwitch: HomeTimelineViewModel.LoadLatestState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == Loading.self || stateClass == LoadingManually.self || stateClass == ContextSwitch.self
}
override func didEnter(from previousState: GKState?) {
guard let viewModel else { return }
guard let diffableDataSource = viewModel.diffableDataSource else {
assertionFailure()
return
}
OperationQueue.main.addOperation {
viewModel.dataController.records = []
var snapshot = NSDiffableDataSourceSnapshot<StatusSection, StatusItem>()
snapshot.appendSections([.main])
snapshot.appendItems([.topLoader], toSection: .main)
diffableDataSource.apply(snapshot) { [weak self] in
guard let self else { return }
self.stateMachine?.enter(Loading.self)
}
}
}
}
@ -78,7 +104,7 @@ extension HomeTimelineViewModel.LoadLatestState {
super.didEnter(from: previousState)
guard let viewModel else { return }
let latestFeedRecords = viewModel.dataController.records.prefix(APIService.onceRequestStatusMaxCount)
Task {

View File

@ -55,6 +55,7 @@ final class HomeTimelineViewModel: NSObject {
LoadLatestState.LoadingManually(viewModel: self),
LoadLatestState.Fail(viewModel: self),
LoadLatestState.Idle(viewModel: self),
LoadLatestState.ContextSwitch(viewModel: self),
])
stateMachine.enter(LoadLatestState.Initial.self)
return stateMachine