Compare commits

..

No commits in common. "a9a9bfebebc83f880ea776bd039ba0bf622231f7" and "6f18d46037751a8a43b5fe3742b005f265b55438" have entirely different histories.

4 changed files with 65 additions and 109 deletions

View File

@ -8,8 +8,6 @@
<dict> <dict>
<key>NSExtensionActivationRule</key> <key>NSExtensionActivationRule</key>
<dict> <dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsImageWithMaxCount</key> <key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>4</integer> <integer>4</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key> <key>NSExtensionActivationSupportsMovieWithMaxCount</key>

View File

@ -71,43 +71,36 @@ class ShareViewController: UIViewController {
private func getDraftConfigurationFromExtensionContext() async -> (String, [DraftAttachment]) { private func getDraftConfigurationFromExtensionContext() async -> (String, [DraftAttachment]) {
guard let extensionContext, guard let extensionContext,
let inputItem = (extensionContext.inputItems as? [NSExtensionItem])?.first else { let inputItem = (extensionContext.inputItems as? [NSExtensionItem])?.first,
let itemProvider = inputItem.attachments?.first else {
return ("", []) return ("", [])
} }
var text: String = "" if let url: NSURL = await getObject(from: itemProvider) {
var url: URL? if let title = inputItem.attributedTitle ?? inputItem.attributedContentText {
var attachments: [DraftAttachment] = [] return ("\n\n\(title.string)\n\(url.absoluteString ?? "")", [])
} else {
for itemProvider in inputItem.attachments ?? [] { return ("\n\n\(url.absoluteString ?? "")", [])
if let attached: NSURL = await getObject(from: itemProvider) {
if url == nil {
url = attached as URL
}
} else if let s: NSString = await getObject(from: itemProvider) {
if text.isEmpty {
text = s as String
}
} else if let attachment: DraftAttachment = await getObject(from: itemProvider) {
attachments.append(attachment)
} }
} } else if let text: NSString = await getObject(from: itemProvider) {
return ("\n\n\(text)", [])
if text.isEmpty, } else if let attributedContent = inputItem.attributedContentText {
let s = inputItem.attributedTitle ?? inputItem.attributedContentText { return ("\n\n\(attributedContent.string)", [])
text = s.string } else {
} let attachments = await withTaskGroup(of: DraftAttachment?.self, returning: [DraftAttachment].self) { group in
for provider in inputItem.attachments! {
if let url { group.addTask { @MainActor in
if !text.isEmpty { await self.getObject(from: provider)
text += "\n" }
}
return await group.reduce(into: [], { partialResult, result in
if let result {
partialResult.append(result)
}
})
} }
text += url.absoluteString return ("", attachments)
} }
if !text.isEmpty {
text = "\n\n\(text)"
}
return (text, attachments)
} }
private func getObject<T: NSItemProviderReading>(from itemProvider: NSItemProvider) async -> T? { private func getObject<T: NSItemProviderReading>(from itemProvider: NSItemProvider) async -> T? {

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
struct ImageGrayscalifier { struct ImageGrayscalifier {
static let queue = DispatchQueue(label: "ImageGrayscalifier", qos: .userInitiated) static let queue = DispatchQueue(label: "ImageGrayscalifier", qos: .default)
private static let context = CIContext() private static let context = CIContext()
private static let cache = NSCache<NSURL, UIImage>() private static let cache = NSCache<NSURL, UIImage>()
@ -24,17 +24,6 @@ struct ImageGrayscalifier {
} }
} }
static func convert(url: URL?, image: UIImage) -> UIImage? {
if let url,
let cached = cache.object(forKey: url as NSURL) {
return cached
}
guard let cgImage = image.cgImage else {
return nil
}
return doConvert(CIImage(cgImage: cgImage), url: url)
}
static func convert(url: URL?, data: Data) -> UIImage? { static func convert(url: URL?, data: Data) -> UIImage? {
if let url = url, if let url = url,
let cached = cache.object(forKey: url as NSURL) { let cached = cache.object(forKey: url as NSURL) {

View File

@ -75,7 +75,9 @@ class AttachmentView: GIFImageView {
gifPlaybackModeChanged() gifPlaybackModeChanged()
if isGrayscale != Preferences.shared.grayscaleImages { if isGrayscale != Preferences.shared.grayscaleImages {
self.displayImage() ImageGrayscalifier.queue.async {
self.displayImage()
}
} }
if getBadges().isEmpty != Preferences.shared.showAttachmentBadges { if getBadges().isEmpty != Preferences.shared.showAttachmentBadges {
@ -187,57 +189,51 @@ class AttachmentView: GIFImageView {
func loadImage() { func loadImage() {
let attachmentURL = attachment.url let attachmentURL = attachment.url
attachmentRequest = ImageCache.attachments.get(attachmentURL) { [weak self] (data, image) in attachmentRequest = ImageCache.attachments.get(attachmentURL) { [weak self] (data, _) in
guard let self = self, guard let self = self, let data = data else { return }
self.attachment.url == attachmentURL else {
return
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.attachmentRequest = nil self.attachmentRequest = nil
}
if attachmentURL.pathExtension == "gif", if self.attachment.url.pathExtension == "gif" {
let data { self.source = .gifData(attachmentURL, data)
self.source = .gifData(attachmentURL, data, image) let controller = GIFController(gifData: data)
DispatchQueue.main.async {
controller.attach(to: self)
if self.autoplayGifs { if self.autoplayGifs {
let controller = GIFController(gifData: data)
controller.attach(to: self)
controller.startAnimating() controller.startAnimating()
} else {
self.displayImage()
} }
} else if let image { }
self.source = .image(attachmentURL, image)
if !self.autoplayGifs {
self.displayImage() self.displayImage()
} }
} else {
self.source = .imageData(attachmentURL, data)
self.displayImage()
} }
} }
} }
func loadVideo() { func loadVideo() {
if let previewURL = self.attachment.previewURL { if let previewURL = self.attachment.previewURL {
attachmentRequest = ImageCache.attachments.get(previewURL, completion: { [weak self] (_, image) in attachmentRequest = ImageCache.attachments.get(previewURL, completion: { [weak self] (data, _ )in
guard let self, let image else { return } guard let self = self, let data = data else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
self.attachmentRequest = nil self.attachmentRequest = nil
self.source = .image(previewURL, image) self.source = .imageData(previewURL, data)
self.displayImage() self.displayImage()
} }
}) })
} else { } else {
let attachmentURL = self.attachment.url let attachmentURL = self.attachment.url
AttachmentView.queue.async { [weak self] in AttachmentView.queue.async {
let asset = AVURLAsset(url: attachmentURL) let asset = AVURLAsset(url: attachmentURL)
let generator = AVAssetImageGenerator(asset: asset) let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true generator.appliesPreferredTrackTransform = true
guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return } guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return }
UIImage(cgImage: image).prepareForDisplay { [weak self] image in self.source = .cgImage(attachmentURL, image)
DispatchQueue.main.async { [weak self] in self.displayImage()
guard let self, let image else { return }
self.source = .image(attachmentURL, image)
self.displayImage()
}
}
} }
} }
@ -280,10 +276,8 @@ class AttachmentView: GIFImageView {
let generator = AVAssetImageGenerator(asset: asset) let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true generator.appliesPreferredTrackTransform = true
guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return } guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return }
DispatchQueue.main.async { self.source = .cgImage(attachmentURL, image)
self.source = .cgImage(attachmentURL, image) self.displayImage()
self.displayImage()
}
} }
let gifvView = GifvAttachmentView(asset: asset, gravity: .resizeAspectFill) let gifvView = GifvAttachmentView(asset: asset, gravity: .resizeAspectFill)
@ -301,51 +295,33 @@ class AttachmentView: GIFImageView {
]) ])
} }
@MainActor
private func displayImage() { private func displayImage() {
isGrayscale = Preferences.shared.grayscaleImages isGrayscale = Preferences.shared.grayscaleImages
let image: UIImage?
switch source { switch source {
case nil: case nil:
self.image = nil image = nil
case let .image(url, sourceImage): case let .imageData(url, data), let .gifData(url, data):
if isGrayscale { if isGrayscale {
ImageGrayscalifier.queue.async { [weak self] in image = ImageGrayscalifier.convert(url: url, data: data)
let grayscale = ImageGrayscalifier.convert(url: url, image: sourceImage)
DispatchQueue.main.async { [weak self] in
self?.image = grayscale
}
}
} else { } else {
self.image = sourceImage image = UIImage(data: data)
}
case let .gifData(url, _, sourceImage):
if isGrayscale,
let sourceImage {
ImageGrayscalifier.queue.async { [weak self] in
let grayscale = ImageGrayscalifier.convert(url: url, image: sourceImage)
DispatchQueue.main.async { [weak self] in
self?.image = grayscale
}
}
} else {
self.image = sourceImage
} }
case let .cgImage(url, cgImage): case let .cgImage(url, cgImage):
if isGrayscale { if isGrayscale {
ImageGrayscalifier.queue.async { [weak self] in image = ImageGrayscalifier.convert(url: url, cgImage: cgImage)
let grayscale = ImageGrayscalifier.convert(url: url, cgImage: cgImage)
DispatchQueue.main.async { [weak self] in
self?.image = grayscale
}
}
} else { } else {
image = UIImage(cgImage: cgImage) image = UIImage(cgImage: cgImage)
} }
} }
DispatchQueue.main.async {
self.image = image
}
} }
private func createBadgesView(_ badges: Badges) { private func createBadgesView(_ badges: Badges) {
@ -433,8 +409,8 @@ class AttachmentView: GIFImageView {
fileprivate extension AttachmentView { fileprivate extension AttachmentView {
enum Source { enum Source {
case image(URL, UIImage) case imageData(URL, Data)
case gifData(URL, Data, UIImage?) case gifData(URL, Data)
case cgImage(URL, CGImage) case cgImage(URL, CGImage)
} }