// // ShareViewController.swift // ShareExtension // // Created by Shadowfacts on 4/17/23. // Copyright © 2023 Shadowfacts. All rights reserved. // import SwiftUI import UserAccounts import ComposeUI import UniformTypeIdentifiers import TuskerPreferences class ShareViewController: UIViewController { private var state: State = .loading required init?(coder: NSCoder) { super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() view.tintColor = Preferences.shared.accentColor.color if let account = UserAccountsManager.shared.getMostRecentAccount() { Task { @MainActor in let draft = await createDraft(account: account) state = .ok let context = ShareMastodonContext(accountInfo: account) let host = ShareHostingController(draft: draft, mastodonContext: context) let nav = UINavigationController(rootViewController: host) self.addChild(nav) nav.view.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(nav.view) NSLayoutConstraint.activate([ nav.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), nav.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), nav.view.topAnchor.constraint(equalTo: self.view.topAnchor), nav.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), ]) nav.didMove(toParent: self) } } else { state = .notLoggedIn } } private func createDraft(account: UserAccountInfo) async -> Draft { let (text, attachments) = await getDraftConfigurationFromExtensionContext() let draft = Draft( accountID: account.id, text: text, contentWarning: "", inReplyToID: nil, // TODO: get the default visibility from preferences visibility: .public, localOnly: false ) draft.attachments = attachments return draft } private func getDraftConfigurationFromExtensionContext() async -> (String, [DraftAttachment]) { guard let extensionContext, let inputItem = (extensionContext.inputItems as? [NSExtensionItem])?.first, let itemProvider = inputItem.attachments?.first else { return ("", []) } if let url: NSURL = await getObject(from: itemProvider) { if let title = inputItem.attributedTitle ?? inputItem.attributedContentText { return ("\n\n\(title.string)\n\(url.absoluteString ?? "")", []) } else { return ("\n\n\(url.absoluteString ?? "")", []) } } else if let text: NSString = await getObject(from: itemProvider) { return ("\n\n\(text)", []) } else if let attachment: DraftAttachment = await getObject(from: itemProvider) { return ("", [attachment]) } else if let attributedContent = inputItem.attributedContentText { return ("\n\n\(attributedContent.string)", []) } else { return ("", []) } } private func getObject(from itemProvider: NSItemProvider) async -> T? { guard itemProvider.canLoadObject(ofClass: T.self) else { return nil } return await withCheckedContinuation({ continuation in itemProvider.loadObject(ofClass: T.self) { object, error in continuation.resume(returning: object as? T) } }) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if case .notLoggedIn = state { let alert = UIAlertController(title: "Not Logged In", message: "You need to log in to an account through the app before you can post.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [unowned self] _ in self.extensionContext!.cancelRequest(withError: Error.notLoggedIn) })) present(alert, animated: true) } } enum State { case loading case notLoggedIn case ok } enum Error: Swift.Error { case notLoggedIn } }