diff --git a/Pachyderm/Client.swift b/Pachyderm/Client.swift index 502ede8b..cb9e04f1 100644 --- a/Pachyderm/Client.swift +++ b/Pachyderm/Client.swift @@ -286,14 +286,14 @@ public class Client { media: [Attachment]? = nil, sensitive: Bool? = nil, spoilerText: String? = nil, - visiblity: Status.Visibility? = nil, + visibility: Status.Visibility? = nil, language: String? = nil) -> Request { return Request(method: .post, path: "/api/v1/statuses", body: .parameters([ "status" => text, "in_reply_to_id" => inReplyTo, "sensitive" => sensitive, "spoiler_text" => spoilerText, - "visibility" => visiblity?.rawValue, + "visibility" => visibility?.rawValue, "language" => language ] + "media_ids" => media?.map { $0.id })) } diff --git a/Tusker/Screens/Compose/ComposeViewController.swift b/Tusker/Screens/Compose/ComposeViewController.swift index 0abb4b2d..d1870001 100644 --- a/Tusker/Screens/Compose/ComposeViewController.swift +++ b/Tusker/Screens/Compose/ComposeViewController.swift @@ -244,7 +244,7 @@ class ComposeViewController: UIViewController { media: attachments, sensitive: sensitive, spoilerText: contentWarning, - visiblity: visibility) + visibility: visibility) MastodonController.shared.client.run(request) { response in guard case let .success(status, _) = response else { fatalError() } self.status = status diff --git a/Tusker/XCallbackURL/XCBActions.swift b/Tusker/XCallbackURL/XCBActions.swift index 15cb7399..30217e60 100644 --- a/Tusker/XCallbackURL/XCBActions.swift +++ b/Tusker/XCallbackURL/XCBActions.swift @@ -11,17 +11,15 @@ import UIKit struct XCBActions { // MARK: - Posts - static func postStatus(_ url: XCallbackURL) -> Bool { - let session = XCBManager.createSession(type: .postStatus, url: url) - + static func postStatus(_ url: XCallbackURL, _ session: XCBSession, _ silent: Bool?) { let mentioning = url.arguments["mentioning"] let text = url.arguments["text"] - func postStatusSilently() { + if silent ?? false { var status = "" if let mentioning = mentioning { status += mentioning } if let text = text { status += text } - let request = MastodonController.shared.client.createStatus(text: status) + let request = MastodonController.shared.client.createStatus(text: status, visibility: Preferences.shared.defaultPostVisibility) MastodonController.shared.client.run(request) { response in if case let .success(status, _) = response { session.complete(with: .success, additionalData: [ @@ -34,40 +32,9 @@ struct XCBActions { ]) } } - } - - func showComposeStatus() { + } else { let vc = ComposeViewController.create(for: session, mentioning: mentioning, text: text) UIApplication.shared.keyWindow!.rootViewController!.present(vc, animated: true) } - - let silent = Bool(url.arguments["silent"] ?? "false") ?? false - if silent { - if let source = url.source { - let permission = Preferences.shared.silentActions[source] ?? .undecided - switch permission { - case .accepted: - postStatusSilently() - case .rejected: - showComposeStatus() - case .undecided: - let alert = UIAlertController(title: "\(source) wants to perform actions silently", message: "Accepting will allow \(source) to perform actions without your confirmation, rejecting will always prompt for confirmation.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Accept", style: .default, handler: { (_) in - Preferences.shared.silentActions[source] = .accepted - postStatusSilently() - })) - alert.addAction(UIAlertAction(title: "Reject", style: .default, handler: { (_) in - Preferences.shared.silentActions[source] = .rejected - showComposeStatus() - })) - UIApplication.shared.keyWindow!.rootViewController!.present(alert, animated: true) - } - } else { - session.complete(with: .error, additionalData: ["error": "Cannot perform silent action without source app, x-source parameter must be specified"]) - } - } else { - showComposeStatus() - } - return true } } diff --git a/Tusker/XCallbackURL/XCBManager.swift b/Tusker/XCallbackURL/XCBManager.swift index 7bc1ce0a..373e7200 100644 --- a/Tusker/XCallbackURL/XCBManager.swift +++ b/Tusker/XCallbackURL/XCBManager.swift @@ -11,7 +11,7 @@ import UIKit class XCBManager { static var specs: [XCallbackURLSpec] = [ - XCallbackURLSpec(path: "/postStatus", arguments: ["mentioning": true, "text": true, "silent": true], handle: XCBActions.postStatus) + XCallbackURLSpec(path: "/postStatus", type: .postStatus, arguments: ["mentioning": true, "text": true], canRunSilently: true, action: XCBActions.postStatus) ] static var currentSession: XCBSession? @@ -20,7 +20,7 @@ class XCBManager { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false } if let spec = specs.first(where: { $0.matches(components) }) { let xcbURL = XCallbackURL(spec: spec, components: components) - return spec.handle(xcbURL) + return spec.handle(url: xcbURL) } return false } diff --git a/Tusker/XCallbackURL/XCallbackURL.swift b/Tusker/XCallbackURL/XCallbackURL.swift index 34d7b4bd..cb5cf1a6 100644 --- a/Tusker/XCallbackURL/XCallbackURL.swift +++ b/Tusker/XCallbackURL/XCallbackURL.swift @@ -6,18 +6,61 @@ // Copyright © 2018 Shadowfacts. All rights reserved. // -import Foundation +import UIKit + +typealias XCBAction = (_ url: XCallbackURL, _ session: XCBSession, _ silent: Bool?) -> Void struct XCallbackURLSpec { let path: String + let type: XCBSessionType let arguments: [String: Bool] - let handle: (XCallbackURL) -> Bool + let canRunSilently: Bool + let action: XCBAction - init(path: String, arguments: [String: Bool], handle: @escaping (XCallbackURL) -> Bool) { + init(path: String, type: XCBSessionType, arguments: [String: Bool], canRunSilently: Bool, action: @escaping XCBAction) { self.path = path + self.type = type + self.canRunSilently = canRunSilently + self.action = action + var arguments = arguments + if canRunSilently { + arguments["silent"] = true + } self.arguments = arguments - self.handle = handle + } + + func handle(url: XCallbackURL) -> Bool { + let session = XCBManager.createSession(type: type, url: url) + if canRunSilently && url.silent { + if let source = url.source { + let permission = Preferences.shared.silentActions[source] ?? .undecided + switch permission { + case .accepted: + action(url, session, true) + case .rejected: + action(url, session, false) + case .undecided: + let alert = UIAlertController(title: "\(source) wants to perform actions silently", message: "Accepting will allow \(source) to perform actions without your confirmation, rejecting will always prompt for confirmation.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Accept", style: .default, handler: { (_) in + Preferences.shared.silentActions[source] = .accepted + self.action(url, session, true) + })) + alert.addAction(UIAlertAction(title: "Reject", style: .default, handler: { (_) in + Preferences.shared.silentActions[source] = .rejected + self.action(url, session, false) + })) + UIApplication.shared.keyWindow!.rootViewController!.present(alert, animated: true) + } + } else { + session.complete(with: .error, additionalData: [ + "error": "Cannot perform silent action without source app, x-source parameter must be specified" + ]) + } + } else { + action(url, session, nil) + } + return true } } @@ -25,6 +68,7 @@ struct XCallbackURLSpec { struct XCallbackURL { let path: String let arguments: [String: String] + let silent: Bool let source: String? let success: URL? let error: URL? @@ -38,6 +82,11 @@ struct XCallbackURL { result[el.key] = value } }) + if spec.canRunSilently { + silent = Bool(arguments["silent"] ?? "false") ?? false + } else { + silent = false + } source = queryItems.first(where: { $0.name == "x-source" }).flatMap { $0.value } success = queryItems.first(where: { $0.name == "x-success" }).flatMap { $0.value }.flatMap { URL(string: $0) } error = queryItems.first(where: { $0.name == "x-error" }).flatMap { $0.value }.flatMap { URL(string: $0) } @@ -47,13 +96,8 @@ struct XCallbackURL { extension XCallbackURLSpec { func matches(_ components: URLComponents) -> Bool { - return path == components.path && arguments.matches(components) - } -} - -extension Dictionary where Key == String, Value == Bool { - func matches(_ components: URLComponents) -> Bool { - for (name, optional) in self { + guard path == components.path else { return false } + for (name, optional) in arguments { if (!optional && components.queryItems?.first(where: { $0.name == name }) == nil) { return false }