mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-01-24 14:58:07 +00:00
Refactor NSItemProvider handler
This commit is contained in:
parent
90a2a19bb1
commit
97798b2c35
3 changed files with 58 additions and 105 deletions
|
@ -7,97 +7,27 @@ import UniformTypeIdentifiers
|
||||||
|
|
||||||
extension StatusEditor {
|
extension StatusEditor {
|
||||||
@MainActor
|
@MainActor
|
||||||
enum UTTypeSupported: String, CaseIterable {
|
struct UTTypeSupported {
|
||||||
case url = "public.url"
|
let value: String
|
||||||
case text = "public.text"
|
|
||||||
case plaintext = "public.plain-text"
|
|
||||||
case image = "public.image"
|
|
||||||
case jpeg = "public.jpeg"
|
|
||||||
case png = "public.png"
|
|
||||||
case tiff = "public.tiff"
|
|
||||||
|
|
||||||
case video = "public.video"
|
|
||||||
case movie = "public.movie"
|
|
||||||
case mp4 = "public.mpeg-4"
|
|
||||||
case gif = "public.gif"
|
|
||||||
case gif2 = "com.compuserve.gif"
|
|
||||||
case quickTimeMovie = "com.apple.quicktime-movie"
|
|
||||||
case adobeRawImage = "com.adobe.raw-image"
|
|
||||||
|
|
||||||
case uiimage = "com.apple.uikit.image"
|
|
||||||
|
|
||||||
// Have to implement this manually here due to compiler not implicitly
|
|
||||||
// inserting `nonisolated`, which leads to a warning:
|
|
||||||
//
|
|
||||||
// Main actor-isolated static property 'allCases' cannot be used to
|
|
||||||
// satisfy nonisolated protocol requirement
|
|
||||||
//
|
|
||||||
public nonisolated static var allCases: [UTTypeSupported] {
|
|
||||||
[.url, .text, .plaintext, .image, .jpeg, .png, .tiff, .video,
|
|
||||||
.movie, .mp4, .gif, .gif2, .quickTimeMovie, .uiimage, .adobeRawImage]
|
|
||||||
}
|
|
||||||
|
|
||||||
static func types() -> [UTType] {
|
|
||||||
[.url, .text, .plainText, .image, .jpeg, .png, .tiff, .video, .mpeg4Movie, .gif, .movie, .quickTimeMovie]
|
|
||||||
}
|
|
||||||
|
|
||||||
var isVideo: Bool {
|
|
||||||
switch self {
|
|
||||||
case .video, .movie, .mp4, .quickTimeMovie:
|
|
||||||
true
|
|
||||||
default:
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isGif: Bool {
|
|
||||||
switch self {
|
|
||||||
case .gif, .gif2:
|
|
||||||
true
|
|
||||||
default:
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadItemContent(item: NSItemProvider) async throws -> Any? {
|
func loadItemContent(item: NSItemProvider) async throws -> Any? {
|
||||||
// Many warnings here about non-sendable type `[AnyHashable: Any]?` crossing
|
if let transferable = await getVideoTransferable(item: item) {
|
||||||
// actor boundaries. Many Radars have been filed.
|
|
||||||
if isVideo, let transferable = await getVideoTransferable(item: item) {
|
|
||||||
return transferable
|
return transferable
|
||||||
} else if isGif, let transferable = await getGifTransferable(item: item) {
|
} else if let transferable = await getGifTransferable(item: item) {
|
||||||
return transferable
|
return transferable
|
||||||
}
|
} else if let transferable = await getImageTansferable(item: item) {
|
||||||
let compressor = Compressor()
|
return transferable
|
||||||
let result = try await item.loadItem(forTypeIdentifier: rawValue)
|
} else {
|
||||||
if self == .jpeg || self == .png || self == .tiff || self == .image || self == .uiimage || self == .adobeRawImage {
|
let result = try await item.loadItem(forTypeIdentifier: value)
|
||||||
if let image = result as? UIImage,
|
if let url = result as? URL {
|
||||||
let compressedData = try? await compressor.compressImageForUpload(image),
|
return url.absoluteString
|
||||||
let compressedImage = UIImage(data: compressedData)
|
} else if let text = result as? String {
|
||||||
{
|
return text
|
||||||
return compressedImage
|
} else if let image = result as? UIImage {
|
||||||
} else if let imageURL = result as? URL,
|
|
||||||
let compressedData = await compressor.compressImageFrom(url: imageURL),
|
|
||||||
let image = UIImage(data: compressedData)
|
|
||||||
{
|
|
||||||
return image
|
|
||||||
} else if let data = result as? Data,
|
|
||||||
let image = UIImage(data: data)
|
|
||||||
{
|
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let transferable = await getImageTansferable(item: item) {
|
return nil
|
||||||
return transferable
|
|
||||||
}
|
|
||||||
if let url = result as? URL {
|
|
||||||
return url.absoluteString
|
|
||||||
} else if let text = result as? String {
|
|
||||||
return text
|
|
||||||
} else if let image = result as? UIImage {
|
|
||||||
return image
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getVideoTransferable(item: NSItemProvider) async -> MovieFileTranseferable? {
|
private func getVideoTransferable(item: NSItemProvider) async -> MovieFileTranseferable? {
|
||||||
|
@ -142,44 +72,65 @@ extension StatusEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusEditor {
|
extension StatusEditor {
|
||||||
struct MovieFileTranseferable: Transferable {
|
final class MovieFileTranseferable: Transferable {
|
||||||
let url: URL
|
let url: URL
|
||||||
|
|
||||||
|
init(url: URL) {
|
||||||
|
self.url = url
|
||||||
|
_ = url.startAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
static var transferRepresentation: some TransferRepresentation {
|
static var transferRepresentation: some TransferRepresentation {
|
||||||
FileRepresentation(contentType: .movie) { movie in
|
FileRepresentation(importedContentType: .movie) { receivedTransferrable in
|
||||||
SentTransferredFile(movie.url)
|
return MovieFileTranseferable(url: receivedTransferrable.localURL)
|
||||||
} importing: { received in
|
|
||||||
Self(url: received.localURL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GifFileTranseferable: Transferable {
|
final class GifFileTranseferable: Transferable {
|
||||||
let url: URL
|
let url: URL
|
||||||
|
|
||||||
|
init(url: URL) {
|
||||||
|
self.url = url
|
||||||
|
_ = url.startAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
var data: Data? {
|
var data: Data? {
|
||||||
try? Data(contentsOf: url)
|
try? Data(contentsOf: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var transferRepresentation: some TransferRepresentation {
|
static var transferRepresentation: some TransferRepresentation {
|
||||||
FileRepresentation(contentType: .gif) { gif in
|
FileRepresentation(importedContentType: .gif) { receivedTransferrable in
|
||||||
SentTransferredFile(gif.url)
|
return GifFileTranseferable(url: receivedTransferrable.localURL)
|
||||||
} importing: { received in
|
|
||||||
Self(url: received.localURL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension StatusEditor {
|
public extension StatusEditor {
|
||||||
struct ImageFileTranseferable: Transferable, Sendable {
|
final class ImageFileTranseferable: Transferable, Sendable {
|
||||||
public let url: URL
|
public let url: URL
|
||||||
|
|
||||||
|
init(url: URL) {
|
||||||
|
self.url = url
|
||||||
|
_ = url.startAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
public static var transferRepresentation: some TransferRepresentation {
|
public static var transferRepresentation: some TransferRepresentation {
|
||||||
FileRepresentation(contentType: .image) { image in
|
FileRepresentation(importedContentType: .image) { receivedTransferrable in
|
||||||
SentTransferredFile(image.url)
|
return ImageFileTranseferable(url: receivedTransferrable.localURL)
|
||||||
} importing: { received in
|
|
||||||
Self(url: received.localURL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,6 +138,9 @@ public extension StatusEditor {
|
||||||
|
|
||||||
public extension ReceivedTransferredFile {
|
public extension ReceivedTransferredFile {
|
||||||
var localURL: URL {
|
var localURL: URL {
|
||||||
|
if self.isOriginalFile {
|
||||||
|
return file
|
||||||
|
}
|
||||||
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(self.file.pathExtension)")
|
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(self.file.pathExtension)")
|
||||||
try? FileManager.default.copyItem(at: self.file, to: copy)
|
try? FileManager.default.copyItem(at: self.file, to: copy)
|
||||||
return copy
|
return copy
|
||||||
|
|
|
@ -125,7 +125,7 @@ extension StatusEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDrop(of: StatusEditor.UTTypeSupported.types(), delegate: focusedSEVM)
|
.onDrop(of: [.image, .video, .gif], delegate: focusedSEVM)
|
||||||
.onChange(of: currentAccount.account?.id) {
|
.onChange(of: currentAccount.account?.id) {
|
||||||
mainSEVM.currentAccount = currentAccount.account
|
mainSEVM.currentAccount = currentAccount.account
|
||||||
for p in followUpSEVMs {
|
for p in followUpSEVMs {
|
||||||
|
|
|
@ -451,9 +451,8 @@ extension StatusEditor {
|
||||||
Task {
|
Task {
|
||||||
var initialText: String = ""
|
var initialText: String = ""
|
||||||
for item in items {
|
for item in items {
|
||||||
if let identifier = item.registeredTypeIdentifiers.first,
|
if let identifier = item.registeredTypeIdentifiers.first {
|
||||||
let handledItemType = UTTypeSupported(rawValue: identifier)
|
let handledItemType = UTTypeSupported(value: identifier)
|
||||||
{
|
|
||||||
do {
|
do {
|
||||||
let compressor = Compressor()
|
let compressor = Compressor()
|
||||||
let content = try await handledItemType.loadItemContent(item: item)
|
let content = try await handledItemType.loadItemContent(item: item)
|
||||||
|
@ -900,7 +899,7 @@ extension StatusEditor {
|
||||||
|
|
||||||
extension StatusEditor.ViewModel: DropDelegate {
|
extension StatusEditor.ViewModel: DropDelegate {
|
||||||
public func performDrop(info: DropInfo) -> Bool {
|
public func performDrop(info: DropInfo) -> Bool {
|
||||||
let item = info.itemProviders(for: StatusEditor.UTTypeSupported.types())
|
let item = info.itemProviders(for: [.image, .video, .gif])
|
||||||
processItemsProvider(items: item)
|
processItemsProvider(items: item)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue