From 19d67d6dab4fbf5e50ca831d25d5275346d80f0f Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Mon, 23 Oct 2023 12:18:03 +0200 Subject: [PATCH] Don't Ddos servers for fetching each relatinoship individually --- Mastodon/Diffable/User/UserItem.swift | 2 +- Mastodon/Diffable/User/UserSection.swift | 34 ++----------------- .../FollowingListViewModel+Diffable.swift | 10 +++++- .../FollowingListViewModel+State.swift | 23 +++++++++---- .../Following/FollowingListViewModel.swift | 3 ++ .../UserTableViewCell+ViewModel.swift | 18 ++++++++++ .../Entity/Mastodon+Entity+Relationship.swift | 2 +- 7 files changed, 52 insertions(+), 40 deletions(-) diff --git a/Mastodon/Diffable/User/UserItem.swift b/Mastodon/Diffable/User/UserItem.swift index 2bc5f33a6..ba44aa52a 100644 --- a/Mastodon/Diffable/User/UserItem.swift +++ b/Mastodon/Diffable/User/UserItem.swift @@ -12,7 +12,7 @@ import MastodonSDK enum UserItem: Hashable { case user(record: ManagedObjectRecord) - case account(account: Mastodon.Entity.Account) + case account(account: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?) case bottomLoader case bottomHeader(text: String) } diff --git a/Mastodon/Diffable/User/UserSection.swift b/Mastodon/Diffable/User/UserSection.swift index 3c6c962af..d636a88b7 100644 --- a/Mastodon/Diffable/User/UserSection.swift +++ b/Mastodon/Diffable/User/UserSection.swift @@ -31,39 +31,11 @@ extension UserSection { return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in 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 - cell.configure(tableView: tableView, account: account, delegate: userTableViewCellDelegate) + cell.userView.setButtonState(.loading) - Task { - 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) - } - } - } + cell.configure(tableView: tableView, account: account, relationship: relationship, delegate: userTableViewCellDelegate) return cell diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift index d40c37ae8..533360520 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift @@ -9,6 +9,7 @@ import UIKit import MastodonAsset import MastodonCore import MastodonLocalization +import MastodonSDK extension FollowingListViewModel { func setupDiffableDataSource( @@ -37,7 +38,14 @@ extension FollowingListViewModel { var snapshot = NSDiffableDataSourceSnapshot() 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) if let currentState = self.stateMachine.currentState { diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift index 32856c643..924e9db55 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift @@ -126,24 +126,34 @@ extension FollowingListViewModel.State { Task { do { - let response = try await viewModel.context.apiService.following( + let accountResponse = try await viewModel.context.apiService.following( userID: userID, maxID: maxID, authenticationBox: viewModel.authContext.mastodonAuthenticationBox ) - + var hasNewAppend = false + + + let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authContext.mastodonAuthenticationBox) + var accounts = viewModel.accounts - - for user in response.value { + + for user in accountResponse.value { guard accounts.contains(user) == false else { continue } accounts.append(user) hasNewAppend = true } + var relationships = viewModel.relationships + + for relationship in newRelationships.value { + guard relationships.contains(relationship) == false else { continue } + relationships.append(relationship) + } + + let maxID = accountResponse.link?.maxID - let maxID = response.link?.maxID - if hasNewAppend, maxID != nil { await enter(state: Idle.self) } else { @@ -151,6 +161,7 @@ extension FollowingListViewModel.State { } viewModel.accounts = accounts + viewModel.relationships = relationships self.maxID = maxID } catch { await enter(state: Fail.self) diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift index 9b634c9a6..e07d1b08e 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift @@ -21,6 +21,8 @@ final class FollowingListViewModel { let context: AppContext let authContext: AuthContext @Published var accounts: [Mastodon.Entity.Account] + @Published var relationships: [Mastodon.Entity.Relationship] + let listBatchFetchViewModel: ListBatchFetchViewModel @Published var domain: String? @@ -52,6 +54,7 @@ final class FollowingListViewModel { self.domain = domain self.userID = userID self.accounts = [] + self.relationships = [] self.listBatchFetchViewModel = ListBatchFetchViewModel() } } diff --git a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift index 5d3e9c2c8..0fe470914 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift @@ -35,10 +35,28 @@ extension UserTableViewCell { me: MastodonUser? = nil, tableView: UITableView, account: Mastodon.Entity.Account, + relationship: Mastodon.Entity.Relationship?, delegate: UserTableViewCellDelegate? ) { //TODO: Implement 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) } diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Relationship.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Relationship.swift index 180b5bd6e..f5d2200e7 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Relationship.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Relationship.swift @@ -16,7 +16,7 @@ extension Mastodon.Entity { /// 2021/1/29 /// # Reference /// [Document](https://docs.joinmastodon.org/entities/relationship/) - public struct Relationship: Codable, Sendable { + public struct Relationship: Codable, Sendable, Equatable, Hashable { public typealias ID = String public let id: ID