Implement fetching of following / blocking for user list buttons (IOS-140)
This commit is contained in:
parent
4de184c85f
commit
92704dc7f7
|
@ -22,6 +22,7 @@
|
||||||
164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; };
|
164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; };
|
||||||
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
|
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||||
27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */; };
|
27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */; };
|
||||||
|
2A15964E2A09012300C603BE /* FollowedBlockedUserIdProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A15964D2A09012300C603BE /* FollowedBlockedUserIdProviding.swift */; };
|
||||||
2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */; };
|
2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */; };
|
||||||
2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; };
|
2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; };
|
||||||
2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */; };
|
2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */; };
|
||||||
|
@ -617,6 +618,7 @@
|
||||||
164F0EBB267D4FE400249499 /* BoopSound.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = BoopSound.caf; sourceTree = "<group>"; };
|
164F0EBB267D4FE400249499 /* BoopSound.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = BoopSound.caf; sourceTree = "<group>"; };
|
||||||
1D6D967E77A5357E2C6110D9 /* Pods-Mastodon.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk - debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk - debug.xcconfig"; sourceTree = "<group>"; };
|
1D6D967E77A5357E2C6110D9 /* Pods-Mastodon.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk - debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk - debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+URL.swift"; sourceTree = "<group>"; };
|
27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+URL.swift"; sourceTree = "<group>"; };
|
||||||
|
2A15964D2A09012300C603BE /* FollowedBlockedUserIdProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedBlockedUserIdProviding.swift; sourceTree = "<group>"; };
|
||||||
2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+UserView.swift"; sourceTree = "<group>"; };
|
2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+UserView.swift"; sourceTree = "<group>"; };
|
||||||
2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowedTagsViewModel+DiffableDataSource.swift"; sourceTree = "<group>"; };
|
2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowedTagsViewModel+DiffableDataSource.swift"; sourceTree = "<group>"; };
|
||||||
2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+IsNotEmpty.swift"; sourceTree = "<group>"; };
|
2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+IsNotEmpty.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -2703,6 +2705,7 @@
|
||||||
DBCC3B8E26148F7B0045B23D /* CachedProfileViewModel.swift */,
|
DBCC3B8E26148F7B0045B23D /* CachedProfileViewModel.swift */,
|
||||||
DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */,
|
DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */,
|
||||||
DBB525632612C988002F1F29 /* MeProfileViewModel.swift */,
|
DBB525632612C988002F1F29 /* MeProfileViewModel.swift */,
|
||||||
|
2A15964D2A09012300C603BE /* FollowedBlockedUserIdProviding.swift */,
|
||||||
);
|
);
|
||||||
path = Profile;
|
path = Profile;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -3879,6 +3882,7 @@
|
||||||
DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */,
|
DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */,
|
||||||
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */,
|
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */,
|
||||||
DB7A9F932818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift in Sources */,
|
DB7A9F932818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift in Sources */,
|
||||||
|
2A15964E2A09012300C603BE /* FollowedBlockedUserIdProviding.swift in Sources */,
|
||||||
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */,
|
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */,
|
||||||
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
|
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
|
||||||
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
|
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
|
||||||
|
|
|
@ -20,7 +20,9 @@ extension SearchHistorySection {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func diffableDataSource(
|
static func diffableDataSource(
|
||||||
|
viewModel: SearchHistoryViewModel,
|
||||||
collectionView: UICollectionView,
|
collectionView: UICollectionView,
|
||||||
|
authContext: AuthContext,
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
) -> UICollectionViewDiffableDataSource<SearchHistorySection, SearchHistoryItem> {
|
) -> UICollectionViewDiffableDataSource<SearchHistorySection, SearchHistoryItem> {
|
||||||
|
@ -28,7 +30,11 @@ extension SearchHistorySection {
|
||||||
let userCellRegister = UICollectionView.CellRegistration<SearchHistoryUserCollectionViewCell, ManagedObjectRecord<MastodonUser>> { cell, indexPath, item in
|
let userCellRegister = UICollectionView.CellRegistration<SearchHistoryUserCollectionViewCell, ManagedObjectRecord<MastodonUser>> { cell, indexPath, item in
|
||||||
context.managedObjectContext.performAndWait {
|
context.managedObjectContext.performAndWait {
|
||||||
guard let user = item.object(in: context.managedObjectContext) else { return }
|
guard let user = item.object(in: context.managedObjectContext) else { return }
|
||||||
cell.configure(viewModel: .init(value: user), delegate: configuration.searchHistorySectionHeaderCollectionReusableViewDelegate)
|
cell.configure(
|
||||||
|
me: authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user,
|
||||||
|
viewModel: .init(value: user, followedUsers: viewModel.followedUserIds.eraseToAnyPublisher(), blockedUsers: viewModel.blockedUserIds.eraseToAnyPublisher()),
|
||||||
|
delegate: configuration.searchHistorySectionHeaderCollectionReusableViewDelegate
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import MastodonAsset
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
enum SearchResultSection: Hashable {
|
enum SearchResultSection: Hashable {
|
||||||
case main
|
case main
|
||||||
|
@ -33,7 +34,10 @@ extension SearchResultSection {
|
||||||
static func tableViewDiffableDataSource(
|
static func tableViewDiffableDataSource(
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
configuration: Configuration
|
authContext: AuthContext,
|
||||||
|
configuration: Configuration,
|
||||||
|
followedUsers: AnyPublisher<[String], Never>,
|
||||||
|
blockedUsers: AnyPublisher<[String], Never>
|
||||||
) -> UITableViewDiffableDataSource<SearchResultSection, SearchResultItem> {
|
) -> UITableViewDiffableDataSource<SearchResultSection, SearchResultItem> {
|
||||||
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self))
|
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self))
|
||||||
tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
|
tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
|
||||||
|
@ -48,9 +52,10 @@ extension SearchResultSection {
|
||||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
guard let user = record.object(in: context.managedObjectContext) else { return }
|
||||||
configure(
|
configure(
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
cell: cell,
|
cell: cell,
|
||||||
viewModel: .init(value: .user(user)),
|
viewModel: .init(value: .user(user), followedUsers: followedUsers, blockedUsers: blockedUsers),
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -116,13 +121,14 @@ extension SearchResultSection {
|
||||||
|
|
||||||
static func configure(
|
static func configure(
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
|
authContext: AuthContext,
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
cell: UserTableViewCell,
|
cell: UserTableViewCell,
|
||||||
viewModel: UserTableViewCell.ViewModel,
|
viewModel: UserTableViewCell.ViewModel,
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
) {
|
) {
|
||||||
cell.configure(
|
cell.configure(
|
||||||
meUserID: context.authenticationService.mastodonAuthenticationBoxes.first?.userID,
|
me: authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
delegate: configuration.userTableViewCellDelegate
|
delegate: configuration.userTableViewCellDelegate
|
||||||
|
|
|
@ -13,6 +13,7 @@ import MastodonCore
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
import MastodonMeta
|
import MastodonMeta
|
||||||
import MetaTextKit
|
import MetaTextKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
enum UserSection: Hashable {
|
enum UserSection: Hashable {
|
||||||
case main
|
case main
|
||||||
|
@ -29,7 +30,10 @@ extension UserSection {
|
||||||
static func diffableDataSource(
|
static func diffableDataSource(
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
configuration: Configuration
|
authContext: AuthContext,
|
||||||
|
configuration: Configuration,
|
||||||
|
followedUsers: AnyPublisher<[String], Never>,
|
||||||
|
blockedUsers: AnyPublisher<[String], Never>
|
||||||
) -> UITableViewDiffableDataSource<UserSection, UserItem> {
|
) -> UITableViewDiffableDataSource<UserSection, UserItem> {
|
||||||
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self))
|
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self))
|
||||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||||
|
@ -43,12 +47,14 @@ extension UserSection {
|
||||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
guard let user = record.object(in: context.managedObjectContext) else { return }
|
||||||
configure(
|
configure(
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
cell: cell,
|
cell: cell,
|
||||||
viewModel: .init(value: .user(user)),
|
viewModel: .init(value: .user(user), followedUsers: followedUsers, blockedUsers: blockedUsers),
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
case .bottomLoader:
|
case .bottomLoader:
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
||||||
|
@ -68,14 +74,14 @@ extension UserSection {
|
||||||
|
|
||||||
static func configure(
|
static func configure(
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
|
authContext: AuthContext,
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
cell: UserTableViewCell,
|
cell: UserTableViewCell,
|
||||||
viewModel: UserTableViewCell.ViewModel,
|
viewModel: UserTableViewCell.ViewModel,
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
) {
|
) {
|
||||||
|
|
||||||
cell.configure(
|
cell.configure(
|
||||||
meUserID: context.authenticationService.mastodonAuthenticationBoxes.first?.userID,
|
me: authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
delegate: configuration.userTableViewCellDelegate
|
delegate: configuration.userTableViewCellDelegate
|
||||||
|
|
|
@ -23,7 +23,7 @@ extension DataSourceFacade {
|
||||||
dependency: dependency,
|
dependency: dependency,
|
||||||
user: user
|
user: user
|
||||||
)
|
)
|
||||||
case .none:
|
case .none, .loading:
|
||||||
break //no-op
|
break //no-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,12 @@ extension FamiliarFollowersViewController: UserTableViewCellDelegate {
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
tableView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
extension FamiliarFollowersViewModel {
|
extension FamiliarFollowersViewModel {
|
||||||
func setupDiffableDataSource(
|
func setupDiffableDataSource(
|
||||||
|
@ -15,9 +16,12 @@ extension FamiliarFollowersViewModel {
|
||||||
diffableDataSource = UserSection.diffableDataSource(
|
diffableDataSource = UserSection.diffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
configuration: UserSection.Configuration(
|
configuration: UserSection.Configuration(
|
||||||
userTableViewCellDelegate: userTableViewCellDelegate
|
userTableViewCellDelegate: userTableViewCellDelegate
|
||||||
)
|
),
|
||||||
|
followedUsers: followedUserIds.eraseToAnyPublisher(),
|
||||||
|
blockedUsers: blockedUserIds.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
|
|
||||||
userFetchedResultsController.$records
|
userFetchedResultsController.$records
|
||||||
|
|
|
@ -11,8 +11,7 @@ import MastodonCore
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
|
||||||
final class FamiliarFollowersViewModel {
|
final class FamiliarFollowersViewModel: FollowedBlockedUserIdProviding {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
// input
|
// input
|
||||||
|
@ -25,6 +24,9 @@ final class FamiliarFollowersViewModel {
|
||||||
// output
|
// output
|
||||||
var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>?
|
var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>?
|
||||||
|
|
||||||
|
var followedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
var blockedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
|
||||||
init(context: AppContext, authContext: AuthContext) {
|
init(context: AppContext, authContext: AuthContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.authContext = authContext
|
self.authContext = authContext
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import MastodonCore
|
||||||
|
|
||||||
|
protocol FollowedBlockedUserIdProviding {
|
||||||
|
var context: AppContext { get }
|
||||||
|
var authContext: AuthContext { get }
|
||||||
|
|
||||||
|
var followedUserIds: CurrentValueSubject<[String], Never> { get }
|
||||||
|
var blockedUserIds: CurrentValueSubject<[String], Never> { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FollowedBlockedUserIdProviding {
|
||||||
|
func fetchFollowedBlockedUserIds() async throws {
|
||||||
|
let followingIds = try await context.apiService.following(
|
||||||
|
userID: authContext.mastodonAuthenticationBox.userID,
|
||||||
|
maxID: nil,
|
||||||
|
authenticationBox: authContext.mastodonAuthenticationBox
|
||||||
|
).value.map { $0.id }
|
||||||
|
|
||||||
|
let blockedIds = try await context.apiService.getBlocked(
|
||||||
|
authenticationBox: authContext.mastodonAuthenticationBox
|
||||||
|
).value.map { $0.id }
|
||||||
|
|
||||||
|
Task { @MainActor in
|
||||||
|
followedUserIds.send(followingIds)
|
||||||
|
blockedUserIds.send(blockedIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -127,7 +127,12 @@ extension FollowerListViewController: UserTableViewCellDelegate {
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
tableView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,12 @@ extension FollowerListViewModel {
|
||||||
diffableDataSource = UserSection.diffableDataSource(
|
diffableDataSource = UserSection.diffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
configuration: UserSection.Configuration(
|
configuration: UserSection.Configuration(
|
||||||
userTableViewCellDelegate: userTableViewCellDelegate
|
userTableViewCellDelegate: userTableViewCellDelegate
|
||||||
)
|
),
|
||||||
|
followedUsers: followedUserIds.eraseToAnyPublisher(),
|
||||||
|
blockedUsers: blockedUserIds.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
|
|
||||||
// workaround to append loader wrong animation issue
|
// workaround to append loader wrong animation issue
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Foundation
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
extension FollowerListViewModel {
|
extension FollowerListViewModel {
|
||||||
class State: GKState {
|
class State: GKState {
|
||||||
|
@ -155,6 +156,8 @@ extension FollowerListViewModel.State {
|
||||||
)
|
)
|
||||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch \(response.value.count) followers")
|
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch \(response.value.count) followers")
|
||||||
|
|
||||||
|
try await viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
|
||||||
var hasNewAppend = false
|
var hasNewAppend = false
|
||||||
var userIDs = viewModel.userFetchedResultsController.userIDs
|
var userIDs = viewModel.userFetchedResultsController.userIDs
|
||||||
for user in response.value {
|
for user in response.value {
|
||||||
|
|
|
@ -13,8 +13,7 @@ import GameplayKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
|
||||||
final class FollowerListViewModel {
|
final class FollowerListViewModel: FollowedBlockedUserIdProviding {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
// input
|
// input
|
||||||
|
@ -26,6 +25,9 @@ final class FollowerListViewModel {
|
||||||
@Published var domain: String?
|
@Published var domain: String?
|
||||||
@Published var userID: String?
|
@Published var userID: String?
|
||||||
|
|
||||||
|
var followedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
var blockedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
|
||||||
// output
|
// output
|
||||||
var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>?
|
var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>?
|
||||||
private(set) lazy var stateMachine: GKStateMachine = {
|
private(set) lazy var stateMachine: GKStateMachine = {
|
||||||
|
|
|
@ -125,7 +125,12 @@ extension FollowingListViewController: UserTableViewCellDelegate {
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
tableView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,12 @@ extension FollowingListViewModel {
|
||||||
diffableDataSource = UserSection.diffableDataSource(
|
diffableDataSource = UserSection.diffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
configuration: UserSection.Configuration(
|
configuration: UserSection.Configuration(
|
||||||
userTableViewCellDelegate: userTableViewCellDelegate
|
userTableViewCellDelegate: userTableViewCellDelegate
|
||||||
)
|
),
|
||||||
|
followedUsers: followedUserIds.eraseToAnyPublisher(),
|
||||||
|
blockedUsers: blockedUserIds.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
|
|
||||||
// workaround to append loader wrong animation issue
|
// workaround to append loader wrong animation issue
|
||||||
|
|
|
@ -159,6 +159,8 @@ extension FollowingListViewModel.State {
|
||||||
hasNewAppend = true
|
hasNewAppend = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try await viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
|
||||||
let maxID = response.link?.maxID
|
let maxID = response.link?.maxID
|
||||||
|
|
||||||
if hasNewAppend, maxID != nil {
|
if hasNewAppend, maxID != nil {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import GameplayKit
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
final class FollowingListViewModel {
|
final class FollowingListViewModel: FollowedBlockedUserIdProviding {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ final class FollowingListViewModel {
|
||||||
return stateMachine
|
return stateMachine
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var followedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
var blockedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
authContext: AuthContext,
|
authContext: AuthContext,
|
||||||
|
|
|
@ -68,7 +68,9 @@ extension FavoritedByViewController {
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
viewModel.setupDiffableDataSource(
|
viewModel.setupDiffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
userTableViewCellDelegate: self
|
userTableViewCellDelegate: self,
|
||||||
|
followedUsers: viewModel.followedUserIds.eraseToAnyPublisher(),
|
||||||
|
blockedUsers: viewModel.blockedUserIds.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
|
|
||||||
// setup batch fetch
|
// setup batch fetch
|
||||||
|
@ -117,7 +119,12 @@ extension FavoritedByViewController: UserTableViewCellDelegate {
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
tableView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,9 @@ extension RebloggedByViewController {
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
viewModel.setupDiffableDataSource(
|
viewModel.setupDiffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
userTableViewCellDelegate: self
|
userTableViewCellDelegate: self,
|
||||||
|
followedUsers: viewModel.followedUserIds.eraseToAnyPublisher(),
|
||||||
|
blockedUsers: viewModel.blockedUserIds.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
|
|
||||||
// setup batch fetch
|
// setup batch fetch
|
||||||
|
@ -117,7 +119,12 @@ extension RebloggedByViewController: UserTableViewCellDelegate {
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
tableView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,25 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
|
import Combine
|
||||||
|
|
||||||
extension UserListViewModel {
|
extension UserListViewModel {
|
||||||
@MainActor
|
@MainActor
|
||||||
func setupDiffableDataSource(
|
func setupDiffableDataSource(
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
userTableViewCellDelegate: UserTableViewCellDelegate?
|
userTableViewCellDelegate: UserTableViewCellDelegate?,
|
||||||
|
followedUsers: AnyPublisher<[String], Never>,
|
||||||
|
blockedUsers: AnyPublisher<[String], Never>
|
||||||
) {
|
) {
|
||||||
diffableDataSource = UserSection.diffableDataSource(
|
diffableDataSource = UserSection.diffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
configuration: UserSection.Configuration(
|
configuration: UserSection.Configuration(
|
||||||
userTableViewCellDelegate: userTableViewCellDelegate
|
userTableViewCellDelegate: userTableViewCellDelegate
|
||||||
)
|
),
|
||||||
|
followedUsers: followedUsers,
|
||||||
|
blockedUsers: blockedUsers
|
||||||
)
|
)
|
||||||
|
|
||||||
// workaround to append loader wrong animation issue
|
// workaround to append loader wrong animation issue
|
||||||
|
|
|
@ -166,6 +166,8 @@ extension UserListViewModel.State {
|
||||||
hasNewAppend = true
|
hasNewAppend = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try await viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
|
||||||
let maxID = response.link?.maxID
|
let maxID = response.link?.maxID
|
||||||
|
|
||||||
if hasNewAppend, maxID != nil {
|
if hasNewAppend, maxID != nil {
|
||||||
|
|
|
@ -12,8 +12,7 @@ import CoreDataStack
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
|
||||||
final class UserListViewModel {
|
final class UserListViewModel: FollowedBlockedUserIdProviding {
|
||||||
|
|
||||||
let logger = Logger(subsystem: "UserListViewModel", category: "ViewModel")
|
let logger = Logger(subsystem: "UserListViewModel", category: "ViewModel")
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@ -38,6 +37,9 @@ final class UserListViewModel {
|
||||||
return stateMachine
|
return stateMachine
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var followedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
var blockedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
authContext: AuthContext,
|
authContext: AuthContext,
|
||||||
|
|
|
@ -8,22 +8,62 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
extension SearchHistoryUserCollectionViewCell {
|
extension SearchHistoryUserCollectionViewCell {
|
||||||
final class ViewModel {
|
final class ViewModel {
|
||||||
let value: MastodonUser
|
let value: MastodonUser
|
||||||
|
|
||||||
init(value: MastodonUser) {
|
let followedUsers: AnyPublisher<[String], Never>
|
||||||
|
let blockedUsers: AnyPublisher<[String], Never>
|
||||||
|
|
||||||
|
init(value: MastodonUser, followedUsers: AnyPublisher<[String], Never>, blockedUsers: AnyPublisher<[String], Never>) {
|
||||||
self.value = value
|
self.value = value
|
||||||
|
self.followedUsers = followedUsers
|
||||||
|
self.blockedUsers = blockedUsers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SearchHistoryUserCollectionViewCell {
|
extension SearchHistoryUserCollectionViewCell {
|
||||||
func configure(
|
func configure(
|
||||||
|
me: MastodonUser?,
|
||||||
viewModel: ViewModel,
|
viewModel: ViewModel,
|
||||||
delegate: UserViewDelegate?
|
delegate: UserViewDelegate?
|
||||||
) {
|
) {
|
||||||
userView.configure(user: viewModel.value, delegate: delegate)
|
let user = viewModel.value
|
||||||
|
|
||||||
|
userView.configure(user: user, delegate: delegate)
|
||||||
|
|
||||||
|
guard let me = me else {
|
||||||
|
return userView.setButtonState(.none)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user == me {
|
||||||
|
userView.setButtonState(.none)
|
||||||
|
} else {
|
||||||
|
userView.setButtonState(.loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
Publishers.CombineLatest(
|
||||||
|
viewModel.followedUsers,
|
||||||
|
viewModel.blockedUsers
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] followed, blocked in
|
||||||
|
if blocked.contains(where: { $0 == user.id }) {
|
||||||
|
self?.userView.setButtonState(.blocked)
|
||||||
|
} else if followed.contains(where: { $0 == user.id }) {
|
||||||
|
self?.userView.setButtonState(.unfollow)
|
||||||
|
} else {
|
||||||
|
self?.userView.setButtonState(.follow)
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.setNeedsLayout()
|
||||||
|
self?.setNeedsUpdateConstraints()
|
||||||
|
self?.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
.store(in: &_disposeBag)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,12 @@ extension SearchHistoryViewController {
|
||||||
collectionView: collectionView,
|
collectionView: collectionView,
|
||||||
searchHistorySectionHeaderCollectionReusableViewDelegate: self
|
searchHistorySectionHeaderCollectionReusableViewDelegate: self
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +138,12 @@ extension SearchHistoryViewController: SearchHistorySectionHeaderCollectionReusa
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
collectionView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ extension SearchHistoryViewModel {
|
||||||
searchHistorySectionHeaderCollectionReusableViewDelegate: SearchHistorySectionHeaderCollectionReusableViewDelegate
|
searchHistorySectionHeaderCollectionReusableViewDelegate: SearchHistorySectionHeaderCollectionReusableViewDelegate
|
||||||
) {
|
) {
|
||||||
diffableDataSource = SearchHistorySection.diffableDataSource(
|
diffableDataSource = SearchHistorySection.diffableDataSource(
|
||||||
|
viewModel: self,
|
||||||
collectionView: collectionView,
|
collectionView: collectionView,
|
||||||
|
authContext: authContext,
|
||||||
context: context,
|
context: context,
|
||||||
configuration: SearchHistorySection.Configuration(
|
configuration: SearchHistorySection.Configuration(
|
||||||
searchHistorySectionHeaderCollectionReusableViewDelegate: searchHistorySectionHeaderCollectionReusableViewDelegate
|
searchHistorySectionHeaderCollectionReusableViewDelegate: searchHistorySectionHeaderCollectionReusableViewDelegate
|
||||||
|
|
|
@ -11,8 +11,7 @@ import CoreDataStack
|
||||||
import CommonOSLog
|
import CommonOSLog
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
|
||||||
final class SearchHistoryViewModel {
|
final class SearchHistoryViewModel: FollowedBlockedUserIdProviding {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
// input
|
// input
|
||||||
|
@ -22,6 +21,8 @@ final class SearchHistoryViewModel {
|
||||||
|
|
||||||
// output
|
// output
|
||||||
var diffableDataSource: UICollectionViewDiffableDataSource<SearchHistorySection, SearchHistoryItem>?
|
var diffableDataSource: UICollectionViewDiffableDataSource<SearchHistorySection, SearchHistoryItem>?
|
||||||
|
var followedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
var blockedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
|
||||||
init(context: AppContext, authContext: AuthContext) {
|
init(context: AppContext, authContext: AuthContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
|
@ -267,7 +267,12 @@ extension SearchResultViewController: UserTableViewCellDelegate {
|
||||||
user: user.asRecord,
|
user: user.asRecord,
|
||||||
buttonState: state
|
buttonState: state
|
||||||
)
|
)
|
||||||
tableView.reloadData()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // hack: otherwise fetchinbg the blocked users will not return the user followed
|
||||||
|
Task { @MainActor in
|
||||||
|
try await self.viewModel.fetchFollowedBlockedUserIds()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,14 @@ extension SearchResultViewModel {
|
||||||
diffableDataSource = SearchResultSection.tableViewDiffableDataSource(
|
diffableDataSource = SearchResultSection.tableViewDiffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
context: context,
|
context: context,
|
||||||
|
authContext: authContext,
|
||||||
configuration: .init(
|
configuration: .init(
|
||||||
authContext: authContext,
|
authContext: authContext,
|
||||||
statusViewTableViewCellDelegate: statusTableViewCellDelegate,
|
statusViewTableViewCellDelegate: statusTableViewCellDelegate,
|
||||||
userTableViewCellDelegate: userTableViewCellDelegate
|
userTableViewCellDelegate: userTableViewCellDelegate
|
||||||
)
|
),
|
||||||
|
followedUsers: followedUserIds.eraseToAnyPublisher(),
|
||||||
|
blockedUsers: blockedUserIds.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<SearchResultSection, SearchResultItem>()
|
var snapshot = NSDiffableDataSourceSnapshot<SearchResultSection, SearchResultItem>()
|
||||||
|
|
|
@ -14,8 +14,7 @@ import CommonOSLog
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
|
||||||
final class SearchResultViewModel {
|
final class SearchResultViewModel: FollowedBlockedUserIdProviding {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
// input
|
// input
|
||||||
|
@ -49,6 +48,9 @@ final class SearchResultViewModel {
|
||||||
}()
|
}()
|
||||||
let didDataSourceUpdate = PassthroughSubject<Void, Never>()
|
let didDataSourceUpdate = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
|
var followedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
var blockedUserIds = CurrentValueSubject<[String], Never>([])
|
||||||
|
|
||||||
init(context: AppContext, authContext: AuthContext, searchScope: SearchDetailViewModel.SearchScope) {
|
init(context: AppContext, authContext: AuthContext, searchScope: SearchDetailViewModel.SearchScope) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.authContext = authContext
|
self.authContext = authContext
|
||||||
|
|
|
@ -7,13 +7,20 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
import MastodonUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
extension UserTableViewCell {
|
extension UserTableViewCell {
|
||||||
final class ViewModel {
|
final class ViewModel {
|
||||||
let value: Value
|
let value: Value
|
||||||
|
|
||||||
init(value: Value) {
|
let followedUsers: AnyPublisher<[String], Never>
|
||||||
|
let blockedUsers: AnyPublisher<[String], Never>
|
||||||
|
|
||||||
|
init(value: Value, followedUsers: AnyPublisher<[String], Never>, blockedUsers: AnyPublisher<[String], Never>) {
|
||||||
self.value = value
|
self.value = value
|
||||||
|
self.followedUsers = followedUsers
|
||||||
|
self.blockedUsers = blockedUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Value {
|
enum Value {
|
||||||
|
@ -26,7 +33,7 @@ extension UserTableViewCell {
|
||||||
extension UserTableViewCell {
|
extension UserTableViewCell {
|
||||||
|
|
||||||
func configure(
|
func configure(
|
||||||
meUserID: MastodonUser.ID?,
|
me: MastodonUser?,
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
viewModel: ViewModel,
|
viewModel: ViewModel,
|
||||||
delegate: UserTableViewCellDelegate?
|
delegate: UserTableViewCellDelegate?
|
||||||
|
@ -35,16 +42,35 @@ extension UserTableViewCell {
|
||||||
case .user(let user):
|
case .user(let user):
|
||||||
userView.configure(user: user, delegate: delegate)
|
userView.configure(user: user, delegate: delegate)
|
||||||
|
|
||||||
if user.id == meUserID {
|
guard let me = me else {
|
||||||
userView.setButtonState(.none)
|
return userView.setButtonState(.none)
|
||||||
} else if user.blockingBy.contains(where: { $0.id == meUserID }) {
|
|
||||||
userView.setButtonState(.blocked)
|
|
||||||
} else if user.followingBy.contains(where: { $0.id == meUserID }) {
|
|
||||||
userView.setButtonState(.unfollow)
|
|
||||||
} else {
|
|
||||||
userView.setButtonState(.follow)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user == me {
|
||||||
|
userView.setButtonState(.none)
|
||||||
|
} else {
|
||||||
|
userView.setButtonState(.loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
Publishers.CombineLatest(
|
||||||
|
viewModel.followedUsers,
|
||||||
|
viewModel.blockedUsers
|
||||||
|
)
|
||||||
|
.debounce(for: 0.1, scheduler: DispatchQueue.main)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] followed, blocked in
|
||||||
|
print(">>> followed.count", followed.count)
|
||||||
|
if blocked.contains(user.id) {
|
||||||
|
self?.userView.setButtonState(.blocked)
|
||||||
|
} else if followed.contains(user.id) {
|
||||||
|
self?.userView.setButtonState(.unfollow)
|
||||||
|
} else {
|
||||||
|
self?.userView.setButtonState(.follow)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
|
|
|
@ -23,9 +23,13 @@ final class UserTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
let separatorLine = UIView.separatorLine
|
let separatorLine = UIView.separatorLine
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
|
|
||||||
|
delegate = nil
|
||||||
|
disposeBag = Set<AnyCancellable>()
|
||||||
userView.prepareForReuse()
|
userView.prepareForReuse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public protocol UserViewDelegate: AnyObject {
|
||||||
public final class UserView: UIView {
|
public final class UserView: UIView {
|
||||||
|
|
||||||
public enum ButtonState {
|
public enum ButtonState {
|
||||||
case none, follow, unfollow, blocked
|
case none, loading, follow, unfollow, blocked
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentButtonState: ButtonState = .none
|
private var currentButtonState: ButtonState = .none
|
||||||
|
@ -269,6 +269,9 @@ public extension UserView {
|
||||||
prepareButtonStateLayout(for: state)
|
prepareButtonStateLayout(for: state)
|
||||||
|
|
||||||
switch state {
|
switch state {
|
||||||
|
case .loading:
|
||||||
|
followButton.setTitle(nil, for: .normal)
|
||||||
|
followButton.setBackgroundColor(Asset.Colors.disabled.color, for: .normal)
|
||||||
case .follow:
|
case .follow:
|
||||||
followButton.setTitle(L10n.Common.Controls.Friendship.follow, for: .normal)
|
followButton.setTitle(L10n.Common.Controls.Friendship.follow, for: .normal)
|
||||||
followButton.setBackgroundColor(Asset.Colors.Button.userFollow.color, for: .normal)
|
followButton.setBackgroundColor(Asset.Colors.Button.userFollow.color, for: .normal)
|
||||||
|
|
Loading…
Reference in New Issue