forked from shadowfacts/Tusker
parent
b560bcd8dc
commit
fe72d8faec
|
@ -98,7 +98,6 @@
|
|||
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; };
|
||||
D6420AEE26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6420AEC26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift */; };
|
||||
D6420AEF26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6420AED26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.xib */; };
|
||||
D6434EB3215B1856001A919A /* XCBRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6434EB2215B1856001A919A /* XCBRequest.swift */; };
|
||||
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */; };
|
||||
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */; };
|
||||
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */; };
|
||||
|
@ -115,7 +114,6 @@
|
|||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
||||
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64F80E1215875CC00BEF393 /* XCBActionType.swift */; };
|
||||
D65234D325618EFA001AF9CF /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */; };
|
||||
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */; };
|
||||
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
||||
|
@ -143,16 +141,12 @@
|
|||
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */; };
|
||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */; };
|
||||
D674A50927F9128D00BA03AC /* Pachyderm in Frameworks */ = {isa = PBXBuildFile; productRef = D674A50827F9128D00BA03AC /* Pachyderm */; };
|
||||
D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A7B2157E01900721E32 /* XCBManager.swift */; };
|
||||
D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */; };
|
||||
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A812157E8FA00721E32 /* XCBSession.swift */; };
|
||||
D677284824ECBCB100C732D3 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D677284724ECBCB100C732D3 /* ComposeView.swift */; };
|
||||
D677284A24ECBDF400C732D3 /* ComposeCurrentAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = D677284924ECBDF400C732D3 /* ComposeCurrentAccount.swift */; };
|
||||
D677284C24ECBE9100C732D3 /* ComposeAvatarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D677284B24ECBE9100C732D3 /* ComposeAvatarImageView.swift */; };
|
||||
D677284E24ECC01D00C732D3 /* Draft.swift in Sources */ = {isa = PBXBuildFile; fileRef = D677284D24ECC01D00C732D3 /* Draft.swift */; };
|
||||
D67895BC24671E6D00D4CD9E /* PKDrawing+Render.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BB24671E6D00D4CD9E /* PKDrawing+Render.swift */; };
|
||||
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */; };
|
||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D679C09E215850EF00DA27FE /* XCBActions.swift */; };
|
||||
D67B506D250B291200FAECFB /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67B506C250B291200FAECFB /* BlurHashDecode.swift */; };
|
||||
D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */; };
|
||||
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */; };
|
||||
|
@ -445,7 +439,6 @@
|
|||
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTextAttachment.swift; sourceTree = "<group>"; };
|
||||
D6420AEC26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineDescriptionTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6420AED26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PublicTimelineDescriptionTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D6434EB2215B1856001A919A /* XCBRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBRequest.swift; sourceTree = "<group>"; };
|
||||
D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageExpandAnimationController.swift; sourceTree = "<group>"; };
|
||||
D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageShrinkAnimationController.swift; sourceTree = "<group>"; };
|
||||
D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageInteractionController.swift; sourceTree = "<group>"; };
|
||||
|
@ -462,7 +455,6 @@
|
|||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
||||
D64F80E1215875CC00BEF393 /* XCBActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActionType.swift; sourceTree = "<group>"; };
|
||||
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
||||
D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
||||
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
||||
|
@ -492,16 +484,12 @@
|
|||
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Equatable.swift"; sourceTree = "<group>"; };
|
||||
D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Traits.swift"; sourceTree = "<group>"; };
|
||||
D674A50727F910F300BA03AC /* Pachyderm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Pachyderm; sourceTree = "<group>"; };
|
||||
D6757A7B2157E01900721E32 /* XCBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBManager.swift; sourceTree = "<group>"; };
|
||||
D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBRequestSpec.swift; sourceTree = "<group>"; };
|
||||
D6757A812157E8FA00721E32 /* XCBSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBSession.swift; sourceTree = "<group>"; };
|
||||
D677284724ECBCB100C732D3 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; };
|
||||
D677284924ECBDF400C732D3 /* ComposeCurrentAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeCurrentAccount.swift; sourceTree = "<group>"; };
|
||||
D677284B24ECBE9100C732D3 /* ComposeAvatarImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAvatarImageView.swift; sourceTree = "<group>"; };
|
||||
D677284D24ECC01D00C732D3 /* Draft.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Draft.swift; sourceTree = "<group>"; };
|
||||
D67895BB24671E6D00D4CD9E /* PKDrawing+Render.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PKDrawing+Render.swift"; sourceTree = "<group>"; };
|
||||
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountAvatarView.swift; sourceTree = "<group>"; };
|
||||
D679C09E215850EF00DA27FE /* XCBActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActions.swift; sourceTree = "<group>"; };
|
||||
D67B506C250B291200FAECFB /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
||||
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastAccountSwitcherIndicatorView.swift; sourceTree = "<group>"; };
|
||||
D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeAccountDetailView.swift; sourceTree = "<group>"; };
|
||||
|
@ -1125,19 +1113,6 @@
|
|||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6757A7A2157E00100721E32 /* XCallbackURL */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D6757A7B2157E01900721E32 /* XCBManager.swift */,
|
||||
D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */,
|
||||
D6434EB2215B1856001A919A /* XCBRequest.swift */,
|
||||
D6757A812157E8FA00721E32 /* XCBSession.swift */,
|
||||
D64F80E1215875CC00BEF393 /* XCBActionType.swift */,
|
||||
D679C09E215850EF00DA27FE /* XCBActions.swift */,
|
||||
);
|
||||
path = XCallbackURL;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D67B506B250B28FF00FAECFB /* Vendor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1390,7 +1365,6 @@
|
|||
D62D241E217AA46B005076CC /* Shortcuts */,
|
||||
D67B506B250B28FF00FAECFB /* Vendor */,
|
||||
D6BED1722126661300F02DA0 /* Views */,
|
||||
D6757A7A2157E00100721E32 /* XCallbackURL */,
|
||||
);
|
||||
path = Tusker;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1751,7 +1725,6 @@
|
|||
D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */,
|
||||
D60E2F292442372B005F8713 /* AccountMO.swift in Sources */,
|
||||
D6412B0324AFF6A600F5412E /* TabBarScrollableViewController.swift in Sources */,
|
||||
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */,
|
||||
D6093FB725BE0CF3004811E6 /* TrendHistoryView.swift in Sources */,
|
||||
D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */,
|
||||
D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */,
|
||||
|
@ -1849,7 +1822,6 @@
|
|||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||
D6D3FDE024F41B8400FF50A5 /* ComposeContainerView.swift in Sources */,
|
||||
D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */,
|
||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
||||
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */,
|
||||
D627943223A5466600D38C68 /* SelectableTableViewCell.swift in Sources */,
|
||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||
|
@ -1890,7 +1862,6 @@
|
|||
D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */,
|
||||
D6B053AB23BD2F1400A066FA /* AssetCollectionViewCell.swift in Sources */,
|
||||
D622757A24EE21D900B82A16 /* ComposeAttachmentRow.swift in Sources */,
|
||||
D6434EB3215B1856001A919A /* XCBRequest.swift in Sources */,
|
||||
D6E4269D2532A3E100C02E1C /* FuzzyMatcher.swift in Sources */,
|
||||
D681E4D3246E2AFF0053414F /* MuteConversationActivity.swift in Sources */,
|
||||
D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */,
|
||||
|
@ -1924,7 +1895,6 @@
|
|||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
|
||||
D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */,
|
||||
D627FF76217E923E00CC0648 /* DraftsManager.swift in Sources */,
|
||||
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */,
|
||||
04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */,
|
||||
D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */,
|
||||
D6E77D0B286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift in Sources */,
|
||||
|
@ -1958,7 +1928,6 @@
|
|||
D6412B0524B0227D00F5412E /* ProfileViewController.swift in Sources */,
|
||||
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */,
|
||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */,
|
||||
D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */,
|
||||
D65234D325618EFA001AF9CF /* TimelineTableViewController.swift in Sources */,
|
||||
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */,
|
||||
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */,
|
||||
|
@ -1972,7 +1941,6 @@
|
|||
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */,
|
||||
D6E4267725327FB400C02E1C /* ComposeAutocompleteView.swift in Sources */,
|
||||
D6E77D09286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift in Sources */,
|
||||
D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */,
|
||||
D677284E24ECC01D00C732D3 /* Draft.swift in Sources */,
|
||||
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */,
|
||||
D6BC8748219738E1006163F1 /* EnhancedTableViewController.swift in Sources */,
|
||||
|
|
|
@ -59,9 +59,7 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
|
||||
let url = URLContexts.first!.url
|
||||
|
||||
if url.host == "x-callback-url" {
|
||||
_ = XCBManager.handle(url: url)
|
||||
} else if var components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||
if var components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||
let rootViewController = rootViewController {
|
||||
components.scheme = "https"
|
||||
let query = components.string!
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
//
|
||||
// XCBActionType.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/23/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum XCBActionType: String {
|
||||
// Statuses
|
||||
case showStatus
|
||||
case postStatus
|
||||
case getStatus
|
||||
case favoriteStatus
|
||||
case reblogStatus
|
||||
// Accounts
|
||||
case showAccount
|
||||
case getAccount
|
||||
case getCurrentUser
|
||||
case followUser
|
||||
// Search
|
||||
case search
|
||||
|
||||
var path: String {
|
||||
return "/\(rawValue)"
|
||||
}
|
||||
}
|
|
@ -1,354 +0,0 @@
|
|||
//
|
||||
// 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 mastodonController: MastodonController {
|
||||
let scene = UIApplication.shared.activeOrBackgroundScene!
|
||||
return scene.session.mastodonController!
|
||||
}
|
||||
|
||||
private static func getMainTabBarController() -> MainTabBarViewController {
|
||||
let scene = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first!
|
||||
let window = scene.windows.first { $0.isKeyWindow }!
|
||||
return window.rootViewController as! MainTabBarViewController
|
||||
}
|
||||
|
||||
private static func show(_ vc: UIViewController) {
|
||||
let tabBarController = getMainTabBarController()
|
||||
if tabBarController.presentedViewController != nil {
|
||||
tabBarController.presentedViewController?.dismiss(animated: false)
|
||||
}
|
||||
tabBarController.selectedViewController!.show(vc, sender: nil)
|
||||
}
|
||||
|
||||
private static func present(_ vc: UIViewController, animated: Bool = true) {
|
||||
getMainTabBarController().present(vc, animated: animated)
|
||||
}
|
||||
|
||||
private static func getStatus(from request: XCBRequest, session: XCBSession, completion: @escaping (Status) -> Void) {
|
||||
if let id = request.arguments["statusID"] {
|
||||
let request = Client.getStatus(id: id)
|
||||
mastodonController.run(request) { (response) in
|
||||
guard case let .success(status, _) = response else {
|
||||
session.complete(with: .error, additionalData: [
|
||||
"error": "Could not get status with ID \(id)"
|
||||
])
|
||||
return
|
||||
}
|
||||
completion(status)
|
||||
}
|
||||
} else if let searchQuery = request.arguments["statusURL"] {
|
||||
let request = Client.search(query: searchQuery)
|
||||
mastodonController.run(request) { (response) in
|
||||
if case let .success(results, _) = response,
|
||||
let status = results.statuses.first {
|
||||
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"] {
|
||||
let request = Client.getAccount(id: id)
|
||||
mastodonController.run(request) { (response) in
|
||||
guard case let .success(account, _) = response else {
|
||||
session.complete(with: .error, additionalData: [
|
||||
"error": "Could not get account with ID \(id)"
|
||||
])
|
||||
return
|
||||
}
|
||||
completion(account)
|
||||
}
|
||||
} else if let searchQuery = request.arguments["accountURL"] {
|
||||
let request = Client.search(query: searchQuery)
|
||||
mastodonController.run(request) { (response) in
|
||||
if case let .success(results, _) = response {
|
||||
if let account = results.accounts.first {
|
||||
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 = Client.searchForAccount(query: acct)
|
||||
mastodonController.run(request) { (response) in
|
||||
if case let .success(accounts, _) = response {
|
||||
if let account = accounts.first {
|
||||
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
|
||||
DispatchQueue.main.async {
|
||||
let vc = ConversationTableViewController(for: status.id, mastodonController: mastodonController)
|
||||
show(vc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = Client.createStatus(text: status, visibility: Preferences.shared.defaultPostVisibility)
|
||||
mastodonController.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 {
|
||||
// todo: use text param
|
||||
let draft = mastodonController.createDraft(mentioningAcct: mentioning)
|
||||
let compose = ComposeHostingController(draft: draft, mastodonController: mastodonController)
|
||||
// compose.xcbSession = session
|
||||
let vc = UINavigationController(rootViewController: compose)
|
||||
present(vc)
|
||||
}
|
||||
}
|
||||
|
||||
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($0) }, alertTitle: "Reblog status?", request, session, silent)
|
||||
}
|
||||
|
||||
static func statusAction(request: @escaping (String) -> Request<Status>, alertTitle: String, _ url: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
|
||||
func performAction(status: Status, completion: ((Status) -> Void)?) {
|
||||
mastodonController.run(request(status.id)) { (response) in
|
||||
if case let .success(status, _) = response {
|
||||
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 = ConversationTableViewController(for: status.id, mastodonController: mastodonController)
|
||||
DispatchQueue.main.async {
|
||||
show(vc)
|
||||
}
|
||||
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 {
|
||||
present(alertController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
DispatchQueue.main.async {
|
||||
let vc = ProfileViewController(accountID: account.id, mastodonController: mastodonController)
|
||||
show(vc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.run(request) { (response) in
|
||||
if case .success(_, _) = response {
|
||||
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 = ProfileViewController(accountID: account.id, mastodonController: mastodonController)
|
||||
DispatchQueue.main.async {
|
||||
show(vc)
|
||||
}
|
||||
// todo: update to use managed objects
|
||||
let alertController = UIAlertController(title: "Follow \(account.displayName)?", 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 {
|
||||
present(alertController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccount(from: request, session: session, completion: follow)
|
||||
}
|
||||
|
||||
// MARK: - Search
|
||||
|
||||
static func search(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
|
||||
let query = request.arguments["query"]!
|
||||
|
||||
let tabBarController = getMainTabBarController()
|
||||
if let navigationController = tabBarController.getTabController(tab: .explore) as? UINavigationController,
|
||||
let exploreController = navigationController.viewControllers.first as? ExploreViewController {
|
||||
|
||||
tabBarController.select(tab: .explore)
|
||||
navigationController.popToRootViewController(animated: false)
|
||||
|
||||
exploreController.loadViewIfNeeded()
|
||||
exploreController.searchController.isActive = true
|
||||
|
||||
exploreController.searchController.searchBar.text = query
|
||||
exploreController.resultsController.performSearch(query: query)
|
||||
} else {
|
||||
session.complete(with: .error)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// XCBManager.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/23/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XCBManager {
|
||||
|
||||
static var specs: [XCBRequestSpec] = [
|
||||
// Statuses
|
||||
XCBRequestSpec(type: .showStatus, arguments: ["statusID": true, "statusURL": true], canRunSilently: false, action: XCBActions.showStatus),
|
||||
XCBRequestSpec(type: .getStatus, arguments: ["statusID": true, "statusURL": true, "html": true], canRunSilently: false, action: XCBActions.getStatus),
|
||||
XCBRequestSpec(type: .postStatus, arguments: ["mentioning": true, "text": true], canRunSilently: true, action: XCBActions.postStatus),
|
||||
XCBRequestSpec(type: .favoriteStatus, arguments: ["statusID": true, "statusURL": true], canRunSilently: true, action: XCBActions.favoriteStatus),
|
||||
XCBRequestSpec(type: .reblogStatus, arguments: ["statusID": true, "statusURL": true], canRunSilently: true, action: XCBActions.reblogStatus),
|
||||
// Accounts
|
||||
XCBRequestSpec(type: .showAccount, arguments: ["accountID": true, "accountURL": true, "acct": true], canRunSilently: false, action: XCBActions.showAccount),
|
||||
XCBRequestSpec(type: .getAccount, arguments: ["accountID": true, "accountURL": true, "acct": true], canRunSilently: false, action: XCBActions.getAccount),
|
||||
XCBRequestSpec(type: .getCurrentUser, arguments: [:], canRunSilently: false, action: XCBActions.getCurrentUser),
|
||||
XCBRequestSpec(type: .followUser, arguments: ["accountID": true, "accountURL": true, "acct": true], canRunSilently: true, action: XCBActions.followUser),
|
||||
// Search
|
||||
XCBRequestSpec(type: .search, arguments: ["query": false], canRunSilently: false, action: XCBActions.search),
|
||||
]
|
||||
|
||||
static var currentSession: XCBSession?
|
||||
|
||||
static func handle(url: URL) -> Bool {
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false }
|
||||
if let spec = specs.first(where: { $0.matches(components) }) {
|
||||
let request = XCBRequest(spec: spec, components: components)
|
||||
return spec.handle(request: request)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static func createSession(type: XCBActionType, request: XCBRequest) -> XCBSession {
|
||||
let session = XCBSession(type: type, request: request)
|
||||
currentSession = session
|
||||
return session
|
||||
}
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
//
|
||||
// XCBRequest.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/25/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct XCBRequest {
|
||||
let path: String
|
||||
let arguments: [String: String]
|
||||
let json: Bool
|
||||
let silent: Bool
|
||||
let source: String?
|
||||
let success: URL?
|
||||
let error: URL?
|
||||
let cancel: URL?
|
||||
|
||||
init(spec: XCBRequestSpec, components: URLComponents) {
|
||||
self.path = spec.path
|
||||
if let queryItems = components.queryItems {
|
||||
self.arguments = spec.arguments.reduce(into: [String: String](), { (result, el) in
|
||||
if let value = queryItems.first(where: { $0.name == el.key })?.value {
|
||||
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) }
|
||||
} else {
|
||||
self.arguments = [:]
|
||||
source = nil
|
||||
success = nil
|
||||
error = nil
|
||||
cancel = nil
|
||||
}
|
||||
if let arg = arguments["json"] {
|
||||
json = Bool(arg) ?? false
|
||||
} else {
|
||||
json = false
|
||||
}
|
||||
if spec.canRunSilently, let arg = arguments["silent"] {
|
||||
silent = Bool(arg) ?? false
|
||||
} else {
|
||||
silent = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
//
|
||||
// XCallbackURL.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/23/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
typealias XCBAction = (_ url: XCBRequest, _ session: XCBSession, _ silent: Bool?) -> Void
|
||||
|
||||
struct XCBRequestSpec {
|
||||
|
||||
let path: String
|
||||
let type: XCBActionType
|
||||
let arguments: [String: Bool]
|
||||
let canRunSilently: Bool
|
||||
let action: XCBAction
|
||||
|
||||
init(type: XCBActionType, arguments: [String: Bool], canRunSilently: Bool, action: @escaping XCBAction) {
|
||||
self.path = type.path
|
||||
self.type = type
|
||||
self.canRunSilently = canRunSilently
|
||||
self.action = action
|
||||
var arguments = arguments
|
||||
if canRunSilently {
|
||||
arguments["silent"] = true
|
||||
}
|
||||
arguments["json"] = true
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
func handle(request: XCBRequest) -> Bool {
|
||||
let session = XCBManager.createSession(type: type, request: request)
|
||||
if canRunSilently && request.silent {
|
||||
if let source = request.source {
|
||||
let permission = Preferences.shared.silentActions[source] ?? .undecided
|
||||
switch permission {
|
||||
case .accepted:
|
||||
action(request, session, true)
|
||||
case .rejected:
|
||||
action(request, 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(request, session, true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Reject", style: .default, handler: { (_) in
|
||||
Preferences.shared.silentActions[source] = .rejected
|
||||
self.action(request, 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(request, session, nil)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XCBRequestSpec {
|
||||
func matches(_ components: URLComponents) -> Bool {
|
||||
guard path == components.path else { return false }
|
||||
for (name, optional) in arguments {
|
||||
if (!optional && components.queryItems?.first(where: { $0.name == name }) == nil) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
//
|
||||
// XCBSession.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/23/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XCBSession {
|
||||
static let encoder = JSONEncoder()
|
||||
|
||||
let type: XCBActionType
|
||||
let request: XCBRequest
|
||||
|
||||
init(type: XCBActionType, request: XCBRequest) {
|
||||
self.type = type
|
||||
self.request = request
|
||||
}
|
||||
|
||||
func complete(with result: XCBSessionResult, additionalData: [String: String?]? = nil) {
|
||||
guard var url = result == .success ? request.success : result == .error ? request.error : request.cancel else { return }
|
||||
XCBManager.currentSession = nil
|
||||
if let additionalData = additionalData {
|
||||
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
|
||||
components.queryItems = components.queryItems ?? []
|
||||
if request.json {
|
||||
let data = try! XCBSession.encoder.encode(additionalData)
|
||||
let response = String(data: data, encoding: .utf8)
|
||||
components.queryItems!.append(URLQueryItem(name: "response", value: response))
|
||||
} else {
|
||||
components.queryItems!.append(contentsOf: additionalData.map(URLQueryItem.init))
|
||||
}
|
||||
url = components.url!
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.open(url, options: [:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum XCBSessionResult {
|
||||
case success, error, cancel
|
||||
}
|
Loading…
Reference in New Issue