diff --git a/MastodonSDK/Sources/MastodonUI/Vendor/ItemProviderLoader.swift b/MastodonSDK/Sources/MastodonUI/Vendor/ItemProviderLoader.swift index 6662f90e..ef0c36f1 100644 --- a/MastodonSDK/Sources/MastodonUI/Vendor/ItemProviderLoader.swift +++ b/MastodonSDK/Sources/MastodonUI/Vendor/ItemProviderLoader.swift @@ -55,6 +55,24 @@ extension ItemProviderLoader { ] as CFDictionary guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else { + // fallback to loadItem when create thumbnail failure + itemProvider.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { image, error in + if let error = error { + promise(.failure(error)) + } + + guard let image = image as? UIImage, + let data = image.jpegData(compressionQuality: 0.75) + else { + promise(.success(nil)) + assertionFailure() + return + } + + let file = Mastodon.Query.MediaAttachment.jpeg(data) + promise(.success(file)) + + } // end itemProvider.loadItem return } diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 21ee332d..9d51584c 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -26,6 +26,8 @@ NSExtensionActivationRule + NSExtensionActivationSupportsText + NSExtensionActivationSupportsWebURLWithMaxCount 1 NSExtensionActivationSupportsImageWithMaxCount diff --git a/ShareActionExtension/Scene/ShareViewModel.swift b/ShareActionExtension/Scene/ShareViewModel.swift index fe54e7e5..fbad8220 100644 --- a/ShareActionExtension/Scene/ShareViewModel.swift +++ b/ShareActionExtension/Scene/ShareViewModel.swift @@ -262,6 +262,10 @@ extension ShareViewModel { itemProviders.append(contentsOf: item.attachments ?? []) } + let _textProvider = itemProviders.first { provider in + return provider.hasRepresentationConforming(toTypeIdentifier: UTType.plainText.identifier, fileOptions: []) + } + let _urlProvider = itemProviders.first { provider in return provider.hasRepresentationConforming(toTypeIdentifier: UTType.url.identifier, fileOptions: []) } @@ -274,25 +278,51 @@ extension ShareViewModel { return provider.hasRepresentationConforming(toTypeIdentifier: UTType.image.identifier, fileOptions: []) } - if let urlProvider = _urlProvider { - urlProvider.loadItem(forTypeIdentifier: UTType.url.identifier) { [weak self] item, error in - guard let self = self else { return } - guard let url = item as? URL else { return } - DispatchQueue.main.async { - self.composeViewModel.statusContent = "\(url.absoluteString) " - } - } - } else if let movieProvider = _movieProvider { + Task { @MainActor in + async let text = ShareViewModel.loadText(textProvider: _textProvider) + async let url = ShareViewModel.loadURL(textProvider: _urlProvider) + + let content = await [text, url] + .compactMap { $0 } + .joined(separator: " ") + self.composeViewModel.statusContent = content + } + + if let movieProvider = _movieProvider { composeViewModel.setupAttachmentViewModels([ StatusAttachmentViewModel(itemProvider: movieProvider) ]) - } else { + } else if !imageProviders.isEmpty { let viewModels = imageProviders.map { provider in StatusAttachmentViewModel(itemProvider: provider) } composeViewModel.setupAttachmentViewModels(viewModels) } + } + + private static func loadText(textProvider: NSItemProvider?) async -> String? { + guard let textProvider = textProvider else { return nil } + do { + let item = try await textProvider.loadItem(forTypeIdentifier: UTType.plainText.identifier) + guard let text = item as? String else { return nil } + return text + } catch { + return nil + } + } + + private static func loadURL(textProvider: NSItemProvider?) async -> String? { + guard let textProvider = textProvider else { return nil } + do { + let item = try await textProvider.loadItem(forTypeIdentifier: UTType.url.identifier) + guard let url = item as? URL else { return nil } + return url.absoluteString + } catch { + return nil + } + } + } extension ShareViewModel {