Compare commits
No commits in common. "a9a9bfebebc83f880ea776bd039ba0bf622231f7" and "6f18d46037751a8a43b5fe3742b005f265b55438" have entirely different histories.
a9a9bfebeb
...
6f18d46037
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
} else if let text: NSString = await getObject(from: itemProvider) {
|
||||||
if text.isEmpty {
|
return ("\n\n\(text)", [])
|
||||||
text = s as String
|
} else if let attributedContent = inputItem.attributedContentText {
|
||||||
}
|
return ("\n\n\(attributedContent.string)", [])
|
||||||
} else if let attachment: DraftAttachment = await getObject(from: itemProvider) {
|
} else {
|
||||||
attachments.append(attachment)
|
let attachments = await withTaskGroup(of: DraftAttachment?.self, returning: [DraftAttachment].self) { group in
|
||||||
|
for provider in inputItem.attachments! {
|
||||||
|
group.addTask { @MainActor in
|
||||||
|
await self.getObject(from: provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if text.isEmpty,
|
return await group.reduce(into: [], { partialResult, result in
|
||||||
let s = inputItem.attributedTitle ?? inputItem.attributedContentText {
|
if let result {
|
||||||
text = s.string
|
partialResult.append(result)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
if let url {
|
|
||||||
if !text.isEmpty {
|
|
||||||
text += "\n"
|
|
||||||
}
|
}
|
||||||
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? {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -75,8 +75,10 @@ class AttachmentView: GIFImageView {
|
||||||
gifPlaybackModeChanged()
|
gifPlaybackModeChanged()
|
||||||
|
|
||||||
if isGrayscale != Preferences.shared.grayscaleImages {
|
if isGrayscale != Preferences.shared.grayscaleImages {
|
||||||
|
ImageGrayscalifier.queue.async {
|
||||||
self.displayImage()
|
self.displayImage()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if getBadges().isEmpty != Preferences.shared.showAttachmentBadges {
|
if getBadges().isEmpty != Preferences.shared.showAttachmentBadges {
|
||||||
createBadgesView(getBadges())
|
createBadgesView(getBadges())
|
||||||
|
@ -187,59 +189,53 @@ 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)
|
|
||||||
if self.autoplayGifs {
|
|
||||||
let controller = GIFController(gifData: data)
|
let controller = GIFController(gifData: data)
|
||||||
|
DispatchQueue.main.async {
|
||||||
controller.attach(to: self)
|
controller.attach(to: self)
|
||||||
|
if self.autoplayGifs {
|
||||||
controller.startAnimating()
|
controller.startAnimating()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.autoplayGifs {
|
||||||
|
self.displayImage()
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
self.source = .imageData(attachmentURL, data)
|
||||||
self.displayImage()
|
self.displayImage()
|
||||||
}
|
}
|
||||||
} else if let image {
|
|
||||||
self.source = .image(attachmentURL, image)
|
|
||||||
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
|
|
||||||
guard let self, let image else { return }
|
|
||||||
self.source = .image(attachmentURL, image)
|
|
||||||
self.displayImage()
|
self.displayImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let playImageView = UIImageView(image: UIImage(systemName: "play.circle.fill"))
|
let playImageView = UIImageView(image: UIImage(systemName: "play.circle.fill"))
|
||||||
playImageView.translatesAutoresizingMaskIntoConstraints = false
|
playImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
@ -280,11 +276,9 @@ 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)
|
||||||
self.gifvView = gifvView
|
self.gifvView = gifvView
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue