From a549534fcfa573fea98438507142e5d813fe78b1 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 19 Oct 2023 16:16:18 +0200 Subject: [PATCH] Get (and show) account-entities on followings-list This is a first step, for now we show the name to see if it works (and it does!), the other properties and functionality will follow. Again, this includes some refactoring, like getting rid of Configuration --- Mastodon/Diffable/User/UserItem.swift | 2 + Mastodon/Diffable/User/UserSection.swift | 31 +++---- .../FamiliarFollowersViewModel+Diffable.swift | 4 +- .../FollowerListViewModel+Diffable.swift | 4 +- .../FollowingListViewModel+Diffable.swift | 38 ++++---- .../FollowingListViewModel+State.swift | 90 +++++++++---------- .../Following/FollowingListViewModel.swift | 15 ++-- .../UserLIst/UserListViewModel+Diffable.swift | 4 +- .../View/Content/UserView+Configuration.swift | 7 ++ .../UserTableViewCell+ViewModel.swift | 12 +++ 10 files changed, 108 insertions(+), 99 deletions(-) diff --git a/Mastodon/Diffable/User/UserItem.swift b/Mastodon/Diffable/User/UserItem.swift index ff533d897..2bc5f33a6 100644 --- a/Mastodon/Diffable/User/UserItem.swift +++ b/Mastodon/Diffable/User/UserItem.swift @@ -8,9 +8,11 @@ import Foundation import CoreData import CoreDataStack +import MastodonSDK enum UserItem: Hashable { case user(record: ManagedObjectRecord) + case account(account: Mastodon.Entity.Account) case bottomLoader case bottomHeader(text: String) } diff --git a/Mastodon/Diffable/User/UserSection.swift b/Mastodon/Diffable/User/UserSection.swift index e6632c337..5667b3450 100644 --- a/Mastodon/Diffable/User/UserSection.swift +++ b/Mastodon/Diffable/User/UserSection.swift @@ -19,15 +19,11 @@ enum UserSection: Hashable { } extension UserSection { - struct Configuration { - weak var userTableViewCellDelegate: UserTableViewCellDelegate? - } - static func diffableDataSource( tableView: UITableView, context: AppContext, authContext: AuthContext, - configuration: Configuration + userTableViewCellDelegate: UserTableViewCellDelegate? ) -> UITableViewDiffableDataSource { tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self)) tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) @@ -35,6 +31,12 @@ extension UserSection { return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in switch item { + case .account(let account): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UserTableViewCell.self), for: indexPath) as! UserTableViewCell + cell.configure(tableView: tableView, account: account, delegate: userTableViewCellDelegate) + + return cell + case .user(let record): let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UserTableViewCell.self), for: indexPath) as! UserTableViewCell context.managedObjectContext.performAndWait { @@ -50,7 +52,7 @@ extension UserSection { blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher(), followRequestedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followRequestedUserIDs.eraseToAnyPublisher() ), - configuration: configuration + userTableViewCellDelegate: userTableViewCellDelegate ) } @@ -60,13 +62,12 @@ extension UserSection { cell.startAnimating() return cell case .bottomHeader(let text): - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineFooterTableViewCell.self), for: indexPath) as! TimelineFooterTableViewCell - cell.messageLabel.text = text - return cell - } // end switch - } // end UITableViewDiffableDataSource - } // end static func tableViewDiffableDataSource { … } - + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineFooterTableViewCell.self), for: indexPath) as! TimelineFooterTableViewCell + cell.messageLabel.text = text + return cell + } + } + } } extension UserSection { @@ -77,13 +78,13 @@ extension UserSection { tableView: UITableView, cell: UserTableViewCell, viewModel: UserTableViewCell.ViewModel, - configuration: Configuration + userTableViewCellDelegate: UserTableViewCellDelegate? ) { cell.configure( me: authContext.mastodonAuthenticationBox.authentication.user(in: context.managedObjectContext), tableView: tableView, viewModel: viewModel, - delegate: configuration.userTableViewCellDelegate + delegate: userTableViewCellDelegate ) } diff --git a/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewModel+Diffable.swift b/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewModel+Diffable.swift index 0b0428c67..291a6b3ff 100644 --- a/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewModel+Diffable.swift +++ b/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewModel+Diffable.swift @@ -17,9 +17,7 @@ extension FamiliarFollowersViewModel { tableView: tableView, context: context, authContext: authContext, - configuration: UserSection.Configuration( - userTableViewCellDelegate: userTableViewCellDelegate - ) + userTableViewCellDelegate: userTableViewCellDelegate ) userFetchedResultsController.$records diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift index 11276c04f..d0676dc59 100644 --- a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift @@ -18,9 +18,7 @@ extension FollowerListViewModel { tableView: tableView, context: context, authContext: authContext, - configuration: UserSection.Configuration( - userTableViewCellDelegate: userTableViewCellDelegate - ) + userTableViewCellDelegate: userTableViewCellDelegate ) // workaround to append loader wrong animation issue diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift index 785937335..d40c37ae8 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift @@ -19,9 +19,7 @@ extension FollowingListViewModel { tableView: tableView, context: context, authContext: authContext, - configuration: UserSection.Configuration( - userTableViewCellDelegate: userTableViewCellDelegate - ) + userTableViewCellDelegate: userTableViewCellDelegate ) // workaround to append loader wrong animation issue @@ -31,30 +29,32 @@ extension FollowingListViewModel { snapshot.appendItems([.bottomLoader], toSection: .main) diffableDataSource?.applySnapshotUsingReloadData(snapshot) - userFetchedResultsController.$records + $accounts .receive(on: DispatchQueue.main) - .sink { [weak self] records in - guard let self = self else { return } + .sink { [weak self] accounts in + guard let self else { return } guard let diffableDataSource = self.diffableDataSource else { return } - + var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) - let items = records.map { UserItem.user(record: $0) } + let items = accounts.map { UserItem.account(account: $0) } snapshot.appendItems(items, toSection: .main) if let currentState = self.stateMachine.currentState { switch currentState { - case is State.Idle, is State.Loading, is State.Fail: - snapshot.appendItems([.bottomLoader], toSection: .main) - case is State.NoMore: - guard let userID = self.userID, - userID != self.authContext.mastodonAuthenticationBox.userID - else { break } - // display footer exclude self - let text = L10n.Scene.Following.footer - snapshot.appendItems([.bottomHeader(text: text)], toSection: .main) - default: - break + case is State.Loading: + snapshot.appendItems([.bottomLoader], toSection: .main) + case is State.NoMore: + guard let userID = self.userID, + userID != self.authContext.mastodonAuthenticationBox.userID + else { break } + // display footer exclude self + let text = L10n.Scene.Following.footer + snapshot.appendItems([.bottomHeader(text: text)], toSection: .main) + case is State.Idle, is State.Fail: + break + default: + break } } diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift index df71c0d7c..32856c643 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift @@ -11,7 +11,7 @@ import MastodonSDK extension FollowingListViewModel { class State: GKState { - + let id = UUID() weak var viewModel: FollowingListViewModel? @@ -32,10 +32,10 @@ extension FollowingListViewModel.State { override func isValidNextState(_ stateClass: AnyClass) -> Bool { guard let viewModel = viewModel else { return false } switch stateClass { - case is Reloading.Type: - return viewModel.userID != nil - default: - return false + case is Reloading.Type: + return viewModel.userID != nil + default: + return false } } } @@ -43,19 +43,19 @@ extension FollowingListViewModel.State { class Reloading: FollowingListViewModel.State { override func isValidNextState(_ stateClass: AnyClass) -> Bool { switch stateClass { - case is Loading.Type: - return true - default: - return false + case is Loading.Type: + return true + default: + return false } } override func didEnter(from previousState: GKState?) { super.didEnter(from: previousState) - guard let viewModel = viewModel, let stateMachine = stateMachine else { return } + guard let viewModel, let stateMachine else { return } // reset - viewModel.userFetchedResultsController.userIDs = [] + viewModel.accounts = [] stateMachine.enter(Loading.self) } @@ -65,10 +65,10 @@ extension FollowingListViewModel.State { override func isValidNextState(_ stateClass: AnyClass) -> Bool { switch stateClass { - case is Loading.Type: - return true - default: - return false + case is Loading.Type: + return true + default: + return false } } @@ -85,10 +85,10 @@ extension FollowingListViewModel.State { class Idle: FollowingListViewModel.State { override func isValidNextState(_ stateClass: AnyClass) -> Bool { switch stateClass { - case is Reloading.Type, is Loading.Type: - return true - default: - return false + case is Reloading.Type, is Loading.Type: + return true + default: + return false } } } @@ -99,14 +99,14 @@ extension FollowingListViewModel.State { override func isValidNextState(_ stateClass: AnyClass) -> Bool { switch stateClass { - case is Fail.Type: - return true - case is Idle.Type: - return true - case is NoMore.Type: - return true - default: - return false + case is Fail.Type: + return true + case is Idle.Type: + return true + case is NoMore.Type: + return true + default: + return false } } @@ -117,9 +117,9 @@ extension FollowingListViewModel.State { maxID = nil } - guard let viewModel = viewModel, let stateMachine = stateMachine else { return } + guard let viewModel, let stateMachine else { return } - guard let userID = viewModel.userID, !userID.isEmpty else { + guard let userID = viewModel.userID, userID.isEmpty == false else { stateMachine.enter(Fail.self) return } @@ -131,15 +131,17 @@ extension FollowingListViewModel.State { maxID: maxID, authenticationBox: viewModel.authContext.mastodonAuthenticationBox ) - + var hasNewAppend = false - var userIDs = viewModel.userFetchedResultsController.userIDs + var accounts = viewModel.accounts + for user in response.value { - guard !userIDs.contains(user.id) else { continue } - userIDs.append(user.id) + guard accounts.contains(user) == false else { continue } + accounts.append(user) hasNewAppend = true } - + + let maxID = response.link?.maxID if hasNewAppend, maxID != nil { @@ -147,28 +149,24 @@ extension FollowingListViewModel.State { } else { await enter(state: NoMore.self) } - self.maxID = maxID - viewModel.userFetchedResultsController.userIDs = userIDs + viewModel.accounts = accounts + self.maxID = maxID } catch { await enter(state: Fail.self) } - } // end Task - } // end func didEnter + } + } } class NoMore: FollowingListViewModel.State { override func isValidNextState(_ stateClass: AnyClass) -> Bool { switch stateClass { - case is Reloading.Type: - return true - default: - return false + case is Reloading.Type: + return true + default: + return false } } - - override func didEnter(from previousState: GKState?) { - super.didEnter(from: previousState) - } } } diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift index e8758e645..9b634c9a6 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel.swift @@ -20,9 +20,9 @@ final class FollowingListViewModel { // input let context: AppContext let authContext: AuthContext - let userFetchedResultsController: UserFetchedResultsController - let listBatchFetchViewModel = ListBatchFetchViewModel() - + @Published var accounts: [Mastodon.Entity.Account] + let listBatchFetchViewModel: ListBatchFetchViewModel + @Published var domain: String? @Published var userID: String? @@ -49,14 +49,9 @@ final class FollowingListViewModel { ) { self.context = context self.authContext = authContext - self.userFetchedResultsController = UserFetchedResultsController( - managedObjectContext: context.managedObjectContext, - domain: domain, - additionalPredicate: nil - ) self.domain = domain self.userID = userID - // super.init() - + self.accounts = [] + self.listBatchFetchViewModel = ListBatchFetchViewModel() } } diff --git a/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift b/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift index bbc46063a..d4830affc 100644 --- a/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift +++ b/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift @@ -20,9 +20,7 @@ extension UserListViewModel { tableView: tableView, context: context, authContext: authContext, - configuration: UserSection.Configuration( - userTableViewCellDelegate: userTableViewCellDelegate - ) + userTableViewCellDelegate: userTableViewCellDelegate ) // workaround to append loader wrong animation issue diff --git a/Mastodon/Scene/Share/View/Content/UserView+Configuration.swift b/Mastodon/Scene/Share/View/Content/UserView+Configuration.swift index c83838492..90633bae0 100644 --- a/Mastodon/Scene/Share/View/Content/UserView+Configuration.swift +++ b/Mastodon/Scene/Share/View/Content/UserView+Configuration.swift @@ -19,6 +19,7 @@ extension UserView { public func configure(user: MastodonUser, delegate: UserViewDelegate?) { self.delegate = delegate viewModel.user = user + viewModel.account = nil Publishers.CombineLatest( user.publisher(for: \.avatar), @@ -67,5 +68,11 @@ extension UserView { func configure(with account: Mastodon.Entity.Account) { //TODO: Implement + viewModel.account = account + viewModel.user = nil + + // username + let metaContent = PlaintextMetaContent(string: "@\(account.username)") + authorUsernameLabel.configure(content: metaContent) } } diff --git a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift index d2f62e2fc..5d3e9c2c8 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift @@ -31,6 +31,18 @@ extension UserTableViewCell { extension UserTableViewCell { + func configure( + me: MastodonUser? = nil, + tableView: UITableView, + account: Mastodon.Entity.Account, + delegate: UserTableViewCellDelegate? + ) { + //TODO: Implement + userView.configure(with: account) + } + + + //TODO: Duplicate func configure( me: MastodonUser? = nil, tableView: UITableView,