forked from shadowfacts/Tusker
Add silent x-callback-url actions
User confirmation is required the first time a source app attempts to run an action silently. Rejecting will always display the UI for the given action (as if the silent parameter had been false).
This commit is contained in:
parent
6c3ae9ab14
commit
af0d0612ba
|
@ -95,6 +95,7 @@
|
|||
D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A7B2157E01900721E32 /* XCBManager.swift */; };
|
||||
D6757A7E2157E02600721E32 /* XCallbackURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A7D2157E02600721E32 /* XCallbackURL.swift */; };
|
||||
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A812157E8FA00721E32 /* XCBSession.swift */; };
|
||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D679C09E215850EF00DA27FE /* XCBActions.swift */; };
|
||||
D6BED170212663DA00F02DA0 /* SwiftSoup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */; };
|
||||
D6C94D852139DFD800CB5196 /* LargeImage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6C94D842139DFD800CB5196 /* LargeImage.storyboard */; };
|
||||
|
@ -253,6 +254,7 @@
|
|||
D6757A7B2157E01900721E32 /* XCBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBManager.swift; sourceTree = "<group>"; };
|
||||
D6757A7D2157E02600721E32 /* XCallbackURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCallbackURL.swift; sourceTree = "<group>"; };
|
||||
D6757A812157E8FA00721E32 /* XCBSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBSession.swift; sourceTree = "<group>"; };
|
||||
D679C09E215850EF00DA27FE /* XCBActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActions.swift; sourceTree = "<group>"; };
|
||||
D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6C94D842139DFD800CB5196 /* LargeImage.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LargeImage.storyboard; sourceTree = "<group>"; };
|
||||
|
@ -578,6 +580,7 @@
|
|||
D6757A7B2157E01900721E32 /* XCBManager.swift */,
|
||||
D6757A812157E8FA00721E32 /* XCBSession.swift */,
|
||||
D6757A7D2157E02600721E32 /* XCallbackURL.swift */,
|
||||
D679C09E215850EF00DA27FE /* XCBActions.swift */,
|
||||
);
|
||||
path = XCallbackURL;
|
||||
sourceTree = "<group>";
|
||||
|
@ -951,6 +954,7 @@
|
|||
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */,
|
||||
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */,
|
||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
||||
|
|
|
@ -39,4 +39,12 @@ class Preferences: Codable {
|
|||
|
||||
var defaultPostVisibility = Status.Visibility.public
|
||||
|
||||
var silentActions: [String: Permission] = [:]
|
||||
|
||||
}
|
||||
|
||||
extension Preferences {
|
||||
enum Permission: String, Codable {
|
||||
case undecided, accepted, rejected
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ class ComposeViewController: UIViewController {
|
|||
self.performSegue(withIdentifier: "postComplete", sender: self)
|
||||
|
||||
self.xcbSession?.complete(with: .success, additionalData: [
|
||||
"statusURL": status.url!.absoluteString,
|
||||
"statusURL": status.url?.absoluteString,
|
||||
"statusURI": status.uri
|
||||
])
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// XCBActions.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/23/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
struct XCBActions {
|
||||
// MARK: - Posts
|
||||
|
||||
static func postStatus(_ url: XCallbackURL) -> Bool {
|
||||
let session = XCBManager.createSession(type: .postStatus, url: url)
|
||||
|
||||
let mentioning = url.arguments["mentioning"]
|
||||
let text = url.arguments["text"]
|
||||
|
||||
func postStatusSilently() {
|
||||
var status = ""
|
||||
if let mentioning = mentioning { status += mentioning }
|
||||
if let text = text { status += text }
|
||||
let request = MastodonController.shared.client.createStatus(text: status)
|
||||
MastodonController.shared.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
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showComposeStatus() {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import UIKit
|
|||
class XCBManager {
|
||||
|
||||
static var specs: [XCallbackURLSpec] = [
|
||||
XCallbackURLSpec(path: "/postStatus", arguments: ["mentioning": true, "text": true], handle: postStatus)
|
||||
XCallbackURLSpec(path: "/postStatus", arguments: ["mentioning": true, "text": true, "silent": true], handle: XCBActions.postStatus)
|
||||
]
|
||||
|
||||
static var currentSession: XCBSession?
|
||||
|
@ -25,30 +25,10 @@ class XCBManager {
|
|||
return false
|
||||
}
|
||||
|
||||
static func didComplete(session type: XCBSessionType, result: XCBSessionResult) {
|
||||
if let currentSession = currentSession,
|
||||
currentSession.type == type {
|
||||
currentSession.complete(with: result)
|
||||
self.currentSession = nil
|
||||
}
|
||||
}
|
||||
|
||||
private static func createSession(type: XCBSessionType, url: XCallbackURL) -> XCBSession {
|
||||
static func createSession(type: XCBSessionType, url: XCallbackURL) -> XCBSession {
|
||||
let session = XCBSession(type: type, success: url.success, error: url.error, cancel: url.cancel)
|
||||
currentSession = session
|
||||
return session
|
||||
}
|
||||
|
||||
static func postStatus(_ url: XCallbackURL) -> Bool {
|
||||
let mentioning = url.arguments["mentioning"]
|
||||
let text = url.arguments["text"]
|
||||
if let window = UIApplication.shared.keyWindow {
|
||||
let session = createSession(type: .postStatus, url: url)
|
||||
let vc = ComposeViewController.create(for: session, mentioning: mentioning, text: text)
|
||||
window.rootViewController?.present(vc, animated: true)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,15 +24,18 @@ class XCBSession {
|
|||
func complete(with result: XCBSessionResult, additionalData: [String: String?]? = nil) {
|
||||
let url = result == .success ? success : result == .error ? error : cancel
|
||||
if var url = url {
|
||||
XCBManager.currentSession = nil
|
||||
if let additionalData = additionalData {
|
||||
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
|
||||
components.queryItems = components.queryItems ?? []
|
||||
components.queryItems!.append(contentsOf: additionalData.map(URLQueryItem.init))
|
||||
url = components.url!
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.open(url, options: [:])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum XCBSessionType {
|
||||
|
|
|
@ -25,6 +25,7 @@ struct XCallbackURLSpec {
|
|||
struct XCallbackURL {
|
||||
let path: String
|
||||
let arguments: [String: String]
|
||||
let source: String?
|
||||
let success: URL?
|
||||
let error: URL?
|
||||
let cancel: URL?
|
||||
|
@ -37,6 +38,7 @@ struct XCallbackURL {
|
|||
result[el.key] = 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) }
|
||||
error = queryItems.first(where: { $0.name == "x-error" }).flatMap { $0.value }.flatMap { URL(string: $0) }
|
||||
cancel = queryItems.first(where: { $0.name == "x-cancel" }).flatMap { $0.value }.flatMap { URL(string: $0) }
|
||||
|
|
Loading…
Reference in New Issue