Use relationships in Report-process (IOS-192)
This commit is contained in:
parent
bb3ad77954
commit
47986262bc
|
@ -502,9 +502,7 @@ private extension SceneCoordinator {
|
|||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
case .report(let viewModel):
|
||||
let _viewController = ReportViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
viewController = ReportViewController(viewModel: viewModel)
|
||||
case .reportServerRules(let viewModel):
|
||||
let _viewController = ReportServerRulesViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
|
|
|
@ -244,10 +244,13 @@ extension DataSourceFacade {
|
|||
case .reportUser:
|
||||
Task {
|
||||
|
||||
guard let relationship = try? await dependency.context.apiService.relationship(forAccounts: [menuContext.author], authenticationBox: dependency.authContext.mastodonAuthenticationBox).value.first else { return }
|
||||
|
||||
let reportViewModel = ReportViewModel(
|
||||
context: dependency.context,
|
||||
authContext: dependency.authContext,
|
||||
account: menuContext.author,
|
||||
relationship: relationship,
|
||||
status: menuContext.statusViewModel?.originalStatus
|
||||
)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class ReportViewController: UIViewController, NeedsDependency, ReportViewControl
|
|||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var viewModel: ReportViewModel!
|
||||
let viewModel: ReportViewModel
|
||||
|
||||
lazy var cancelBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .cancel,
|
||||
|
@ -28,10 +28,13 @@ class ReportViewController: UIViewController, NeedsDependency, ReportViewControl
|
|||
action: #selector(ReportViewController.cancelBarButtonItemDidPressed(_:))
|
||||
)
|
||||
|
||||
init(viewModel: ReportViewModel) {
|
||||
self.viewModel = viewModel
|
||||
|
||||
}
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
extension ReportViewController {
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
@ -46,10 +49,9 @@ extension ReportViewController {
|
|||
viewModel.reportStatusViewModel.delegate = self
|
||||
viewModel.reportSupplementaryViewModel.delegate = self
|
||||
|
||||
let reportReasonViewController = ReportReasonViewController()
|
||||
let reportReasonViewController = ReportReasonViewController(viewModel: viewModel.reportReasonViewModel)
|
||||
reportReasonViewController.context = context
|
||||
reportReasonViewController.coordinator = coordinator
|
||||
reportReasonViewController.viewModel = viewModel.reportReasonViewModel
|
||||
|
||||
addChild(reportReasonViewController)
|
||||
reportReasonViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -58,10 +60,6 @@ extension ReportViewController {
|
|||
reportReasonViewController.view.pinToParent()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportViewController {
|
||||
|
||||
@objc private func cancelBarButtonItemDidPressed(_ sender: UIBarButtonItem) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
@ -85,6 +83,7 @@ extension ReportViewController: ReportReasonViewControllerDelegate {
|
|||
context: context,
|
||||
authContext: viewModel.authContext,
|
||||
account: viewModel.account,
|
||||
relationship: viewModel.relationship,
|
||||
isReported: false
|
||||
)
|
||||
_ = coordinator.present(
|
||||
|
@ -161,6 +160,7 @@ extension ReportViewController: ReportSupplementaryViewControllerDelegate {
|
|||
context: context,
|
||||
authContext: viewModel.authContext,
|
||||
account: viewModel.account,
|
||||
relationship: viewModel.relationship,
|
||||
isReported: true
|
||||
)
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ class ReportViewModel {
|
|||
let context: AppContext
|
||||
let authContext: AuthContext
|
||||
let account: Mastodon.Entity.Account
|
||||
let relationship: Mastodon.Entity.Relationship
|
||||
let status: MastodonStatus?
|
||||
|
||||
// output
|
||||
|
@ -40,11 +41,13 @@ class ReportViewModel {
|
|||
context: AppContext,
|
||||
authContext: AuthContext,
|
||||
account: Mastodon.Entity.Account,
|
||||
relationship: Mastodon.Entity.Relationship,
|
||||
status: MastodonStatus?
|
||||
) {
|
||||
self.context = context
|
||||
self.authContext = authContext
|
||||
self.account = account
|
||||
self.relationship = relationship
|
||||
self.status = status
|
||||
self.reportReasonViewModel = ReportReasonViewModel(context: context)
|
||||
self.reportServerRulesViewModel = ReportServerRulesViewModel(context: context)
|
||||
|
|
|
@ -25,8 +25,8 @@ final class ReportReasonViewController: UIViewController, NeedsDependency, Repor
|
|||
var disposeBag = Set<AnyCancellable>()
|
||||
private var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
var viewModel: ReportReasonViewModel!
|
||||
private(set) lazy var reportReasonView = ReportReasonView(viewModel: viewModel)
|
||||
let viewModel: ReportReasonViewModel
|
||||
let reportReasonView: ReportReasonView
|
||||
|
||||
let navigationActionView: NavigationActionView = {
|
||||
let navigationActionView = NavigationActionView()
|
||||
|
@ -35,10 +35,14 @@ final class ReportReasonViewController: UIViewController, NeedsDependency, Repor
|
|||
return navigationActionView
|
||||
}()
|
||||
|
||||
init(viewModel: ReportReasonViewModel) {
|
||||
self.viewModel = viewModel
|
||||
reportReasonView = ReportReasonView(viewModel: viewModel)
|
||||
|
||||
}
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
extension ReportReasonViewController {
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
|
|
@ -75,7 +75,7 @@ struct ReportResultView: View {
|
|||
action: {
|
||||
viewModel.followActionPublisher.send()
|
||||
},
|
||||
title: viewModel.relationshipViewModel.isFollowing ? L10n.Scene.Report.StepFinal.unfollow : L10n.Scene.Report.StepFinal.unfollowed,
|
||||
title: viewModel.relationship.following ? L10n.Scene.Report.StepFinal.unfollow : L10n.Scene.Report.StepFinal.unfollowed,
|
||||
isBusy: viewModel.isRequestFollow
|
||||
)
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ struct ReportResultView: View {
|
|||
action: {
|
||||
viewModel.muteActionPublisher.send()
|
||||
},
|
||||
title: viewModel.relationshipViewModel.isMuting ? L10n.Common.Controls.Friendship.muted : L10n.Common.Controls.Friendship.mute,
|
||||
title: viewModel.relationship.muting ? L10n.Common.Controls.Friendship.muted : L10n.Common.Controls.Friendship.mute,
|
||||
isBusy: viewModel.isRequestMute
|
||||
)
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ struct ReportResultView: View {
|
|||
action: {
|
||||
viewModel.blockActionPublisher.send()
|
||||
},
|
||||
title: viewModel.relationshipViewModel.isBlocking ? L10n.Common.Controls.Friendship.blocked : L10n.Common.Controls.Friendship.block,
|
||||
title: viewModel.relationship.blocking ? L10n.Common.Controls.Friendship.blocked : L10n.Common.Controls.Friendship.block,
|
||||
isBusy: viewModel.isRequestBlock
|
||||
)
|
||||
}
|
||||
|
|
|
@ -88,10 +88,11 @@ extension ReportResultViewController {
|
|||
guard !self.viewModel.isRequestFollow else { return }
|
||||
self.viewModel.isRequestFollow = true
|
||||
do {
|
||||
try await DataSourceFacade.responseToUserFollowAction(
|
||||
let newRelationship = try await DataSourceFacade.responseToUserFollowAction(
|
||||
dependency: self,
|
||||
account: self.viewModel.account
|
||||
)
|
||||
self.viewModel.relationship = newRelationship
|
||||
} catch {
|
||||
// handle error
|
||||
}
|
||||
|
@ -108,10 +109,11 @@ extension ReportResultViewController {
|
|||
guard !self.viewModel.isRequestMute else { return }
|
||||
self.viewModel.isRequestMute = true
|
||||
do {
|
||||
_ = try await DataSourceFacade.responseToUserMuteAction(
|
||||
let newRelationship = try await DataSourceFacade.responseToUserMuteAction(
|
||||
dependency: self,
|
||||
account: self.viewModel.account
|
||||
)
|
||||
self.viewModel.relationship = newRelationship
|
||||
} catch {
|
||||
// handle error
|
||||
}
|
||||
|
@ -128,10 +130,11 @@ extension ReportResultViewController {
|
|||
guard !self.viewModel.isRequestBlock else { return }
|
||||
self.viewModel.isRequestBlock = true
|
||||
do {
|
||||
_ = try await DataSourceFacade.responseToUserBlockAction(
|
||||
let newRelationship = try await DataSourceFacade.responseToUserBlockAction(
|
||||
dependency: self,
|
||||
account: self.viewModel.account
|
||||
)
|
||||
self.viewModel.relationship = newRelationship
|
||||
} catch {
|
||||
// handle error
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class ReportResultViewModel: ObservableObject {
|
|||
let context: AppContext
|
||||
let authContext: AuthContext
|
||||
let account: Mastodon.Entity.Account
|
||||
var relationship: Mastodon.Entity.Relationship
|
||||
let isReported: Bool
|
||||
|
||||
var headline: String {
|
||||
|
@ -40,7 +41,6 @@ class ReportResultViewModel: ObservableObject {
|
|||
@Published var avatarURL: URL?
|
||||
@Published var username: String = ""
|
||||
|
||||
let relationshipViewModel = RelationshipViewModel()
|
||||
let muteActionPublisher = PassthroughSubject<Void, Never>()
|
||||
let followActionPublisher = PassthroughSubject<Void, Never>()
|
||||
let blockActionPublisher = PassthroughSubject<Void, Never>()
|
||||
|
@ -49,11 +49,13 @@ class ReportResultViewModel: ObservableObject {
|
|||
context: AppContext,
|
||||
authContext: AuthContext,
|
||||
account: Mastodon.Entity.Account,
|
||||
relationship: Mastodon.Entity.Relationship,
|
||||
isReported: Bool
|
||||
) {
|
||||
self.context = context
|
||||
self.authContext = authContext
|
||||
self.account = account
|
||||
self.relationship = relationship
|
||||
self.isReported = isReported
|
||||
// end init
|
||||
|
||||
|
|
|
@ -94,25 +94,6 @@ extension ProfileRelationshipActionButton {
|
|||
}
|
||||
}
|
||||
|
||||
public func configure(actionOptionSet: RelationshipActionOptionSet) {
|
||||
setTitle(actionOptionSet.title, for: .normal)
|
||||
|
||||
configureAppearance()
|
||||
|
||||
titleEdgeInsets = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4)
|
||||
|
||||
activityIndicatorView.stopAnimating()
|
||||
|
||||
if let option = actionOptionSet.highPriorityAction(except: .editOptions), option == .blocked || option == .suspended {
|
||||
isEnabled = false
|
||||
} else if actionOptionSet.contains(.updating) {
|
||||
isEnabled = false
|
||||
activityIndicatorView.startAnimating()
|
||||
} else {
|
||||
isEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
private func configureAppearance() {
|
||||
setTitleColor(Asset.Colors.Label.primaryReverse.color, for: .normal)
|
||||
setTitleColor(Asset.Colors.Label.primaryReverse.color.withAlphaComponent(0.5), for: .highlighted)
|
||||
|
|
|
@ -1,269 +0,0 @@
|
|||
//
|
||||
// RelationshipViewModel.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2022-4-14.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import CoreDataStack
|
||||
|
||||
public enum RelationshipAction: Int, CaseIterable {
|
||||
case showReblogs
|
||||
case isMyself
|
||||
case followingBy
|
||||
case blockingBy
|
||||
case none // set hide from UI
|
||||
case follow
|
||||
case request
|
||||
case pending
|
||||
case following
|
||||
case muting
|
||||
case blocked
|
||||
case blocking
|
||||
case suspended
|
||||
case edit
|
||||
case editing
|
||||
case updating
|
||||
|
||||
public var option: RelationshipActionOptionSet {
|
||||
return RelationshipActionOptionSet(rawValue: 1 << rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// construct option set on the enum for safe iterator
|
||||
public struct RelationshipActionOptionSet: OptionSet {
|
||||
|
||||
public let rawValue: Int
|
||||
|
||||
public init(rawValue: Int) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public static let isMyself = RelationshipAction.isMyself.option
|
||||
public static let followingBy = RelationshipAction.followingBy.option
|
||||
public static let blockingBy = RelationshipAction.blockingBy.option
|
||||
public static let none = RelationshipAction.none.option
|
||||
public static let follow = RelationshipAction.follow.option
|
||||
public static let request = RelationshipAction.request.option
|
||||
public static let pending = RelationshipAction.pending.option
|
||||
public static let following = RelationshipAction.following.option
|
||||
public static let muting = RelationshipAction.muting.option
|
||||
public static let blocked = RelationshipAction.blocked.option
|
||||
public static let blocking = RelationshipAction.blocking.option
|
||||
public static let suspended = RelationshipAction.suspended.option
|
||||
public static let edit = RelationshipAction.edit.option
|
||||
public static let editing = RelationshipAction.editing.option
|
||||
public static let updating = RelationshipAction.updating.option
|
||||
public static let showReblogs = RelationshipAction.showReblogs.option
|
||||
public static let editOptions: RelationshipActionOptionSet = [.edit, .editing, .updating]
|
||||
|
||||
public func highPriorityAction(except: RelationshipActionOptionSet) -> RelationshipAction? {
|
||||
let set = subtracting(except)
|
||||
for action in RelationshipAction.allCases.reversed() where set.contains(action.option) {
|
||||
return action
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public var title: String {
|
||||
guard let highPriorityAction = self.highPriorityAction(except: []) else {
|
||||
assertionFailure()
|
||||
return " "
|
||||
}
|
||||
switch highPriorityAction {
|
||||
case .isMyself: return ""
|
||||
case .followingBy: return " "
|
||||
case .blockingBy: return " "
|
||||
case .none: return " "
|
||||
case .follow: return L10n.Common.Controls.Friendship.follow
|
||||
case .request: return L10n.Common.Controls.Friendship.request
|
||||
case .pending: return L10n.Common.Controls.Friendship.pending
|
||||
case .following: return L10n.Common.Controls.Friendship.following
|
||||
case .muting: return L10n.Common.Controls.Friendship.muted
|
||||
case .blocked: return L10n.Common.Controls.Friendship.follow // blocked by user (deprecated)
|
||||
case .blocking: return L10n.Common.Controls.Friendship.blocked
|
||||
case .suspended: return L10n.Common.Controls.Friendship.follow
|
||||
case .edit: return L10n.Common.Controls.Friendship.editInfo
|
||||
case .editing: return L10n.Common.Controls.Actions.done
|
||||
case .updating: return " "
|
||||
case .showReblogs: return " "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Replace with Mastodon.Entity.Relationship")
|
||||
public final class RelationshipViewModel {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
public var userObserver: AnyCancellable?
|
||||
public var meObserver: AnyCancellable?
|
||||
|
||||
// input
|
||||
@Published public var user: MastodonUser?
|
||||
@Published public var me: MastodonUser?
|
||||
public let relationshipUpdatePublisher = CurrentValueSubject<Void, Never>(Void()) // needs initial event
|
||||
|
||||
// output
|
||||
@Published public var isMyself = false
|
||||
@Published public var optionSet: RelationshipActionOptionSet?
|
||||
|
||||
@Published public var isFollowing = false
|
||||
@Published public var isFollowingBy = false
|
||||
@Published public var isMuting = false
|
||||
@Published public var showReblogs = false
|
||||
@Published public var isBlocking = false
|
||||
@Published public var isBlockingBy = false
|
||||
@Published public var isSuspended = false
|
||||
|
||||
public init() {
|
||||
Publishers.CombineLatest3(
|
||||
$user,
|
||||
$me,
|
||||
relationshipUpdatePublisher
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] user, me, _ in
|
||||
guard let self = self else { return }
|
||||
self.update(user: user, me: me)
|
||||
|
||||
guard let user = user, let me = me else {
|
||||
self.userObserver = nil
|
||||
self.meObserver = nil
|
||||
return
|
||||
}
|
||||
|
||||
// do not modify object to prevent infinity loop
|
||||
self.userObserver = RelationshipViewModel.createObjectChangePublisher(user: user)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.relationshipUpdatePublisher.send()
|
||||
}
|
||||
|
||||
self.meObserver = RelationshipViewModel.createObjectChangePublisher(user: me)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.relationshipUpdatePublisher.send()
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension RelationshipViewModel {
|
||||
|
||||
public static func createObjectChangePublisher(user: MastodonUser) -> AnyPublisher<Void, Never> {
|
||||
return ManagedObjectObserver
|
||||
.observe(object: user)
|
||||
.map { _ in Void() }
|
||||
.catch { error in
|
||||
return Just(Void())
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension RelationshipViewModel {
|
||||
private func update(user: MastodonUser?, me: MastodonUser?) {
|
||||
guard let user = user,
|
||||
let me = me
|
||||
else {
|
||||
reset()
|
||||
return
|
||||
}
|
||||
|
||||
let optionSet = RelationshipViewModel.optionSet(user: user, me: me)
|
||||
|
||||
self.isMyself = optionSet.contains(.isMyself)
|
||||
self.isFollowingBy = optionSet.contains(.followingBy)
|
||||
self.isFollowing = optionSet.contains(.following)
|
||||
self.isMuting = optionSet.contains(.muting)
|
||||
self.isBlockingBy = optionSet.contains(.blockingBy)
|
||||
self.isBlocking = optionSet.contains(.blocking)
|
||||
self.isSuspended = optionSet.contains(.suspended)
|
||||
self.showReblogs = optionSet.contains(.showReblogs)
|
||||
|
||||
self.optionSet = optionSet
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
isMyself = false
|
||||
isFollowingBy = false
|
||||
isFollowing = false
|
||||
isMuting = false
|
||||
isBlockingBy = false
|
||||
isBlocking = false
|
||||
optionSet = nil
|
||||
showReblogs = false
|
||||
}
|
||||
}
|
||||
|
||||
extension RelationshipViewModel {
|
||||
|
||||
public static func optionSet(user: MastodonUser, me: MastodonUser) -> RelationshipActionOptionSet {
|
||||
let isMyself = user.id == me.id && user.domain == me.domain
|
||||
guard !isMyself else {
|
||||
return [.isMyself, .edit]
|
||||
}
|
||||
|
||||
let isProtected = user.locked
|
||||
let isFollowingBy = me.followingBy.contains(user)
|
||||
let isFollowing = user.followingBy.contains(me)
|
||||
let isPending = user.followRequestedBy.contains(me)
|
||||
let isMuting = user.mutingBy.contains(me)
|
||||
let isBlockingBy = me.blockingBy.contains(user)
|
||||
let isBlocking = user.blockingBy.contains(me)
|
||||
let isShowingReblogs = me.showingReblogsBy.contains(user)
|
||||
|
||||
var optionSet: RelationshipActionOptionSet = [.follow]
|
||||
|
||||
if isMyself {
|
||||
optionSet.insert(.isMyself)
|
||||
}
|
||||
|
||||
if isProtected {
|
||||
optionSet.insert(.request)
|
||||
}
|
||||
|
||||
if isFollowingBy {
|
||||
optionSet.insert(.followingBy)
|
||||
}
|
||||
|
||||
if isFollowing {
|
||||
optionSet.insert(.following)
|
||||
}
|
||||
|
||||
if isPending {
|
||||
optionSet.insert(.pending)
|
||||
}
|
||||
|
||||
if isMuting {
|
||||
optionSet.insert(.muting)
|
||||
}
|
||||
|
||||
if isBlockingBy {
|
||||
optionSet.insert(.blockingBy)
|
||||
}
|
||||
|
||||
if isBlocking {
|
||||
optionSet.insert(.blocking)
|
||||
}
|
||||
|
||||
if user.suspended {
|
||||
optionSet.insert(.suspended)
|
||||
}
|
||||
|
||||
if isShowingReblogs {
|
||||
optionSet.insert(.showReblogs)
|
||||
}
|
||||
|
||||
return optionSet
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue