mirror of
https://github.com/mastodon/mastodon-ios.git
synced 2025-02-03 02:37:37 +01:00
Merge pull request #1198 from mastodon/ios-5-domain-blocks
Add Domain Block (IOS-5)
This commit is contained in:
commit
01eff2b596
@ -222,10 +222,11 @@
|
||||
"pending": "Pending",
|
||||
"block": "Block",
|
||||
"block_user": "Block %s",
|
||||
"block_domain": "Block %s",
|
||||
"block_domain": "Block domain %s",
|
||||
"unblock": "Unblock",
|
||||
"unblock_user": "Unblock %s",
|
||||
"blocked": "Blocked",
|
||||
"domain_blocked": "Domain Blocked",
|
||||
"mute": "Mute",
|
||||
"mute_user": "Mute %s",
|
||||
"unmute": "Unmute",
|
||||
@ -603,6 +604,14 @@
|
||||
"confirm_hide_reblogs": {
|
||||
"title": "Hide Reblogs",
|
||||
"message": "Confirm to hide reblogs"
|
||||
},
|
||||
"confirm_block_domain": {
|
||||
"title": "Block domain",
|
||||
"message": "Confirm to block domain %s"
|
||||
},
|
||||
"confirm_unblock_domain": {
|
||||
"title": "Unblock domain",
|
||||
"message": "Confirm to unblock domain %s"
|
||||
}
|
||||
},
|
||||
"accessibility": {
|
||||
|
@ -52,4 +52,17 @@ extension DataSourceFacade {
|
||||
)
|
||||
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
|
||||
}
|
||||
|
||||
static func responseToDomainBlockAction(
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
user: ManagedObjectRecord<MastodonUser>
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
let apiService = dependency.context.apiService
|
||||
let authBox = dependency.authContext.mastodonAuthenticationBox
|
||||
|
||||
_ = try await apiService.toggleDomainBlock(user: user, authenticationBox: authBox)
|
||||
}
|
||||
}
|
||||
|
@ -394,8 +394,48 @@ extension DataSourceFacade {
|
||||
|
||||
try await DataSourceFacade.responseToUserFollowAction(dependency: dependency,
|
||||
user: author)
|
||||
case .blockDomain(let context):
|
||||
let title: String
|
||||
let message: String
|
||||
let actionTitle: String
|
||||
|
||||
if context.isBlocking {
|
||||
title = L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.title
|
||||
message = L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.message(context.domain)
|
||||
actionTitle = L10n.Common.Controls.Friendship.unblockDomain(context.domain)
|
||||
} else {
|
||||
title = L10n.Scene.Profile.RelationshipActionAlert.ConfirmBlockDomain.title
|
||||
message = L10n.Common.Alerts.BlockDomain.title(context.domain)
|
||||
actionTitle = L10n.Common.Alerts.BlockDomain.blockEntireDomain
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
let confirmAction = UIAlertAction(title: actionTitle, style: .destructive ) { [weak dependency] _ in
|
||||
guard let dependency = dependency else { return }
|
||||
Task {
|
||||
let managedObjectContext = dependency.context.managedObjectContext
|
||||
let _user: ManagedObjectRecord<MastodonUser>? = try? await managedObjectContext.perform {
|
||||
guard let user = menuContext.author?.object(in: managedObjectContext) else { return nil }
|
||||
return ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
||||
}
|
||||
guard let user = _user else { return }
|
||||
try await DataSourceFacade.responseToDomainBlockAction(
|
||||
dependency: dependency,
|
||||
user: user
|
||||
)
|
||||
}
|
||||
}
|
||||
alertController.addAction(confirmAction)
|
||||
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
|
||||
alertController.addAction(cancelAction)
|
||||
dependency.present(alertController, animated: true)
|
||||
}
|
||||
} // end func
|
||||
}
|
||||
}
|
||||
|
||||
extension DataSourceFacade {
|
||||
|
@ -119,7 +119,7 @@ extension ProfileHeaderView.ViewModel {
|
||||
.store(in: &disposeBag)
|
||||
// blur for blocking & blockingBy
|
||||
$relationshipActionOptionSet
|
||||
.map { $0.contains(.blocking) || $0.contains(.blockingBy) }
|
||||
.map { $0.contains(.blocking) || $0.contains(.blockingBy) || $0.contains(.domainBlocking) }
|
||||
.sink { needsImageOverlayBlurred in
|
||||
UIView.animate(withDuration: 0.33) {
|
||||
let bannerEffect: UIVisualEffect? = needsImageOverlayBlurred ? ProfileHeaderView.bannerImageViewOverlayBlurEffect : nil
|
||||
@ -185,7 +185,7 @@ extension ProfileHeaderView.ViewModel {
|
||||
$relationshipActionOptionSet
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { optionSet in
|
||||
let isBlocking = optionSet.contains(.blocking)
|
||||
let isBlocking = optionSet.contains(.blocking) || optionSet.contains(.domainBlocking)
|
||||
let isBlockedBy = optionSet.contains(.blockingBy)
|
||||
let isSuspended = optionSet.contains(.suspended)
|
||||
let isNeedsHidden = isBlocking || isBlockedBy || isSuspended
|
||||
|
@ -325,6 +325,7 @@ extension ProfileViewController {
|
||||
viewModel.relationshipViewModel.$isBlocking.assign(to: \.isBlocking, on: userTimelineViewModel).store(in: &disposeBag)
|
||||
viewModel.relationshipViewModel.$isBlockingBy.assign(to: \.isBlockedBy, on: userTimelineViewModel).store(in: &disposeBag)
|
||||
viewModel.relationshipViewModel.$isSuspended.assign(to: \.isSuspended, on: userTimelineViewModel).store(in: &disposeBag)
|
||||
viewModel.relationshipViewModel.$isDomainBlocking.assign(to: \.isDomainBlocking, on: userTimelineViewModel).store(in: &disposeBag)
|
||||
}
|
||||
|
||||
// about
|
||||
@ -395,16 +396,16 @@ extension ProfileViewController {
|
||||
viewModel.relationshipViewModel.$optionSet
|
||||
)
|
||||
.asyncMap { [weak self] user, relationshipSet -> UIMenu? in
|
||||
guard let self = self else { return nil }
|
||||
guard let user = user else {
|
||||
return nil
|
||||
}
|
||||
guard let self, let user else { return nil }
|
||||
|
||||
let name = user.displayNameWithFallback
|
||||
let domain = user.domainFromAcct
|
||||
let _ = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
||||
|
||||
var menuActions: [MastodonMenu.Action] = [
|
||||
.muteUser(.init(name: name, isMuting: self.viewModel.relationshipViewModel.isMuting)),
|
||||
.blockUser(.init(name: name, isBlocking: self.viewModel.relationshipViewModel.isBlocking)),
|
||||
.blockDomain(.init(domain: domain, isBlocking: self.viewModel.relationshipViewModel.isDomainBlocking)),
|
||||
.reportUser(.init(name: name)),
|
||||
.shareUser(.init(name: name)),
|
||||
]
|
||||
@ -829,6 +830,27 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
||||
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
|
||||
alertController.addAction(cancelAction)
|
||||
present(alertController, animated: true, completion: nil)
|
||||
case .domainBlocking:
|
||||
guard let user = viewModel.user else { return }
|
||||
let domain = user.domainFromAcct
|
||||
|
||||
let alertController = UIAlertController(
|
||||
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.title,
|
||||
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.message(domain),
|
||||
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.responseToDomainBlockAction(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 .blocking:
|
||||
guard let user = viewModel.user else { return }
|
||||
let name = user.displayNameWithFallback
|
||||
|
@ -41,11 +41,12 @@ extension UserTimelineViewModel {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
let needsTimelineHidden = Publishers.CombineLatest3(
|
||||
let needsTimelineHidden = Publishers.CombineLatest4(
|
||||
$isBlocking,
|
||||
$isBlockedBy,
|
||||
$isSuspended
|
||||
).map { $0 || $1 || $2 }
|
||||
$isSuspended,
|
||||
$isDomainBlocking
|
||||
).map { $0 || $1 || $2 || $3 }
|
||||
|
||||
Publishers.CombineLatest(
|
||||
statusFetchedResultsController.$records,
|
||||
|
@ -27,6 +27,7 @@ final class UserTimelineViewModel {
|
||||
@Published var queryFilter: QueryFilter
|
||||
|
||||
@Published var isBlocking = false
|
||||
@Published var isDomainBlocking = false
|
||||
@Published var isBlockedBy = false
|
||||
@Published var isSuspended = false
|
||||
|
||||
|
@ -66,7 +66,31 @@ extension APIService {
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
public func toggleDomainBlock(
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Empty> {
|
||||
guard let originalRelationship = try await relationship(records: [user], authenticationBox: authenticationBox).value.first else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let response: Mastodon.Response.Content<Mastodon.Entity.Empty>
|
||||
let domainBlocking = originalRelationship.domainBlocking ?? false
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
|
||||
guard let _user = user.object(in: managedObjectContext) else { throw APIError.implicit(.badRequest) }
|
||||
|
||||
if domainBlocking {
|
||||
response = try await unblockDomain(user: _user, authorizationBox: authenticationBox).singleOutput()
|
||||
} else {
|
||||
response = try await blockDomain(user: _user, authorizationBox: authenticationBox).singleOutput()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func blockDomain(
|
||||
user: MastodonUser,
|
||||
authorizationBox: MastodonAuthenticationBox
|
||||
|
@ -216,6 +216,8 @@ public enum L10n {
|
||||
public static func blockUser(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Friendship.BlockUser", String(describing: p1), fallback: "Block %@")
|
||||
}
|
||||
/// Domain Blocked
|
||||
public static let domainBlocked = L10n.tr("Localizable", "Common.Controls.Friendship.DomainBlocked", fallback: "Domain Blocked")
|
||||
/// Edit Info
|
||||
public static let editInfo = L10n.tr("Localizable", "Common.Controls.Friendship.EditInfo", fallback: "Edit Info")
|
||||
/// Follow
|
||||
@ -241,6 +243,10 @@ public enum L10n {
|
||||
/// Unblock
|
||||
public static let unblock = L10n.tr("Localizable", "Common.Controls.Friendship.Unblock", fallback: "Unblock")
|
||||
/// Unblock %@
|
||||
public static func unblockDomain(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Friendship.UnblockDomain", String(describing: p1), fallback: "Unblock %@")
|
||||
}
|
||||
/// Unblock %@
|
||||
public static func unblockUser(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Friendship.UnblockUser", String(describing: p1), fallback: "Unblock %@")
|
||||
}
|
||||
@ -953,6 +959,10 @@ public enum L10n {
|
||||
public static let followsYou = L10n.tr("Localizable", "Scene.Profile.Header.FollowsYou", fallback: "Follows You")
|
||||
}
|
||||
public enum RelationshipActionAlert {
|
||||
public enum ConfirmBlockDomain {
|
||||
/// Block Domain
|
||||
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmBlockDomain.Title", fallback: "Block Domain")
|
||||
}
|
||||
public enum ConfirmBlockUser {
|
||||
/// Confirm to block %@
|
||||
public static func message(_ p1: Any) -> String {
|
||||
@ -981,6 +991,14 @@ public enum L10n {
|
||||
/// Show Reblogs
|
||||
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title", fallback: "Show Reblogs")
|
||||
}
|
||||
public enum ConfirmUnblockDomain {
|
||||
/// Confirm to unblock domain %@
|
||||
public static func message(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.Message", String(describing: p1), fallback: "Confirm to unblock domain %@")
|
||||
}
|
||||
/// Unblock Domain
|
||||
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.Title", fallback: "Unblock Domain")
|
||||
}
|
||||
public enum ConfirmUnblockUser {
|
||||
/// Confirm to unblock %@
|
||||
public static func message(_ p1: Any) -> String {
|
||||
|
@ -73,6 +73,7 @@ Please check your internet connection.";
|
||||
"Common.Controls.Friendship.BlockDomain" = "Block %@";
|
||||
"Common.Controls.Friendship.BlockUser" = "Block %@";
|
||||
"Common.Controls.Friendship.Blocked" = "Blocked";
|
||||
"Common.Controls.Friendship.DomainBlocked" = "Domain Blocked";
|
||||
"Common.Controls.Friendship.EditInfo" = "Edit Info";
|
||||
"Common.Controls.Friendship.Follow" = "Follow";
|
||||
"Common.Controls.Friendship.Following" = "Following";
|
||||
@ -85,6 +86,7 @@ Please check your internet connection.";
|
||||
"Common.Controls.Friendship.ShowReblogs" = "Show Reblogs";
|
||||
"Common.Controls.Friendship.Unblock" = "Unblock";
|
||||
"Common.Controls.Friendship.UnblockUser" = "Unblock %@";
|
||||
"Common.Controls.Friendship.UnblockDomain" = "Unblock %@";
|
||||
"Common.Controls.Friendship.Unmute" = "Unmute";
|
||||
"Common.Controls.Friendship.UnmuteUser" = "Unmute %@";
|
||||
"Common.Controls.Keyboard.Common.ComposeNewPost" = "Compose New Post";
|
||||
@ -336,6 +338,9 @@ uploaded to Mastodon.";
|
||||
"Scene.Profile.Header.FollowsYou" = "Follows You";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Message" = "Confirm to block %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Title" = "Block Account";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmBlockDomain.Title" = "Block Domain";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.Message" = "Confirm to unblock domain %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.Title" = "Unblock Domain";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message" = "Confirm to hide reblogs";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title" = "Hide Reblogs";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.Message" = "Confirm to mute %@";
|
||||
@ -563,4 +568,4 @@ uploaded to Mastodon.";
|
||||
"Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts.";
|
||||
"Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers";
|
||||
"Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social";
|
||||
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";
|
||||
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";
|
||||
|
@ -59,7 +59,8 @@ extension MastodonMenu {
|
||||
case deleteStatus
|
||||
case editStatus
|
||||
case followUser(FollowUserActionContext)
|
||||
|
||||
case blockDomain(BlockDomainActionContext)
|
||||
|
||||
func build(delegate: MastodonMenuDelegate) -> LabeledAction {
|
||||
switch self {
|
||||
case .hideReblogs(let context):
|
||||
@ -194,14 +195,29 @@ extension MastodonMenu {
|
||||
image = UIImage(systemName: "person.fill.badge.plus")
|
||||
}
|
||||
let action = LabeledAction(title: title, image: image) { [weak delegate] in
|
||||
guard let delegate = delegate else { return }
|
||||
guard let delegate else { return }
|
||||
delegate.menuAction(self)
|
||||
}
|
||||
return action
|
||||
case .blockDomain(let context):
|
||||
let title: String
|
||||
let image: UIImage?
|
||||
if context.isBlocking {
|
||||
title = L10n.Common.Controls.Actions.unblockDomain(context.domain)
|
||||
image = UIImage(systemName: "hand.raised.slash.fill")
|
||||
} else {
|
||||
title = L10n.Common.Controls.Actions.blockDomain(context.domain)
|
||||
image = UIImage(systemName: "hand.raised.fill")
|
||||
}
|
||||
let action = LabeledAction(title: title, image: image) { [weak delegate] in
|
||||
guard let delegate else { return }
|
||||
|
||||
} // end switch
|
||||
} // end func build
|
||||
} // end enum Action
|
||||
delegate.menuAction(self)
|
||||
}
|
||||
return action
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MastodonMenu {
|
||||
@ -275,4 +291,14 @@ extension MastodonMenu {
|
||||
self.isFollowing = isFollowing
|
||||
}
|
||||
}
|
||||
|
||||
public struct BlockDomainActionContext {
|
||||
public let domain: String
|
||||
public let isBlocking: Bool
|
||||
|
||||
public init(domain: String, isBlocking: Bool) {
|
||||
self.domain = domain
|
||||
self.isBlocking = isBlocking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public enum RelationshipAction: Int, CaseIterable {
|
||||
case edit
|
||||
case editing
|
||||
case updating
|
||||
case domainBlocking
|
||||
|
||||
public var option: RelationshipActionOptionSet {
|
||||
return RelationshipActionOptionSet(rawValue: 1 << rawValue)
|
||||
@ -60,7 +61,8 @@ public struct RelationshipActionOptionSet: OptionSet {
|
||||
public static let updating = RelationshipAction.updating.option
|
||||
public static let showReblogs = RelationshipAction.showReblogs.option
|
||||
public static let editOptions: RelationshipActionOptionSet = [.edit, .editing, .updating]
|
||||
|
||||
public static let domainBlocking = RelationshipAction.domainBlocking.option
|
||||
|
||||
public func highPriorityAction(except: RelationshipActionOptionSet) -> RelationshipAction? {
|
||||
let set = subtracting(except)
|
||||
for action in RelationshipAction.allCases.reversed() where set.contains(action.option) {
|
||||
@ -92,6 +94,7 @@ public struct RelationshipActionOptionSet: OptionSet {
|
||||
case .editing: return L10n.Common.Controls.Actions.done
|
||||
case .updating: return " "
|
||||
case .showReblogs: return " "
|
||||
case .domainBlocking: return L10n.Common.Controls.Friendship.domainBlocked
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,7 +122,8 @@ public final class RelationshipViewModel {
|
||||
@Published public var isBlocking = false
|
||||
@Published public var isBlockingBy = false
|
||||
@Published public var isSuspended = false
|
||||
|
||||
@Published public var isDomainBlocking = false
|
||||
|
||||
public init() {
|
||||
Publishers.CombineLatest3(
|
||||
$user,
|
||||
@ -171,9 +175,7 @@ extension RelationshipViewModel {
|
||||
|
||||
extension RelationshipViewModel {
|
||||
private func update(user: MastodonUser?, me: MastodonUser?) {
|
||||
guard let user = user,
|
||||
let me = me
|
||||
else {
|
||||
guard let user, let me else {
|
||||
reset()
|
||||
return
|
||||
}
|
||||
@ -188,6 +190,7 @@ extension RelationshipViewModel {
|
||||
self.isBlocking = optionSet.contains(.blocking)
|
||||
self.isSuspended = optionSet.contains(.suspended)
|
||||
self.showReblogs = optionSet.contains(.showReblogs)
|
||||
self.isDomainBlocking = optionSet.contains(.domainBlocking)
|
||||
|
||||
self.optionSet = optionSet
|
||||
}
|
||||
@ -201,6 +204,7 @@ extension RelationshipViewModel {
|
||||
isBlocking = false
|
||||
optionSet = nil
|
||||
showReblogs = false
|
||||
isDomainBlocking = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,6 +224,7 @@ extension RelationshipViewModel {
|
||||
let isBlockingBy = me.blockingBy.contains(user)
|
||||
let isBlocking = user.blockingBy.contains(me)
|
||||
let isShowingReblogs = me.showingReblogsBy.contains(user)
|
||||
let isDomainBlocking = user.domainBlockingBy.contains(me)
|
||||
|
||||
var optionSet: RelationshipActionOptionSet = [.follow]
|
||||
|
||||
@ -263,6 +268,10 @@ extension RelationshipViewModel {
|
||||
optionSet.insert(.showReblogs)
|
||||
}
|
||||
|
||||
if isDomainBlocking {
|
||||
optionSet.insert(.domainBlocking)
|
||||
}
|
||||
|
||||
return optionSet
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user