Migrate UserList to use Accounts (IOS-192)

This commit is contained in:
Nathan Mattes 2023-12-14 13:48:59 +01:00
parent f51d5b7fe2
commit 3abb80a5df
6 changed files with 66 additions and 39 deletions

View File

@ -7,8 +7,6 @@
import Foundation import Foundation
import Combine import Combine
import CoreData
import CoreDataStack
import GameplayKit import GameplayKit
import MastodonSDK import MastodonSDK
import MastodonCore import MastodonCore

View File

@ -21,10 +21,10 @@ extension FavoritedByViewController: DataSourceProvider {
} }
switch item { switch item {
case .user(let record): case .user(_), .bottomHeader(_), .bottomLoader:
return .user(record: record) return nil
default: case .account(let account, let relationship):
return nil return .account(account: account, relationship: relationship)
} }
} }

View File

@ -20,12 +20,12 @@ extension RebloggedByViewController: DataSourceProvider {
guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else {
return nil return nil
} }
switch item { switch item {
case .user(let record): case .user(_), .bottomHeader(_), .bottomLoader:
return .user(record: record) return nil
default: case .account(let account, let relationship):
return nil return .account(account: account, relationship: relationship)
} }
} }

View File

@ -9,6 +9,7 @@ import UIKit
import MastodonAsset import MastodonAsset
import MastodonLocalization import MastodonLocalization
import Combine import Combine
import MastodonSDK
extension UserListViewModel { extension UserListViewModel {
@MainActor @MainActor
@ -33,15 +34,23 @@ extension UserListViewModel {
// trigger initial loading // trigger initial loading
stateMachine.enter(UserListViewModel.State.Reloading.self) stateMachine.enter(UserListViewModel.State.Reloading.self)
userFetchedResultsController.$records $accounts
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] records in .sink { [weak self] accounts in
guard let self = self else { return } guard let self = self else { return }
guard let diffableDataSource = self.diffableDataSource else { return } guard let diffableDataSource = self.diffableDataSource else { return }
var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>() var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>()
snapshot.appendSections([.main]) snapshot.appendSections([.main])
let items = records.map { UserItem.user(record: $0) }
let accountsWithRelationship: [(account: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?)] = accounts.compactMap { account in
guard let relationship = self.relationships.first(where: {$0.id == account.id }) else { return (account: account, relationship: nil)}
return (account: account, relationship: relationship)
}
let items = accountsWithRelationship.map { UserItem.account(account: $0.account, relationship: $0.relationship) }
snapshot.appendItems(items, toSection: .main) snapshot.appendItems(items, toSection: .main)
if let currentState = self.stateMachine.currentState { if let currentState = self.stateMachine.currentState {

View File

@ -30,7 +30,7 @@ extension UserListViewModel {
extension UserListViewModel.State { extension UserListViewModel.State {
class Initial: UserListViewModel.State { class Initial: UserListViewModel.State {
override func isValidNextState(_ stateClass: AnyClass) -> Bool { override func isValidNextState(_ stateClass: AnyClass) -> Bool {
guard let _ = viewModel else { return false } guard viewModel != nil else { return false }
switch stateClass { switch stateClass {
case is Reloading.Type: case is Reloading.Type:
return true return true
@ -52,10 +52,10 @@ extension UserListViewModel.State {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
guard let viewModel = viewModel, let stateMachine = stateMachine else { return } guard let viewModel, let stateMachine else { return }
// reset // reset
viewModel.userFetchedResultsController.userIDs = [] viewModel.accounts = []
stateMachine.enter(Loading.self) stateMachine.enter(Loading.self)
} }
@ -74,8 +74,8 @@ extension UserListViewModel.State {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
guard let _ = viewModel, let stateMachine = stateMachine else { return } guard viewModel != nil, let stateMachine else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
stateMachine.enter(Loading.self) stateMachine.enter(Loading.self)
} }
@ -117,10 +117,11 @@ extension UserListViewModel.State {
maxID = nil maxID = nil
} }
guard let viewModel = viewModel else { return } guard let viewModel else { return }
let maxID = self.maxID let maxID = self.maxID
let authenticationBox = viewModel.authContext.mastodonAuthenticationBox
Task { Task {
do { do {
let response: Mastodon.Response.Content<[Mastodon.Entity.Account]> let response: Mastodon.Response.Content<[Mastodon.Entity.Account]>
@ -129,24 +130,45 @@ extension UserListViewModel.State {
response = try await viewModel.context.apiService.favoritedBy( response = try await viewModel.context.apiService.favoritedBy(
status: status, status: status,
query: .init(maxID: maxID, limit: nil), query: .init(maxID: maxID, limit: nil),
authenticationBox: viewModel.authContext.mastodonAuthenticationBox authenticationBox: authenticationBox
) )
case .rebloggedBy(let status): case .rebloggedBy(let status):
response = try await viewModel.context.apiService.rebloggedBy( response = try await viewModel.context.apiService.rebloggedBy(
status: status, status: status,
query: .init(maxID: maxID, limit: nil), query: .init(maxID: maxID, limit: nil),
authenticationBox: viewModel.authContext.mastodonAuthenticationBox authenticationBox: authenticationBox
) )
} }
if response.value.isEmpty {
await enter(state: NoMore.self)
viewModel.accounts = []
viewModel.relationships = []
return
}
let newRelationships = try await viewModel.context.apiService.relationship(
forAccounts: response.value,
authenticationBox: authenticationBox
)
var hasNewAppend = false var hasNewAppend = false
var userIDs = viewModel.userFetchedResultsController.userIDs var accounts = viewModel.accounts
for user in response.value { for account in response.value {
guard !userIDs.contains(user.id) else { continue } guard !accounts.contains(account) else { continue }
userIDs.append(user.id)
accounts.append(account)
hasNewAppend = true hasNewAppend = true
} }
var relationships = viewModel.relationships
for relationship in newRelationships.value {
guard relationships.contains(relationship) == false else { continue }
relationships.append(relationship)
}
let maxID = response.link?.maxID let maxID = response.link?.maxID
if hasNewAppend, maxID != nil { if hasNewAppend, maxID != nil {
@ -155,8 +177,9 @@ extension UserListViewModel.State {
await enter(state: NoMore.self) await enter(state: NoMore.self)
} }
self.maxID = maxID self.maxID = maxID
viewModel.userFetchedResultsController.userIDs = userIDs viewModel.relationships = relationships
viewModel.accounts = accounts
} catch { } catch {
await enter(state: Fail.self) await enter(state: Fail.self)
} }
@ -177,9 +200,9 @@ extension UserListViewModel.State {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
guard let viewModel = viewModel else { return } guard let viewModel else { return }
// trigger reload // trigger reload
viewModel.userFetchedResultsController.userIDs = viewModel.userFetchedResultsController.userIDs viewModel.accounts = viewModel.accounts
} }
} }
} }

View File

@ -19,7 +19,8 @@ final class UserListViewModel {
let context: AppContext let context: AppContext
let authContext: AuthContext let authContext: AuthContext
let kind: Kind let kind: Kind
let userFetchedResultsController: UserFetchedResultsController @Published var accounts: [Mastodon.Entity.Account]
@Published var relationships: [Mastodon.Entity.Relationship]
let listBatchFetchViewModel = ListBatchFetchViewModel() let listBatchFetchViewModel = ListBatchFetchViewModel()
// output // output
@ -44,12 +45,8 @@ final class UserListViewModel {
self.context = context self.context = context
self.authContext = authContext self.authContext = authContext
self.kind = kind self.kind = kind
self.userFetchedResultsController = UserFetchedResultsController( self.accounts = []
managedObjectContext: context.managedObjectContext, self.relationships = []
domain: authContext.mastodonAuthenticationBox.domain,
additionalPredicate: nil
)
// end init
} }
} }