Use entities on suggestion-screen (IOS-190)

This commit is contained in:
Nathan Mattes 2023-11-13 14:44:26 +01:00
parent 92fcd2e665
commit 49f6cd6d29
5 changed files with 58 additions and 86 deletions

View File

@ -6,8 +6,8 @@
// //
import Foundation import Foundation
import CoreDataStack import MastodonSDK
enum RecommendAccountItem: Hashable { enum RecommendAccountItem: Hashable {
case account(ManagedObjectRecord<MastodonUser>) case account(Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?)
} }

View File

@ -34,21 +34,11 @@ extension RecommendAccountSection {
UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SuggestionAccountTableViewCell.self)) as! SuggestionAccountTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SuggestionAccountTableViewCell.self)) as! SuggestionAccountTableViewCell
switch item { switch item {
case .account(let record): case .account(let account, let relationship):
cell.delegate = configuration.suggestionAccountTableViewCellDelegate cell.delegate = configuration.suggestionAccountTableViewCellDelegate
context.managedObjectContext.performAndWait { cell.configure(account: account, relationship: relationship)
guard let user = record.object(in: context.managedObjectContext) else { return }
cell.configure(viewModel:
SuggestionAccountTableViewCell.ViewModel(
user: user,
followedUsers: configuration.authContext.mastodonAuthenticationBox.inMemoryCache.followingUserIds,
blockedUsers: configuration.authContext.mastodonAuthenticationBox.inMemoryCache.blockedUserIds,
followRequestedUsers: configuration.authContext.mastodonAuthenticationBox.inMemoryCache.followRequestedUserIDs)
)
}
} }
return cell return cell
} }
} }
} }

View File

@ -85,18 +85,21 @@ class SuggestionAccountViewController: UIViewController, NeedsDependency {
// MARK: - UITableViewDelegate // MARK: - UITableViewDelegate
extension SuggestionAccountViewController: UITableViewDelegate { extension SuggestionAccountViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let tableViewDiffableDataSource = viewModel.tableViewDiffableDataSource else { return } guard let tableViewDiffableDataSource = viewModel.tableViewDiffableDataSource else { return }
guard let item = tableViewDiffableDataSource.itemIdentifier(for: indexPath) else { return } guard let item = tableViewDiffableDataSource.itemIdentifier(for: indexPath) else { return }
switch item { switch item {
case .account(let record): case .account(let account, _):
guard let account = record.object(in: context.managedObjectContext) else { return } print("Show \(account.acct)")
let cachedProfileViewModel = CachedProfileViewModel(context: context, authContext: viewModel.authContext, mastodonUser: account) // let cachedProfileViewModel = CachedProfileViewModel(context: context, authContext: viewModel.authContext, mastodonUser: account)
_ = coordinator.present( // _ = coordinator.present(
scene: .profile(viewModel: cachedProfileViewModel), // scene: .profile(viewModel: cachedProfileViewModel),
from: self, // from: self,
transition: .show // transition: .show
) // )
} }
tableView.deselectRow(at: indexPath, animated: true)
} }
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
@ -104,7 +107,7 @@ extension SuggestionAccountViewController: UITableViewDelegate {
return nil return nil
} }
footerView.followAllButton.isEnabled = viewModel.userFetchedResultsController.records.isNotEmpty footerView.followAllButton.isEnabled = viewModel.accounts.isNotEmpty
footerView.delegate = self footerView.delegate = self
return footerView return footerView

View File

@ -6,9 +6,6 @@
// //
import Combine import Combine
import CoreData
import CoreDataStack
import GameplayKit
import MastodonSDK import MastodonSDK
import MastodonCore import MastodonCore
import UIKit import UIKit
@ -25,7 +22,8 @@ final class SuggestionAccountViewModel: NSObject {
// input // input
let context: AppContext let context: AppContext
let authContext: AuthContext let authContext: AuthContext
let userFetchedResultsController: UserFetchedResultsController @Published var accounts: [Mastodon.Entity.V2.SuggestionAccount]
var relationships: [Mastodon.Entity.Relationship]
var viewWillAppear = PassthroughSubject<Void, Never>() var viewWillAppear = PassthroughSubject<Void, Never>()
@ -38,51 +36,38 @@ final class SuggestionAccountViewModel: NSObject {
) { ) {
self.context = context self.context = context
self.authContext = authContext self.authContext = authContext
self.userFetchedResultsController = UserFetchedResultsController(
managedObjectContext: context.managedObjectContext,
domain: nil,
additionalPredicate: nil
)
super.init()
userFetchedResultsController.domain = authContext.mastodonAuthenticationBox.domain accounts = []
relationships = []
super.init()
// fetch recommended users // fetch recommended users
Task { Task {
var userIDs: [MastodonUser.ID] = [] var suggestedAccounts: [Mastodon.Entity.V2.SuggestionAccount] = []
do { do {
let response = try await context.apiService.suggestionAccountV2( let response = try await context.apiService.suggestionAccountV2(
query: .init(limit: 5), query: .init(limit: 5),
authenticationBox: authContext.mastodonAuthenticationBox authenticationBox: authContext.mastodonAuthenticationBox
) )
userIDs = response.value.map { $0.account.id } suggestedAccounts = response.value
} catch let error as Mastodon.API.Error where error.httpResponseStatus == .notFound {
let response = try await context.apiService.suggestionAccount( guard suggestedAccounts.isNotEmpty else { return }
query: nil,
let accounts = suggestedAccounts.compactMap { $0.account }
let relationships = try await context.apiService.relationship(
forAccounts: accounts,
authenticationBox: authContext.mastodonAuthenticationBox authenticationBox: authContext.mastodonAuthenticationBox
) ).value
userIDs = response.value.map { $0.id }
self.relationships = relationships
self.accounts = suggestedAccounts
} catch { } catch {
self.relationships = []
self.accounts = []
} }
guard userIDs.isNotEmpty else { return }
userFetchedResultsController.userIDs = userIDs
} }
// fetch relationship
userFetchedResultsController.$records
.removeDuplicates()
.sink { [weak self] records in
guard let _ = self else { return }
Task {
_ = try await context.apiService.relationship(
records: records,
authenticationBox: authContext.mastodonAuthenticationBox
)
}
}
.store(in: &disposeBag)
} }
func setupDiffableDataSource( func setupDiffableDataSource(
@ -98,15 +83,22 @@ final class SuggestionAccountViewModel: NSObject {
) )
) )
userFetchedResultsController.$records $accounts
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] records in .sink { [weak self] suggestedAccounts in
guard let self = self else { return } guard let self, let tableViewDiffableDataSource = self.tableViewDiffableDataSource else { return }
guard let tableViewDiffableDataSource = self.tableViewDiffableDataSource else { return }
let accounts = suggestedAccounts.compactMap { $0.account }
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)
}
var snapshot = NSDiffableDataSourceSnapshot<RecommendAccountSection, RecommendAccountItem>() var snapshot = NSDiffableDataSourceSnapshot<RecommendAccountSection, RecommendAccountItem>()
snapshot.appendSections([.main]) snapshot.appendSections([.main])
let items: [RecommendAccountItem] = records.map { RecommendAccountItem.account($0) } let items: [RecommendAccountItem] = accountsWithRelationship.map { RecommendAccountItem.account($0.account, relationship: $0.relationship) }
snapshot.appendItems(items, toSection: .main) snapshot.appendItems(items, toSection: .main)
tableViewDiffableDataSource.applySnapshotUsingReloadData(snapshot) tableViewDiffableDataSource.applySnapshotUsingReloadData(snapshot)
@ -116,17 +108,15 @@ final class SuggestionAccountViewModel: NSObject {
func followAllSuggestedAccounts(_ dependency: NeedsDependency & AuthContextProvider, completion: (() -> Void)? = nil) { func followAllSuggestedAccounts(_ dependency: NeedsDependency & AuthContextProvider, completion: (() -> Void)? = nil) {
let userRecords = userFetchedResultsController.records.compactMap { let tmpAccounts = accounts.compactMap { $0.account }
$0.object(in: dependency.context.managedObjectContext)?.asRecord
}
Task { Task {
await withTaskGroup(of: Void.self, body: { taskGroup in await withTaskGroup(of: Void.self, body: { taskGroup in
for user in userRecords { for account in tmpAccounts {
taskGroup.addTask { taskGroup.addTask {
try? await DataSourceFacade.responseToUserViewButtonAction( try? await DataSourceFacade.responseToUserViewButtonAction(
dependency: dependency, dependency: dependency,
user: user, user: account,
buttonState: .follow buttonState: .follow
) )
} }

View File

@ -85,28 +85,17 @@ final class SuggestionAccountTableViewCell: UITableViewCell {
disposeBag.removeAll() disposeBag.removeAll()
} }
func configure(viewModel: SuggestionAccountTableViewCell.ViewModel) { func configure(account: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?) {
userView.configure(user: viewModel.user, delegate: delegate) userView.configure(with: account, relationship: relationship, delegate: delegate)
userView.updateButtonState(with: relationship, isMe: false)
if viewModel.blockedUsers.contains(viewModel.user.id) {
self.userView.setButtonState(.blocked)
} else if viewModel.followedUsers.contains(viewModel.user.id) {
self.userView.setButtonState(.unfollow)
} else if viewModel.followRequestedUsers.contains(viewModel.user.id) {
self.userView.setButtonState(.pending)
} else if viewModel.user.locked {
self.userView.setButtonState(.request)
} else {
self.userView.setButtonState(.follow)
}
let metaContent: MetaContent = { let metaContent: MetaContent = {
do { do {
let mastodonContent = MastodonContent(content: viewModel.user.note ?? "", emojis: viewModel.user.emojis.asDictionary) let mastodonContent = MastodonContent(content: account.note, emojis: account.emojis?.asDictionary ?? [:])
return try MastodonMetaContent.convert(document: mastodonContent) return try MastodonMetaContent.convert(document: mastodonContent)
} catch { } catch {
assertionFailure() assertionFailure()
return PlaintextMetaContent(string: viewModel.user.note ?? "") return PlaintextMetaContent(string: account.note)
} }
}() }()