Don't Ddos servers for fetching each relatinoship individually

This commit is contained in:
Nathan Mattes 2023-10-23 12:18:03 +02:00
parent fa34df26df
commit 19d67d6dab
7 changed files with 52 additions and 40 deletions

View File

@ -12,7 +12,7 @@ import MastodonSDK
enum UserItem: Hashable { enum UserItem: Hashable {
case user(record: ManagedObjectRecord<MastodonUser>) case user(record: ManagedObjectRecord<MastodonUser>)
case account(account: Mastodon.Entity.Account) case account(account: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?)
case bottomLoader case bottomLoader
case bottomHeader(text: String) case bottomHeader(text: String)
} }

View File

@ -31,39 +31,11 @@ extension UserSection {
return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in
switch item { switch item {
case .account(let account): case .account(let account, let relationship):
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UserTableViewCell.self), for: indexPath) as! UserTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UserTableViewCell.self), for: indexPath) as! UserTableViewCell
cell.configure(tableView: tableView, account: account, delegate: userTableViewCellDelegate)
cell.userView.setButtonState(.loading) cell.userView.setButtonState(.loading)
Task { cell.configure(tableView: tableView, account: account, relationship: relationship, delegate: userTableViewCellDelegate)
do {
guard let relationship = try await context.apiService.relationship(forAccounts: [account], authenticationBox: authContext.mastodonAuthenticationBox).value.first else {
return await MainActor.run {
cell.userView.setButtonState(.none)
}
}
let buttonState: UserView.ButtonState
if relationship.following {
buttonState = .unfollow
} else if relationship.blocking || (relationship.domainBlocking ?? false) {
buttonState = .blocked
} else if relationship.requested ?? false {
buttonState = .pending
} else {
buttonState = .follow
}
await MainActor.run {
cell.userView.setButtonState(buttonState)
}
} catch {
await MainActor.run {
cell.userView.setButtonState(.none)
}
}
}
return cell return cell

View File

@ -9,6 +9,7 @@ import UIKit
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonLocalization import MastodonLocalization
import MastodonSDK
extension FollowingListViewModel { extension FollowingListViewModel {
func setupDiffableDataSource( func setupDiffableDataSource(
@ -37,7 +38,14 @@ extension FollowingListViewModel {
var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>() var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>()
snapshot.appendSections([.main]) snapshot.appendSections([.main])
let items = accounts.map { UserItem.account(account: $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

@ -126,23 +126,33 @@ extension FollowingListViewModel.State {
Task { Task {
do { do {
let response = try await viewModel.context.apiService.following( let accountResponse = try await viewModel.context.apiService.following(
userID: userID, userID: userID,
maxID: maxID, maxID: maxID,
authenticationBox: viewModel.authContext.mastodonAuthenticationBox authenticationBox: viewModel.authContext.mastodonAuthenticationBox
) )
var hasNewAppend = false var hasNewAppend = false
let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authContext.mastodonAuthenticationBox)
var accounts = viewModel.accounts var accounts = viewModel.accounts
for user in response.value { for user in accountResponse.value {
guard accounts.contains(user) == false else { continue } guard accounts.contains(user) == false else { continue }
accounts.append(user) accounts.append(user)
hasNewAppend = true hasNewAppend = true
} }
var relationships = viewModel.relationships
let maxID = response.link?.maxID for relationship in newRelationships.value {
guard relationships.contains(relationship) == false else { continue }
relationships.append(relationship)
}
let maxID = accountResponse.link?.maxID
if hasNewAppend, maxID != nil { if hasNewAppend, maxID != nil {
await enter(state: Idle.self) await enter(state: Idle.self)
@ -151,6 +161,7 @@ extension FollowingListViewModel.State {
} }
viewModel.accounts = accounts viewModel.accounts = accounts
viewModel.relationships = relationships
self.maxID = maxID self.maxID = maxID
} catch { } catch {
await enter(state: Fail.self) await enter(state: Fail.self)

View File

@ -21,6 +21,8 @@ final class FollowingListViewModel {
let context: AppContext let context: AppContext
let authContext: AuthContext let authContext: AuthContext
@Published var accounts: [Mastodon.Entity.Account] @Published var accounts: [Mastodon.Entity.Account]
@Published var relationships: [Mastodon.Entity.Relationship]
let listBatchFetchViewModel: ListBatchFetchViewModel let listBatchFetchViewModel: ListBatchFetchViewModel
@Published var domain: String? @Published var domain: String?
@ -52,6 +54,7 @@ final class FollowingListViewModel {
self.domain = domain self.domain = domain
self.userID = userID self.userID = userID
self.accounts = [] self.accounts = []
self.relationships = []
self.listBatchFetchViewModel = ListBatchFetchViewModel() self.listBatchFetchViewModel = ListBatchFetchViewModel()
} }
} }

View File

@ -35,10 +35,28 @@ extension UserTableViewCell {
me: MastodonUser? = nil, me: MastodonUser? = nil,
tableView: UITableView, tableView: UITableView,
account: Mastodon.Entity.Account, account: Mastodon.Entity.Account,
relationship: Mastodon.Entity.Relationship?,
delegate: UserTableViewCellDelegate? delegate: UserTableViewCellDelegate?
) { ) {
//TODO: Implement //TODO: Implement
userView.configure(with: account) userView.configure(with: account)
let buttonState: UserView.ButtonState
if let relationship {
if relationship.following {
buttonState = .unfollow
} else if relationship.blocking || (relationship.domainBlocking ?? false) {
buttonState = .blocked
} else if relationship.requested ?? false {
buttonState = .pending
} else {
buttonState = .follow
}
} else {
buttonState = .none
}
userView.setButtonState(buttonState)
} }

View File

@ -16,7 +16,7 @@ extension Mastodon.Entity {
/// 2021/1/29 /// 2021/1/29
/// # Reference /// # Reference
/// [Document](https://docs.joinmastodon.org/entities/relationship/) /// [Document](https://docs.joinmastodon.org/entities/relationship/)
public struct Relationship: Codable, Sendable { public struct Relationship: Codable, Sendable, Equatable, Hashable {
public typealias ID = String public typealias ID = String
public let id: ID public let id: ID