// // XCBActions.swift // Tusker // // Created by Shadowfacts on 9/23/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import Pachyderm import SwiftSoup struct XCBActions { // MARK: - Utils private static var router: AppRouter = { return (UIApplication.shared.delegate as! AppDelegate).router }() private static func getStatus(from request: XCBRequest, session: XCBSession, completion: @escaping (Status) -> Void) { if let id = request.arguments["statusID"] { MastodonCache.status(for: id) { (status) in if let status = status { completion(status) } else { session.complete(with: .error, additionalData: [ "error": "Could not get status with ID \(id)" ]) } } } else if let searchQuery = request.arguments["statusURL"] { let request = MastodonController.client.search(query: searchQuery) MastodonController.client.run(request) { (response) in if case let .success(results, _) = response, let status = results.statuses.first { MastodonCache.add(status: status) completion(status) } else { session.complete(with: .error, additionalData: [ "error": "Could not find status by searching '\(searchQuery)'" ]) } } } else { session.complete(with: .error, additionalData: [ "error": "No status provided. Specify either instance-local statusID or remote statusURL." ]) } } private static func getAccount(from request: XCBRequest, session: XCBSession, completion: @escaping (Account) -> Void) { if let id = request.arguments["accountID"] { MastodonCache.account(for: id) { (account) in if let account = account { completion(account) } else { session.complete(with: .error, additionalData: [ "error": "Could not get account with ID \(id)" ]) } } } else if let searchQuery = request.arguments["accountURL"] { let request = MastodonController.client.search(query: searchQuery) MastodonController.client.run(request) { (response) in if case let .success(results, _) = response { if let account = results.accounts.first { MastodonCache.add(account: account) completion(account) } else { session.complete(with: .error, additionalData: [ "error": "Could not find account by searching '\(searchQuery)'" ]) } } else if case let .failure(error) = response { session.complete(with: .error, additionalData: [ "error": error.localizedDescription ]) } } } else if let acct = request.arguments["acct"] { let request = MastodonController.client.searchForAccount(query: acct) MastodonController.client.run(request) { (response) in if case let .success(accounts, _) = response { if let account = accounts.first { MastodonCache.add(account: account) completion(account) } else { session.complete(with: .error, additionalData: [ "error": "Could not find account \(acct)" ]) } } else if case let .failure(error) = response { session.complete(with: .error, additionalData: [ "error": error.localizedDescription ]) } } } else { session.complete(with: .error, additionalData: [ "error": "No status provided. Specify either instance-local ID, account URL, or qualified username." ]) } } // MARK: - Statuses static func showStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { getStatus(from: request, session: session) { (status) in let vc = router.conversation(for: status.id) DispatchQueue.main.async { router.push(vc, animated: true) } } } static func postStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { let mentioning = request.arguments["mentioning"] let text = request.arguments["text"] if silent ?? false { var status = "" if let mentioning = mentioning { status += mentioning } if let text = text { status += text } guard CharacterCounter.count(text: status) <= MastodonController.instance.maxStatusCharacters ?? 500 else { session.complete(with: .error, additionalData: [ "error": "Too many characters. Instance maximum is \(MastodonController.instance.maxStatusCharacters ?? 500)" ]) return } let request = MastodonController.client.createStatus(text: status, visibility: Preferences.shared.defaultPostVisibility) MastodonController.client.run(request) { response in if case let .success(status, _) = response { session.complete(with: .success, additionalData: [ "statusURL": status.url?.absoluteString, "statusURI": status.uri ]) } else if case let .failure(error) = response { session.complete(with: .error, additionalData: [ "error": error.localizedDescription ]) } } } else { let compose = router.compose(mentioningAcct: mentioning, text: text) compose.xcbSession = session let vc = UINavigationController(rootViewController: compose) router.present(vc, animated: true) } } static func getStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { getStatus(from: request, session: session) { (status) in let html = Bool(request.arguments["html"] ?? "false") ?? false let content: String if html { content = status.content } else { do { let doc = try SwiftSoup.parse(status.content) content = try doc.body()!.text() } catch { session.complete(with: .error, additionalData: [ "error": error.localizedDescription ]) return } } session.complete(with: .success, additionalData: [ "url": status.url?.absoluteString, "uri": status.uri, "id": status.id, "account": status.account.acct, "inReplyTo": status.inReplyToID, "posted": status.createdAt.timeIntervalSince1970.description, "content": content, "reblog": status.reblog?.id ]) } } static func favoriteStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { statusAction(request: Status.favourite, alertTitle: "Favorite status?", request, session, silent) } static func reblogStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { statusAction(request: Status.reblog, alertTitle: "Reblog status?", request, session, silent) } static func statusAction(request: @escaping (Status) -> Request, alertTitle: String, _ url: XCBRequest, _ session: XCBSession, _ silent: Bool?) { func performAction(status: Status, completion: ((Status) -> Void)?) { MastodonController.client.run(request(status)) { (response) in if case let .success(status, _) = response { MastodonCache.add(status: status) completion?(status) session.complete(with: .success, additionalData: [ "statusURL": status.url?.absoluteString, "statusURI": status.uri ]) } else if case let .failure(error) = response { session.complete(with: .error, additionalData: [ "error": error.localizedDescription ]) } } } func favorite(_ status: Status) { if silent ?? false { performAction(status: status, completion: nil) } else { let vc = router.conversation(for: status.id) DispatchQueue.main.async { router.push(vc, animated: true) } let alertController = UIAlertController(title: alertTitle, message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in performAction(status: status, completion: { (status) in DispatchQueue.main.async { vc.tableView.reloadData() } }) })) alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in session.complete(with: .cancel) })) DispatchQueue.main.async { router.present(alertController, animated: true) } } } getStatus(from: url, session: session, completion: favorite) } // MARK: - Accounts static func showAccount(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { getAccount(from: request, session: session) { (account) in let vc = router.profile(for: account.id) DispatchQueue.main.async { router.push(vc, animated: true) } } } static func getAccount(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { getAccount(from: request, session: session) { (account) in session.complete(with: .success, additionalData: [ "username": account.acct, "displayName": account.displayName, "locked": account.locked.description, "followers": account.followersCount.description, "following": account.followingCount.description, "url": account.url.absoluteString, "avatarURL": account.avatar.absoluteString, "headerURL": account.header.absoluteString ]) } } static func getCurrentUser(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { let account = MastodonController.account! session.complete(with: .success, additionalData: [ "username": account.acct, "displayName": account.displayName, "locked": account.locked.description, "followers": account.followersCount.description, "following": account.followingCount.description, "url": account.url.absoluteString, "avatarURL": account.avatar.absoluteString, "headerURL": account.header.absoluteString ]) } static func followUser(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) { func performAction(_ account: Account) { let request = Account.follow(account.id) MastodonController.client.run(request) { (response) in if case let .success(relationship, _) = response { MastodonCache.add(relationship: relationship) session.complete(with: .success, additionalData: [ "url": account.url.absoluteString ]) } else if case let .failure(error) = response { session.complete(with: .error, additionalData: [ "error": error.localizedDescription ]) } } } func follow(_ account: Account) { if silent ?? false { performAction(account) } else { let vc = router.profile(for: account.id) DispatchQueue.main.async { router.push(vc, animated: true) } let alertController = UIAlertController(title: "Follow \(account.realDisplayName)?", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in performAction(account) })) alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in session.complete(with: .cancel) })) DispatchQueue.main.async { router.present(alertController, animated: true) } } } getAccount(from: request, session: session, completion: follow) } }