Cleanup silent XCB actions code

This commit is contained in:
Shadowfacts 2018-09-23 21:10:45 -04:00
parent af0d0612ba
commit 68300f930b
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
5 changed files with 64 additions and 53 deletions

View File

@ -286,14 +286,14 @@ public class Client {
media: [Attachment]? = nil, media: [Attachment]? = nil,
sensitive: Bool? = nil, sensitive: Bool? = nil,
spoilerText: String? = nil, spoilerText: String? = nil,
visiblity: Status.Visibility? = nil, visibility: Status.Visibility? = nil,
language: String? = nil) -> Request<Status> { language: String? = nil) -> Request<Status> {
return Request<Status>(method: .post, path: "/api/v1/statuses", body: .parameters([ return Request<Status>(method: .post, path: "/api/v1/statuses", body: .parameters([
"status" => text, "status" => text,
"in_reply_to_id" => inReplyTo, "in_reply_to_id" => inReplyTo,
"sensitive" => sensitive, "sensitive" => sensitive,
"spoiler_text" => spoilerText, "spoiler_text" => spoilerText,
"visibility" => visiblity?.rawValue, "visibility" => visibility?.rawValue,
"language" => language "language" => language
] + "media_ids" => media?.map { $0.id })) ] + "media_ids" => media?.map { $0.id }))
} }

View File

@ -244,7 +244,7 @@ class ComposeViewController: UIViewController {
media: attachments, media: attachments,
sensitive: sensitive, sensitive: sensitive,
spoilerText: contentWarning, spoilerText: contentWarning,
visiblity: visibility) visibility: visibility)
MastodonController.shared.client.run(request) { response in MastodonController.shared.client.run(request) { response in
guard case let .success(status, _) = response else { fatalError() } guard case let .success(status, _) = response else { fatalError() }
self.status = status self.status = status

View File

@ -11,17 +11,15 @@ import UIKit
struct XCBActions { struct XCBActions {
// MARK: - Posts // MARK: - Posts
static func postStatus(_ url: XCallbackURL) -> Bool { static func postStatus(_ url: XCallbackURL, _ session: XCBSession, _ silent: Bool?) {
let session = XCBManager.createSession(type: .postStatus, url: url)
let mentioning = url.arguments["mentioning"] let mentioning = url.arguments["mentioning"]
let text = url.arguments["text"] let text = url.arguments["text"]
func postStatusSilently() { if silent ?? false {
var status = "" var status = ""
if let mentioning = mentioning { status += mentioning } if let mentioning = mentioning { status += mentioning }
if let text = text { status += text } 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 MastodonController.shared.client.run(request) { response in
if case let .success(status, _) = response { if case let .success(status, _) = response {
session.complete(with: .success, additionalData: [ session.complete(with: .success, additionalData: [
@ -34,40 +32,9 @@ struct XCBActions {
]) ])
} }
} }
} } else {
func showComposeStatus() {
let vc = ComposeViewController.create(for: session, mentioning: mentioning, text: text) let vc = ComposeViewController.create(for: session, mentioning: mentioning, text: text)
UIApplication.shared.keyWindow!.rootViewController!.present(vc, animated: true) 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
} }
} }

View File

@ -11,7 +11,7 @@ import UIKit
class XCBManager { class XCBManager {
static var specs: [XCallbackURLSpec] = [ 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? static var currentSession: XCBSession?
@ -20,7 +20,7 @@ class XCBManager {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false } guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false }
if let spec = specs.first(where: { $0.matches(components) }) { if let spec = specs.first(where: { $0.matches(components) }) {
let xcbURL = XCallbackURL(spec: spec, components: components) let xcbURL = XCallbackURL(spec: spec, components: components)
return spec.handle(xcbURL) return spec.handle(url: xcbURL)
} }
return false return false
} }

View File

@ -6,18 +6,61 @@
// Copyright © 2018 Shadowfacts. All rights reserved. // Copyright © 2018 Shadowfacts. All rights reserved.
// //
import Foundation import UIKit
typealias XCBAction = (_ url: XCallbackURL, _ session: XCBSession, _ silent: Bool?) -> Void
struct XCallbackURLSpec { struct XCallbackURLSpec {
let path: String let path: String
let type: XCBSessionType
let arguments: [String: Bool] 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.path = path
self.type = type
self.canRunSilently = canRunSilently
self.action = action
var arguments = arguments
if canRunSilently {
arguments["silent"] = true
}
self.arguments = arguments 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 { struct XCallbackURL {
let path: String let path: String
let arguments: [String: String] let arguments: [String: String]
let silent: Bool
let source: String? let source: String?
let success: URL? let success: URL?
let error: URL? let error: URL?
@ -38,6 +82,11 @@ struct XCallbackURL {
result[el.key] = value 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 } 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) } 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) } error = queryItems.first(where: { $0.name == "x-error" }).flatMap { $0.value }.flatMap { URL(string: $0) }
@ -47,13 +96,8 @@ struct XCallbackURL {
extension XCallbackURLSpec { extension XCallbackURLSpec {
func matches(_ components: URLComponents) -> Bool { func matches(_ components: URLComponents) -> Bool {
return path == components.path && arguments.matches(components) guard path == components.path else { return false }
} for (name, optional) in arguments {
}
extension Dictionary where Key == String, Value == Bool {
func matches(_ components: URLComponents) -> Bool {
for (name, optional) in self {
if (!optional && components.queryItems?.first(where: { $0.name == name }) == nil) { if (!optional && components.queryItems?.first(where: { $0.name == name }) == nil) {
return false return false
} }