Implement follow/unfollow/block in UserView (IOS-140)
This commit is contained in:
parent
52fb1eff1f
commit
e2a05cd747
|
@ -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 */; };
|
||||||
|
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 */; };
|
||||||
2A33062D2987DBFA001D4C51 /* FollowersCountHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */; };
|
2A33062D2987DBFA001D4C51 /* FollowersCountHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */; };
|
||||||
|
@ -613,6 +614,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>"; };
|
||||||
|
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>"; };
|
||||||
2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountHistory.swift; sourceTree = "<group>"; };
|
2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountHistory.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2390,6 +2392,7 @@
|
||||||
DB63F7532799491600455B82 /* DataSourceFacade+SearchHistory.swift */,
|
DB63F7532799491600455B82 /* DataSourceFacade+SearchHistory.swift */,
|
||||||
DB159C2A27A17BAC0068DC77 /* DataSourceFacade+Media.swift */,
|
DB159C2A27A17BAC0068DC77 /* DataSourceFacade+Media.swift */,
|
||||||
2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */,
|
2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */,
|
||||||
|
2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */,
|
||||||
DB697DD5278F4C29004EF2F7 /* DataSourceProvider.swift */,
|
DB697DD5278F4C29004EF2F7 /* DataSourceProvider.swift */,
|
||||||
DB697DDA278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift */,
|
DB697DDA278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift */,
|
||||||
DB023D2927A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift */,
|
DB023D2927A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift */,
|
||||||
|
@ -3598,6 +3601,7 @@
|
||||||
DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */,
|
DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */,
|
||||||
DB0FCB9C27980AB6006C02E2 /* HashtagTimelineViewController+DataSourceProvider.swift in Sources */,
|
DB0FCB9C27980AB6006C02E2 /* HashtagTimelineViewController+DataSourceProvider.swift in Sources */,
|
||||||
DB63F76F279A7D1100455B82 /* NotificationTableViewCell.swift in Sources */,
|
DB63F76F279A7D1100455B82 /* NotificationTableViewCell.swift in Sources */,
|
||||||
|
2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */,
|
||||||
DB0FCB8C2796BF8D006C02E2 /* SearchViewModel+Diffable.swift in Sources */,
|
DB0FCB8C2796BF8D006C02E2 /* SearchViewModel+Diffable.swift in Sources */,
|
||||||
DBEFCD76282A143F00C0ABEA /* ReportStatusViewController.swift in Sources */,
|
DBEFCD76282A143F00C0ABEA /* ReportStatusViewController.swift in Sources */,
|
||||||
DBDFF1952805561700557A48 /* DiscoveryPostsViewModel+Diffable.swift in Sources */,
|
DBDFF1952805561700557A48 /* DiscoveryPostsViewModel+Diffable.swift in Sources */,
|
||||||
|
|
|
@ -122,6 +122,7 @@ extension SearchResultSection {
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
) {
|
) {
|
||||||
cell.configure(
|
cell.configure(
|
||||||
|
meUserID: context.authenticationService.mastodonAuthenticationBoxes.first?.userID,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
delegate: configuration.userTableViewCellDelegate
|
delegate: configuration.userTableViewCellDelegate
|
||||||
|
|
|
@ -42,6 +42,7 @@ extension UserSection {
|
||||||
context.managedObjectContext.performAndWait {
|
context.managedObjectContext.performAndWait {
|
||||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
guard let user = record.object(in: context.managedObjectContext) else { return }
|
||||||
configure(
|
configure(
|
||||||
|
context: context,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
cell: cell,
|
cell: cell,
|
||||||
viewModel: .init(value: .user(user)),
|
viewModel: .init(value: .user(user)),
|
||||||
|
@ -66,6 +67,7 @@ extension UserSection {
|
||||||
extension UserSection {
|
extension UserSection {
|
||||||
|
|
||||||
static func configure(
|
static func configure(
|
||||||
|
context: AppContext,
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
cell: UserTableViewCell,
|
cell: UserTableViewCell,
|
||||||
viewModel: UserTableViewCell.ViewModel,
|
viewModel: UserTableViewCell.ViewModel,
|
||||||
|
@ -73,6 +75,7 @@ extension UserSection {
|
||||||
) {
|
) {
|
||||||
|
|
||||||
cell.configure(
|
cell.configure(
|
||||||
|
meUserID: context.authenticationService.mastodonAuthenticationBoxes.first?.userID,
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
delegate: configuration.userTableViewCellDelegate
|
delegate: configuration.userTableViewCellDelegate
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MastodonUI
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonCore
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension DataSourceFacade {
|
||||||
|
static func responseToUserViewButtonAction(
|
||||||
|
dependency: NeedsDependency & AuthContextProvider,
|
||||||
|
user: ManagedObjectRecord<MastodonUser>,
|
||||||
|
buttonState: UserView.ButtonState
|
||||||
|
) async throws {
|
||||||
|
switch buttonState {
|
||||||
|
case .follow, .unfollow:
|
||||||
|
try await DataSourceFacade.responseToUserFollowAction(
|
||||||
|
dependency: dependency,
|
||||||
|
user: user
|
||||||
|
)
|
||||||
|
case .blocked:
|
||||||
|
try await DataSourceFacade.responseToUserBlockAction(
|
||||||
|
dependency: dependency,
|
||||||
|
user: user
|
||||||
|
)
|
||||||
|
case .none:
|
||||||
|
break //no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
|
import MastodonUI
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
final class FamiliarFollowersViewController: UIViewController, NeedsDependency {
|
final class FamiliarFollowersViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -91,4 +93,15 @@ extension FamiliarFollowersViewController: UITableViewDelegate, AutoGenerateTabl
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UserTableViewCellDelegate
|
// MARK: - UserTableViewCellDelegate
|
||||||
extension FamiliarFollowersViewController: UserTableViewCellDelegate { }
|
extension FamiliarFollowersViewController: UserTableViewCellDelegate {
|
||||||
|
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||||
|
Task {
|
||||||
|
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||||
|
dependency: self,
|
||||||
|
user: user.asRecord,
|
||||||
|
buttonState: state
|
||||||
|
)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import Combine
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
final class FollowerListViewController: UIViewController, NeedsDependency {
|
final class FollowerListViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -118,4 +119,15 @@ extension FollowerListViewController: UITableViewDelegate, AutoGenerateTableView
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UserTableViewCellDelegate
|
// MARK: - UserTableViewCellDelegate
|
||||||
extension FollowerListViewController: UserTableViewCellDelegate { }
|
extension FollowerListViewController: UserTableViewCellDelegate {
|
||||||
|
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||||
|
Task {
|
||||||
|
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||||
|
dependency: self,
|
||||||
|
user: user.asRecord,
|
||||||
|
buttonState: state
|
||||||
|
)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import Combine
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
final class FollowingListViewController: UIViewController, NeedsDependency {
|
final class FollowingListViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -116,4 +117,15 @@ extension FollowingListViewController: UITableViewDelegate, AutoGenerateTableVie
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UserTableViewCellDelegate
|
// MARK: - UserTableViewCellDelegate
|
||||||
extension FollowingListViewController: UserTableViewCellDelegate { }
|
extension FollowingListViewController: UserTableViewCellDelegate {
|
||||||
|
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||||
|
Task {
|
||||||
|
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||||
|
dependency: self,
|
||||||
|
user: user.asRecord,
|
||||||
|
buttonState: state
|
||||||
|
)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import GameplayKit
|
||||||
import Combine
|
import Combine
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
|
import MastodonUI
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
final class FavoritedByViewController: UIViewController, NeedsDependency {
|
final class FavoritedByViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -107,4 +109,15 @@ extension FavoritedByViewController: UITableViewDelegate, AutoGenerateTableViewD
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UserTableViewCellDelegate
|
// MARK: - UserTableViewCellDelegate
|
||||||
extension FavoritedByViewController: UserTableViewCellDelegate { }
|
extension FavoritedByViewController: UserTableViewCellDelegate {
|
||||||
|
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||||
|
Task {
|
||||||
|
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||||
|
dependency: self,
|
||||||
|
user: user.asRecord,
|
||||||
|
buttonState: state
|
||||||
|
)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import GameplayKit
|
||||||
import Combine
|
import Combine
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
|
import MastodonUI
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
final class RebloggedByViewController: UIViewController, NeedsDependency {
|
final class RebloggedByViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -107,4 +109,15 @@ extension RebloggedByViewController: UITableViewDelegate, AutoGenerateTableViewD
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UserTableViewCellDelegate
|
// MARK: - UserTableViewCellDelegate
|
||||||
extension RebloggedByViewController: UserTableViewCellDelegate { }
|
extension RebloggedByViewController: UserTableViewCellDelegate {
|
||||||
|
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||||
|
Task {
|
||||||
|
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||||
|
dependency: self,
|
||||||
|
user: user.asRecord,
|
||||||
|
buttonState: state
|
||||||
|
)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,6 @@ final class UserListViewModel {
|
||||||
)
|
)
|
||||||
// end init
|
// end init
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UserListViewModel {
|
extension UserListViewModel {
|
||||||
|
|
|
@ -22,6 +22,6 @@ extension SearchHistoryUserCollectionViewCell {
|
||||||
func configure(
|
func configure(
|
||||||
viewModel: ViewModel
|
viewModel: ViewModel
|
||||||
) {
|
) {
|
||||||
userView.configure(user: viewModel.value)
|
userView.configure(user: viewModel.value, delegate: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,10 @@ import MastodonCore
|
||||||
import Meta
|
import Meta
|
||||||
|
|
||||||
extension UserView {
|
extension UserView {
|
||||||
public func configure(user: MastodonUser) {
|
public func configure(user: MastodonUser, delegate: UserViewDelegate?) {
|
||||||
|
self.delegate = delegate
|
||||||
|
viewModel.user = user
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest(
|
||||||
user.publisher(for: \.avatar),
|
user.publisher(for: \.avatar),
|
||||||
UserDefaults.shared.publisher(for: \.preferredStaticAvatar)
|
UserDefaults.shared.publisher(for: \.preferredStaticAvatar)
|
||||||
|
|
|
@ -26,13 +26,25 @@ extension UserTableViewCell {
|
||||||
extension UserTableViewCell {
|
extension UserTableViewCell {
|
||||||
|
|
||||||
func configure(
|
func configure(
|
||||||
|
meUserID: MastodonUser.ID?,
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
viewModel: ViewModel,
|
viewModel: ViewModel,
|
||||||
delegate: UserTableViewCellDelegate?
|
delegate: UserTableViewCellDelegate?
|
||||||
) {
|
) {
|
||||||
switch viewModel.value {
|
switch viewModel.value {
|
||||||
case .user(let user):
|
case .user(let user):
|
||||||
userView.configure(user: user)
|
userView.configure(user: user, delegate: delegate)
|
||||||
|
|
||||||
|
if user.id == meUserID {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
|
|
|
@ -13,7 +13,7 @@ import MastodonLocalization
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
protocol UserTableViewCellDelegate: AnyObject { }
|
protocol UserTableViewCellDelegate: UserViewDelegate, AnyObject { }
|
||||||
|
|
||||||
final class UserTableViewCell: UITableViewCell {
|
final class UserTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
@ -21,7 +21,9 @@ final class UserTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
let userView: UserView = {
|
let userView: UserView = {
|
||||||
let view = UserView()
|
let view = UserView()
|
||||||
// view.setButtonState(.follow)
|
[UserView.ButtonState.follow, UserView.ButtonState.unfollow, UserView.ButtonState.blocked].randomElement().map {
|
||||||
|
view.setButtonState($0)
|
||||||
|
}
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.150",
|
||||||
|
"blue" : "0x30",
|
||||||
|
"green" : "0x3B",
|
||||||
|
"red" : "0xFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.800",
|
||||||
|
"green" : "0.227",
|
||||||
|
"red" : "0.337"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.150",
|
||||||
|
"blue" : "0.988",
|
||||||
|
"green" : "0.173",
|
||||||
|
"red" : "0.337"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,9 @@ public enum Asset {
|
||||||
public static let inactive = ColorAsset(name: "Colors/Button/inactive")
|
public static let inactive = ColorAsset(name: "Colors/Button/inactive")
|
||||||
public static let tagFollow = ColorAsset(name: "Colors/Button/tagFollow")
|
public static let tagFollow = ColorAsset(name: "Colors/Button/tagFollow")
|
||||||
public static let tagUnfollow = ColorAsset(name: "Colors/Button/tagUnfollow")
|
public static let tagUnfollow = ColorAsset(name: "Colors/Button/tagUnfollow")
|
||||||
|
public static let userBlocked = ColorAsset(name: "Colors/Button/userBlocked")
|
||||||
|
public static let userFollow = ColorAsset(name: "Colors/Button/userFollow")
|
||||||
|
public static let userFollowing = ColorAsset(name: "Colors/Button/userFollowing")
|
||||||
}
|
}
|
||||||
public enum Icon {
|
public enum Icon {
|
||||||
public static let plus = ColorAsset(name: "Colors/Icon/plus")
|
public static let plus = ColorAsset(name: "Colors/Icon/plus")
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by MainasuK on 2022-1-19.
|
// Created by MainasuK on 2022-1-19.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreDataStack
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
@ -26,6 +27,7 @@ extension UserView {
|
||||||
@Published public var authorUsername: String?
|
@Published public var authorUsername: String?
|
||||||
@Published public var authorFollowers: Int?
|
@Published public var authorFollowers: Int?
|
||||||
@Published public var authorVerifiedLink: String?
|
@Published public var authorVerifiedLink: String?
|
||||||
|
@Published public var user: MastodonUser?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,13 @@ import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import MetaTextKit
|
import MetaTextKit
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
|
import MastodonLocalization
|
||||||
import os
|
import os
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
|
public protocol UserViewDelegate: AnyObject {
|
||||||
|
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser)
|
||||||
|
}
|
||||||
|
|
||||||
public final class UserView: UIView {
|
public final class UserView: UIView {
|
||||||
|
|
||||||
|
@ -17,6 +23,10 @@ public final class UserView: UIView {
|
||||||
case none, follow, unfollow, blocked
|
case none, follow, unfollow, blocked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var currentButtonState: ButtonState = .none
|
||||||
|
|
||||||
|
public weak var delegate: UserViewDelegate?
|
||||||
|
|
||||||
public var disposeBag = Set<AnyCancellable>()
|
public var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
public private(set) lazy var viewModel: ViewModel = {
|
public private(set) lazy var viewModel: ViewModel = {
|
||||||
|
@ -92,7 +102,6 @@ public final class UserView: UIView {
|
||||||
private let followButton: UIButton = {
|
private let followButton: UIButton = {
|
||||||
let button = FollowButton()
|
let button = FollowButton()
|
||||||
button.cornerRadius = 10
|
button.cornerRadius = 10
|
||||||
button.setTitle("Follow", for: .normal)
|
|
||||||
button.isHidden = true
|
button.isHidden = true
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
@ -105,22 +114,7 @@ public final class UserView: UIView {
|
||||||
|
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
public func setButtonState(_ state: ButtonState) {
|
|
||||||
switch state {
|
|
||||||
case .follow, .unfollow, .blocked:
|
|
||||||
verifiedStackView.axis = .vertical
|
|
||||||
verifiedStackView.alignment = .leading
|
|
||||||
verifiedStackCenterSpacerView.isHidden = true
|
|
||||||
followButton.isHidden = false
|
|
||||||
case .none:
|
|
||||||
verifiedStackView.axis = .horizontal
|
|
||||||
verifiedStackView.alignment = .leading
|
|
||||||
verifiedStackCenterSpacerView.isHidden = false
|
|
||||||
followButton.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func prepareForReuse() {
|
public func prepareForReuse() {
|
||||||
disposeBag.removeAll()
|
disposeBag.removeAll()
|
||||||
|
|
||||||
|
@ -128,6 +122,7 @@ public final class UserView: UIView {
|
||||||
viewModel.authorAvatarImageURL = nil
|
viewModel.authorAvatarImageURL = nil
|
||||||
|
|
||||||
avatarButton.avatarImageView.cancelTask()
|
avatarButton.avatarImageView.cancelTask()
|
||||||
|
setButtonState(.none)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
|
@ -246,3 +241,51 @@ private final class FollowButton: RoundedEdgesButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension UserView {
|
||||||
|
private func prepareButtonStateLayout(for state: ButtonState) {
|
||||||
|
switch state {
|
||||||
|
case .none:
|
||||||
|
verifiedStackView.axis = .horizontal
|
||||||
|
verifiedStackView.alignment = .leading
|
||||||
|
verifiedStackCenterSpacerView.isHidden = false
|
||||||
|
followButton.isHidden = true
|
||||||
|
default:
|
||||||
|
verifiedStackView.axis = .vertical
|
||||||
|
verifiedStackView.alignment = .leading
|
||||||
|
verifiedStackCenterSpacerView.isHidden = true
|
||||||
|
followButton.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func didTapButton() {
|
||||||
|
guard let user = viewModel.user else { return }
|
||||||
|
delegate?.userView(self, didTapButtonWith: currentButtonState, for: user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setButtonState(_ state: ButtonState) {
|
||||||
|
currentButtonState = state
|
||||||
|
prepareButtonStateLayout(for: state)
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case .follow:
|
||||||
|
followButton.setTitle(L10n.Common.Controls.Friendship.follow, for: .normal)
|
||||||
|
followButton.setBackgroundColor(Asset.Colors.Button.userFollow.color, for: .normal)
|
||||||
|
followButton.setTitleColor(.white, for: .normal)
|
||||||
|
case .unfollow:
|
||||||
|
followButton.setTitle(L10n.Common.Controls.Friendship.following, for: .normal)
|
||||||
|
followButton.setBackgroundColor(Asset.Colors.Button.userFollowing.color, for: .normal)
|
||||||
|
followButton.setTitleColor(Asset.Colors.Button.userFollow.color, for: .normal)
|
||||||
|
case .blocked:
|
||||||
|
followButton.setTitle(L10n.Common.Controls.Friendship.blocked, for: .normal)
|
||||||
|
followButton.setBackgroundColor(Asset.Colors.Button.userBlocked.color, for: .normal)
|
||||||
|
followButton.setTitleColor(.systemRed, for: .normal)
|
||||||
|
|
||||||
|
case .none:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
followButton.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
|
||||||
|
followButton.titleLabel?.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .boldSystemFont(ofSize: 15))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue