Merge branch 'pachyderm-immutable'

This commit is contained in:
Shadowfacts 2018-09-17 19:37:58 -04:00
commit 266ebddd43
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
23 changed files with 149 additions and 319 deletions

View File

@ -42,8 +42,7 @@ public class Client {
self.session = session self.session = session
} }
// MARK: - Internal Helpers public func run<Result>(_ request: Request<Result>, completion: @escaping Callback<Result>) {
func run<Result>(_ request: Request<Result>, completion: @escaping Callback<Result>) {
guard let request = createURLRequest(request: request) else { guard let request = createURLRequest(request: request) else {
completion(.failure(Error.invalidRequest)) completion(.failure(Error.invalidRequest))
return return

View File

@ -8,13 +8,7 @@
import Foundation import Foundation
public class Account: Decodable, ClientModel { public class Account: Decodable {
var client: Client! {
didSet {
emojis.client = client
}
}
public let id: String public let id: String
public let username: String public let username: String
public let acct: String public let acct: String
@ -35,78 +29,68 @@ public class Account: Decodable, ClientModel {
public let fields: [Field]? public let fields: [Field]?
public let bot: Bool? public let bot: Bool?
public func authorizeFollowRequest(completion: @escaping Client.Callback<Empty>) { public static func authorizeFollowRequest(_ account: Account) -> Request<Empty> {
let request = Request<Empty>(method: .post, path: "/api/v1/follow_requests/\(id)/authorize") return Request<Empty>(method: .post, path: "/api/v1/follow_requests/\(account.id)/authorize")
client.run(request, completion: completion)
} }
public func rejectFollowRequest(completion: @escaping Client.Callback<Empty>) { public static func rejectFollowRequest(_ account: Account) -> Request<Empty> {
let request = Request<Empty>(method: .post, path: "/api/v1/follow_requests/\(id)/reject") return Request<Empty>(method: .post, path: "/api/v1/follow_requests/\(account.id)/reject")
client.run(request, completion: completion)
} }
public func removeFromFollowRequests(completion: @escaping Client.Callback<Empty>) { public static func removeFromFollowRequests(_ account: Account) -> Request<Empty> {
let request = Request<Empty>(method: .delete, path: "/api/v1/suggestions/\(id)") return Request<Empty>(method: .delete, path: "/api/v1/suggestions/\(account.id)")
client.run(request, completion: completion)
} }
public func getFollowers(range: RequestRange = .default, completion: @escaping Client.Callback<[Account]>) { public static func getFollowers(_ account: Account, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/accounts/\(id)/followers") var request = Request<[Account]>(method: .get, path: "/api/v1/accounts/\(account.id)/followers")
request.range = range request.range = range
client.run(request, completion: completion) return request
} }
public func getFollowing(range: RequestRange = .default, completion: @escaping Client.Callback<[Account]>) { public static func getFollowing(_ account: Account, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/accounts/\(id)/following") var request = Request<[Account]>(method: .get, path: "/api/v1/accounts/\(account.id)/following")
request.range = range request.range = range
client.run(request, completion: completion) return request
} }
public func getStatuses(range: RequestRange = .default, onlyMedia: Bool? = nil, pinned: Bool? = nil, excludeReplies: Bool? = nil, completion: @escaping Client.Callback<[Status]>) { public static func getStatuses(_ account: Account, range: RequestRange = .default, onlyMedia: Bool? = nil, pinned: Bool? = nil, excludeReplies: Bool? = nil) -> Request<[Status]> {
var request = Request<[Status]>(method: .get, path: "/api/v1/accounts/\(id)/statuses", queryParameters: [ var request = Request<[Status]>(method: .get, path: "/api/v1/accounts/\(account.id)/statuses", queryParameters: [
"only_media" => onlyMedia, "only_media" => onlyMedia,
"pinned" => pinned, "pinned" => pinned,
"exclude_replies" => excludeReplies "exclude_replies" => excludeReplies
]) ])
request.range = range request.range = range
client.run(request, completion: completion) return request
} }
public func follow(completion: @escaping Client.Callback<Relationship>) { public static func follow(_ account: Account) -> Request<Relationship> {
let request = Request<Relationship>(method: .post, path: "/api/v1/accounts/\(id)/follow") return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/follow")
client.run(request, completion: completion)
} }
public func unfollow(completion: @escaping Client.Callback<Relationship>) { public static func unfollow(_ account: Account) -> Request<Relationship> {
let request = Request<Relationship>(method: .post, path: "/api/v1/accounts/\(id)/unfollow") return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/unfollow")
client.run(request, completion: completion)
} }
public func block(completion: @escaping Client.Callback<Relationship>) { public static func block(_ account: Account) -> Request<Relationship> {
let request = Request<Relationship>(method: .post, path: "/api/v1/accounts/\(id)/block") return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/block")
client.run(request, completion: completion)
} }
public func unblock(completion: @escaping Client.Callback<Relationship>) { public static func unblock(_ account: Account) -> Request<Relationship> {
let request = Request<Relationship>(method: .post, path: "/api/v1/accounts/\(id)/unblock") return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/unblock")
client.run(request, completion: completion)
} }
public func mute(notifications: Bool? = nil, completion: @escaping Client.Callback<Relationship>) { public static func mute(_ account: Account, notifications: Bool? = nil) -> Request<Relationship> {
let request = Request<Relationship>(method: .post, path: "/api/v1/accounts/\(id)/mute", body: .parameters([ return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/mute", body: .parameters([
"notifications" => notifications "notifications" => notifications
])) ]))
client.run(request, completion: completion)
} }
public func unmute(completion: @escaping Client.Callback<Relationship>) { public static func unmute(_ account: Account) -> Request<Relationship> {
let request = Request<Relationship>(method: .post, path: "/api/v1/accounts/\(id)/unmute") return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/unmute")
client.run(request, completion: completion)
} }
public func getLists(completion: @escaping Client.Callback<[List]>) { public static func getLists(_ account: Account) -> Request<[List]> {
let request = Request<[List]>(method: .get, path: "/api/v1/accounts/\(id)/lists") return Request<[List]>(method: .get, path: "/api/v1/accounts/\(account.id)/lists")
client.run(request, completion: completion)
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Application: Decodable, ClientModel { public class Application: Decodable {
var client: Client!
public let name: String public let name: String
public let website: URL? public let website: URL?

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Attachment: Decodable, ClientModel { public class Attachment: Decodable {
var client: Client!
public let id: String public let id: String
public let kind: Kind public let kind: Kind
public let url: URL public let url: URL
@ -18,16 +16,13 @@ public class Attachment: Decodable, ClientModel {
public let previewURL: URL public let previewURL: URL
public let textURL: URL? public let textURL: URL?
public let meta: Metadata? public let meta: Metadata?
public var description: String? public let description: String?
public func update(focus: (Float, Float)?, completion: Client.Callback<Attachment>?) { public static func update(_ attachment: Attachment, focus: (Float, Float)?, description: String?) -> Request<Attachment> {
let request = Request<Attachment>(method: .put, path: "/api/v1/media/\(id)", body: .formData([ return Request<Attachment>(method: .put, path: "/api/v1/media/\(attachment.id)", body: .formData([
"description" => description, "description" => (description ?? attachment.description),
"focus" => focus "focus" => focus
], nil)) ], nil))
client.run(request) { result in
completion?(result)
}
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Card: Decodable, ClientModel { public class Card: Decodable {
var client: Client!
public let url: URL public let url: URL
public let title: String public let title: String
public let description: String public let description: String

View File

@ -8,16 +8,9 @@
import Foundation import Foundation
public class ConversationContext: Decodable, ClientModel { public class ConversationContext: Decodable {
var client: Client! { public let ancestors: [Status]
didSet { public let descendants: [Status]
ancestors.client = client
descendants.client = client
}
}
public private(set) var ancestors: [Status]
public private(set) var descendants: [Status]
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case ancestors case ancestors

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Emoji: Decodable, ClientModel { public class Emoji: Decodable {
var client: Client!
let shortcode: String let shortcode: String
let url: URL let url: URL
let staticURL: URL let staticURL: URL

View File

@ -8,40 +8,31 @@
import Foundation import Foundation
public class Filter: Decodable, ClientModel { public class Filter: Decodable {
var client: Client!
public let id: String public let id: String
public var phrase: String public let phrase: String
private var context: [String] private let context: [String]
public var expiresAt: Date? public let expiresAt: Date?
public var irreversible: Bool public let irreversible: Bool
public var wholeWord: Bool public let wholeWord: Bool
public var contexts: [Context] { public var contexts: [Context] {
get { get {
return context.compactMap(Context.init) return context.compactMap(Context.init)
} }
set {
context = contexts.contextStrings
}
} }
public func update(completion: Client.Callback<Filter>?) { public static func update(_ filter: Filter, phrase: String? = nil, context: [Context]? = nil, irreversible: Bool? = nil, wholeWord: Bool? = nil, expiresAt: Date? = nil) -> Request<Filter> {
let request = Request<Filter>(method: .put, path: "/api/v1/filters/\(id)", body: .parameters([ return Request<Filter>(method: .put, path: "/api/v1/filters/\(filter.id)", body: .parameters([
"phrase" => phrase, "phrase" => (phrase ?? filter.phrase),
"irreversible" => irreversible, "irreversible" => (irreversible ?? filter.irreversible),
"whole_word" => wholeWord, "whole_word" => (wholeWord ?? filter.wholeWord),
"expires_at" => expiresAt "expires_at" => (expiresAt ?? filter.expiresAt)
] + "context" => context)) ] + "context" => (context?.contextStrings ?? filter.context)))
client.run(request) { result in
completion?(result)
}
} }
public func delete(completion: @escaping Client.Callback<Empty>) { public static func delete(_ filter: Filter) -> Request<Empty> {
let request = Request<Empty>(method: .delete, path: "/api/v1/filters/\(id)") return Request<Empty>(method: .delete, path: "/api/v1/filters/\(filter.id)")
client.run(request, completion: completion)
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Hashtag: Decodable, ClientModel { public class Hashtag: Decodable {
var client: Client!
public let name: String public let name: String
public let url: URL public let url: URL
public let history: [History]? public let history: [History]?

View File

@ -8,13 +8,7 @@
import Foundation import Foundation
public class Instance: Decodable, ClientModel { public class Instance: Decodable {
var client: Client! {
didSet {
contactAccount.client = client
}
}
public let uri: String public let uri: String
public let title: String public let title: String
public let description: String public let description: String

View File

@ -8,42 +8,34 @@
import Foundation import Foundation
public class List: Decodable, ClientModel { public class List: Decodable {
var client: Client!
public let id: String public let id: String
public var title: String public let title: String
public func getAccounts(range: RequestRange = .default, completion: @escaping Client.Callback<[Account]>) { public static func getAccounts(_ list: List, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/lists/\(id)/accounts") var request = Request<[Account]>(method: .get, path: "/api/v1/lists/\(list.id)/accounts")
request.range = range request.range = range
client.run(request, completion: completion) return request
} }
public func update(completion: Client.Callback<List>?) { public static func update(_ list: List, title: String) -> Request<List> {
let request = Request<List>(method: .put, path: "/api/v1/lists/\(id)", body: .parameters(["title" => title])) return Request<List>(method: .put, path: "/api/v1/lists/\(list.id)", body: .parameters(["title" => title]))
client.run(request) { result in
completion?(result)
}
} }
public func delete(completion: @escaping Client.Callback<Empty>) { public static func delete(_ list: List) -> Request<Empty> {
let request = Request<Empty>(method: .delete, path: "/api/v1/lists/\(id)") return Request<Empty>(method: .delete, path: "/api/v1/lists/\(list.id)")
client.run(request, completion: completion)
} }
public func add(accounts: [Account], completion: @escaping Client.Callback<Empty>) { public static func add(_ list: List, accounts: [Account]) -> Request<Empty> {
let request = Request<Empty>(method: .post, path: "/api/v1/lists/\(id)/accounts", body: .parameters( return Request<Empty>(method: .post, path: "/api/v1/lists/\(list.id)/accounts", body: .parameters(
"account_ids" => accounts.map { $0.id } "account_ids" => accounts.map { $0.id }
)) ))
client.run(request, completion: completion)
} }
public func remove(accounts: [Account], completion: @escaping Client.Callback<Empty>) { public static func remove(_ list: List, accounts: [Account]) -> Request<Empty> {
let request = Request<Empty>(method: .delete, path: "/api/v1/lists/\(id)/accounts", body: .parameters( return Request<Empty>(method: .delete, path: "/api/v1/lists/\(list.id)/accounts", body: .parameters(
"account_ids" => accounts.map { $0.id } "account_ids" => accounts.map { $0.id }
)) ))
client.run(request, completion: completion)
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Mention: Decodable, ClientModel { public class Mention: Decodable {
var client: Client!
public let url: URL public let url: URL
public let username: String public let username: String
public let acct: String public let acct: String

View File

@ -8,25 +8,17 @@
import Foundation import Foundation
public class Notification: Decodable, ClientModel { public class Notification: Decodable {
var client: Client! {
didSet {
account.client = client
status?.client = client
}
}
public let id: String public let id: String
public let kind: Kind public let kind: Kind
public let createdAt: Date public let createdAt: Date
public let account: Account public let account: Account
public let status: Status? public let status: Status?
public func dismiss(completion: @escaping Client.Callback<Empty>) { public static func dismiss(_ notification: Notification) -> Request<Empty> {
let request = Request<Empty>(method: .post, path: "/api/v1/notifications/dismiss", body: .parameters([ return Request<Empty>(method: .post, path: "/api/v1/notifications/dismiss", body: .parameters([
"id" => id "id" => notification.id
])) ]))
client.run(request, completion: completion)
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class PushSubscription: Decodable, ClientModel { public class PushSubscription: Decodable {
var client: Client!
public let id: String public let id: String
public let endpoint: URL public let endpoint: URL
public let serverKey: String public let serverKey: String

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Relationship: Decodable, ClientModel { public class Relationship: Decodable {
var client: Client!
public let id: String public let id: String
public let following: Bool public let following: Bool
public let followedBy: Bool public let followedBy: Bool

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
public class Report: Decodable, ClientModel { public class Report: Decodable {
var client: Client!
public let id: String public let id: String
public let actionTaken: Bool public let actionTaken: Bool

View File

@ -8,16 +8,9 @@
import Foundation import Foundation
public class SearchResults: Decodable, ClientModel { public class SearchResults: Decodable {
var client: Client! { public let accounts: [Account]
didSet { public let statuses: [Status]
accounts.client = client
statuses.client = client
}
}
public private(set) var accounts: [Account]
public private(set) var statuses: [Status]
public let hashtags: [String] public let hashtags: [String]
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,183 +8,88 @@
import Foundation import Foundation
public class Status: Decodable, ClientModel { public class Status: Decodable {
var client: Client! {
didSet {
didSetClient()
}
}
// when reblog.client is set directly from self.client didSet, reblog.client didSet is never called
private func didSetClient() {
account.client = client
reblog?.client = client
emojis.client = client
attachments.client = client
mentions.client = client
hashtags.client = client
application?.client = client
}
public let id: String public let id: String
public let uri: String public let uri: String
public let url: URL? public let url: URL?
public let account: Account public let account: Account
public let inReplyToID: String? public let inReplyToID: String?
public let inReplyToAccountID: String? public let inReplyToAccountID: String?
public private(set) var reblog: Status? public let reblog: Status?
public let content: String public let content: String
public let createdAt: Date public let createdAt: Date
public private(set) var emojis: [Emoji] public let emojis: [Emoji]
// TODO: missing from pleroma // TODO: missing from pleroma
// public let repliesCount: Int // public let repliesCount: Int
public let reblogsCount: Int public let reblogsCount: Int
public let favouritesCount: Int public let favouritesCount: Int
public var reblogged: Bool? public let reblogged: Bool?
public var favourited: Bool? public let favourited: Bool?
public var muted: Bool? public let muted: Bool?
public let sensitive: Bool public let sensitive: Bool
public let spoilerText: String public let spoilerText: String
public let visibility: Visibility public let visibility: Visibility
public private(set) var attachments: [Attachment] public let attachments: [Attachment]
public private(set) var mentions: [Mention] public let mentions: [Mention]
public private(set) var hashtags: [Hashtag] public let hashtags: [Hashtag]
public private(set) var application: Application? public let application: Application?
public let language: String? public let language: String?
public var pinned: Bool? public let pinned: Bool?
public func getContext(completion: @escaping Client.Callback<ConversationContext>) { public static func getContext(_ status: Status) -> Request<ConversationContext> {
let request = Request<ConversationContext>(method: .get, path: "/api/v1/statuses/\(id)/context") return Request<ConversationContext>(method: .get, path: "/api/v1/statuses/\(status.id)/context")
client.run(request, completion: completion)
} }
public func getCard(completion: @escaping Client.Callback<Card>) { public static func getCard(_ status: Status) -> Request<Card> {
let request = Request<Card>(method: .get, path: "/api/v1/statuses/\(id)/card") return Request<Card>(method: .get, path: "/api/v1/statuses/\(status.id)/card")
client.run(request, completion: completion)
} }
public func getFavourites(range: RequestRange = .default, completion: @escaping Client.Callback<[Account]>) { public static func getFavourites(_ status: Status, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(id)/favourited_by") var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(status.id)/favourited_by")
request.range = range request.range = range
client.run(request, completion: completion) return request
} }
public func getReblogs(range: RequestRange = .default, completion: @escaping Client.Callback<[Account]>) { public static func getReblogs(_ status: Status, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(id)/reblogged_by") var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(status.id)/reblogged_by")
request.range = range request.range = range
client.run(request, completion: completion) return request
} }
public func delete(completion: @escaping Client.Callback<Empty>) { public static func delete(_ status: Status) -> Request<Empty> {
let request = Request<Empty>(method: .delete, path: "/api/v1/statuses/\(id)") return Request<Empty>(method: .delete, path: "/api/v1/statuses/\(status.id)")
client.run(request, completion: completion)
} }
public func reblog(completion: @escaping Client.Callback<Status>) { public static func reblog(_ status: Status) -> Request<Status> {
let oldValue = reblogged return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/reblog")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/reblog")
client.run(request) { response in
if case .success = response {
self.reblogged = true
} else {
self.reblogged = oldValue
}
completion(response)
}
} }
public func unreblog(completion: @escaping Client.Callback<Status>) { public static func unreblog(_ status: Status) -> Request<Status> {
let oldValue = reblogged return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/unreblog")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/unreblog")
client.run(request) { response in
if case .success = response {
self.reblogged = false
} else {
self.reblogged = oldValue
}
completion(response)
}
} }
public func favourite(completion: @escaping Client.Callback<Status>) { public static func favourite(_ status: Status) -> Request<Status> {
let oldValue = favourited return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/favourite")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/favourite")
client.run(request) { response in
if case .success = response {
self.favourited = true
self.reblog?.favourited = true
} else {
self.favourited = oldValue
self.reblog?.favourited = oldValue
}
completion(response)
}
} }
public func unfavourite(completion: @escaping Client.Callback<Status>) { public static func unfavourite(_ status: Status) -> Request<Status> {
let oldValue = favourited return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/unfavourite")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/unfavourite")
client.run(request) { response in
if case .success = response {
self.favourited = false
self.reblog?.favourited = false
} else {
self.favourited = oldValue
self.reblog?.favourited = oldValue
}
completion(response)
}
} }
public func pin(completion: @escaping Client.Callback<Status>) { public static func pin(_ status: Status) -> Request<Status> {
let oldValue = pinned return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/pin")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/pin")
client.run(request) { response in
if case .success = response {
self.pinned = true
} else {
self.pinned = oldValue
}
completion(response)
}
} }
public func unpin(completion: @escaping Client.Callback<Status>) { public static func unpin(_ status: Status) -> Request<Status> {
let oldValue = pinned return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/unpin")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/unpin")
client.run(request) { response in
if case .success = response {
self.pinned = false
} else {
self.pinned = oldValue
}
completion(response)
}
} }
public func muteConversation(completion: @escaping Client.Callback<Status>) { public static func muteConversation(_ status: Status) -> Request<Status> {
let oldValue = muted return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/mute")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/mute")
client.run(request) { response in
if case .success = response {
self.muted = true
} else {
self.muted = oldValue
}
completion(response)
}
} }
public func unmuteConversation(completion: @escaping Client.Callback<Status>) { public static func unmuteConversation(_ status: Status) -> Request<Status> {
let oldValue = muted return Request<Status>(method: .post, path: "/api/v1/statuses/\(status.id)/unmute")
let request = Request<Status>(method: .post, path: "/api/v1/statuses/\(id)/unmute")
client.run(request) { response in
if case .success = response {
self.muted = false
} else {
self.muted = oldValue
}
completion(response)
}
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
struct Request<ResultType: Decodable> { public struct Request<ResultType: Decodable> {
let method: Method let method: Method
let path: String let path: String
let body: Body let body: Body

View File

@ -40,7 +40,8 @@ class ConversationViewController: UIViewController, UITableViewDataSource, UITab
statuses = [mainStatus] statuses = [mainStatus]
mainStatus.getContext { response in let request = Status.getContext(mainStatus)
MastodonController.shared.client.run(request) { response in
guard case let .success(context, _) = response else { fatalError() } guard case let .success(context, _) = response else { fatalError() }
var statuses = self.getDirectParents(of: self.mainStatus, from: context.ancestors) var statuses = self.getDirectParents(of: self.mainStatus, from: context.ancestors)
statuses.append(self.mainStatus) statuses.append(self.mainStatus)

View File

@ -32,7 +32,8 @@ class ProfileTableViewController: UITableViewController, PreferencesAdaptive {
var newer: RequestRange? var newer: RequestRange?
func getStatuses(for range: RequestRange = .default, completion: @escaping Client.Callback<[Status]>) { func getStatuses(for range: RequestRange = .default, completion: @escaping Client.Callback<[Status]>) {
account.getStatuses(range: range, onlyMedia: false, pinned: false, excludeReplies: !Preferences.shared.showRepliesInProfiles, completion: completion) let request = Account.getStatuses(account, range: range, onlyMedia: false, pinned: false, excludeReplies: !Preferences.shared.showRepliesInProfiles)
MastodonController.shared.client.run(request, completion: completion)
} }
override func viewDidLoad() { override func viewDidLoad() {

View File

@ -166,7 +166,8 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
let realStatus: Status = status.reblog ?? status let realStatus: Status = status.reblog ?? status
(favorited ? realStatus.favourite : realStatus.unfavourite)() { response in let request = (favorited ? Status.favourite : Status.unfavourite)(realStatus)
MastodonController.shared.client.run(request) { response in
self.favorited = realStatus.favourited ?? false self.favorited = realStatus.favourited ?? false
DispatchQueue.main.async { DispatchQueue.main.async {
if case .success = response { if case .success = response {
@ -186,7 +187,8 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
let realStatus: Status = status.reblog ?? status let realStatus: Status = status.reblog ?? status
(reblogged ? realStatus.reblog : realStatus.unreblog)() { response in let request = (reblogged ? Status.reblog : Status.unreblog)(realStatus)
MastodonController.shared.client.run(request) { response in
self.reblogged = realStatus.reblogged ?? false self.reblogged = realStatus.reblogged ?? false
DispatchQueue.main.async { DispatchQueue.main.async {
if case .success = response { if case .success = response {

View File

@ -210,16 +210,19 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
} }
@IBAction func favoritePressed(_ sender: Any) { @IBAction func favoritePressed(_ sender: Any) {
let oldValue = favorited
favorited = !favorited favorited = !favorited
let realStatus: Status = status.reblog ?? status let realStatus: Status = status.reblog ?? status
let request = (favorited ? Status.favourite : Status.unfavourite)(realStatus)
(favorited ? realStatus.favourite : realStatus.unfavourite)() { response in MastodonController.shared.client.run(request) { response in
DispatchQueue.main.async { DispatchQueue.main.async {
self.favorited = realStatus.favourited ?? false self.favorited = realStatus.favourited ?? false
if case .success = response { if case .success = response {
self.favorited = realStatus.favourited ?? false
UIImpactFeedbackGenerator(style: .light).impactOccurred() UIImpactFeedbackGenerator(style: .light).impactOccurred()
} else { } else {
self.favorited = oldValue
print("Couldn't favorite status \(realStatus.id)") print("Couldn't favorite status \(realStatus.id)")
// todo: display error message // todo: display error message
UINotificationFeedbackGenerator().notificationOccurred(.error) UINotificationFeedbackGenerator().notificationOccurred(.error)
@ -233,8 +236,9 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
reblogged = !reblogged reblogged = !reblogged
let realStatus: Status = status.reblog ?? status let realStatus: Status = status.reblog ?? status
let request = (reblogged ? Status.reblog : Status.unreblog)(realStatus)
(reblogged ? realStatus.reblog : realStatus.unreblog)() { response in MastodonController.shared.client.run(request) { response in
self.reblogged = realStatus.reblogged ?? false
DispatchQueue.main.async { DispatchQueue.main.async {
self.reblogged = realStatus.reblogged ?? false self.reblogged = realStatus.reblogged ?? false
if case .success = response { if case .success = response {
@ -271,20 +275,20 @@ extension StatusTableViewCell: TableViewSwipeActionProvider {
func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? { func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? {
let favoriteTitle: String let favoriteTitle: String
let favoriteAction: (@escaping Client.Callback<Status>) -> Void let favoriteRequest: Request<Status>
let favoriteColor: UIColor let favoriteColor: UIColor
if status.favourited ?? false { if status.favourited ?? false {
favoriteTitle = "Unfavorite" favoriteTitle = "Unfavorite"
favoriteAction = status.unfavourite favoriteRequest = Status.unfavourite(status)
favoriteColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) favoriteColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1)
} else { } else {
favoriteTitle = "Favorite" favoriteTitle = "Favorite"
favoriteAction = status.favourite favoriteRequest = Status.favourite(status)
favoriteColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1) favoriteColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1)
} }
let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { (action, view, completion) in let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { (action, view, completion) in
favoriteAction { response in MastodonController.shared.client.run(favoriteRequest, completion: { response in
DispatchQueue.main.async { DispatchQueue.main.async {
if case .success = response { if case .success = response {
completion(true) completion(true)
@ -293,25 +297,25 @@ extension StatusTableViewCell: TableViewSwipeActionProvider {
completion(false) completion(false)
} }
} }
} })
} }
favorite.image = StatusTableViewCell.favoriteActionImage favorite.image = StatusTableViewCell.favoriteActionImage
favorite.backgroundColor = favoriteColor favorite.backgroundColor = favoriteColor
let reblogTitle: String let reblogTitle: String
let reblogAction: (@escaping Client.Callback<Status>) -> Void let reblogRequest: Request<Status>
let reblogColor: UIColor let reblogColor: UIColor
if status.reblogged ?? false { if status.reblogged ?? false {
reblogTitle = "Unreblog" reblogTitle = "Unreblog"
reblogAction = status.unreblog reblogRequest = Status.unreblog(status)
reblogColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) reblogColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1)
} else { } else {
reblogTitle = "Reblog" reblogTitle = "Reblog"
reblogAction = status.reblog reblogRequest = Status.reblog(status)
reblogColor = tintColor reblogColor = tintColor
} }
let reblog = UIContextualAction(style: .normal, title: reblogTitle) { (action, view, completion) in let reblog = UIContextualAction(style: .normal, title: reblogTitle) { (action, view, completion) in
reblogAction { response in MastodonController.shared.client.run(reblogRequest, completion: { response in
DispatchQueue.main.async { DispatchQueue.main.async {
if case .success = response { if case .success = response {
completion(true) completion(true)
@ -320,7 +324,7 @@ extension StatusTableViewCell: TableViewSwipeActionProvider {
completion(false) completion(false)
} }
} }
} })
} }
reblog.image = StatusTableViewCell.reblogActionImage reblog.image = StatusTableViewCell.reblogActionImage
reblog.backgroundColor = reblogColor reblog.backgroundColor = reblogColor