feature: finish domainBlock action and domainUnblick action

This commit is contained in:
sunxiaojian 2021-04-30 12:53:25 +08:00
parent ccdc48add1
commit 33401b4e1f
13 changed files with 234 additions and 74 deletions

View File

@ -26,6 +26,7 @@
</entity>
<entity name="DomainBlock" representedClassName=".DomainBlock" syncable="YES">
<attribute name="blockedDomain" attributeType="String"/>
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="domain" attributeType="String"/>
<attribute name="userID" attributeType="String"/>
<uniquenessConstraints>
@ -264,7 +265,7 @@
<elements>
<element name="Application" positionX="0" positionY="0" width="128" height="104"/>
<element name="Attachment" positionX="0" positionY="0" width="128" height="254"/>
<element name="DomainBlock" positionX="45" positionY="162" width="128" height="74"/>
<element name="DomainBlock" positionX="45" positionY="162" width="128" height="89"/>
<element name="Emoji" positionX="0" positionY="0" width="128" height="149"/>
<element name="History" positionX="0" positionY="0" width="128" height="119"/>
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>

View File

@ -61,7 +61,8 @@
"manually_search": "Manually search instead",
"skip": "Skip",
"report_user": "Report %s",
"block_domain": "Block %s"
"block_domain": "Block %s",
"unblock_domain": "Unblock %s"
},
"status": {
"user_reblogged": "%s reblogged",
@ -91,7 +92,7 @@
"pending": "Pending",
"block": "Block",
"block_user": "Block %s",
"block_domain": "Block %s",
"block_domain": "Domain Blocked",
"unblock": "Unblock",
"unblock_user": "Unblock %s",
"blocked": "Blocked",

View File

@ -51,7 +51,7 @@
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
"state": {
"branch": null,
"revision": "bbc4bc4def7eb05a7ba8e1219f80ee9be327334e",
"revision": "15d199e84677303a7004ed2c5ecaa1a90f3863f8",
"version": "6.2.1"
}
},

View File

@ -781,24 +781,43 @@ extension StatusSection {
}
let author = status.authorForUserProvider
let canReport = authenticationBox.userID != author.id
let canBlockDomain = authenticationBox.domain != author.domain
let isInSameDomain = authenticationBox.domain == author.domainFromAcct
let isMuting = (author.mutingBy ?? Set()).map(\.id).contains(authenticationBox.userID)
let isBlocking = (author.blockingBy ?? Set()).map(\.id).contains(authenticationBox.userID)
cell.statusView.actionToolbarContainer.moreButton.showsMenuAsPrimaryAction = true
cell.statusView.actionToolbarContainer.moreButton.menu = UserProviderFacade.createProfileActionMenu(
for: author,
isMuting: isMuting,
isBlocking: isBlocking,
canReport: canReport,
canBlockDomain: canBlockDomain,
provider: userProvider,
cell: cell,
indexPath: indexPath,
sourceView: cell.statusView.actionToolbarContainer.moreButton,
barButtonItem: nil,
shareUser: nil,
shareStatus: status
)
let managedObjectContext = userProvider.context.backgroundManagedObjectContext
managedObjectContext.perform {
let blockedDomain: DomainBlock? = {
let request = DomainBlock.sortedFetchRequest
request.predicate = DomainBlock.predicate(domain: authenticationBox.domain, userID: authenticationBox.userID, blockedDomain: author.domainFromAcct)
request.fetchLimit = 1
request.returnsObjectsAsFaults = false
do {
return try managedObjectContext.fetch(request).first
} catch {
assertionFailure(error.localizedDescription)
return nil
}
}()
let isDomainBlocking = blockedDomain != nil
DispatchQueue.main.async {
cell.statusView.actionToolbarContainer.moreButton.menu = UserProviderFacade.createProfileActionMenu(
for: author,
isMuting: isMuting,
isBlocking: isBlocking,
canReport: canReport,
isInSameDomain: isInSameDomain,
isDomainBlocking: isDomainBlocking,
provider: userProvider,
cell: cell,
indexPath: indexPath,
sourceView: cell.statusView.actionToolbarContainer.moreButton,
barButtonItem: nil,
shareUser: nil,
shareStatus: status
)
}
}
}
}

View File

@ -124,14 +124,16 @@ internal enum L10n {
internal static let takePhoto = L10n.tr("Localizable", "Common.Controls.Actions.TakePhoto")
/// Try Again
internal static let tryAgain = L10n.tr("Localizable", "Common.Controls.Actions.TryAgain")
/// Unblock %@
internal static func unblockDomain(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Actions.UnblockDomain", String(describing: p1))
}
}
internal enum Firendship {
/// Block
internal static let block = L10n.tr("Localizable", "Common.Controls.Firendship.Block")
/// Block %@
internal static func blockDomain(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Firendship.BlockDomain", String(describing: p1))
}
/// Domain Blocked
internal static let blockDomain = L10n.tr("Localizable", "Common.Controls.Firendship.BlockDomain")
/// Blocked
internal static let blocked = L10n.tr("Localizable", "Common.Controls.Firendship.Blocked")
/// Block %@

View File

@ -159,7 +159,8 @@ extension UserProviderFacade {
isMuting: Bool,
isBlocking: Bool,
canReport: Bool,
canBlockDomain: Bool,
isInSameDomain: Bool,
isDomainBlocking: Bool,
provider: UserProvider,
cell: UITableViewCell?,
indexPath: IndexPath?,
@ -248,21 +249,37 @@ extension UserProviderFacade {
children.append(reportAction)
}
if canBlockDomain {
let blockDomainAction = UIAction(title: L10n.Common.Controls.Actions.blockDomain(mastodonUser.domain), image: UIImage(systemName: "nosign"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in
guard let provider = provider else { return }
let alertController = UIAlertController(title: "", message: L10n.Common.Alerts.BlockDomain.message(mastodonUser.domain), preferredStyle: .alert)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .default) { _ in
if !isInSameDomain {
if isDomainBlocking {
let unblockDomainAction = UIAction(title: L10n.Common.Controls.Actions.unblockDomain(mastodonUser.domainFromAcct), image: UIImage(systemName: "nosign"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in
guard let provider = provider else { return }
BlockDomainService(userProvider: provider,
cell: cell,
indexPath: indexPath
)
.unblockDomain()
}
children.append(unblockDomainAction)
} else {
let blockDomainAction = UIAction(title: L10n.Common.Controls.Actions.blockDomain(mastodonUser.domainFromAcct), image: UIImage(systemName: "nosign"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in
guard let provider = provider else { return }
let alertController = UIAlertController(title: "", message: L10n.Common.Alerts.BlockDomain.message(mastodonUser.domainFromAcct), preferredStyle: .alert)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .default) { _ in
}
alertController.addAction(cancelAction)
let blockDomainAction = UIAlertAction(title: L10n.Common.Alerts.BlockDomain.blockEntireDomain, style: .destructive) { _ in
BlockDomainService(userProvider: provider,
cell: cell,
indexPath: indexPath
)
.blockDomain()
}
alertController.addAction(blockDomainAction)
provider.present(alertController, animated: true, completion: nil)
}
alertController.addAction(cancelAction)
let blockDomainAction = UIAlertAction(title: L10n.Common.Alerts.BlockDomain.blockEntireDomain, style: .destructive) { _ in
BlockDomainService(context: provider.context).blockDomain(domain: mastodonUser.domain)
}
alertController.addAction(blockDomainAction)
provider.present(alertController, animated: true, completion: nil)
children.append(blockDomainAction)
}
children.append(blockDomainAction)
}
if let shareUser = shareUser {

View File

@ -41,8 +41,9 @@ Please check your internet connection.";
"Common.Controls.Actions.Skip" = "Skip";
"Common.Controls.Actions.TakePhoto" = "Take photo";
"Common.Controls.Actions.TryAgain" = "Try Again";
"Common.Controls.Actions.UnblockDomain" = "Unblock %@";
"Common.Controls.Firendship.Block" = "Block";
"Common.Controls.Firendship.BlockDomain" = "Block %@";
"Common.Controls.Firendship.BlockDomain" = "Domain Blocked";
"Common.Controls.Firendship.BlockUser" = "Block %@";
"Common.Controls.Firendship.Blocked" = "Blocked";
"Common.Controls.Firendship.EditInfo" = "Edit info";

View File

@ -377,14 +377,16 @@ extension ProfileViewController {
guard let currentDomain = self.viewModel.domain.value else { return }
let isMuting = relationshipActionOptionSet.contains(.muting)
let isBlocking = relationshipActionOptionSet.contains(.blocking)
let isDomainBlocking = relationshipActionOptionSet.contains(.domainBlocking)
let needsShareAction = self.viewModel.isMeBarButtonItemsHidden.value
let canBlockDomain = mastodonUser.domain != currentDomain
let isInSameDomain = mastodonUser.domainFromAcct == currentDomain
self.moreMenuBarButtonItem.menu = UserProviderFacade.createProfileActionMenu(
for: mastodonUser,
isMuting: isMuting,
isBlocking: isBlocking,
canReport: true,
canBlockDomain: canBlockDomain,
isInSameDomain: isInSameDomain,
isDomainBlocking: isDomainBlocking,
provider: self,
cell: nil,
indexPath: nil,

View File

@ -327,6 +327,7 @@ extension ProfileViewModel {
case muting
case blocked
case blocking
case domainBlocking
case suspended
case edit
case editing
@ -349,6 +350,7 @@ extension ProfileViewModel {
static let muting = RelationshipAction.muting.option
static let blocked = RelationshipAction.blocked.option
static let blocking = RelationshipAction.blocking.option
static let domainBlocking = RelationshipAction.domainBlocking.option
static let suspended = RelationshipAction.suspended.option
static let edit = RelationshipAction.edit.option
static let editing = RelationshipAction.editing.option
@ -379,6 +381,7 @@ extension ProfileViewModel {
case .muting: return L10n.Common.Controls.Firendship.muted
case .blocked: return L10n.Common.Controls.Firendship.follow // blocked by user
case .blocking: return L10n.Common.Controls.Firendship.blocked
case .domainBlocking: return L10n.Common.Controls.Firendship.blockDomain
case .suspended: return L10n.Common.Controls.Firendship.follow
case .edit: return L10n.Common.Controls.Firendship.editInfo
case .editing: return L10n.Common.Controls.Actions.done
@ -400,6 +403,7 @@ extension ProfileViewModel {
case .muting: return Asset.Colors.Background.alertYellow.color
case .blocked: return Asset.Colors.Button.normal.color
case .blocking: return Asset.Colors.Background.danger.color
case .domainBlocking: return Asset.Colors.Button.normal.color
case .suspended: return Asset.Colors.Button.normal.color
case .edit: return Asset.Colors.Button.normal.color
case .editing: return Asset.Colors.Button.normal.color

View File

@ -5,16 +5,15 @@
// Created by sxiaojian on 2021/4/29.
//
import Foundation
import Combine
import CommonOSLog
import CoreData
import CoreDataStack
import CommonOSLog
import DateToolsSwift
import Foundation
import MastodonSDK
extension APIService {
func getDomainblocks(
domain: String,
limit: Int = onceRequestDomainBlocksMaxCount,
@ -32,10 +31,10 @@ extension APIService {
query: query
)
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[String]>, Error> in
return self.backgroundManagedObjectContext.performChanges {
self.backgroundManagedObjectContext.performChanges {
response.value.forEach { domain in
// use constrain to avoid repeated save
let _ = DomainBlock.insert(
_ = DomainBlock.insert(
into: self.backgroundManagedObjectContext,
blockedDomain: domain,
domain: authorizationBox.domain,
@ -58,28 +57,33 @@ extension APIService {
}
func blockDomain(
domain: String,
user: MastodonUser,
authorizationBox: AuthenticationService.MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<String>, Error> {
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> {
let authorization = authorizationBox.userAuthorization
return Mastodon.API.DomainBlock.blockDomain(
domain: authorizationBox.domain,
blockDomain: domain,
blockDomain: user.domainFromAcct,
session: session,
authorization: authorization
)
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<String>, Error> in
return self.backgroundManagedObjectContext.performChanges {
let _ = DomainBlock.insert(
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> in
self.backgroundManagedObjectContext.performChanges {
let requestMastodonUserRequest = MastodonUser.sortedFetchRequest
requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: authorizationBox.domain, id: authorizationBox.userID)
requestMastodonUserRequest.fetchLimit = 1
guard let requestMastodonUser = self.backgroundManagedObjectContext.safeFetch(requestMastodonUserRequest).first else { return }
_ = DomainBlock.insert(
into: self.backgroundManagedObjectContext,
blockedDomain: domain,
blockedDomain: user.domainFromAcct,
domain: authorizationBox.domain,
userID: authorizationBox.userID
)
user.update(isDomainBlocking: true, by: requestMastodonUser)
}
.setFailureType(to: Error.self)
.tryMap { result -> Mastodon.Response.Content<String> in
.tryMap { result -> Mastodon.Response.Content<Mastodon.Entity.Empty> in
switch result {
case .success:
return response
@ -93,28 +97,42 @@ extension APIService {
}
func unblockDomain(
domain: String,
user: MastodonUser,
authorizationBox: AuthenticationService.MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<String>, Error> {
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> {
let authorization = authorizationBox.userAuthorization
return Mastodon.API.DomainBlock.unblockDomain(
domain: authorizationBox.domain,
blockDomain: domain,
blockDomain: user.domainFromAcct,
session: session,
authorization: authorization
)
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<String>, Error> in
return self.backgroundManagedObjectContext.performChanges {
// let _ = DomainBlock.insert(
// into: self.backgroundManagedObjectContext,
// blockedDomain: domain,
// domain: authorizationBox.domain,
// userID: authorizationBox.userID
// )
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> in
self.backgroundManagedObjectContext.performChanges {
let blockedDomain: DomainBlock? = {
let request = DomainBlock.sortedFetchRequest
request.predicate = DomainBlock.predicate(domain: authorizationBox.domain, userID: authorizationBox.userID, blockedDomain: user.domainFromAcct)
request.fetchLimit = 1
request.returnsObjectsAsFaults = false
do {
return try self.backgroundManagedObjectContext.fetch(request).first
} catch {
assertionFailure(error.localizedDescription)
return nil
}
}()
if let blockedDomain = blockedDomain {
self.backgroundManagedObjectContext.delete(blockedDomain)
}
let requestMastodonUserRequest = MastodonUser.sortedFetchRequest
requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: authorizationBox.domain, id: authorizationBox.userID)
requestMastodonUserRequest.fetchLimit = 1
guard let requestMastodonUser = self.backgroundManagedObjectContext.safeFetch(requestMastodonUserRequest).first else { return }
user.update(isDomainBlocking: false, by: requestMastodonUser)
}
.setFailureType(to: Error.self)
.tryMap { result -> Mastodon.Response.Content<String> in
.tryMap { result -> Mastodon.Response.Content<Mastodon.Entity.Empty> in
switch result {
case .success:
return response

View File

@ -8,15 +8,95 @@
import CoreData
import CoreDataStack
import Foundation
import Combine
import MastodonSDK
import OSLog
import UIKit
final class BlockDomainService {
let context: AppContext
init(context: AppContext) {
self.context = context
let userProvider: UserProvider
let cell: UITableViewCell?
let indexPath: IndexPath?
init(userProvider: UserProvider,
cell: UITableViewCell?,
indexPath: IndexPath?
) {
self.userProvider = userProvider
self.cell = cell
self.indexPath = indexPath
}
func blockDomain(domain: String) {
func blockDomain() {
guard let activeMastodonAuthenticationBox = self.userProvider.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
guard let context = self.userProvider.context else {
return
}
var mastodonUser: AnyPublisher<MastodonUser?, Never>
if let cell = self.cell, let indexPath = self.indexPath {
mastodonUser = userProvider.mastodonUser(for: cell, indexPath: indexPath).eraseToAnyPublisher()
} else {
mastodonUser = userProvider.mastodonUser().eraseToAnyPublisher()
}
mastodonUser
.compactMap { mastodonUser -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error>? in
guard let mastodonUser = mastodonUser else {
return nil
}
return context.apiService.blockDomain(user: mastodonUser, authorizationBox: activeMastodonAuthenticationBox)
}
.switchToLatest()
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[String]>, Error> in
return context.apiService.getDomainblocks(domain: activeMastodonAuthenticationBox.domain, authorizationBox: activeMastodonAuthenticationBox)
}
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
print(error)
}
} receiveValue: { response in
print(response)
}
.store(in: &userProvider.disposeBag)
}
func unblockDomain() {
guard let activeMastodonAuthenticationBox = self.userProvider.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
guard let context = self.userProvider.context else {
return
}
var mastodonUser: AnyPublisher<MastodonUser?, Never>
if let cell = self.cell, let indexPath = self.indexPath {
mastodonUser = userProvider.mastodonUser(for: cell, indexPath: indexPath).eraseToAnyPublisher()
} else {
mastodonUser = userProvider.mastodonUser().eraseToAnyPublisher()
}
mastodonUser
.compactMap { mastodonUser -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error>? in
guard let mastodonUser = mastodonUser else {
return nil
}
return context.apiService.unblockDomain(user: mastodonUser, authorizationBox: activeMastodonAuthenticationBox)
}
.switchToLatest()
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[String]>, Error> in
return context.apiService.getDomainblocks(domain: activeMastodonAuthenticationBox.domain, authorizationBox: activeMastodonAuthenticationBox)
}
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
print(error)
}
} receiveValue: { response in
print(response)
}
.store(in: &userProvider.disposeBag)
}
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function)
}
}

View File

@ -54,7 +54,7 @@ extension Mastodon.API.DomainBlock {
blockDomain:String,
session: URLSession,
authorization: Mastodon.API.OAuth.Authorization
) -> AnyPublisher<Mastodon.Response.Content<String>, Error> {
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> {
let query = Mastodon.API.DomainBlock.BlockQuery(domain: blockDomain)
let request = Mastodon.API.post(
url: domainBlockEndpointURL(domain: domain),
@ -63,7 +63,7 @@ extension Mastodon.API.DomainBlock {
)
return session.dataTaskPublisher(for: request)
.tryMap { data, response in
let value = try Mastodon.API.decode(type: String.self, from: data, response: response)
let value = try Mastodon.API.decode(type: Mastodon.Entity.Empty.self, from: data, response: response)
return Mastodon.Response.Content(value: value, response: response)
}
.eraseToAnyPublisher()
@ -84,7 +84,7 @@ extension Mastodon.API.DomainBlock {
blockDomain:String,
session: URLSession,
authorization: Mastodon.API.OAuth.Authorization
) -> AnyPublisher<Mastodon.Response.Content<String>, Error> {
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> {
let query = Mastodon.API.DomainBlock.BlockQuery(domain: blockDomain)
let request = Mastodon.API.delete(
url: domainBlockEndpointURL(domain: domain),
@ -93,7 +93,7 @@ extension Mastodon.API.DomainBlock {
)
return session.dataTaskPublisher(for: request)
.tryMap { data, response in
let value = try Mastodon.API.decode(type: String.self, from: data, response: response)
let value = try Mastodon.API.decode(type: Mastodon.Entity.Empty.self, from: data, response: response)
return Mastodon.Response.Content(value: value, response: response)
}
.eraseToAnyPublisher()

View File

@ -0,0 +1,15 @@
//
// File.swift
//
//
// Created by sxiaojian on 2021/4/30.
//
import Foundation
extension Mastodon.Entity {
public struct Empty: Codable {
}
}