forked from zelo72/mastodon-ios
feat: handle suspended account in profile scene
This commit is contained in:
parent
ba48adb470
commit
14176be4ed
|
@ -82,6 +82,7 @@
|
||||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="note" optional="YES" attributeType="String"/>
|
<attribute name="note" optional="YES" attributeType="String"/>
|
||||||
<attribute name="statusesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="statusesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="suspended" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="url" optional="YES" attributeType="String"/>
|
<attribute name="url" optional="YES" attributeType="String"/>
|
||||||
<attribute name="username" attributeType="String"/>
|
<attribute name="username" attributeType="String"/>
|
||||||
|
@ -199,7 +200,7 @@
|
||||||
<element name="History" positionX="27" positionY="126" width="128" height="119"/>
|
<element name="History" positionX="27" positionY="126" width="128" height="119"/>
|
||||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="MastodonAuthentication" positionX="18" positionY="162" width="128" height="209"/>
|
<element name="MastodonAuthentication" positionX="18" positionY="162" width="128" height="209"/>
|
||||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="659"/>
|
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="674"/>
|
||||||
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
||||||
<element name="Poll" positionX="72" positionY="162" width="128" height="194"/>
|
<element name="Poll" positionX="72" positionY="162" width="128" height="194"/>
|
||||||
<element name="PollOption" positionX="81" positionY="171" width="128" height="134"/>
|
<element name="PollOption" positionX="81" positionY="171" width="128" height="134"/>
|
||||||
|
|
|
@ -31,6 +31,7 @@ final public class MastodonUser: NSManagedObject {
|
||||||
|
|
||||||
@NSManaged public private(set) var locked: Bool
|
@NSManaged public private(set) var locked: Bool
|
||||||
@NSManaged public private(set) var bot: Bool
|
@NSManaged public private(set) var bot: Bool
|
||||||
|
@NSManaged public private(set) var suspended: Bool
|
||||||
|
|
||||||
@NSManaged public private(set) var createdAt: Date
|
@NSManaged public private(set) var createdAt: Date
|
||||||
@NSManaged public private(set) var updatedAt: Date
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
@ -93,6 +94,7 @@ extension MastodonUser {
|
||||||
|
|
||||||
user.locked = property.locked
|
user.locked = property.locked
|
||||||
user.bot = property.bot ?? false
|
user.bot = property.bot ?? false
|
||||||
|
user.suspended = property.suspended ?? false
|
||||||
|
|
||||||
// Mastodon do not provide relationship on the `Account`
|
// Mastodon do not provide relationship on the `Account`
|
||||||
// Update relationship via attribute updating interface
|
// Update relationship via attribute updating interface
|
||||||
|
@ -174,6 +176,11 @@ extension MastodonUser {
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public func update(suspended: Bool) {
|
||||||
|
if self.suspended != suspended {
|
||||||
|
self.suspended = suspended
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func update(isFollowing: Bool, by mastodonUser: MastodonUser) {
|
public func update(isFollowing: Bool, by mastodonUser: MastodonUser) {
|
||||||
if isFollowing {
|
if isFollowing {
|
||||||
|
@ -268,6 +275,7 @@ extension MastodonUser {
|
||||||
public let followersCount: Int
|
public let followersCount: Int
|
||||||
public let locked: Bool
|
public let locked: Bool
|
||||||
public let bot: Bool?
|
public let bot: Bool?
|
||||||
|
public let suspended: Bool?
|
||||||
|
|
||||||
public let createdAt: Date
|
public let createdAt: Date
|
||||||
public let networkDate: Date
|
public let networkDate: Date
|
||||||
|
@ -289,6 +297,7 @@ extension MastodonUser {
|
||||||
followersCount: Int,
|
followersCount: Int,
|
||||||
locked: Bool,
|
locked: Bool,
|
||||||
bot: Bool?,
|
bot: Bool?,
|
||||||
|
suspended: Bool?,
|
||||||
createdAt: Date,
|
createdAt: Date,
|
||||||
networkDate: Date
|
networkDate: Date
|
||||||
) {
|
) {
|
||||||
|
@ -309,6 +318,7 @@ extension MastodonUser {
|
||||||
self.followersCount = followersCount
|
self.followersCount = followersCount
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
self.suspended = suspended
|
||||||
self.createdAt = createdAt
|
self.createdAt = createdAt
|
||||||
self.networkDate = networkDate
|
self.networkDate = networkDate
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
"firendship": {
|
"firendship": {
|
||||||
"follow": "Follow",
|
"follow": "Follow",
|
||||||
"following": "Following",
|
"following": "Following",
|
||||||
|
"request": "Request",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"block": "Block",
|
"block": "Block",
|
||||||
"block_user": "Block %s",
|
"block_user": "Block %s",
|
||||||
|
@ -91,7 +92,8 @@
|
||||||
"no_status_found": "No Status Found",
|
"no_status_found": "No Status Found",
|
||||||
"blocking_warning": "You can’t view Artbot’s profile\n until you unblock them.\nYour account looks like this to them.",
|
"blocking_warning": "You can’t view Artbot’s profile\n until you unblock them.\nYour account looks like this to them.",
|
||||||
"blocked_warning": "You can’t view Artbot’s profile\n until they unblock you.",
|
"blocked_warning": "You can’t view Artbot’s profile\n until they unblock you.",
|
||||||
"suspended_warning": "This account is suspended."
|
"suspended_warning": "This account has been suspended.",
|
||||||
|
"user_suspended_warning": "%s's account has been suspended."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -757,17 +757,9 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
0F1E2D102615C39800C38565 /* View */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
path = View;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
0F2021F5261325ED000C64BF /* HashtagTimeline */ = {
|
0F2021F5261325ED000C64BF /* HashtagTimeline */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0F1E2D102615C39800C38565 /* View */,
|
|
||||||
0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */,
|
0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */,
|
||||||
0F202226261411BA000C64BF /* HashtagTimelineViewController+StatusProvider.swift */,
|
0F202226261411BA000C64BF /* HashtagTimelineViewController+StatusProvider.swift */,
|
||||||
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */,
|
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */,
|
||||||
|
|
|
@ -63,11 +63,21 @@ extension Item {
|
||||||
let id = UUID()
|
let id = UUID()
|
||||||
let reason: Reason
|
let reason: Reason
|
||||||
|
|
||||||
enum Reason {
|
enum Reason: Equatable {
|
||||||
case noStatusFound
|
case noStatusFound
|
||||||
case blocking
|
case blocking
|
||||||
case blocked
|
case blocked
|
||||||
case suspended
|
case suspended(name: String?)
|
||||||
|
|
||||||
|
static func == (lhs: Item.EmptyStateHeaderAttribute.Reason, rhs: Item.EmptyStateHeaderAttribute.Reason) -> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case (.noStatusFound, noStatusFound): return true
|
||||||
|
case (.blocking, blocking): return true
|
||||||
|
case (.blocked, blocked): return true
|
||||||
|
case (.suspended(let nameLeft), .suspended(let nameRight)): return nameLeft == nameRight
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(reason: Reason) {
|
init(reason: Reason) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ extension MastodonUser.Property {
|
||||||
followersCount: entity.followersCount,
|
followersCount: entity.followersCount,
|
||||||
locked: entity.locked,
|
locked: entity.locked,
|
||||||
bot: entity.bot,
|
bot: entity.bot,
|
||||||
|
suspended: entity.suspended,
|
||||||
createdAt: entity.createdAt,
|
createdAt: entity.createdAt,
|
||||||
networkDate: networkDate
|
networkDate: networkDate
|
||||||
)
|
)
|
||||||
|
|
|
@ -112,6 +112,8 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
/// Pending
|
/// Pending
|
||||||
internal static let pending = L10n.tr("Localizable", "Common.Controls.Firendship.Pending")
|
internal static let pending = L10n.tr("Localizable", "Common.Controls.Firendship.Pending")
|
||||||
|
/// Request
|
||||||
|
internal static let request = L10n.tr("Localizable", "Common.Controls.Firendship.Request")
|
||||||
/// Unblock
|
/// Unblock
|
||||||
internal static let unblock = L10n.tr("Localizable", "Common.Controls.Firendship.Unblock")
|
internal static let unblock = L10n.tr("Localizable", "Common.Controls.Firendship.Unblock")
|
||||||
/// Unblock %@
|
/// Unblock %@
|
||||||
|
@ -179,8 +181,12 @@ internal enum L10n {
|
||||||
internal static let blockingWarning = L10n.tr("Localizable", "Common.Controls.Timeline.Header.BlockingWarning")
|
internal static let blockingWarning = L10n.tr("Localizable", "Common.Controls.Timeline.Header.BlockingWarning")
|
||||||
/// No Status Found
|
/// No Status Found
|
||||||
internal static let noStatusFound = L10n.tr("Localizable", "Common.Controls.Timeline.Header.NoStatusFound")
|
internal static let noStatusFound = L10n.tr("Localizable", "Common.Controls.Timeline.Header.NoStatusFound")
|
||||||
/// This account is suspended.
|
/// This account has been suspended.
|
||||||
internal static let suspendedWarning = L10n.tr("Localizable", "Common.Controls.Timeline.Header.SuspendedWarning")
|
internal static let suspendedWarning = L10n.tr("Localizable", "Common.Controls.Timeline.Header.SuspendedWarning")
|
||||||
|
/// %@'s account has been suspended.
|
||||||
|
internal static func userSuspendedWarning(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "Common.Controls.Timeline.Header.UserSuspendedWarning", String(describing: p1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
internal enum Loader {
|
internal enum Loader {
|
||||||
/// Loading missing posts...
|
/// Loading missing posts...
|
||||||
|
|
|
@ -38,6 +38,7 @@ Please check your internet connection.";
|
||||||
"Common.Controls.Firendship.MuteUser" = "Mute %@";
|
"Common.Controls.Firendship.MuteUser" = "Mute %@";
|
||||||
"Common.Controls.Firendship.Muted" = "Muted";
|
"Common.Controls.Firendship.Muted" = "Muted";
|
||||||
"Common.Controls.Firendship.Pending" = "Pending";
|
"Common.Controls.Firendship.Pending" = "Pending";
|
||||||
|
"Common.Controls.Firendship.Request" = "Request";
|
||||||
"Common.Controls.Firendship.Unblock" = "Unblock";
|
"Common.Controls.Firendship.Unblock" = "Unblock";
|
||||||
"Common.Controls.Firendship.UnblockUser" = "Unblock %@";
|
"Common.Controls.Firendship.UnblockUser" = "Unblock %@";
|
||||||
"Common.Controls.Firendship.Unmute" = "Unmute";
|
"Common.Controls.Firendship.Unmute" = "Unmute";
|
||||||
|
@ -60,7 +61,8 @@ Please check your internet connection.";
|
||||||
until you unblock them.
|
until you unblock them.
|
||||||
Your account looks like this to them.";
|
Your account looks like this to them.";
|
||||||
"Common.Controls.Timeline.Header.NoStatusFound" = "No Status Found";
|
"Common.Controls.Timeline.Header.NoStatusFound" = "No Status Found";
|
||||||
"Common.Controls.Timeline.Header.SuspendedWarning" = "This account is suspended.";
|
"Common.Controls.Timeline.Header.SuspendedWarning" = "This account has been suspended.";
|
||||||
|
"Common.Controls.Timeline.Header.UserSuspendedWarning" = "%@'s account has been suspended.";
|
||||||
"Common.Controls.Timeline.Loader.LoadMissingPosts" = "Load missing posts";
|
"Common.Controls.Timeline.Loader.LoadMissingPosts" = "Load missing posts";
|
||||||
"Common.Controls.Timeline.Loader.LoadingMissingPosts" = "Loading missing posts...";
|
"Common.Controls.Timeline.Loader.LoadingMissingPosts" = "Loading missing posts...";
|
||||||
"Common.Countable.Photo.Multiple" = "photos";
|
"Common.Countable.Photo.Multiple" = "photos";
|
||||||
|
|
|
@ -25,6 +25,10 @@ extension HomeTimelineViewController {
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.showPublicTimelineAction(action)
|
self.showPublicTimelineAction(action)
|
||||||
},
|
},
|
||||||
|
UIAction(title: "Show Profile", image: UIImage(systemName: "person.crop.circle"), attributes: []) { [weak self] action in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.showProfileAction(action)
|
||||||
|
},
|
||||||
UIAction(title: "Sign Out", image: UIImage(systemName: "escape"), attributes: .destructive) { [weak self] action in
|
UIAction(title: "Sign Out", image: UIImage(systemName: "escape"), attributes: .destructive) { [weak self] action in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.signOutAction(action)
|
self.signOutAction(action)
|
||||||
|
@ -277,5 +281,20 @@ extension HomeTimelineViewController {
|
||||||
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func showProfileAction(_ sender: UIAction) {
|
||||||
|
let alertController = UIAlertController(title: "Enter User ID", message: nil, preferredStyle: .alert)
|
||||||
|
alertController.addTextField()
|
||||||
|
let showAction = UIAlertAction(title: "Show", style: .default) { [weak self, weak alertController] _ in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let textField = alertController?.textFields?.first else { return }
|
||||||
|
let profileViewModel = RemoteProfileViewModel(context: self.context, userID: textField.text ?? "")
|
||||||
|
self.coordinator.present(scene: .profile(viewModel: profileViewModel), from: self, transition: .show)
|
||||||
|
}
|
||||||
|
alertController.addAction(showAction)
|
||||||
|
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
||||||
|
alertController.addAction(cancelAction)
|
||||||
|
coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
protocol ProfileHeaderViewControllerDelegate: class {
|
protocol ProfileHeaderViewControllerDelegate: class {
|
||||||
func profileHeaderViewController(_ viewController: ProfileHeaderViewController, viewLayoutDidUpdate view: UIView)
|
func profileHeaderViewController(_ viewController: ProfileHeaderViewController, viewLayoutDidUpdate view: UIView)
|
||||||
|
@ -21,6 +22,8 @@ final class ProfileHeaderViewController: UIViewController {
|
||||||
|
|
||||||
weak var delegate: ProfileHeaderViewControllerDelegate?
|
weak var delegate: ProfileHeaderViewControllerDelegate?
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
let profileHeaderView = ProfileHeaderView()
|
let profileHeaderView = ProfileHeaderView()
|
||||||
let pageSegmentedControl: UISegmentedControl = {
|
let pageSegmentedControl: UISegmentedControl = {
|
||||||
let segmenetedControl = UISegmentedControl(items: ["A", "B"])
|
let segmenetedControl = UISegmentedControl(items: ["A", "B"])
|
||||||
|
@ -33,6 +36,8 @@ final class ProfileHeaderViewController: UIViewController {
|
||||||
|
|
||||||
// private var isAdjustBannerImageViewForSafeAreaInset = false
|
// private var isAdjustBannerImageViewForSafeAreaInset = false
|
||||||
private var containerSafeAreaInset: UIEdgeInsets = .zero
|
private var containerSafeAreaInset: UIEdgeInsets = .zero
|
||||||
|
|
||||||
|
let needsSetupBottomShadow = CurrentValueSubject<Bool, Never>(true)
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
@ -67,6 +72,14 @@ extension ProfileHeaderViewController {
|
||||||
])
|
])
|
||||||
|
|
||||||
pageSegmentedControl.addTarget(self, action: #selector(ProfileHeaderViewController.pageSegmentedControlValueChanged(_:)), for: .valueChanged)
|
pageSegmentedControl.addTarget(self, action: #selector(ProfileHeaderViewController.pageSegmentedControlValueChanged(_:)), for: .valueChanged)
|
||||||
|
|
||||||
|
needsSetupBottomShadow
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] needsSetupBottomShadow in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.setupBottomShadow()
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
@ -85,7 +98,7 @@ extension ProfileHeaderViewController {
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
delegate?.profileHeaderViewController(self, viewLayoutDidUpdate: view)
|
delegate?.profileHeaderViewController(self, viewLayoutDidUpdate: view)
|
||||||
view.layer.setupShadow(color: UIColor.black.withAlphaComponent(0.12), alpha: Float(bottomShadowAlpha), x: 0, y: 2, blur: 2, spread: 0, roundedRect: view.bounds, byRoundingCorners: .allCorners, cornerRadii: .zero)
|
setupBottomShadow()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -105,6 +118,15 @@ extension ProfileHeaderViewController {
|
||||||
containerSafeAreaInset = inset
|
containerSafeAreaInset = inset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupBottomShadow() {
|
||||||
|
guard needsSetupBottomShadow.value else {
|
||||||
|
view.layer.shadowColor = nil
|
||||||
|
view.layer.shadowRadius = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
view.layer.setupShadow(color: UIColor.black.withAlphaComponent(0.12), alpha: Float(bottomShadowAlpha), x: 0, y: 2, blur: 2, spread: 0, roundedRect: view.bounds, byRoundingCorners: .allCorners, cornerRadii: .zero)
|
||||||
|
}
|
||||||
|
|
||||||
private func updateHeaderBottomShadow(progress: CGFloat) {
|
private func updateHeaderBottomShadow(progress: CGFloat) {
|
||||||
let alpha = min(max(0, 10 * progress - 9), 1)
|
let alpha = min(max(0, 10 * progress - 9), 1)
|
||||||
if bottomShadowAlpha != alpha {
|
if bottomShadowAlpha != alpha {
|
||||||
|
|
|
@ -34,7 +34,7 @@ extension ProfileRelationshipActionButton {
|
||||||
setTitleColor(UIColor.white.withAlphaComponent(0.5), for: .highlighted)
|
setTitleColor(UIColor.white.withAlphaComponent(0.5), for: .highlighted)
|
||||||
setBackgroundImage(.placeholder(color: actionOptionSet.backgroundColor), for: .normal)
|
setBackgroundImage(.placeholder(color: actionOptionSet.backgroundColor), for: .normal)
|
||||||
setBackgroundImage(.placeholder(color: actionOptionSet.backgroundColor.withAlphaComponent(0.5)), for: .highlighted)
|
setBackgroundImage(.placeholder(color: actionOptionSet.backgroundColor.withAlphaComponent(0.5)), for: .highlighted)
|
||||||
setBackgroundImage(.placeholder(color: actionOptionSet.backgroundColor), for: .disabled)
|
setBackgroundImage(.placeholder(color: actionOptionSet.backgroundColor.withAlphaComponent(0.5)), for: .disabled)
|
||||||
|
|
||||||
if let option = actionOptionSet.highPriorityAction(except: .editOptions), option == .blocked {
|
if let option = actionOptionSet.highPriorityAction(except: .editOptions), option == .blocked {
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
|
|
|
@ -136,19 +136,24 @@ extension ProfileViewController {
|
||||||
|
|
||||||
navigationItem.titleView = UIView()
|
navigationItem.titleView = UIView()
|
||||||
|
|
||||||
Publishers.CombineLatest3(
|
Publishers.CombineLatest4(
|
||||||
|
viewModel.suspended.eraseToAnyPublisher(),
|
||||||
viewModel.isMeBarButtonItemsHidden.eraseToAnyPublisher(),
|
viewModel.isMeBarButtonItemsHidden.eraseToAnyPublisher(),
|
||||||
viewModel.isReplyBarButtonItemHidden.eraseToAnyPublisher(),
|
viewModel.isReplyBarButtonItemHidden.eraseToAnyPublisher(),
|
||||||
viewModel.isMoreMenuBarButtonItemHidden.eraseToAnyPublisher()
|
viewModel.isMoreMenuBarButtonItemHidden.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] isMeBarButtonItemsHidden, isReplyBarButtonItemHidden, isMoreMenuBarButtonItemHidden in
|
.sink { [weak self] suspended, isMeBarButtonItemsHidden, isReplyBarButtonItemHidden, isMoreMenuBarButtonItemHidden in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
var items: [UIBarButtonItem] = []
|
var items: [UIBarButtonItem] = []
|
||||||
defer {
|
defer {
|
||||||
self.navigationItem.rightBarButtonItems = !items.isEmpty ? items : nil
|
self.navigationItem.rightBarButtonItems = !items.isEmpty ? items : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard !suspended else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
guard isMeBarButtonItemsHidden else {
|
guard isMeBarButtonItemsHidden else {
|
||||||
items.append(self.settingBarButtonItem)
|
items.append(self.settingBarButtonItem)
|
||||||
items.append(self.shareBarButtonItem)
|
items.append(self.shareBarButtonItem)
|
||||||
|
@ -345,6 +350,21 @@ extension ProfileViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
Publishers.CombineLatest3(
|
||||||
|
viewModel.isBlocking.eraseToAnyPublisher(),
|
||||||
|
viewModel.isBlockedBy.eraseToAnyPublisher(),
|
||||||
|
viewModel.suspended.eraseToAnyPublisher()
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] isBlocking, isBlockedBy, suspended in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let isNeedSetHidden = isBlocking || isBlockedBy || suspended
|
||||||
|
self.profileHeaderViewController.needsSetupBottomShadow.value = !isNeedSetHidden
|
||||||
|
self.profileHeaderViewController.profileHeaderView.bioContainerView.isHidden = isNeedSetHidden
|
||||||
|
self.profileHeaderViewController.pageSegmentedControl.isHidden = isNeedSetHidden
|
||||||
|
self.viewModel.needsPagePinToTop.value = isNeedSetHidden
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
viewModel.bioDescription
|
viewModel.bioDescription
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink(receiveValue: { [weak self] bio in
|
.sink(receiveValue: { [weak self] bio in
|
||||||
|
@ -411,6 +431,8 @@ extension ProfileViewController {
|
||||||
viewModel.userID.assign(to: \.value, on: userTimelineViewModel.userID).store(in: &disposeBag)
|
viewModel.userID.assign(to: \.value, on: userTimelineViewModel.userID).store(in: &disposeBag)
|
||||||
viewModel.isBlocking.assign(to: \.value, on: userTimelineViewModel.isBlocking).store(in: &disposeBag)
|
viewModel.isBlocking.assign(to: \.value, on: userTimelineViewModel.isBlocking).store(in: &disposeBag)
|
||||||
viewModel.isBlockedBy.assign(to: \.value, on: userTimelineViewModel.isBlockedBy).store(in: &disposeBag)
|
viewModel.isBlockedBy.assign(to: \.value, on: userTimelineViewModel.isBlockedBy).store(in: &disposeBag)
|
||||||
|
viewModel.suspended.assign(to: \.value, on: userTimelineViewModel.isSuspended).store(in: &disposeBag)
|
||||||
|
viewModel.name.assign(to: \.value, on: userTimelineViewModel.userDisplayName).store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -476,10 +498,15 @@ extension ProfileViewController: UIScrollViewDelegate {
|
||||||
contentOffsets.removeAll()
|
contentOffsets.removeAll()
|
||||||
} else {
|
} else {
|
||||||
containerScrollView.contentOffset.y = topMaxContentOffsetY
|
containerScrollView.contentOffset.y = topMaxContentOffsetY
|
||||||
if let customScrollViewContainerController = profileSegmentedViewController.pagingViewController.currentViewController as? ScrollViewContainer {
|
if viewModel.needsPagePinToTop.value {
|
||||||
let contentOffsetY = scrollView.contentOffset.y - containerScrollView.contentOffset.y
|
// do nothing
|
||||||
customScrollViewContainerController.scrollView.contentOffset.y = contentOffsetY
|
} else {
|
||||||
|
if let customScrollViewContainerController = profileSegmentedViewController.pagingViewController.currentViewController as? ScrollViewContainer {
|
||||||
|
let contentOffsetY = scrollView.contentOffset.y - containerScrollView.contentOffset.y
|
||||||
|
customScrollViewContainerController.scrollView.contentOffset.y = contentOffsetY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// elastically banner image
|
// elastically banner image
|
||||||
|
@ -538,16 +565,14 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||||
switch relationshipAction {
|
switch relationshipAction {
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case .follow, .following:
|
case .follow, .reqeust, .pending, .following:
|
||||||
UserProviderFacade.toggleUserFollowRelationship(provider: self)
|
UserProviderFacade.toggleUserFollowRelationship(provider: self)
|
||||||
.sink { _ in
|
.sink { _ in
|
||||||
|
// TODO: handle error
|
||||||
} receiveValue: { _ in
|
} receiveValue: { _ in
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
case .pending:
|
|
||||||
break
|
|
||||||
case .muting:
|
case .muting:
|
||||||
guard let mastodonUser = viewModel.mastodonUser.value else { return }
|
guard let mastodonUser = viewModel.mastodonUser.value else { return }
|
||||||
let name = mastodonUser.displayNameWithFallback
|
let name = mastodonUser.displayNameWithFallback
|
||||||
|
|
|
@ -41,7 +41,7 @@ class ProfileViewModel: NSObject {
|
||||||
let followersCount: CurrentValueSubject<Int?, Never>
|
let followersCount: CurrentValueSubject<Int?, Never>
|
||||||
|
|
||||||
let protected: CurrentValueSubject<Bool?, Never>
|
let protected: CurrentValueSubject<Bool?, Never>
|
||||||
// let suspended: CurrentValueSubject<Bool, Never>
|
let suspended: CurrentValueSubject<Bool, Never>
|
||||||
|
|
||||||
let relationshipActionOptionSet = CurrentValueSubject<RelationshipActionOptionSet, Never>(.none)
|
let relationshipActionOptionSet = CurrentValueSubject<RelationshipActionOptionSet, Never>(.none)
|
||||||
let isEditing = CurrentValueSubject<Bool, Never>(false)
|
let isEditing = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
@ -55,6 +55,8 @@ class ProfileViewModel: NSObject {
|
||||||
let isMoreMenuBarButtonItemHidden = CurrentValueSubject<Bool, Never>(true)
|
let isMoreMenuBarButtonItemHidden = CurrentValueSubject<Bool, Never>(true)
|
||||||
let isMeBarButtonItemsHidden = CurrentValueSubject<Bool, Never>(true)
|
let isMeBarButtonItemsHidden = CurrentValueSubject<Bool, Never>(true)
|
||||||
|
|
||||||
|
let needsPagePinToTop = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
|
||||||
init(context: AppContext, optionalMastodonUser mastodonUser: MastodonUser?) {
|
init(context: AppContext, optionalMastodonUser mastodonUser: MastodonUser?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mastodonUser = CurrentValueSubject(mastodonUser)
|
self.mastodonUser = CurrentValueSubject(mastodonUser)
|
||||||
|
@ -62,7 +64,6 @@ class ProfileViewModel: NSObject {
|
||||||
self.userID = CurrentValueSubject(mastodonUser?.id)
|
self.userID = CurrentValueSubject(mastodonUser?.id)
|
||||||
self.bannerImageURL = CurrentValueSubject(mastodonUser?.headerImageURL())
|
self.bannerImageURL = CurrentValueSubject(mastodonUser?.headerImageURL())
|
||||||
self.avatarImageURL = CurrentValueSubject(mastodonUser?.avatarImageURL())
|
self.avatarImageURL = CurrentValueSubject(mastodonUser?.avatarImageURL())
|
||||||
// self.protected = CurrentValueSubject(twitterUser?.protected)
|
|
||||||
self.name = CurrentValueSubject(mastodonUser?.displayNameWithFallback)
|
self.name = CurrentValueSubject(mastodonUser?.displayNameWithFallback)
|
||||||
self.username = CurrentValueSubject(mastodonUser?.acctWithDomain)
|
self.username = CurrentValueSubject(mastodonUser?.acctWithDomain)
|
||||||
self.bioDescription = CurrentValueSubject(mastodonUser?.note)
|
self.bioDescription = CurrentValueSubject(mastodonUser?.note)
|
||||||
|
@ -71,6 +72,7 @@ class ProfileViewModel: NSObject {
|
||||||
self.followingCount = CurrentValueSubject(mastodonUser.flatMap { Int(truncating: $0.followingCount) })
|
self.followingCount = CurrentValueSubject(mastodonUser.flatMap { Int(truncating: $0.followingCount) })
|
||||||
self.followersCount = CurrentValueSubject(mastodonUser.flatMap { Int(truncating: $0.followersCount) })
|
self.followersCount = CurrentValueSubject(mastodonUser.flatMap { Int(truncating: $0.followersCount) })
|
||||||
self.protected = CurrentValueSubject(mastodonUser?.locked)
|
self.protected = CurrentValueSubject(mastodonUser?.locked)
|
||||||
|
self.suspended = CurrentValueSubject(mastodonUser?.suspended ?? false)
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
relationshipActionOptionSet
|
relationshipActionOptionSet
|
||||||
|
@ -226,6 +228,7 @@ extension ProfileViewModel {
|
||||||
self.followingCount.value = mastodonUser.flatMap { Int(truncating: $0.followingCount) }
|
self.followingCount.value = mastodonUser.flatMap { Int(truncating: $0.followingCount) }
|
||||||
self.followersCount.value = mastodonUser.flatMap { Int(truncating: $0.followersCount) }
|
self.followersCount.value = mastodonUser.flatMap { Int(truncating: $0.followersCount) }
|
||||||
self.protected.value = mastodonUser?.locked
|
self.protected.value = mastodonUser?.locked
|
||||||
|
self.suspended.value = mastodonUser?.suspended ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update(mastodonUser: MastodonUser?, currentMastodonUser: MastodonUser?) {
|
private func update(mastodonUser: MastodonUser?, currentMastodonUser: MastodonUser?) {
|
||||||
|
@ -255,6 +258,14 @@ extension ProfileViewModel {
|
||||||
// set with follow action default
|
// set with follow action default
|
||||||
var relationshipActionSet = RelationshipActionOptionSet([.follow])
|
var relationshipActionSet = RelationshipActionOptionSet([.follow])
|
||||||
|
|
||||||
|
if mastodonUser.locked {
|
||||||
|
relationshipActionSet.insert(.request)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mastodonUser.suspended {
|
||||||
|
relationshipActionSet.insert(.suspended)
|
||||||
|
}
|
||||||
|
|
||||||
let isFollowing = mastodonUser.followingBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
let isFollowing = mastodonUser.followingBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
||||||
if isFollowing {
|
if isFollowing {
|
||||||
relationshipActionSet.insert(.following)
|
relationshipActionSet.insert(.following)
|
||||||
|
@ -308,11 +319,13 @@ extension ProfileViewModel {
|
||||||
enum RelationshipAction: Int, CaseIterable {
|
enum RelationshipAction: Int, CaseIterable {
|
||||||
case none // set hide from UI
|
case none // set hide from UI
|
||||||
case follow
|
case follow
|
||||||
|
case reqeust
|
||||||
case pending
|
case pending
|
||||||
case following
|
case following
|
||||||
case muting
|
case muting
|
||||||
case blocked
|
case blocked
|
||||||
case blocking
|
case blocking
|
||||||
|
case suspended
|
||||||
case edit
|
case edit
|
||||||
case editing
|
case editing
|
||||||
|
|
||||||
|
@ -327,11 +340,13 @@ extension ProfileViewModel {
|
||||||
|
|
||||||
static let none = RelationshipAction.none.option
|
static let none = RelationshipAction.none.option
|
||||||
static let follow = RelationshipAction.follow.option
|
static let follow = RelationshipAction.follow.option
|
||||||
|
static let request = RelationshipAction.reqeust.option
|
||||||
static let pending = RelationshipAction.pending.option
|
static let pending = RelationshipAction.pending.option
|
||||||
static let following = RelationshipAction.following.option
|
static let following = RelationshipAction.following.option
|
||||||
static let muting = RelationshipAction.muting.option
|
static let muting = RelationshipAction.muting.option
|
||||||
static let blocked = RelationshipAction.blocked.option
|
static let blocked = RelationshipAction.blocked.option
|
||||||
static let blocking = RelationshipAction.blocking.option
|
static let blocking = RelationshipAction.blocking.option
|
||||||
|
static let suspended = RelationshipAction.suspended.option
|
||||||
static let edit = RelationshipAction.edit.option
|
static let edit = RelationshipAction.edit.option
|
||||||
static let editing = RelationshipAction.editing.option
|
static let editing = RelationshipAction.editing.option
|
||||||
|
|
||||||
|
@ -354,11 +369,13 @@ extension ProfileViewModel {
|
||||||
switch highPriorityAction {
|
switch highPriorityAction {
|
||||||
case .none: return " "
|
case .none: return " "
|
||||||
case .follow: return L10n.Common.Controls.Firendship.follow
|
case .follow: return L10n.Common.Controls.Firendship.follow
|
||||||
|
case .reqeust: return L10n.Common.Controls.Firendship.request
|
||||||
case .pending: return L10n.Common.Controls.Firendship.pending
|
case .pending: return L10n.Common.Controls.Firendship.pending
|
||||||
case .following: return L10n.Common.Controls.Firendship.following
|
case .following: return L10n.Common.Controls.Firendship.following
|
||||||
case .muting: return L10n.Common.Controls.Firendship.muted
|
case .muting: return L10n.Common.Controls.Firendship.muted
|
||||||
case .blocked: return L10n.Common.Controls.Firendship.follow // blocked by user
|
case .blocked: return L10n.Common.Controls.Firendship.follow // blocked by user
|
||||||
case .blocking: return L10n.Common.Controls.Firendship.blocked
|
case .blocking: return L10n.Common.Controls.Firendship.blocked
|
||||||
|
case .suspended: return L10n.Common.Controls.Firendship.follow
|
||||||
case .edit: return L10n.Common.Controls.Firendship.editInfo
|
case .edit: return L10n.Common.Controls.Firendship.editInfo
|
||||||
case .editing: return L10n.Common.Controls.Actions.done
|
case .editing: return L10n.Common.Controls.Actions.done
|
||||||
}
|
}
|
||||||
|
@ -372,11 +389,13 @@ extension ProfileViewModel {
|
||||||
switch highPriorityAction {
|
switch highPriorityAction {
|
||||||
case .none: return Asset.Colors.Button.normal.color
|
case .none: return Asset.Colors.Button.normal.color
|
||||||
case .follow: return Asset.Colors.Button.normal.color
|
case .follow: return Asset.Colors.Button.normal.color
|
||||||
|
case .reqeust: return Asset.Colors.Button.normal.color
|
||||||
case .pending: return Asset.Colors.Button.normal.color
|
case .pending: return Asset.Colors.Button.normal.color
|
||||||
case .following: return Asset.Colors.Button.normal.color
|
case .following: return Asset.Colors.Button.normal.color
|
||||||
case .muting: return Asset.Colors.Background.alertYellow.color
|
case .muting: return Asset.Colors.Background.alertYellow.color
|
||||||
case .blocked: return Asset.Colors.Button.disabled.color
|
case .blocked: return Asset.Colors.Button.normal.color
|
||||||
case .blocking: return Asset.Colors.Background.danger.color
|
case .blocking: return Asset.Colors.Background.danger.color
|
||||||
|
case .suspended: return Asset.Colors.Button.normal.color
|
||||||
case .edit: return Asset.Colors.Button.normal.color
|
case .edit: return Asset.Colors.Button.normal.color
|
||||||
case .editing: return Asset.Colors.Button.normal.color
|
case .editing: return Asset.Colors.Button.normal.color
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ final class UserTimelineViewModel {
|
||||||
|
|
||||||
let isBlocking = CurrentValueSubject<Bool, Never>(false)
|
let isBlocking = CurrentValueSubject<Bool, Never>(false)
|
||||||
let isBlockedBy = CurrentValueSubject<Bool, Never>(false)
|
let isBlockedBy = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
let isSuspended = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
let userDisplayName = CurrentValueSubject<String?, Never>(nil) // for suspended prompt label
|
||||||
|
|
||||||
// output
|
// output
|
||||||
var diffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>?
|
var diffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>?
|
||||||
|
@ -59,14 +61,15 @@ final class UserTimelineViewModel {
|
||||||
.assign(to: \.value, on: statusFetchedResultsController.domain)
|
.assign(to: \.value, on: statusFetchedResultsController.domain)
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
Publishers.CombineLatest3(
|
Publishers.CombineLatest4(
|
||||||
statusFetchedResultsController.objectIDs.eraseToAnyPublisher(),
|
statusFetchedResultsController.objectIDs.eraseToAnyPublisher(),
|
||||||
isBlocking.eraseToAnyPublisher(),
|
isBlocking.eraseToAnyPublisher(),
|
||||||
isBlockedBy.eraseToAnyPublisher()
|
isBlockedBy.eraseToAnyPublisher(),
|
||||||
|
isSuspended.eraseToAnyPublisher()
|
||||||
)
|
)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
||||||
.sink { [weak self] objectIDs, isBlocking, isBlockedBy in
|
.sink { [weak self] objectIDs, isBlocking, isBlockedBy, isSuspended in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard let diffableDataSource = self.diffableDataSource else { return }
|
guard let diffableDataSource = self.diffableDataSource else { return }
|
||||||
|
|
||||||
|
@ -89,6 +92,12 @@ final class UserTimelineViewModel {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let name = self.userDisplayName.value
|
||||||
|
guard !isSuspended else {
|
||||||
|
snapshot.appendItems([Item.emptyStateHeader(attribute: Item.EmptyStateHeaderAttribute(reason: .suspended(name: name)))], toSection: .main)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var oldSnapshotAttributeDict: [NSManagedObjectID : Item.StatusAttribute] = [:]
|
var oldSnapshotAttributeDict: [NSManagedObjectID : Item.StatusAttribute] = [:]
|
||||||
let oldSnapshot = diffableDataSource.snapshot()
|
let oldSnapshot = diffableDataSource.snapshot()
|
||||||
for item in oldSnapshot.itemIdentifiers {
|
for item in oldSnapshot.itemIdentifiers {
|
||||||
|
|
|
@ -84,8 +84,10 @@ extension TimelineHeaderView {
|
||||||
extension Item.EmptyStateHeaderAttribute.Reason {
|
extension Item.EmptyStateHeaderAttribute.Reason {
|
||||||
var iconImage: UIImage? {
|
var iconImage: UIImage? {
|
||||||
switch self {
|
switch self {
|
||||||
case .noStatusFound, .blocking, .blocked, .suspended:
|
case .noStatusFound, .blocking, .blocked:
|
||||||
return UIImage(systemName: "nosign", withConfiguration: UIImage.SymbolConfiguration(pointSize: 64, weight: .bold))!
|
return UIImage(systemName: "nosign", withConfiguration: UIImage.SymbolConfiguration(pointSize: 64, weight: .bold))!
|
||||||
|
case .suspended:
|
||||||
|
return UIImage(systemName: "person.crop.circle.badge.xmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 64, weight: .bold))!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +99,12 @@ extension Item.EmptyStateHeaderAttribute.Reason {
|
||||||
return L10n.Common.Controls.Timeline.Header.blockingWarning
|
return L10n.Common.Controls.Timeline.Header.blockingWarning
|
||||||
case .blocked:
|
case .blocked:
|
||||||
return L10n.Common.Controls.Timeline.Header.blockedWarning
|
return L10n.Common.Controls.Timeline.Header.blockedWarning
|
||||||
case .suspended:
|
case .suspended(let name):
|
||||||
return L10n.Common.Controls.Timeline.Header.suspendedWarning
|
if let name = name {
|
||||||
|
return L10n.Common.Controls.Timeline.Header.userSuspendedWarning(name)
|
||||||
|
} else {
|
||||||
|
return L10n.Common.Controls.Timeline.Header.suspendedWarning
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,7 @@ extension APIService {
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
|
||||||
let domain = mastodonAuthenticationBox.domain
|
let domain = mastodonAuthenticationBox.domain
|
||||||
let authorization = mastodonAuthenticationBox.userAuthorization
|
let authorization = mastodonAuthenticationBox.userAuthorization
|
||||||
|
let requestMastodonUserID = mastodonAuthenticationBox.userID
|
||||||
|
|
||||||
return Mastodon.API.Account.follow(
|
return Mastodon.API.Account.follow(
|
||||||
session: session,
|
session: session,
|
||||||
|
@ -166,22 +167,50 @@ extension APIService {
|
||||||
followQueryType: followQueryType,
|
followQueryType: followQueryType,
|
||||||
authorization: authorization
|
authorization: authorization
|
||||||
)
|
)
|
||||||
.handleEvents(receiveCompletion: { [weak self] completion in
|
// .handleEvents(receiveCompletion: { [weak self] completion in
|
||||||
guard let _ = self else { return }
|
// guard let _ = self else { return }
|
||||||
switch completion {
|
// switch completion {
|
||||||
case .failure(let error):
|
// case .failure(let error):
|
||||||
// TODO: handle error
|
// // TODO: handle error
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Relationship] update follow fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
// os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Relationship] update follow fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||||
break
|
// break
|
||||||
case .finished:
|
// case .finished:
|
||||||
switch followQueryType {
|
// switch followQueryType {
|
||||||
case .follow:
|
// case .follow:
|
||||||
break
|
// break
|
||||||
case .unfollow:
|
// case .unfollow:
|
||||||
break
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> in
|
||||||
|
let managedObjectContext = self.backgroundManagedObjectContext
|
||||||
|
return managedObjectContext.performChanges {
|
||||||
|
let requestMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||||
|
requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: requestMastodonUserID)
|
||||||
|
requestMastodonUserRequest.fetchLimit = 1
|
||||||
|
guard let requestMastodonUser = managedObjectContext.safeFetch(requestMastodonUserRequest).first else { return }
|
||||||
|
|
||||||
|
let lookUpMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||||
|
lookUpMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: mastodonUserID)
|
||||||
|
lookUpMastodonUserRequest.fetchLimit = 1
|
||||||
|
let lookUpMastodonuser = managedObjectContext.safeFetch(lookUpMastodonUserRequest).first
|
||||||
|
|
||||||
|
if let lookUpMastodonuser = lookUpMastodonuser {
|
||||||
|
let entity = response.value
|
||||||
|
APIService.CoreData.update(user: lookUpMastodonuser, entity: entity, requestMastodonUser: requestMastodonUser, domain: domain, networkDate: response.networkDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
.tryMap { result -> Mastodon.Response.Content<Mastodon.Entity.Relationship> in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
return response
|
||||||
|
case .failure(let error):
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,9 @@ extension APIService.CoreData {
|
||||||
user.update(statusesCount: property.statusesCount)
|
user.update(statusesCount: property.statusesCount)
|
||||||
user.update(followingCount: property.followingCount)
|
user.update(followingCount: property.followingCount)
|
||||||
user.update(followersCount: property.followersCount)
|
user.update(followersCount: property.followersCount)
|
||||||
|
user.update(locked: property.locked)
|
||||||
|
property.bot.flatMap { user.update(bot: $0) }
|
||||||
|
property.suspended.flatMap { user.update(suspended: $0) }
|
||||||
|
|
||||||
user.didUpdate(at: networkDate)
|
user.didUpdate(at: networkDate)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue