diff --git a/frontend/src-tauri/gen/apple/Sharing/ShareViewController.swift b/frontend/src-tauri/gen/apple/Sharing/ShareViewController.swift index 28e93bd..68166ed 100644 --- a/frontend/src-tauri/gen/apple/Sharing/ShareViewController.swift +++ b/frontend/src-tauri/gen/apple/Sharing/ShareViewController.swift @@ -1,162 +1,243 @@ // +// ShareViewController.swift +// Haystack // -//  ShareViewController.swift -//  Haystack -// -//  Created by Rio Keefe on 03/05/2025. +// Created by Rio Keefe on 03/05/2025. // import UIKit import Social -import MobileCoreServices +import MobileCoreServices // For kUTTypeImage class ShareViewController: SLComposeServiceViewController { let appGroupName = "group.com.haystack.app" // Replace with your actual App Group identifier let tokenKey = "sharedAuthToken" - let uploadURL = URL(string: "https://haystack.johncosta.tech/image/")! + let uploadURLBase = URL(string: "https://haystack.johncosta.tech/image/")! // Base URL var bearerToken: String? - // Store the item provider to access it later in didSelectPost private var imageItemProvider: NSItemProvider? - private var extractedImageName: String = "image" // Default name + // Store a base name, extension will be determined during item loading + private var baseImageName: String = "SharedImage" // A more descriptive default override func viewDidLoad() { super.viewDidLoad() - // Load the bearer token from the App Group in viewDidLoad - // This is okay as reading from UserDefaults is fast if let sharedDefaults = UserDefaults(suiteName: appGroupName) { bearerToken = sharedDefaults.string(forKey: tokenKey) print("Retrieved bearer token: \(bearerToken ?? "nil")") } else { print("Error accessing App Group UserDefaults.") - // Optionally inform the user or disable posting if token is crucial - // self.isContentValid() could check if bearerToken is nil + // Invalidate content if token is crucial and missing + // This will be caught by isContentValid() } - // 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[..:") + self.baseImageName = self.baseImageName.components(separatedBy: anInvalidCharacters).joined(separator: "_") + if self.baseImageName.isEmpty { self.baseImageName = "SharedImage" } // Ensure not empty + + } else { + print("Attachment is not an image.") + self.imageItemProvider = nil // Ensure it's nil so isContentValid fails } } override func isContentValid() -> Bool { // Content is valid only if we have an item provider for an image AND a bearer token - return imageItemProvider != nil && bearerToken != nil + let isValid = imageItemProvider != nil && bearerToken != nil + if imageItemProvider == nil { + print("isContentValid: imageItemProvider is nil") + } + if bearerToken == nil { + print("isContentValid: bearerToken is nil") + } + return isValid } override func didSelectPost() { - // This method is called when the user taps the "Post" button. - // Start the asynchronous operation here. - guard let provider = imageItemProvider else { print("Error: No image item provider found when posting.") - // Inform the user or log an error - extensionContext!.completeRequest(returningItems: [], completionHandler: nil) + informUserAndCancel(message: "No image found to share.") return } guard let token = bearerToken else { - print("Error: Bearer token is missing when posting.") - // Inform the user or log an error - extensionContext!.completeRequest(returningItems: [], completionHandler: nil) - return + print("Error: Bearer token is missing when posting.") + informUserAndCancel(message: "Authentication error. Please try again.") + return } - // Load the image data asynchronously + // Start activity indicator or similar UI feedback + // For SLComposeServiceViewController, the system provides some UI + 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) + self.informUserAndCancel(message: "Could not load image: \(error.localizedDescription)") return } - var rawImageData: Data? - var finalImageName = self.extractedImageName // Use the name extracted earlier + var imageData: Data? + var finalImageNameWithExtension: String + var mimeType: String = "application/octet-stream" // Default MIME type - 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[..:") + currentBaseName = currentBaseName.components(separatedBy: anInvalidCharacters).joined(separator: "_") + if currentBaseName.isEmpty { currentBaseName = "DefaultImageName" } + } + + if let url = item as? URL { + print("Image provided as URL: \(url)") + finalImageNameWithExtension = url.lastPathComponent // Includes extension + // Ensure baseName is updated if URL provides a different one + if let dotRange = finalImageNameWithExtension.range(of:".", options: .backwards) { + currentBaseName = String(finalImageNameWithExtension[.. [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. + // No configuration items needed for this simple image uploader. return [] } + + // Helper to inform user and cancel request + private func informUserAndCancel(message: String) { + let error = NSError(domain: "com.haystack.ShareExtension", code: 0, userInfo: [NSLocalizedDescriptionKey: message]) + print("Informing user: \(message)") + // You could present an alert here if SLComposeServiceViewController allows easy alert presentation. + // For now, just cancel the request. The system might show a generic error. + self.extensionContext!.cancelRequest(withError: error) + } + + // Helper to get MIME type from path extension + private func mimeType(forPathExtension pathExtension: String) -> String { + let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue() + if let uti = uti { + let mimeType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() + if let mimeType = mimeType { + return mimeType as String + } + } + // Fallback for common types if UTType fails or for robustness + switch pathExtension.lowercased() { + case "jpg", "jpeg": return "image/jpeg" + case "png": return "image/png" + case "gif": return "image/gif" + case "bmp": return "image/bmp" + case "tiff", "tif": return "image/tiff" + default: return "application/octet-stream" // Generic fallback + } + } }