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
This commit is contained in:
parent
1750ef83a6
commit
a549534fcf
|
@ -8,9 +8,11 @@
|
|||
import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
enum UserItem: Hashable {
|
||||
case user(record: ManagedObjectRecord<MastodonUser>)
|
||||
case account(account: Mastodon.Entity.Account)
|
||||
case bottomLoader
|
||||
case bottomHeader(text: String)
|
||||
}
|
||||
|
|
|
@ -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<UserSection, UserItem> {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,7 @@ extension FamiliarFollowersViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
configuration: UserSection.Configuration(
|
||||
userTableViewCellDelegate: userTableViewCellDelegate
|
||||
)
|
||||
userTableViewCellDelegate: userTableViewCellDelegate
|
||||
)
|
||||
|
||||
userFetchedResultsController.$records
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<UserSection, UserItem>()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue