Duplicate methods to work with Entity.Account

This is preparation, but as you know: Proper Preperation and Planning Prevent Piss Poor Performance
This commit is contained in:
Nathan Mattes 2023-10-19 14:39:07 +02:00
parent e640befd9e
commit b92ffe8a46
9 changed files with 319 additions and 24 deletions

View File

@ -8,6 +8,7 @@
import UIKit
import CoreDataStack
import MastodonCore
import MastodonSDK
extension DataSourceFacade {
static func responseToUserBlockAction(
@ -29,5 +30,26 @@ extension DataSourceFacade {
authenticationBox: authBox
)
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
} // end func
}
static func responseToUserBlockAction(
dependency: NeedsDependency & AuthContextProvider,
user: Mastodon.Entity.Account
) async throws {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
let apiService = dependency.context.apiService
let authBox = dependency.authContext.mastodonAuthenticationBox
_ = try await apiService.toggleBlock(
user: user,
authenticationBox: authBox
)
try await dependency.context.apiService.getBlocked(
authenticationBox: authBox
)
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
}
}

View File

@ -25,7 +25,22 @@ extension DataSourceFacade {
authenticationBox: dependency.authContext.mastodonAuthenticationBox
)
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
} // end func
}
static func responseToUserFollowAction(
dependency: NeedsDependency & AuthContextProvider,
user: Mastodon.Entity.Account
) async throws {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
_ = try await dependency.context.apiService.toggleFollow(
user: user,
authenticationBox: dependency.authContext.mastodonAuthenticationBox
)
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
}
}
extension DataSourceFacade {

View File

@ -64,16 +64,52 @@ extension DataSourceFacade {
break //no-op
}
}
}
extension UserTableViewCellDelegate where Self: NeedsDependency & AuthContextProvider {
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
Task {
try await DataSourceFacade.responseToUserViewButtonAction(
dependency: self,
user: user.asRecord,
buttonState: state
)
static func responseToUserViewButtonAction(
dependency: NeedsDependency & AuthContextProvider,
user: Mastodon.Entity.Account,
buttonState: UserView.ButtonState
) async throws {
switch buttonState {
case .follow:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
user: user
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followingUserIds.append(user.id)
case .request:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
user: user
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followRequestedUserIDs.append(user.id)
case .unfollow:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
user: user
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followingUserIds.removeAll(where: { $0 == user.id })
case .blocked:
try await DataSourceFacade.responseToUserBlockAction(
dependency: dependency,
user: user
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.blockedUserIds.append(user.id)
case .pending:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
user: user
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followRequestedUserIDs.removeAll(where: { $0 == user.id })
case .none, .loading:
break //no-op
}
}
}

View File

@ -13,6 +13,7 @@ import MastodonLocalization
import MastodonMeta
import MastodonCore
import Meta
import MastodonSDK
extension UserView {
public func configure(user: MastodonUser, delegate: UserViewDelegate?) {
@ -63,4 +64,8 @@ extension UserView {
.assign(to: \.authorVerifiedLink, on: viewModel)
.store(in: &disposeBag)
}
func configure(with account: Mastodon.Entity.Account) {
//TODO: Implement
}
}

View File

@ -9,6 +9,8 @@ import UIKit
import CoreDataStack
import MastodonUI
import Combine
import MastodonCore
import MastodonSDK
extension UserTableViewCell {
final class ViewModel {
@ -72,5 +74,27 @@ extension UserTableViewCell {
self.delegate = delegate
}
}
extension UserTableViewCellDelegate where Self: NeedsDependency & AuthContextProvider {
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
Task {
try await DataSourceFacade.responseToUserViewButtonAction(
dependency: self,
user: user.asRecord,
buttonState: state
)
}
}
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: Mastodon.Entity.Account) {
Task {
try await DataSourceFacade.responseToUserViewButtonAction(
dependency: self,
user: user,
buttonState: state
)
}
}
}

View File

@ -148,7 +148,98 @@ extension APIService {
let response = try result.get()
return response
}
public func toggleBlock(
user: Mastodon.Entity.Account,
authenticationBox: MastodonAuthenticationBox
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
fatalError("Not implemented yet")
// let managedObjectContext = backgroundManagedObjectContext
// let blockContext: MastodonBlockContext = try await managedObjectContext.performChanges {
// let authentication = authenticationBox.authentication
//
// guard
// let user = user.object(in: managedObjectContext),
// let me = authentication.user(in: managedObjectContext)
// else {
// throw APIError.implicit(.badRequest)
// }
//
// let isBlocking = user.blockingBy.contains(me)
// let isFollowing = user.followingBy.contains(me)
// // toggle block state
// user.update(isBlocking: !isBlocking, by: me)
// // update follow state implicitly
// if !isBlocking {
// // will do block action. set to unfollow
// user.update(isFollowing: false, by: me)
// }
//
// return MastodonBlockContext(
// sourceUserID: me.id,
// targetUserID: user.id,
// targetUsername: user.username,
// isBlocking: isBlocking,
// isFollowing: isFollowing
// )
// }
//
// let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
// do {
// if blockContext.isBlocking {
// let response = try await Mastodon.API.Account.unblock(
// session: session,
// domain: authenticationBox.domain,
// accountID: blockContext.targetUserID,
// authorization: authenticationBox.userAuthorization
// ).singleOutput()
// result = .success(response)
// } else {
// let response = try await Mastodon.API.Account.block(
// session: session,
// domain: authenticationBox.domain,
// accountID: blockContext.targetUserID,
// authorization: authenticationBox.userAuthorization
// ).singleOutput()
// result = .success(response)
// }
// } catch {
// result = .failure(error)
// }
//
// try await managedObjectContext.performChanges {
// let authentication = authenticationBox.authentication
//
// guard
// let user = user.object(in: managedObjectContext),
// let me = authentication.user(in: managedObjectContext)
// else { return }
//
//
// switch result {
// case .success(let response):
// let relationship = response.value
// Persistence.MastodonUser.update(
// mastodonUser: user,
// context: Persistence.MastodonUser.RelationshipContext(
// entity: relationship,
// me: me,
// networkDate: response.networkDate
// )
// )
// case .failure:
// // rollback
// user.update(isBlocking: blockContext.isBlocking, by: me)
// user.update(isFollowing: blockContext.isFollowing, by: me)
// }
// }
//
// let response = try result.get()
// return response
}
}
extension MastodonUser {

View File

@ -38,11 +38,11 @@ extension APIService {
let _followContext: MastodonFollowContext? = try await managedObjectContext.performChanges {
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return nil }
guard let user = user.object(in: managedObjectContext) else { return nil }
let isFollowing = user.followingBy.contains(me)
let isPending = user.followRequestedBy.contains(me)
let needsUnfollow = isFollowing || isPending
if needsUnfollow {
// unfollow
user.update(isFollowing: false, by: me)
@ -66,11 +66,11 @@ extension APIService {
)
return context
}
guard let followContext = _followContext else {
throw APIError.implicit(.badRequest)
}
// request follow or unfollow
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
do {
@ -85,13 +85,13 @@ extension APIService {
} catch {
result = .failure(error)
}
// update friendship state
try await managedObjectContext.performChanges {
guard let me = authenticationBox.authentication.user(in: managedObjectContext),
let user = user.object(in: managedObjectContext)
else { return }
switch result {
case .success(let response):
Persistence.MastodonUser.update(
@ -108,11 +108,105 @@ extension APIService {
user.update(isFollowRequested: followContext.isPending, by: me)
}
}
let response = try result.get()
return response
}
public func toggleFollow(
user: Mastodon.Entity.Account,
authenticationBox: MastodonAuthenticationBox
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
fatalError("Not implemented yet")
/**
1. Get relation between me and user
2. check if I follow them:
if so: unfollow
if not: follow
3. return result of 2.
*/
// let managedObjectContext = backgroundManagedObjectContext
// let _followContext: MastodonFollowContext? = try await managedObjectContext.performChanges {
// guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return nil }
// guard let user = user.object(in: managedObjectContext) else { return nil }
//
// let isFollowing = user.followingBy.contains(me)
// let isPending = user.followRequestedBy.contains(me)
// let needsUnfollow = isFollowing || isPending
//
// if needsUnfollow {
// // unfollow
// user.update(isFollowing: false, by: me)
// user.update(isFollowRequested: false, by: me)
// } else {
// // follow
// if user.locked {
// user.update(isFollowing: false, by: me)
// user.update(isFollowRequested: true, by: me)
// } else {
// user.update(isFollowing: true, by: me)
// user.update(isFollowRequested: false, by: me)
// }
// }
// let context = MastodonFollowContext(
// sourceUserID: me.id,
// targetUserID: user.id,
// isFollowing: isFollowing,
// isPending: isPending,
// needsUnfollow: needsUnfollow
// )
// return context
// }
//
// guard let followContext = _followContext else {
// throw APIError.implicit(.badRequest)
// }
//
// // request follow or unfollow
// let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
// do {
// let response = try await Mastodon.API.Account.follow(
// session: session,
// domain: authenticationBox.domain,
// accountID: followContext.targetUserID,
// followQueryType: followContext.needsUnfollow ? .unfollow : .follow(query: .init()),
// authorization: authenticationBox.userAuthorization
// ).singleOutput()
// result = .success(response)
// } catch {
// result = .failure(error)
// }
//
// // update friendship state
// try await managedObjectContext.performChanges {
// guard let me = authenticationBox.authentication.user(in: managedObjectContext),
// let user = user.object(in: managedObjectContext)
// else { return }
//
// switch result {
// case .success(let response):
// Persistence.MastodonUser.update(
// mastodonUser: user,
// context: Persistence.MastodonUser.RelationshipContext(
// entity: response.value,
// me: me,
// networkDate: response.networkDate
// )
// )
// case .failure:
// // rollback
// user.update(isFollowing: followContext.isFollowing, by: me)
// user.update(isFollowRequested: followContext.isPending, by: me)
// }
// }
//
// let response = try result.get()
// return response
}
public func toggleShowReblogs(
for user: ManagedObjectRecord<MastodonUser>,
authenticationBox: MastodonAuthenticationBox

View File

@ -13,6 +13,7 @@ import MastodonCore
import MastodonMeta
import MastodonAsset
import MastodonLocalization
import MastodonSDK
extension UserView {
public final class ViewModel: ObservableObject {
@ -26,6 +27,7 @@ extension UserView {
@Published public var authorFollowers: Int?
@Published public var authorVerifiedLink: String?
@Published public var user: MastodonUser?
@Published public var account: Mastodon.Entity.Account?
}
}

View File

@ -12,9 +12,11 @@ import MastodonAsset
import MastodonLocalization
import os
import CoreDataStack
import MastodonSDK
public protocol UserViewDelegate: AnyObject {
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser)
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: Mastodon.Entity.Account)
}
public final class UserView: UIView {
@ -251,9 +253,12 @@ public extension UserView {
}
}
@objc private func didTapButton() {
guard let user = viewModel.user else { return }
delegate?.userView(self, didTapButtonWith: currentButtonState, for: user)
@objc private func didTapFollowButton() {
if let user = viewModel.user {
delegate?.userView(self, didTapButtonWith: currentButtonState, for: user)
} else if let account = viewModel.account {
delegate?.userView(self, didTapButtonWith: currentButtonState, for: account)
}
}
func setButtonState(_ state: ButtonState) {
@ -310,7 +315,8 @@ public extension UserView {
followButton.configuration?.baseBackgroundColor = .clear
}
followButton.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
followButton.addTarget(self, action: #selector(didTapFollowButton), for: .touchUpInside)
followButton.titleLabel?.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .boldSystemFont(ofSize: 15))
}
}