Add some actions to Relationship-action-button (IOS-192)

Well, it's basically just unblock, unmute or follow/unfollow
This commit is contained in:
Nathan Mattes 2023-12-27 23:48:16 +01:00
parent 59fe79fe49
commit 6225c50008
9 changed files with 160 additions and 203 deletions

View File

@ -14,27 +14,6 @@ extension DataSourceFacade {
static func responseToUserBlockAction(
dependency: NeedsDependency & AuthContextProvider,
account: 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(
account: account,
authenticationBox: authBox
)
try await dependency.context.apiService.getBlocked(
authenticationBox: authBox
)
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
}
static func responseToUserBlockAction(
dependency: NeedsDependency & AuthContextProvider,
user: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Relationship {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
@ -43,7 +22,7 @@ extension DataSourceFacade {
let authBox = dependency.authContext.mastodonAuthenticationBox
let response = try await apiService.toggleBlock(
user: user,
account: account,
authenticationBox: authBox
)

View File

@ -229,7 +229,7 @@ extension DataSourceFacade {
Task {
let newRelationship = try await DataSourceFacade.responseToUserBlockAction(
dependency: dependency,
user: menuContext.author
account: menuContext.author
)
if let completion {

View File

@ -21,21 +21,21 @@ extension DataSourceFacade {
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followingUserIds.append(account.id)
case .request:
_ = try await DataSourceFacade.responseToUserFollowAction(
_ = try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
account: account
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followRequestedUserIDs.append(account.id)
case .unfollow:
_ = try await DataSourceFacade.responseToUserFollowAction(
_ = try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
account: account
)
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followingUserIds.removeAll(where: { $0 == account.id })
case .blocked:
try await DataSourceFacade.responseToUserBlockAction(
_ = try await DataSourceFacade.responseToUserBlockAction(
dependency: dependency,
account: account
)
@ -43,7 +43,7 @@ extension DataSourceFacade {
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.blockedUserIds.append(account.id)
case .pending:
_ = try await DataSourceFacade.responseToUserFollowAction(
_ = try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
account: account
)

View File

@ -686,62 +686,65 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
profileHeaderView: ProfileHeaderView,
relationshipButtonDidPressed button: ProfileRelationshipActionButton
) {
// let relationshipActionSet = viewModel.relationshipViewModel.optionSet ?? .none
#warning("TODO: Implement")
// handle edit logic for editable profile
// handle relationship logic for non-editable profile
if viewModel.me == viewModel.account {
// // do nothing when updating
guard !viewModel.isUpdating else { return }
editProfile()
} else {
editRelationship()
}
}
let profileHeaderViewModel = profileHeaderViewController.viewModel
guard let profileAboutViewModel = profilePagingViewController.viewModel.profileAboutViewController.viewModel else { return }
let isEdited = profileHeaderViewModel.isEdited || profileAboutViewModel.isEdited
if isEdited {
// update profile when edited
viewModel.isUpdating = true
Task { @MainActor in
do {
// TODO: handle error
let updatedAccount = try await viewModel.updateProfileInfo(
headerProfileInfo: profileHeaderViewModel.profileInfoEditing,
aboutProfileInfo: profileAboutViewModel.profileInfoEditing
).value
self.viewModel.isEditing = false
self.viewModel.account = updatedAccount
} catch {
let alertController = UIAlertController(
for: error,
title: L10n.Common.Alerts.EditProfileFailure.title,
preferredStyle: .alert
)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
alertController.addAction(okAction)
self.present(alertController, animated: true)
private func editProfile() {
// do nothing when updating
guard !viewModel.isUpdating else { return }
let profileHeaderViewModel = profileHeaderViewController.viewModel
guard let profileAboutViewModel = profilePagingViewController.viewModel.profileAboutViewController.viewModel else { return }
let isEdited = profileHeaderViewModel.isEdited || profileAboutViewModel.isEdited
if isEdited {
// update profile when edited
viewModel.isUpdating = true
Task { @MainActor in
do {
// TODO: handle error
let updatedAccount = try await viewModel.updateProfileInfo(
headerProfileInfo: profileHeaderViewModel.profileInfoEditing,
aboutProfileInfo: profileAboutViewModel.profileInfoEditing
).value
self.viewModel.isEditing = false
self.viewModel.account = updatedAccount
} catch {
let alertController = UIAlertController(
for: error,
title: L10n.Common.Alerts.EditProfileFailure.title,
preferredStyle: .alert
)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
alertController.addAction(okAction)
self.present(alertController, animated: true)
}
// finish updating
self.viewModel.isUpdating = false
}
} else {
// set `updating` then toggle `edit` state
viewModel.isUpdating = true
viewModel.fetchEditProfileInfo()
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
guard let self = self else { return }
defer {
// finish updating
self.viewModel.isUpdating = false
}
// finish updating
self.viewModel.isUpdating = false
} // end Task
} else {
// set `updating` then toggle `edit` state
viewModel.isUpdating = true
viewModel.fetchEditProfileInfo()
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
guard let self = self else { return }
defer {
// finish updating
self.viewModel.isUpdating = false
}
switch completion {
switch completion {
case .failure(let error):
let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
alertController.addAction(okAction)
_ = self.coordinator.present(
scene: .alertController(alertController: alertController),
@ -751,82 +754,101 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
case .finished:
// enter editing mode
self.viewModel.isEditing.toggle()
}
} receiveValue: { [weak self] response in
guard let self = self else { return }
self.viewModel.accountForEdit = response.value
}
.store(in: &disposeBag)
}
} else {
// guard let relationshipAction = relationshipActionSet.highPriorityAction(except: .editOptions) else { return }
guard let relationship = viewModel.relationship else { return }
print(relationship)
// switch relationshipAction {
// case .none:
// break
// case .follow, .request, .pending, .following:
// guard let user = viewModel.user else { return }
// let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
// Task {
// try await DataSourceFacade.responseToUserFollowAction(
// dependency: self,
// user: record
// )
// }
// case .muting:
// guard let user = viewModel.user else { return }
// let name = user.displayNameWithFallback
//
// let alertController = UIAlertController(
// title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title,
// message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.message(name),
// preferredStyle: .alert
// )
// let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
// let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unmute, style: .default) { [weak self] _ in
// guard let self = self else { return }
// Task {
// try await DataSourceFacade.responseToUserMuteAction(
// dependency: self,
// user: record
// )
// }
// }
// alertController.addAction(unmuteAction)
// let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
// alertController.addAction(cancelAction)
// present(alertController, animated: true, completion: nil)
// case .blocking:
// guard let user = viewModel.user else { return }
// let name = user.displayNameWithFallback
//
// let alertController = UIAlertController(
// title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.title,
// message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.message(name),
// preferredStyle: .alert
// )
// let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
// let unblockAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unblock, style: .default) { [weak self] _ in
// guard let self = self else { return }
// Task {
// try await DataSourceFacade.responseToUserBlockAction(
// dependency: self,
// user: record
// )
// }
// }
// alertController.addAction(unblockAction)
// let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
// alertController.addAction(cancelAction)
// present(alertController, animated: true, completion: nil)
// case .blocked, .showReblogs, .isMyself,.followingBy, .blockingBy, .suspended, .edit, .editing, .updating:
// break
// }
} receiveValue: { [weak self] response in
guard let self = self else { return }
self.viewModel.accountForEdit = response.value
}
.store(in: &disposeBag)
}
}
private func editRelationship() {
guard let relationship = viewModel.relationship else { return }
let account = viewModel.account
if relationship.blocking {
let name = account.displayNameWithFallback
let alertController = UIAlertController(
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.title,
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.message(name),
preferredStyle: .alert
)
let unblockAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unblock, style: .default) { [weak self] _ in
guard let self else { return }
Task {
let newRelationship = try await DataSourceFacade.responseToUserBlockAction(
dependency: self,
account: account
)
self.viewModel.relationship = newRelationship
}
}
alertController.addAction(unblockAction)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
alertController.addAction(cancelAction)
coordinator.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true))
} else if relationship.muting {
let name = account.displayNameWithFallback
let alertController = UIAlertController(
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title,
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.message(name),
preferredStyle: .alert
)
let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unmute, style: .default) { [weak self] _ in
guard let self else { return }
Task {
let newRelationship = try await DataSourceFacade.responseToUserMuteAction(
dependency: self,
account: account
)
self.viewModel.relationship = newRelationship
}
}
alertController.addAction(unmuteAction)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
alertController.addAction(cancelAction)
coordinator.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true))
} else {
Task { [weak self] in
guard let self else { return }
let newRelationship = try await DataSourceFacade.responseToUserFollowAction(
dependency: self,
account: viewModel.account
)
self.viewModel.relationship = newRelationship
// update account?
// update me?
}
}
// switch relationshipAction {
// case .none:
// break
// case .follow, .request, .pending, .following:
// guard let user = viewModel.user else { return }
// let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
// Task {
// try await DataSourceFacade.responseToUserFollowAction(
// dependency: self,
// user: record
// )
// }
// case .muting:
// case .blocked, .showReblogs, .isMyself,.followingBy, .blockingBy, .suspended, .edit, .editing, .updating:
// break
// }
}
func profileHeaderViewController(
_ profileHeaderViewController: ProfileHeaderViewController,
profileHeaderView: ProfileHeaderView,

View File

@ -108,7 +108,7 @@ extension ReportResultViewController {
guard !self.viewModel.isRequestMute else { return }
self.viewModel.isRequestMute = true
do {
try await DataSourceFacade.responseToUserMuteAction(
_ = try await DataSourceFacade.responseToUserMuteAction(
dependency: self,
account: self.viewModel.account
)
@ -128,9 +128,9 @@ extension ReportResultViewController {
guard !self.viewModel.isRequestBlock else { return }
self.viewModel.isRequestBlock = true
do {
try await DataSourceFacade.responseToUserBlockAction(
_ = try await DataSourceFacade.responseToUserBlockAction(
dependency: self,
user: self.viewModel.account
account: self.viewModel.account
)
} catch {
// handle error

View File

@ -147,7 +147,7 @@ extension Persistence.MastodonUser {
let me = context.me
user.update(isFollowing: relationship.following, by: me)
relationship.requested.flatMap { user.update(isFollowRequested: $0, by: me) }
user.update(isFollowRequested: relationship.requested, by: me)
// relationship.endorsed.flatMap { user.update(isEndorsed: $0, by: me) }
me.update(isFollowing: relationship.followedBy, by: user)
user.update(isMuting: relationship.muting, by: me)

View File

@ -64,51 +64,7 @@ extension APIService {
account: Mastodon.Entity.Account,
authenticationBox: MastodonAuthenticationBox
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
guard let me = authenticationBox.authentication.account(),
let relationship = try await relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first
else { throw APIError.implicit(.badRequest) }
let blockContext = MastodonBlockContext(
sourceUserID: me.id,
targetUserID: account.id,
targetUsername: account.username,
isBlocking: relationship.blocking,
isFollowing: relationship.following
)
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)
}
let response = try result.get()
return response
}
public func toggleBlock(
user: Mastodon.Entity.Account,
authenticationBox: MastodonAuthenticationBox
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
guard let relationship = try await relationship(forAccounts: [user], authenticationBox: authenticationBox).value.first else {
guard let relationship = try await relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first else {
throw APIError.implicit(.badRequest)
}
@ -118,14 +74,14 @@ extension APIService {
response = try await Mastodon.API.Account.unblock(
session: session,
domain: authenticationBox.domain,
accountID: user.id,
accountID: account.id,
authorization: authenticationBox.userAuthorization
).singleOutput()
} else {
response = try await Mastodon.API.Account.block(
session: session,
domain: authenticationBox.domain,
accountID: user.id,
accountID: account.id,
authorization: authenticationBox.userAuthorization
).singleOutput()
}

View File

@ -22,7 +22,7 @@ extension Mastodon.Entity {
/// Are you following this user?
public let following: Bool
/// Do you have a pending follow request for this user?
public let requested: Bool?
public let requested: Bool
/// Are you featuring this user on your profile?
public let endorsed: Bool
/// Are you followed by this user?

View File

@ -87,7 +87,7 @@ extension ProfileRelationshipActionButton {
setTitle(title, for: .normal)
if relationship.blocking || account.suspended ?? false {
if relationship.blockedBy || account.suspended ?? false {
isEnabled = false
} else {
isEnabled = true