A WIP iOS app for Mastodon and Pleroma.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
14 KiB

//
// 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<Status>, 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)
}
}