diff --git a/Pachyderm/Client.swift b/Pachyderm/Client.swift index 502ede8b..c03db8e8 100644 --- a/Pachyderm/Client.swift +++ b/Pachyderm/Client.swift @@ -138,8 +138,8 @@ public class Client { return Request<[Status]>(method: .get, path: "/api/v1/favourites") } - public func getRelationships(accounts: [Account]? = nil) -> Request<[Relationship]> { - return Request<[Relationship]>(method: .get, path: "/api/v1/accounts/relationships", queryParameters: "id" => accounts?.map { $0.id }) + public func getRelationships(accounts: [String]? = nil) -> Request<[Relationship]> { + return Request<[Relationship]>(method: .get, path: "/api/v1/accounts/relationships", queryParameters: "id" => accounts) } public func getInstance() -> Request { diff --git a/Pachyderm/Model/Account.swift b/Pachyderm/Model/Account.swift index 7acd7a88..c1a3ba31 100644 --- a/Pachyderm/Model/Account.swift +++ b/Pachyderm/Model/Account.swift @@ -63,12 +63,12 @@ public class Account: Decodable { return request } - public static func follow(_ account: Account) -> Request { - return Request(method: .post, path: "/api/v1/accounts/\(account.id)/follow") + public static func follow(_ accountID: String) -> Request { + return Request(method: .post, path: "/api/v1/accounts/\(accountID)/follow") } - public static func unfollow(_ account: Account) -> Request { - return Request(method: .post, path: "/api/v1/accounts/\(account.id)/unfollow") + public static func unfollow(_ accountID: String) -> Request { + return Request(method: .post, path: "/api/v1/accounts/\(accountID)/unfollow") } public static func block(_ account: Account) -> Request { diff --git a/Pachyderm/Model/Relationship.swift b/Pachyderm/Model/Relationship.swift index 39f4f99c..38c046d2 100644 --- a/Pachyderm/Model/Relationship.swift +++ b/Pachyderm/Model/Relationship.swift @@ -12,7 +12,7 @@ public class Relationship: Decodable { public let id: String public let following: Bool public let followedBy: Bool - public let blocked: Bool + public let blocking: Bool public let muting: Bool public let mutingNotifications: Bool public let followRequested: Bool @@ -23,7 +23,7 @@ public class Relationship: Decodable { case id case following case followedBy = "followed_by" - case blocked + case blocking case muting case mutingNotifications = "muting_notifications" case followRequested = "requested" diff --git a/Tusker/MastodonCache.swift b/Tusker/MastodonCache.swift index e8e1badd..f77baee7 100644 --- a/Tusker/MastodonCache.swift +++ b/Tusker/MastodonCache.swift @@ -13,6 +13,7 @@ class MastodonCache { private static let statuses = NSCache() private static let accounts = NSCache() + private static let relationships = NSCache() // MARK: - Statuses static func status(for id: String) -> Status? { @@ -76,4 +77,34 @@ class MastodonCache { accounts.forEach(add) } + // MARK: - Relationships + static func relationship(for id: String) -> Relationship? { + return relationships.object(forKey: id as NSString) + } + + static func set(relationship: Relationship, id: String) { + relationships.setObject(relationship, forKey: id as NSString) + } + + static func relationship(for id: String, completion: @escaping (Relationship?) -> Void) { + let request = MastodonController.shared.client.getRelationships(accounts: [id]) + MastodonController.shared.client.run(request) { response in + guard case let .success(relationships, _) = response, + let relationship = relationships.first else { + completion(nil) + return + } + set(relationship: relationship, id: relationship.id) + completion(relationship) + } + } + + static func add(relationship: Relationship) { + set(relationship: relationship, id: relationship.id) + } + + static func addAll(relationships: [Relationship]) { + relationships.forEach(add) + } + } diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index c51d5170..2c8faa42 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -187,7 +187,32 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { self.sendMessageMentioning() })) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - present(alert, animated: true) + + MastodonCache.relationship(for: account.id) { (relationship) in + guard let relationship = relationship else { + DispatchQueue.main.async { + self.present(alert, animated: true) + } + return + } + let title = relationship.following ? "Unfollow": "Follow" + DispatchQueue.main.async { + alert.addAction(UIAlertAction(title: title, style: .default, handler: { (_) in + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + let request = (relationship.following ? Account.unfollow : Account.follow)(account.id) + MastodonController.shared.client.run(request, completion: { (response) in + if case let .success(relationship, _) = response { + MastodonCache.add(relationship: relationship) + } else { + // todo: display error message + UINotificationFeedbackGenerator().notificationOccurred(.error) + fatalError() + } + }) + })) + self.present(alert, animated: true) + } + } } } diff --git a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift index 7f99507f..f76146ad 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift +++ b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift @@ -18,13 +18,14 @@ protocol ProfileHeaderTableViewCellDelegate: StatusTableViewCellDelegate { class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive { var delegate: ProfileHeaderTableViewCellDelegate? - - @IBOutlet weak var displayNameLabel: UILabel! - @IBOutlet weak var usernameLabel: UILabel! - @IBOutlet weak var noteLabel: HTMLContentLabel! + + @IBOutlet weak var headerImageView: UIImageView! @IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var avatarImageView: UIImageView! - @IBOutlet weak var headerImageView: UIImageView! + @IBOutlet weak var displayNameLabel: UILabel! + @IBOutlet weak var usernameLabel: UILabel! + @IBOutlet weak var followsYouLabel: UILabel! + @IBOutlet weak var noteLabel: HTMLContentLabel! var accountID: String! @@ -77,6 +78,16 @@ class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive { // todo: HTML parsing noteLabel.text = account.note noteLabel.delegate = self + + if let relationship = MastodonCache.relationship(for: accountID) { + followsYouLabel.isHidden = !relationship.followedBy + } else { + MastodonCache.relationship(for: accountID) { relationship in + DispatchQueue.main.async { + self.followsYouLabel.isHidden = !(relationship?.followedBy ?? false) + } + } + } } override func prepareForReuse() { diff --git a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.xib b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.xib index 84307e48..cb98d8a9 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.xib +++ b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.xib @@ -1,10 +1,10 @@ - + - + @@ -12,7 +12,7 @@ - + @@ -21,24 +21,6 @@ - - - @@ -59,6 +41,35 @@ + + + + + + + + +