// //  ShareViewController.swift //  Haystack // //  Created by Rio Keefe on 03/05/2025. // import UIKit import Social import MobileCoreServices class ShareViewController: SLComposeServiceViewController { let appGroupName = "group.com.haystack.app" // Replace with your actual App Group identifier let tokenKey = "sharedAuthToken" // This key holds the refresh token. let uploadURL = URL(string: "https://haystack.johncosta.tech/images/")! var refreshToken: String? private var imageItemProvider: NSItemProvider? private var extractedImageName: String = "image" // Default name override func viewDidLoad() { super.viewDidLoad() if let sharedDefaults = UserDefaults(suiteName: appGroupName) { refreshToken = sharedDefaults.string(forKey: tokenKey) print("Retrieved refresh token: \(refreshToken ?? "nil")") } else { print("Error accessing App Group UserDefaults.") } // Store the item provider, but don't load the data synchronously yet if let item = extensionContext?.inputItems.first as? NSExtensionItem, let provider = item.attachments?.first as? NSItemProvider { if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { self.imageItemProvider = provider // Attempt to get a suggested name early if available extractedImageName = provider.suggestedName ?? "image" if let dotRange = extractedImageName.range(of: ".", options: .backwards) { extractedImageName = String(extractedImageName[.. Bool { // Content is valid only if we have an item provider for an image AND a refresh token return imageItemProvider != nil && refreshToken != nil } override func didSelectPost() { refreshToken { accessToken in guard let token = accessToken else { // Inform the user about the authentication failure let error = NSError(domain: "ShareExtension", code: -1, userInfo: [NSLocalizedDescriptionKey: "Authentication failed. Please log in again."]) self.extensionContext!.cancelRequest(withError: error) return } guard let provider = self.imageItemProvider else { print("Error: No image item provider found when posting.") // Inform the user or log an error self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) return } // Load the image data asynchronously provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { [weak self] (item, error) in guard let self = self else { return } if let error = error { print("Error loading image data for upload: \(error.localizedDescription)") // Inform the user about the failure self.extensionContext!.cancelRequest(withError: error) return } var rawImageData: Data? var finalImageName = self.extractedImageName // Use the name extracted earlier if let url = item as? URL, let data = try? Data(contentsOf: url) { rawImageData = data // Refine the name extraction here if necessary, though doing it in viewDidLoad is also an option finalImageName = url.lastPathComponent if let dotRange = finalImageName.range(of: ".", options: .backwards) { finalImageName = String(finalImageName[.. Void) { guard let refreshToken = self.refreshToken else { completion(nil) return } let url = URL(string: "https://haystack.johncosta.tech/auth/refresh")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") let body = ["refresh": refreshToken] request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: []) let task = URLSession.shared.dataTask(with: request) { (data, response, error) in if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], let accessToken = json["access"] as? String { completion(accessToken) } else { completion(nil) } } task.resume() } override func configurationItems() -> [Any]! { // You can add items here if you want to allow the user to enter additional info // e.g., a text field for a caption. // This example only handles image upload, so no config items are needed. return [] } }