From 46cecde0145a0ffe24483b7cb2f32871b6ca6347 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 22 Dec 2022 17:26:50 -0500 Subject: [PATCH] Add more prominent follow button to profile pages --- .../Profile Header/ProfileHeaderView.swift | 104 +++++++++++++++++- .../Profile Header/ProfileHeaderView.xib | 17 ++- 2 files changed, 115 insertions(+), 6 deletions(-) diff --git a/Tusker/Views/Profile Header/ProfileHeaderView.swift b/Tusker/Views/Profile Header/ProfileHeaderView.swift index 4f9de6989b..f3110b16e9 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderView.swift +++ b/Tusker/Views/Profile Header/ProfileHeaderView.swift @@ -32,6 +32,7 @@ class ProfileHeaderView: UIView { @IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var moreButton: VisualEffectImageButton! + @IBOutlet weak var followButton: UIButton! @IBOutlet weak var displayNameLabel: EmojiLabel! @IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var lockImageView: UIImageView! @@ -47,6 +48,11 @@ class ProfileHeaderView: UIView { private var headerRequest: ImageCache.Request? private var isGrayscale = false + private var followButtonMode = FollowButtonMode.follow { + didSet { + followButton.configuration = followButtonMode.makeConfig(showsActivityIndicator: false) + } + } private var cancellables = [AnyCancellable]() @@ -72,6 +78,8 @@ class ProfileHeaderView: UIView { moreButton.showsMenuAsPrimaryAction = true moreButton.isContextMenuInteractionEnabled = true + followButton.addInteraction(UIPointerInteraction(delegate: self)) + displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold)) displayNameLabel.adjustsFontForContentSizeCategory = true @@ -150,8 +158,11 @@ class ProfileHeaderView: UIView { noteTextView.setTextFromHtml(account.note) noteTextView.setEmojis(account.emojis, identifier: account.id) - // don't show relationship label for the user's own account - if accountID != mastodonController.account?.id { + if accountID == mastodonController.account?.id { + followButton.isHidden = true + } else { + followButton.isHidden = false + // while fetching the most up-to-date, show the current data (if any) updateRelationship() @@ -183,8 +194,11 @@ class ProfileHeaderView: UIView { private func updateRelationship() { guard let mastodonController = mastodonController, let relationship = mastodonController.persistentContainer.relationship(forAccount: accountID) else { - return - } + relationshipLabel.isHidden = true + followButton.isEnabled = false + followButtonMode = .follow + return + } var relationshipStr: String? if relationship.following && relationship.followedBy { @@ -202,6 +216,20 @@ class ProfileHeaderView: UIView { } else { relationshipLabel.isHidden = true } + + if relationship.blocking || relationship.domainBlocking { + followButton.isEnabled = false + followButtonMode = .blocked + } else { + followButton.isEnabled = true + if relationship.following { + followButtonMode = .unfollow + } else if relationship.requested { + followButtonMode = .cancelRequest + } else { + followButtonMode = .follow + } + } } @objc private func updateUIForPreferences() { @@ -282,11 +310,77 @@ class ProfileHeaderView: UIView { delegate?.showLoadingLargeImage(url: header, cache: .headers, description: nil, animatingFrom: headerImageView) } + @IBAction func followPressed() { + let req: Request + let action: String + switch followButtonMode { + case .follow: + req = Account.follow(accountID) + action = "Following" + case .unfollow, .cancelRequest: + req = Account.unfollow(accountID) + action = followButtonMode == .unfollow ? "Unfollowing" : "Cancelling Request" + case .blocked: + return + } + followButton.configuration = followButtonMode.makeConfig(showsActivityIndicator: true) + followButton.isEnabled = false + Task { + do { + let (relationship, _) = try await mastodonController.run(req) + mastodonController.persistentContainer.addOrUpdate(relationship: relationship) + // don't need to update the button, since the relationship observer will do so anyways + } catch { + followButton.isEnabled = true + followButton.configuration = followButtonMode.makeConfig(showsActivityIndicator: false) + if let toastable = delegate?.toastableViewController { + let config = ToastConfiguration(from: error, with: "Error \(action)", in: toastable) { toast in + toast.dismissToast(animated: true) + self.followPressed() + } + toastable.showToast(configuration: config, animated: true) + } + } + } + } + +} + +extension ProfileHeaderView { + enum FollowButtonMode { + case follow, unfollow, cancelRequest, blocked + + func makeConfig(showsActivityIndicator: Bool) -> UIButton.Configuration { + var config = UIButton.Configuration.plain() + config.showsActivityIndicator = showsActivityIndicator + config.baseForegroundColor = .label + config.imagePadding = 4 + config.cornerStyle = .capsule + var backgroundConfig = UIBackgroundConfiguration.clear() + backgroundConfig.visualEffect = UIBlurEffect(style: .systemThickMaterial) + config.background = backgroundConfig + switch self { + case .follow: + config.title = "Follow" + config.image = UIImage(systemName: "person.badge.plus") + case .unfollow: + config.title = "Unfollow" + config.image = UIImage(systemName: "person.badge.minus") + case .cancelRequest: + config.title = "Cancel Request" + config.image = UIImage(systemName: "person.badge.clock") + case .blocked: + config.title = "Blocked" + config.image = UIImage(systemName: "circle.slash") + } + return config + } + } } extension ProfileHeaderView: UIPointerInteractionDelegate { func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - let preview = UITargetedPreview(view: moreButton) + let preview = UITargetedPreview(view: interaction.view!) return UIPointerStyle(effect: .lift(preview), shape: .none) } } diff --git a/Tusker/Views/Profile Header/ProfileHeaderView.xib b/Tusker/Views/Profile Header/ProfileHeaderView.xib index 163f7835b6..aca7aaab93 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderView.xib +++ b/Tusker/Views/Profile Header/ProfileHeaderView.xib @@ -59,6 +59,17 @@ + @@ -130,6 +141,7 @@ + @@ -137,12 +149,14 @@ + + @@ -151,12 +165,13 @@ - + +